/** * Performs a batch insert into a specific table using either LOAD DATA INFILE or plain INSERTs, * as a fallback. On MySQL, LOAD DATA INFILE is 20x faster than a series of plain INSERTs. * * @param string $tableName PREFIXED table name! you must call Common::prefixTable() before passing the table name * @param array $fields array of unquoted field names * @param array $values array of data to be inserted * @param bool $throwException Whether to throw an exception that was caught while trying * LOAD DATA INFILE, or not. * @throws Exception * @return bool True if the bulk LOAD was used, false if we fallback to plain INSERTs */ public static function tableInsertBatch($tableName, $fields, $values, $throwException = false) { $filePath = StaticContainer::get('path.tmp') . '/assets/' . $tableName . '-' . Common::generateUniqId() . '.csv'; $loadDataInfileEnabled = Config::getInstance()->General['enable_load_data_infile']; if ($loadDataInfileEnabled && Db::get()->hasBulkLoader()) { try { $fileSpec = array('delim' => "\t", 'quote' => '"', 'escape' => '\\\\', 'escapespecial_cb' => function ($str) { return str_replace(array(chr(92), chr(34)), array(chr(92) . chr(92), chr(92) . chr(34)), $str); }, 'eol' => "\r\n", 'null' => 'NULL'); // hack for charset mismatch if (!DbHelper::isDatabaseConnectionUTF8() && !isset(Config::getInstance()->database['charset'])) { $fileSpec['charset'] = 'latin1'; } self::createCSVFile($filePath, $fileSpec, $values); if (!is_readable($filePath)) { throw new Exception("File {$filePath} could not be read."); } $rc = self::createTableFromCSVFile($tableName, $fields, $filePath, $fileSpec); if ($rc) { unlink($filePath); return true; } } catch (Exception $e) { if ($throwException) { throw $e; } } } // if all else fails, fallback to a series of INSERTs @unlink($filePath); self::tableInsertBatchIterate($tableName, $fields, $values); return false; }
private function createStorage($idSite = null) { if (!isset($idSite)) { $idSite = $this->idSite; } return new Storage(Db::get(), $idSite); }
/** * Performs a batch insert into a specific table using either LOAD DATA INFILE or plain INSERTs, * as a fallback. On MySQL, LOAD DATA INFILE is 20x faster than a series of plain INSERTs. * * @param string $tableName PREFIXED table name! you must call Common::prefixTable() before passing the table name * @param array $fields array of unquoted field names * @param array $values array of data to be inserted * @param bool $throwException Whether to throw an exception that was caught while trying * LOAD DATA INFILE, or not. * @param string $charset The charset to use, defaults to utf8 * @throws Exception * @return bool True if the bulk LOAD was used, false if we fallback to plain INSERTs */ public static function tableInsertBatch($tableName, $fields, $values, $throwException = false, $charset = 'utf8') { $loadDataInfileEnabled = Config::getInstance()->General['enable_load_data_infile']; if ($loadDataInfileEnabled && Db::get()->hasBulkLoader()) { $path = self::getBestPathForLoadData(); $filePath = $path . $tableName . '-' . Common::generateUniqId() . '.csv'; try { $fileSpec = array('delim' => "\t", 'quote' => '"', 'escape' => '\\\\', 'escapespecial_cb' => function ($str) { return str_replace(array(chr(92), chr(34)), array(chr(92) . chr(92), chr(92) . chr(34)), $str); }, 'eol' => "\r\n", 'null' => 'NULL', 'charset' => $charset); self::createCSVFile($filePath, $fileSpec, $values); if (!is_readable($filePath)) { throw new Exception("File {$filePath} could not be read."); } $rc = self::createTableFromCSVFile($tableName, $fields, $filePath, $fileSpec); if ($rc) { unlink($filePath); return true; } } catch (Exception $e) { if ($throwException) { throw $e; } } // if all else fails, fallback to a series of INSERTs if (file_exists($filePath)) { @unlink($filePath); } } self::tableInsertBatchIterate($tableName, $fields, $values); return false; }
public function install() { $queries[] = 'CREATE TABLE `' . Common::prefixTable('segment') . '` ( `idsegment` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, `definition` TEXT NOT NULL, `login` VARCHAR(100) NOT NULL, `enable_all_users` tinyint(4) NOT NULL default 0, `enable_only_idsite` INTEGER(11) NULL, `auto_archive` tinyint(4) NOT NULL default 0, `ts_created` TIMESTAMP NULL, `ts_last_edit` TIMESTAMP NULL, `deleted` tinyint(4) NOT NULL default 0, PRIMARY KEY (`idsegment`) ) DEFAULT CHARSET=utf8'; try { foreach ($queries as $query) { Db::exec($query); } } catch (Exception $e) { if (!Db::get()->isErrNo($e, '1050')) { throw $e; } } }
private static function migrateConfigSuperUserToDb() { $config = Config::getInstance(); if (!$config->existsLocalConfig()) { return; } try { $superUser = $config->superuser; } catch (\Exception $e) { $superUser = null; } if (!empty($superUser['bridge']) || empty($superUser) || empty($superUser['login'])) { // there is a super user which is not from the config but from the bridge, that means we already have // a super user in the database return; } $userApi = UsersManagerApi::getInstance(); try { Db::get()->insert(Common::prefixTable('user'), array('login' => $superUser['login'], 'password' => $superUser['password'], 'alias' => $superUser['login'], 'email' => $superUser['email'], 'token_auth' => $userApi->getTokenAuth($superUser['login'], $superUser['password']), 'date_registered' => Date::now()->getDatetime(), 'superuser_access' => 1)); } catch (\Exception $e) { echo "There was an issue, but we proceed: " . $e->getMessage(); } if (array_key_exists('salt', $superUser)) { $salt = $superUser['salt']; } else { $salt = Common::generateUniqId(); } $config->General['salt'] = $salt; $config->superuser = array(); $config->forceSave(); }
private function initDbIfNeeded() { if (!isset($this->db)) { // we need to avoid db creation on instance creation, especially important in tracker mode // the db might be never actually used when values are eg fetched from a cache $this->db = Db::get(); } }
/** * @param int $idSite The id of a site. If you want to get settings for a not yet created site just pass an empty value ("0") * @param string $idType If no typeId is given, the type of the site will be used. * * @throws \Exception */ public function __construct($idSite, $idType) { $this->idSite = $idSite; $this->idType = $idType; $this->storage = new Storage(Db::get(), $this->idSite); $this->pluginName = 'MeasurableSettings'; $this->init(); }
public function test_getUrlToCheckForLatestAvailableVersion() { $version = Version::VERSION; $phpVersion = urlencode(PHP_VERSION); $mysqlVersion = Db::get()->getServerVersion(); $url = urlencode(Url::getCurrentUrlWithoutQueryString()); $urlToCheck = $this->channel->getUrlToCheckForLatestAvailableVersion(); $this->assertStringStartsWith("http://api.piwik.org/1.0/getLatestVersion/?piwik_version={$version}&php_version={$phpVersion}&mysql_version={$mysqlVersion}&release_channel=my_channel&url={$url}&trigger=&timezone=", $urlToCheck); }
/** * Returns all stored segments that are available for the given site and login. * * @param string $userLogin * @param int $idSite Whether to return stored segments for a specific idSite, or all of them. If supplied, must be a valid site ID. * @return array */ public function getAllSegmentsForSite($idSite, $userLogin) { $bind = array($idSite, $userLogin); $sql = $this->buildQuerySortedByName('(enable_only_idsite = ? OR enable_only_idsite = 0) AND deleted = 0 AND (enable_all_users = 1 OR login = ?)'); $segments = Db::get()->fetchAll($sql, $bind); return $segments; }
public function onSiteDeleted($idSite) { // we do not delete logs here on purpose (you can run these queries on the log_ tables to delete all data) Cache::deleteCacheWebsiteAttributes($idSite); $archiveInvalidator = new ArchiveInvalidator(); $archiveInvalidator->forgetRememberedArchivedReportsToInvalidateForSite($idSite); $measurableStorage = new Storage(Db::get(), $idSite); $measurableStorage->deleteAllValues(); }
/** * Returns migrations to hash existing password with bcrypt. * @param Migration[] $queries * @return Migration[] */ private function getUserPasswordMigrations($queries) { $db = Db::get(); $userTable = Common::prefixTable($this->userTable); $users = $db->fetchAll('SELECT `login`, `password` FROM `' . $userTable . '` WHERE LENGTH(`password`) = 32'); foreach ($users as $user) { $queries[] = $this->migration->db->boundSql('UPDATE `' . $userTable . '`' . ' SET `password` = ?' . ' WHERE `login` = ?', [password_hash($user['password'], PASSWORD_BCRYPT), $user['login']]); } return $queries; }
/** * Installs the plugin. Derived classes should implement this class if the plugin * needs to: * * - create tables * - update existing tables * - etc. * * @throws \Exception if installation of fails for some reason. */ public function install() { try { DbHelper::createTable('bannerstats', "\n `label` varchar(100) not null,\n `content_name_id` int not null,\n `impression` int not null,\n `interaction` int not null,\n `referrer` varchar(200),\n `target` varchar(200),\n `date` date,\n `custom_var_v1`\tvarchar(200),\n `custom_var_v2`\tvarchar(200),\n `custom_var_v3`\tvarchar(200),\n `custom_var_v4`\tvarchar(200),\n `custom_var_v5`\tvarchar(200),\n\n UNIQUE KEY `unique_combination` (`date`, `label`, `content_name_id`, `referrer`, `target`)\n "); } catch (Exception $e) { // ignore error if table already exists (1050 code is for 'table already exists') if (!Db::get()->isErrNo($e, '1050')) { throw $e; } } }
/** * @dataProvider getDbAdapter */ public function test_SqlMode_IsSet_PDO($adapter, $expectedClass) { Db::destroyDatabaseObject(); Config::getInstance()->database['adapter'] = $adapter; $db = Db::get(); // make sure test is useful and setting adapter works $this->assertInstanceOf($expectedClass, $db); $result = $db->fetchOne('SELECT @@SESSION.sql_mode'); $expected = 'NO_AUTO_VALUE_ON_ZERO,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'; $this->assertSame($expected, $result); }
public function setUp() { parent::setUp(); self::updateDatabase(); // make sure site has an early enough creation date (for period selector tests) Db::get()->update(Common::prefixTable("site"), array('ts_created' => '2011-01-01'), "idsite = 1"); $this->addOverlayVisits(); $this->addNewSitesForSiteSelector(); DbHelper::createAnonymousUser(); UsersManagerAPI::getInstance()->setSuperUserAccess('superUserLogin', true); SitesManagerAPI::getInstance()->updateSite(1, null, null, true); }
public function getMySQLVersion() { if (isset($this->mySqlCache)) { return $this->mySqlCache; } $this->mySqlCache = ''; $db = Db::get(); if (method_exists($db, 'getServerVersion')) { $this->mySqlCache = $db->getServerVersion(); } return $this->mySqlCache; }
public function shouldIgnoreError($exception) { if (empty($this->errorCodesToIgnore)) { return false; } foreach ($this->errorCodesToIgnore as $code) { if (Db::get()->isErrNo($exception, $code)) { return true; } } return false; }
/** * Incremental version update */ public static function update() { foreach (self::getSql() as $sql => $errorCode) { try { Db::exec($sql); } catch (\Exception $e) { if (!Db::get()->isErrNo($e, '1091') && !Db::get()->isErrNo($e, '1060')) { PiwikUpdater::handleQueryError($e, $sql, false, __FILE__); } } } }
/** * @@depends testApi * @group Integration */ public function testCheckPostConditions() { $sql = "SELECT count(*) FROM " . Common::prefixTable('log_action'); $count = Db::get()->fetchOne($sql); $expected = 9; // 4 urls + 5 titles $this->assertEquals($expected, $count, "only {$expected} actions expected"); $sql = "SELECT name, url_prefix FROM " . Common::prefixTable('log_action') . " WHERE type = " . Action::TYPE_PAGE_URL . " ORDER BY idaction ASC"; $urls = Db::get()->fetchAll($sql); $expected = array(array('name' => 'example.org/foo/bar.html', 'url_prefix' => 0), array('name' => 'example.org/foo/bar2.html', 'url_prefix' => 3), array('name' => 'example.org/foo/bar3.html', 'url_prefix' => 1), array('name' => 'example.org/foo/bar4.html', 'url_prefix' => 2)); $this->assertEquals($expected, $urls, "normalization went wrong"); }
public function install() { // add column hostname / hostname ext in the visit table $query = "ALTER IGNORE TABLE `" . Common::prefixTable('log_visit') . "` ADD `location_provider` VARCHAR( 100 ) NULL"; // if the column already exist do not throw error. Could be installed twice... try { Db::exec($query); } catch (Exception $e) { if (!Db::get()->isErrNo($e, '1060')) { throw $e; } } }
public function setUp() { parent::setUp(); // make sure site has an early enough creation date (for period selector tests) Db::get()->update(Common::prefixTable("site"), array('ts_created' => '2011-01-01'), "idsite = 1"); $this->addOverlayVisits(); $this->addNewSitesForSiteSelector(); DbHelper::createAnonymousUser(); UsersManagerAPI::getInstance()->setSuperUserAccess('superUserLogin', true); Option::set("Tests.forcedNowTimestamp", $this->now->getTimestamp()); // launch archiving so tests don't run out of time $date = Date::factory($this->dateTime)->toString(); VisitsSummaryAPI::getInstance()->get($this->idSite, 'year', $date); VisitsSummaryAPI::getInstance()->get($this->idSite, 'year', $date, urlencode($this->segment)); }
/** * @param array $dimension * @param int $idSite * @param int $maxValuesToReturn * @return array */ public function getMostUsedActionDimensionValues($dimension, $idSite, $maxValuesToReturn) { $maxValuesToReturn = (int) $maxValuesToReturn; $idSite = (int) $idSite; $startDate = Date::now()->subDay(60)->toString(); $name = LogTable::buildCustomDimensionColumnName($dimension); $table = Common::prefixTable('log_link_visit_action'); $query = "SELECT {$name}, count({$name}) as countName FROM {$table}\n WHERE idsite = ? and server_time > {$startDate} and {$name} is not null\n GROUP by {$name}\n ORDER BY countName DESC LIMIT {$maxValuesToReturn}"; $rows = Db::get()->fetchAll($query, array($idSite)); $values = array(); foreach ($rows as $row) { $values[] = $row[$name]; } return $values; }
public function install() { try { $sql = "CREATE TABLE IF NOT EXISTS " . Common::prefixTable("snoopy") . " (\n id int(11) NOT NULL AUTO_INCREMENT,\n idvisitor varchar(45) DEFAULT NULL,\n score float DEFAULT NULL,\n updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n created_at datetime DEFAULT NULL,\n PRIMARY KEY (id),\n KEY idvisitor_idx (idvisitor),\n\t\t\t \t\t\tKEY id_idvisitor_idx (id,idvisitor)\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8 "; Db::exec($sql); $sql = "CREATE TABLE IF NOT EXISTS " . Common::prefixTable("snoopy_visitors") . " (\n id int(11) NOT NULL AUTO_INCREMENT,\n idvisitor varchar(45) DEFAULT NULL,\n custom_1 varchar(255) DEFAULT NULL,\n custom_2 varchar(255) DEFAULT NULL,\n custom_3 varchar(255) DEFAULT NULL,\n custom_4 TEXT DEFAULT NULL,\n custom_5 TEXT DEFAULT NULL,\n updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n created_at datetime DEFAULT NULL,\n PRIMARY KEY (id),\n UNIQUE KEY (idvisitor)\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8 "; Db::exec($sql); $sql = "CREATE TABLE IF NOT EXISTS " . Common::prefixTable("snoopy_visitors_statuses") . "(\n\t\t\t\t\t\tid int(11) NOT NULL AUTO_INCREMENT,\n\t\t\t\t\t\tidvisitor varchar(45) DEFAULT NULL,\n\t\t\t\t\t\tstatus varchar(45) DEFAULT NULL,\n\t\t\t\t\t\tPRIMARY KEY (id),\n\t\t\t\t\t\tUNIQUE KEY idvisitor_uniq (idvisitor)\n\t\t\t\t\t) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; Db::exec($sql); } catch (Exception $e) { // ignore error if table already exists (1050 code is for 'table already exists') if (!Db::get()->isErrNo($e, '1050')) { throw $e; } } }
public function test_getDbLock_shouldGetLock() { $db = Db::get(); $this->assertTrue(Db::getDbLock('MyLock')); // same session still has lock $this->assertTrue(Db::getDbLock('MyLock')); Db::setDatabaseObject(null); // different session, should not be able to acquire lock $this->assertFalse(Db::getDbLock('MyLock', 1)); // different session cannot release lock $this->assertFalse(Db::releaseDbLock('MyLock')); Db::destroyDatabaseObject(); // release lock again by using previous session Db::setDatabaseObject($db); $this->assertTrue(Db::releaseDbLock('MyLock')); Db::destroyDatabaseObject(); }
/** * @depends testApi * @group Integration */ public function testCheck() { // ---------------------------------------------- // Implementation Checks // ---------------------------------------------- // Verify that, when a segment is specified, only the requested report is processed // In this case, check that only the Custom Variables blobs have been processed $tests = array('archive_blob_2010_01' => 28, 'archive_numeric_2010_01' => 144, 'archive_blob_2009_12' => 28, 'archive_numeric_2009_12' => (7 + 2 + 3) * 2); foreach ($tests as $table => $expectedRows) { $sql = "SELECT count(*) FROM " . Common::prefixTable($table); $countBlobs = Db::get()->fetchOne($sql); if ($expectedRows != $countBlobs) { var_export(Db::get()->fetchAll("SELECT * FROM " . Common::prefixTable($table) . " ORDER BY name, idarchive ASC")); } $this->assertEquals($expectedRows, $countBlobs, "{$table}: %s"); } }
public function test_getServerVisitCustomVariables_shouldReturnSystemReport() { FakeAccess::clearAccess($superUser = true); for ($i = 1; $i < 15; $i++) { if (!Fixture::siteCreated($i)) { Fixture::createWebsite('2014-01-01 00:00:00'); } } for ($i = 1; $i < 9; $i++) { UsersApi::getInstance()->addUser($login = '******' . $i, 'password0815', "lorem{$i}@piwik.org"); } for ($i = 1; $i < 5; $i++) { SegmentApi::getInstance()->add('Segment' . $i, 'pageUrl%3D@inde'); } $customVars = array(array('id' => 1, 'name' => 'Piwik Version', 'value' => '2.14.3'), array('id' => 2, 'name' => 'PHP Version', 'value' => '5.5.27'), array('id' => 3, 'name' => 'Num Users', 'value' => 8), array('id' => 4, 'name' => 'Num Websites', 'value' => 14), array('id' => 5, 'name' => 'Num Segments', 'value' => 4), array('id' => 6, 'name' => 'MySQL Version', 'value' => Db::get()->getServerVersion())); $this->assertSame($customVars, $this->customVars->getServerVisitCustomVariables()); }
public static function getSql() { $sqls = array(); // update dashboard to use new ecommerce widgets, they were moved from goals plugin to ecommerce $oldWidgets = array(array('module' => 'Goals', 'action' => 'getEcommerceLog', 'params' => array()), array('module' => 'Goals', 'action' => 'widgetGoalReport', 'params' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER))); $newWidgets = array(array('module' => 'Ecommerce', 'action' => 'getEcommerceLog', 'params' => array()), array('module' => 'Ecommerce', 'action' => 'widgetGoalReport', 'params' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER))); $allDashboards = Db::get()->fetchAll(sprintf("SELECT * FROM %s", Common::prefixTable('user_dashboard'))); foreach ($allDashboards as $dashboard) { $dashboardLayout = json_decode($dashboard['layout']); $dashboardLayout = DashboardModel::replaceDashboardWidgets($dashboardLayout, $oldWidgets, $newWidgets); $newLayout = json_encode($dashboardLayout); if ($newLayout != $dashboard['layout']) { $sqls["UPDATE " . Common::prefixTable('user_dashboard') . " SET layout = '" . addslashes($newLayout) . "' WHERE iddashboard = " . $dashboard['iddashboard']] = false; } } return $sqls; }
public function testUsageOfCorrectMysqlAdapter() { $mysqlAdapter = getenv('MYSQL_ADAPTER'); if (empty($mysqlAdapter)) { return; } $this->assertTrue(in_array($mysqlAdapter, array('PDO_MYSQL', 'PDO\\MYSQL', 'MYSQLI'))); $db = Db::get(); switch ($mysqlAdapter) { case 'PDO_MYSQL': case 'PDO\\MYSQL': $this->assertInstanceOf('Piwik\\Db\\Adapter\\Pdo\\Mysql', $db); break; case 'MYSQLI': $this->assertInstanceOf('Piwik\\Db\\Adapter\\Mysqli', $db); break; } }
public function setUp() { self::downloadGeoIpDbs(); parent::setUp(); self::updateDatabase(); // make sure site has an early enough creation date (for period selector tests) Db::get()->update(Common::prefixTable("site"), array('ts_created' => '2011-01-01'), "idsite = 1"); // for proper geolocation LocationProvider::setCurrentProvider(LocationProvider\GeoIp\Php::ID); IPAnonymizer::deactivate(); $this->addOverlayVisits(); $this->addNewSitesForSiteSelector(); DbHelper::createAnonymousUser(); UsersManagerAPI::getInstance()->setSuperUserAccess('superUserLogin', true); SitesManagerAPI::getInstance()->updateSite(1, null, null, true); // create non super user UsersManagerAPI::getInstance()->addUser('oliverqueen', 'smartypants', '*****@*****.**'); UsersManagerAPI::getInstance()->setUserAccess('oliverqueen', 'view', array(1)); }
public function getMigrations(Updater $updater) { $migrations = array(); // update dashboard to use new ecommerce widgets, they were moved from goals plugin to ecommerce $oldWidgets = array(array('module' => 'Goals', 'action' => 'getEcommerceLog', 'params' => array()), array('module' => 'Goals', 'action' => 'widgetGoalReport', 'params' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER))); $newWidgets = array(array('module' => 'Ecommerce', 'action' => 'getEcommerceLog', 'params' => array()), array('module' => 'Ecommerce', 'action' => 'widgetGoalReport', 'params' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER))); $allDashboards = Db::get()->fetchAll(sprintf("SELECT * FROM %s", Common::prefixTable('user_dashboard'))); $sql = "UPDATE " . Common::prefixTable('user_dashboard') . " SET layout = ? WHERE iddashboard = ?"; foreach ($allDashboards as $dashboard) { $dashboardLayout = json_decode($dashboard['layout']); $dashboardLayout = DashboardModel::replaceDashboardWidgets($dashboardLayout, $oldWidgets, $newWidgets); $newLayout = json_encode($dashboardLayout); if ($newLayout != $dashboard['layout']) { $migrations[] = $this->migration->db->boundSql($sql, array($newLayout, $dashboard['iddashboard'])); } } $migrations[] = $this->migration->plugin->activate('Ecommerce'); return $migrations; }
private static function updateBrowserEngine($sql) { $sql[sprintf("ALTER TABLE `%s` ADD COLUMN `config_browser_engine` VARCHAR(10) NOT NULL", Common::prefixTable('log_visit'))] = 1060; $browserEngineMatch = array('Trident' => array('IE'), 'Gecko' => array('NS', 'PX', 'FF', 'FB', 'CA', 'GA', 'KM', 'MO', 'SM', 'CO', 'FE', 'KP', 'KZ', 'TB'), 'KHTML' => array('KO'), 'WebKit' => array('SF', 'CH', 'OW', 'AR', 'EP', 'FL', 'WO', 'AB', 'IR', 'CS', 'FD', 'HA', 'MI', 'GE', 'DF', 'BB', 'BP', 'TI', 'CF', 'RK', 'B2', 'NF'), 'Presto' => array('OP')); // Update visits, fill in now missing engine $engineUpdate = "''"; $ifFragment = "IF (`config_browser_name` IN ('%s'), '%s', %s)"; foreach ($browserEngineMatch as $engine => $browsers) { $engineUpdate = sprintf($ifFragment, implode("','", $browsers), $engine, $engineUpdate); } $engineUpdate = sprintf("UPDATE %s SET `config_browser_engine` = %s", Common::prefixTable('log_visit'), $engineUpdate); $sql[$engineUpdate] = false; $archiveBlobTables = Db::get()->fetchCol("SHOW TABLES LIKE '%archive_blob%'"); // for each blob archive table, rename UserSettings_browserType to DevicesDetection_browserEngines foreach ($archiveBlobTables as $table) { // try to rename old archives $sql[sprintf("UPDATE IGNORE %s SET name='DevicesDetection_browserEngines' WHERE name = 'UserSettings_browserType'", $table)] = false; } return $sql; }