migration = $factory; } public function getMigrations(Updater $updater) { $customVarType = 'VARCHAR(100) DEFAULT NULL'; $customVarColumns = array( 'custom_var_k1' => $customVarType, 'custom_var_v1' => $customVarType, 'custom_var_k2' => $customVarType, 'custom_var_v2' => $customVarType, 'custom_var_k3' => $customVarType, 'custom_var_v3' => $customVarType, 'custom_var_k4' => $customVarType, 'custom_var_v4' => $customVarType, 'custom_var_k5' => $customVarType, 'custom_var_v5' => $customVarType, ); return array( // Various performance improvements schema updates $this->migration->db->sql('ALTER TABLE `' . Common::prefixTable('log_visit') . '` DROP `visit_server_date`, DROP INDEX `index_idsite_date_config`, DROP INDEX `index_idsite_datetime_config`, ADD `idvisitor` BINARY(8) NOT NULL AFTER `idsite`, ADD `config_id` BINARY(8) NOT NULL AFTER `config_md5config` ', array(Updater\Migration\Db::ERROR_CODE_UNKNOWN_COLUMN, Updater\Migration\Db::ERROR_CODE_COLUMN_NOT_EXISTS)), $this->migration->db->sql('ALTER TABLE `' . Common::prefixTable('log_visit') . '` ADD `visit_entry_idaction_name` INT UNSIGNED NOT NULL AFTER `visit_entry_idaction_url`, ADD `visit_exit_idaction_name` INT UNSIGNED NOT NULL AFTER `visit_exit_idaction_url`, CHANGE `visit_exit_idaction_url` `visit_exit_idaction_url` INT UNSIGNED NOT NULL, CHANGE `visit_entry_idaction_url` `visit_entry_idaction_url` INT UNSIGNED NOT NULL, CHANGE `referer_type` `referer_type` TINYINT UNSIGNED NULL DEFAULT NULL, ADD visitor_count_visits SMALLINT(5) UNSIGNED NOT NULL AFTER `visitor_returning`, ADD visitor_days_since_last SMALLINT(5) UNSIGNED NOT NULL, ADD visitor_days_since_first SMALLINT(5) UNSIGNED NOT NULL ', Updater\Migration\Db::ERROR_CODE_DUPLICATE_COLUMN), $this->migration->db->addColumns('log_visit', $customVarColumns), $this->migration->db->addColumns('log_link_visit_action', array( 'idsite' => 'INT( 10 ) UNSIGNED NOT NULL', 'idvisitor' => 'BINARY(8) NOT NULL', 'idaction_name_ref' => 'INT UNSIGNED NOT NULL' ), 'idlink_va'), $this->migration->db->addColumn('log_link_visit_action', 'server_time', 'DATETIME', 'idsite'), $this->migration->db->addIndex('log_link_visit_action', array('idsite', 'server_time'), 'index_idsite_servertime'), $this->migration->db->sql('ALTER TABLE `' . Common::prefixTable('log_conversion') . '` DROP `referer_idvisit`, ADD `idvisitor` BINARY(8) NOT NULL AFTER `idsite` ', array(Updater\Migration\Db::ERROR_CODE_DUPLICATE_COLUMN, Updater\Migration\Db::ERROR_CODE_COLUMN_NOT_EXISTS)), $this->migration->db->addColumns('log_conversion', array( 'visitor_count_visits' => 'SMALLINT(5) UNSIGNED NOT NULL', 'visitor_days_since_first' => 'SMALLINT(5) UNSIGNED NOT NULL', )), $this->migration->db->addColumns('log_conversion', $customVarColumns), // Migrate 128bits IDs inefficiently stored as 8bytes (256 bits) into 64bits $this->migration->db->sql('UPDATE ' . Common::prefixTable('log_visit') . ' SET idvisitor = binary(unhex(substring(visitor_idcookie,1,16))), config_id = binary(unhex(substring(config_md5config,1,16))) ', Updater\Migration\Db::ERROR_CODE_UNKNOWN_COLUMN), $this->migration->db->sql('UPDATE ' . Common::prefixTable('log_conversion') . ' SET idvisitor = binary(unhex(substring(visitor_idcookie,1,16))) ', Updater\Migration\Db::ERROR_CODE_UNKNOWN_COLUMN), // Drop migrated fields $this->migration->db->sql('ALTER TABLE `' . Common::prefixTable('log_visit') . '` DROP visitor_idcookie, DROP config_md5config ', Updater\Migration\Db::ERROR_CODE_COLUMN_NOT_EXISTS), $this->migration->db->sql('ALTER TABLE `' . Common::prefixTable('log_conversion') . '` DROP visitor_idcookie ', Updater\Migration\Db::ERROR_CODE_COLUMN_NOT_EXISTS), // Recreate INDEX on new field $this->migration->db->addIndex('log_visit', array('idsite', 'visit_last_action_time', 'config_id'), 'index_idsite_datetime_config'), // Backfill action logs as best as we can $this->migration->db->sql('UPDATE ' . Common::prefixTable('log_link_visit_action') . ' as action, ' . Common::prefixTable('log_visit') . ' as visit SET action.idsite = visit.idsite, action.server_time = visit.visit_last_action_time, action.idvisitor = visit.idvisitor WHERE action.idvisit=visit.idvisit '), $this->migration->db->changeColumnType('log_link_visit_action', 'server_time', 'DATETIME NOT NULL'), // New index used max once per request, in case this table grows significantly in the future $this->migration->db->addIndex('option', 'autoload', 'autoload'), // new field for websites $this->migration->db->addColumn('site', 'group', 'VARCHAR( 250 ) NOT NULL'), ); } public function doUpdate(Updater $updater) { // first we disable the plugins and keep an array of warnings messages $pluginsToDisableMessage = array( 'GeoIP' => "GeoIP plugin was disabled, because it is not compatible with the new Piwik 1.2. \nYou can download the latest version of the plugin, compatible with Piwik 1.2.\nClick here.", 'EntryPage' => "EntryPage plugin is not compatible with this version of Matomo, it was disabled.", ); $disabledPlugins = array(); foreach ($pluginsToDisableMessage as $pluginToDisable => $warningMessage) { if (\Piwik\Plugin\Manager::getInstance()->isPluginActivated($pluginToDisable)) { \Piwik\Plugin\Manager::getInstance()->deactivatePlugin($pluginToDisable); $disabledPlugins[] = $warningMessage; } } // Run the SQL $updater->executeMigrations(__FILE__, $this->getMigrations($updater)); // Outputs warning message, pointing users to the plugin download page if (!empty($disabledPlugins)) { throw new \Exception("The following plugins were disabled during the upgrade:" . ""); } } }