Mysql.php
591 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Piwik - free/libre analytics platform |
| 4 | * |
| 5 | * @link https://matomo.org |
| 6 | * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later |
| 7 | * |
| 8 | */ |
| 9 | namespace Piwik\Db\Schema; |
| 10 | |
| 11 | use Exception; |
| 12 | use Piwik\Common; |
| 13 | use Piwik\Concurrency\Lock; |
| 14 | use Piwik\Date; |
| 15 | use Piwik\Db\SchemaInterface; |
| 16 | use Piwik\Db; |
| 17 | use Piwik\DbHelper; |
| 18 | use Piwik\Option; |
| 19 | use Piwik\Plugins\Installation\Installation; |
| 20 | use Piwik\Version; |
| 21 | |
| 22 | /** |
| 23 | * MySQL schema |
| 24 | */ |
| 25 | class Mysql implements SchemaInterface |
| 26 | { |
| 27 | const OPTION_NAME_MATOMO_INSTALL_VERSION = 'install_version'; |
| 28 | const MAX_TABLE_NAME_LENGTH = 64; |
| 29 | |
| 30 | private $tablesInstalled = null; |
| 31 | |
| 32 | /** |
| 33 | * Get the SQL to create Piwik tables |
| 34 | * |
| 35 | * @return array array of strings containing SQL |
| 36 | */ |
| 37 | public function getTablesCreateSql() |
| 38 | { |
| 39 | $engine = $this->getTableEngine(); |
| 40 | $prefixTables = $this->getTablePrefix(); |
| 41 | |
| 42 | $tables = array( |
| 43 | 'user' => "CREATE TABLE {$prefixTables}user ( |
| 44 | login VARCHAR(100) NOT NULL, |
| 45 | password VARCHAR(255) NOT NULL, |
| 46 | alias VARCHAR(45) NOT NULL, |
| 47 | email VARCHAR(100) NOT NULL, |
| 48 | twofactor_secret VARCHAR(40) NOT NULL DEFAULT '', |
| 49 | token_auth CHAR(32) NOT NULL, |
| 50 | superuser_access TINYINT(2) unsigned NOT NULL DEFAULT '0', |
| 51 | date_registered TIMESTAMP NULL, |
| 52 | ts_password_modified TIMESTAMP NULL, |
| 53 | PRIMARY KEY(login), |
| 54 | UNIQUE KEY uniq_keytoken(token_auth) |
| 55 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 56 | ", |
| 57 | |
| 58 | 'twofactor_recovery_code' => "CREATE TABLE {$prefixTables}twofactor_recovery_code ( |
| 59 | idrecoverycode BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, |
| 60 | login VARCHAR(100) NOT NULL, |
| 61 | recovery_code VARCHAR(40) NOT NULL, |
| 62 | PRIMARY KEY(idrecoverycode) |
| 63 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 64 | ", |
| 65 | |
| 66 | 'access' => "CREATE TABLE {$prefixTables}access ( |
| 67 | idaccess INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, |
| 68 | login VARCHAR(100) NOT NULL, |
| 69 | idsite INTEGER UNSIGNED NOT NULL, |
| 70 | access VARCHAR(50) NULL, |
| 71 | PRIMARY KEY(idaccess), |
| 72 | INDEX index_loginidsite (login, idsite) |
| 73 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 74 | ", |
| 75 | |
| 76 | 'site' => "CREATE TABLE {$prefixTables}site ( |
| 77 | idsite INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, |
| 78 | name VARCHAR(90) NOT NULL, |
| 79 | main_url VARCHAR(255) NOT NULL, |
| 80 | ts_created TIMESTAMP NULL, |
| 81 | ecommerce TINYINT DEFAULT 0, |
| 82 | sitesearch TINYINT DEFAULT 1, |
| 83 | sitesearch_keyword_parameters TEXT NOT NULL, |
| 84 | sitesearch_category_parameters TEXT NOT NULL, |
| 85 | timezone VARCHAR( 50 ) NOT NULL, |
| 86 | currency CHAR( 3 ) NOT NULL, |
| 87 | exclude_unknown_urls TINYINT(1) DEFAULT 0, |
| 88 | excluded_ips TEXT NOT NULL, |
| 89 | excluded_parameters TEXT NOT NULL, |
| 90 | excluded_user_agents TEXT NOT NULL, |
| 91 | `group` VARCHAR(250) NOT NULL, |
| 92 | `type` VARCHAR(255) NOT NULL, |
| 93 | keep_url_fragment TINYINT NOT NULL DEFAULT 0, |
| 94 | creator_login VARCHAR(100) NULL, |
| 95 | PRIMARY KEY(idsite) |
| 96 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 97 | ", |
| 98 | |
| 99 | 'plugin_setting' => "CREATE TABLE {$prefixTables}plugin_setting ( |
| 100 | `plugin_name` VARCHAR(60) NOT NULL, |
| 101 | `setting_name` VARCHAR(255) NOT NULL, |
| 102 | `setting_value` LONGTEXT NOT NULL, |
| 103 | `json_encoded` TINYINT UNSIGNED NOT NULL DEFAULT 0, |
| 104 | `user_login` VARCHAR(100) NOT NULL DEFAULT '', |
| 105 | `idplugin_setting` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, |
| 106 | PRIMARY KEY (idplugin_setting), |
| 107 | INDEX(plugin_name, user_login) |
| 108 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 109 | ", |
| 110 | |
| 111 | 'site_setting' => "CREATE TABLE {$prefixTables}site_setting ( |
| 112 | idsite INTEGER(10) UNSIGNED NOT NULL, |
| 113 | `plugin_name` VARCHAR(60) NOT NULL, |
| 114 | `setting_name` VARCHAR(255) NOT NULL, |
| 115 | `setting_value` LONGTEXT NOT NULL, |
| 116 | `json_encoded` TINYINT UNSIGNED NOT NULL DEFAULT 0, |
| 117 | `idsite_setting` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, |
| 118 | PRIMARY KEY (idsite_setting), |
| 119 | INDEX(idsite, plugin_name) |
| 120 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 121 | ", |
| 122 | |
| 123 | 'site_url' => "CREATE TABLE {$prefixTables}site_url ( |
| 124 | idsite INTEGER(10) UNSIGNED NOT NULL, |
| 125 | url VARCHAR(255) NOT NULL, |
| 126 | PRIMARY KEY(idsite, url) |
| 127 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 128 | ", |
| 129 | |
| 130 | 'goal' => "CREATE TABLE `{$prefixTables}goal` ( |
| 131 | `idsite` int(11) NOT NULL, |
| 132 | `idgoal` int(11) NOT NULL, |
| 133 | `name` varchar(50) NOT NULL, |
| 134 | `description` varchar(255) NOT NULL DEFAULT '', |
| 135 | `match_attribute` varchar(20) NOT NULL, |
| 136 | `pattern` varchar(255) NOT NULL, |
| 137 | `pattern_type` varchar(25) NOT NULL, |
| 138 | `case_sensitive` tinyint(4) NOT NULL, |
| 139 | `allow_multiple` tinyint(4) NOT NULL, |
| 140 | `revenue` float NOT NULL, |
| 141 | `deleted` tinyint(4) NOT NULL default '0', |
| 142 | `event_value_as_revenue` tinyint(4) NOT NULL default '0', |
| 143 | PRIMARY KEY (`idsite`,`idgoal`) |
| 144 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 145 | ", |
| 146 | |
| 147 | 'logger_message' => "CREATE TABLE {$prefixTables}logger_message ( |
| 148 | idlogger_message INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, |
| 149 | tag VARCHAR(50) NULL, |
| 150 | timestamp TIMESTAMP NULL, |
| 151 | level VARCHAR(16) NULL, |
| 152 | message TEXT NULL, |
| 153 | PRIMARY KEY(idlogger_message) |
| 154 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 155 | ", |
| 156 | |
| 157 | 'log_action' => "CREATE TABLE {$prefixTables}log_action ( |
| 158 | idaction INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, |
| 159 | name VARCHAR(4096), |
| 160 | hash INTEGER(10) UNSIGNED NOT NULL, |
| 161 | type TINYINT UNSIGNED NULL, |
| 162 | url_prefix TINYINT(2) NULL, |
| 163 | PRIMARY KEY(idaction), |
| 164 | INDEX index_type_hash (type, hash) |
| 165 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 166 | ", |
| 167 | |
| 168 | 'log_visit' => "CREATE TABLE {$prefixTables}log_visit ( |
| 169 | idvisit BIGINT(10) UNSIGNED NOT NULL AUTO_INCREMENT, |
| 170 | idsite INTEGER(10) UNSIGNED NOT NULL, |
| 171 | idvisitor BINARY(8) NOT NULL, |
| 172 | visit_last_action_time DATETIME NOT NULL, |
| 173 | config_id BINARY(8) NOT NULL, |
| 174 | location_ip VARBINARY(16) NOT NULL, |
| 175 | PRIMARY KEY(idvisit), |
| 176 | INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time), |
| 177 | INDEX index_idsite_datetime (idsite, visit_last_action_time), |
| 178 | INDEX index_idsite_idvisitor (idsite, idvisitor) |
| 179 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 180 | ", |
| 181 | |
| 182 | 'log_conversion_item' => "CREATE TABLE `{$prefixTables}log_conversion_item` ( |
| 183 | idsite int(10) UNSIGNED NOT NULL, |
| 184 | idvisitor BINARY(8) NOT NULL, |
| 185 | server_time DATETIME NOT NULL, |
| 186 | idvisit BIGINT(10) UNSIGNED NOT NULL, |
| 187 | idorder varchar(100) NOT NULL, |
| 188 | idaction_sku INTEGER(10) UNSIGNED NOT NULL, |
| 189 | idaction_name INTEGER(10) UNSIGNED NOT NULL, |
| 190 | idaction_category INTEGER(10) UNSIGNED NOT NULL, |
| 191 | idaction_category2 INTEGER(10) UNSIGNED NOT NULL, |
| 192 | idaction_category3 INTEGER(10) UNSIGNED NOT NULL, |
| 193 | idaction_category4 INTEGER(10) UNSIGNED NOT NULL, |
| 194 | idaction_category5 INTEGER(10) UNSIGNED NOT NULL, |
| 195 | price FLOAT NOT NULL, |
| 196 | quantity INTEGER(10) UNSIGNED NOT NULL, |
| 197 | deleted TINYINT(1) UNSIGNED NOT NULL, |
| 198 | PRIMARY KEY(idvisit, idorder, idaction_sku), |
| 199 | INDEX index_idsite_servertime ( idsite, server_time ) |
| 200 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 201 | ", |
| 202 | |
| 203 | 'log_conversion' => "CREATE TABLE `{$prefixTables}log_conversion` ( |
| 204 | idvisit BIGINT(10) unsigned NOT NULL, |
| 205 | idsite int(10) unsigned NOT NULL, |
| 206 | idvisitor BINARY(8) NOT NULL, |
| 207 | server_time datetime NOT NULL, |
| 208 | idaction_url INTEGER(10) UNSIGNED default NULL, |
| 209 | idlink_va BIGINT(10) UNSIGNED default NULL, |
| 210 | idgoal int(10) NOT NULL, |
| 211 | buster int unsigned NOT NULL, |
| 212 | idorder varchar(100) default NULL, |
| 213 | items SMALLINT UNSIGNED DEFAULT NULL, |
| 214 | url VARCHAR(4096) NOT NULL, |
| 215 | PRIMARY KEY (idvisit, idgoal, buster), |
| 216 | UNIQUE KEY unique_idsite_idorder (idsite, idorder), |
| 217 | INDEX index_idsite_datetime ( idsite, server_time ) |
| 218 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 219 | ", |
| 220 | |
| 221 | 'log_link_visit_action' => "CREATE TABLE {$prefixTables}log_link_visit_action ( |
| 222 | idlink_va BIGINT(10) UNSIGNED NOT NULL AUTO_INCREMENT, |
| 223 | idsite int(10) UNSIGNED NOT NULL, |
| 224 | idvisitor BINARY(8) NOT NULL, |
| 225 | idvisit BIGINT(10) UNSIGNED NOT NULL, |
| 226 | idaction_url_ref INTEGER(10) UNSIGNED NULL DEFAULT 0, |
| 227 | idaction_name_ref INTEGER(10) UNSIGNED NULL, |
| 228 | custom_float DOUBLE NULL DEFAULT NULL, |
| 229 | PRIMARY KEY(idlink_va), |
| 230 | INDEX index_idvisit(idvisit) |
| 231 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 232 | ", |
| 233 | |
| 234 | 'log_profiling' => "CREATE TABLE {$prefixTables}log_profiling ( |
| 235 | query TEXT NOT NULL, |
| 236 | count INTEGER UNSIGNED NULL, |
| 237 | sum_time_ms FLOAT NULL, |
| 238 | idprofiling BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, |
| 239 | PRIMARY KEY (idprofiling), |
| 240 | UNIQUE KEY query(query(100)) |
| 241 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 242 | ", |
| 243 | |
| 244 | 'option' => "CREATE TABLE `{$prefixTables}option` ( |
| 245 | option_name VARCHAR( 255 ) NOT NULL, |
| 246 | option_value LONGTEXT NOT NULL, |
| 247 | autoload TINYINT NOT NULL DEFAULT '1', |
| 248 | PRIMARY KEY ( option_name ), |
| 249 | INDEX autoload( autoload ) |
| 250 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 251 | ", |
| 252 | |
| 253 | 'session' => "CREATE TABLE {$prefixTables}session ( |
| 254 | id VARCHAR( 255 ) NOT NULL, |
| 255 | modified INTEGER, |
| 256 | lifetime INTEGER, |
| 257 | data TEXT, |
| 258 | PRIMARY KEY ( id ) |
| 259 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 260 | ", |
| 261 | |
| 262 | 'archive_numeric' => "CREATE TABLE {$prefixTables}archive_numeric ( |
| 263 | idarchive INTEGER UNSIGNED NOT NULL, |
| 264 | name VARCHAR(255) NOT NULL, |
| 265 | idsite INTEGER UNSIGNED NULL, |
| 266 | date1 DATE NULL, |
| 267 | date2 DATE NULL, |
| 268 | period TINYINT UNSIGNED NULL, |
| 269 | ts_archived DATETIME NULL, |
| 270 | value DOUBLE NULL, |
| 271 | PRIMARY KEY(idarchive, name), |
| 272 | INDEX index_idsite_dates_period(idsite, date1, date2, period, ts_archived), |
| 273 | INDEX index_period_archived(period, ts_archived) |
| 274 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 275 | ", |
| 276 | |
| 277 | 'archive_blob' => "CREATE TABLE {$prefixTables}archive_blob ( |
| 278 | idarchive INTEGER UNSIGNED NOT NULL, |
| 279 | name VARCHAR(255) NOT NULL, |
| 280 | idsite INTEGER UNSIGNED NULL, |
| 281 | date1 DATE NULL, |
| 282 | date2 DATE NULL, |
| 283 | period TINYINT UNSIGNED NULL, |
| 284 | ts_archived DATETIME NULL, |
| 285 | value MEDIUMBLOB NULL, |
| 286 | PRIMARY KEY(idarchive, name), |
| 287 | INDEX index_period_archived(period, ts_archived) |
| 288 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 289 | ", |
| 290 | |
| 291 | 'sequence' => "CREATE TABLE {$prefixTables}sequence ( |
| 292 | `name` VARCHAR(120) NOT NULL, |
| 293 | `value` BIGINT(20) UNSIGNED NOT NULL , |
| 294 | PRIMARY KEY(`name`) |
| 295 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 296 | ", |
| 297 | |
| 298 | 'brute_force_log' => "CREATE TABLE {$prefixTables}brute_force_log ( |
| 299 | `id_brute_force_log` bigint(11) NOT NULL AUTO_INCREMENT, |
| 300 | `ip_address` VARCHAR(60) DEFAULT NULL, |
| 301 | `attempted_at` datetime NOT NULL, |
| 302 | INDEX index_ip_address(ip_address), |
| 303 | PRIMARY KEY(`id_brute_force_log`) |
| 304 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 305 | ", |
| 306 | |
| 307 | 'tracking_failure' => "CREATE TABLE {$prefixTables}tracking_failure ( |
| 308 | `idsite` BIGINT(20) UNSIGNED NOT NULL , |
| 309 | `idfailure` SMALLINT UNSIGNED NOT NULL , |
| 310 | `date_first_occurred` DATETIME NOT NULL , |
| 311 | `request_url` MEDIUMTEXT NOT NULL , |
| 312 | PRIMARY KEY(`idsite`, `idfailure`) |
| 313 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 314 | ", |
| 315 | 'locks' => "CREATE TABLE `{$prefixTables}locks` ( |
| 316 | `key` VARCHAR(".Lock::MAX_KEY_LEN.") NOT NULL, |
| 317 | `value` VARCHAR(255) NULL DEFAULT NULL, |
| 318 | `expiry_time` BIGINT UNSIGNED DEFAULT 9999999999, |
| 319 | PRIMARY KEY (`key`) |
| 320 | ) ENGINE=$engine DEFAULT CHARSET=utf8 |
| 321 | ", |
| 322 | ); |
| 323 | |
| 324 | return $tables; |
| 325 | } |
| 326 | |
| 327 | /** |
| 328 | * Get the SQL to create a specific Piwik table |
| 329 | * |
| 330 | * @param string $tableName |
| 331 | * @throws Exception |
| 332 | * @return string SQL |
| 333 | */ |
| 334 | public function getTableCreateSql($tableName) |
| 335 | { |
| 336 | $tables = DbHelper::getTablesCreateSql(); |
| 337 | |
| 338 | if (!isset($tables[$tableName])) { |
| 339 | throw new Exception("The table '$tableName' SQL creation code couldn't be found."); |
| 340 | } |
| 341 | |
| 342 | return $tables[$tableName]; |
| 343 | } |
| 344 | |
| 345 | /** |
| 346 | * Names of all the prefixed tables in piwik |
| 347 | * Doesn't use the DB |
| 348 | * |
| 349 | * @return array Table names |
| 350 | */ |
| 351 | public function getTablesNames() |
| 352 | { |
| 353 | $aTables = array_keys($this->getTablesCreateSql()); |
| 354 | $prefixTables = $this->getTablePrefix(); |
| 355 | |
| 356 | $return = array(); |
| 357 | foreach ($aTables as $table) { |
| 358 | $return[] = $prefixTables . $table; |
| 359 | } |
| 360 | |
| 361 | return $return; |
| 362 | } |
| 363 | |
| 364 | /** |
| 365 | * Get list of installed columns in a table |
| 366 | * |
| 367 | * @param string $tableName The name of a table. |
| 368 | * |
| 369 | * @return array Installed columns indexed by the column name. |
| 370 | */ |
| 371 | public function getTableColumns($tableName) |
| 372 | { |
| 373 | $db = $this->getDb(); |
| 374 | |
| 375 | $allColumns = $db->fetchAll("SHOW COLUMNS FROM " . $tableName); |
| 376 | |
| 377 | $fields = array(); |
| 378 | foreach ($allColumns as $column) { |
| 379 | $fields[trim($column['Field'])] = $column; |
| 380 | } |
| 381 | |
| 382 | return $fields; |
| 383 | } |
| 384 | |
| 385 | /** |
| 386 | * Get list of tables installed |
| 387 | * |
| 388 | * @param bool $forceReload Invalidate cache |
| 389 | * @return array installed Tables |
| 390 | */ |
| 391 | public function getTablesInstalled($forceReload = true) |
| 392 | { |
| 393 | if (is_null($this->tablesInstalled) |
| 394 | || $forceReload === true |
| 395 | ) { |
| 396 | $db = $this->getDb(); |
| 397 | $prefixTables = $this->getTablePrefixEscaped(); |
| 398 | |
| 399 | $allTables = $this->getAllExistingTables($prefixTables); |
| 400 | |
| 401 | // all the tables to be installed |
| 402 | $allMyTables = $this->getTablesNames(); |
| 403 | |
| 404 | // we get the intersection between all the tables in the DB and the tables to be installed |
| 405 | $tablesInstalled = array_intersect($allMyTables, $allTables); |
| 406 | |
| 407 | // at this point we have the static list of core tables, but let's add the monthly archive tables |
| 408 | $allArchiveNumeric = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_numeric%'"); |
| 409 | $allArchiveBlob = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_blob%'"); |
| 410 | |
| 411 | $allTablesReallyInstalled = array_merge($tablesInstalled, $allArchiveNumeric, $allArchiveBlob); |
| 412 | |
| 413 | $this->tablesInstalled = $allTablesReallyInstalled; |
| 414 | } |
| 415 | |
| 416 | return $this->tablesInstalled; |
| 417 | } |
| 418 | |
| 419 | /** |
| 420 | * Checks whether any table exists |
| 421 | * |
| 422 | * @return bool True if tables exist; false otherwise |
| 423 | */ |
| 424 | public function hasTables() |
| 425 | { |
| 426 | return count($this->getTablesInstalled()) != 0; |
| 427 | } |
| 428 | |
| 429 | /** |
| 430 | * Create database |
| 431 | * |
| 432 | * @param string $dbName Name of the database to create |
| 433 | */ |
| 434 | public function createDatabase($dbName = null) |
| 435 | { |
| 436 | if (is_null($dbName)) { |
| 437 | $dbName = $this->getDbName(); |
| 438 | } |
| 439 | |
| 440 | $dbName = str_replace('`', '', $dbName); |
| 441 | |
| 442 | Db::exec("CREATE DATABASE IF NOT EXISTS `" . $dbName . "` DEFAULT CHARACTER SET utf8"); |
| 443 | } |
| 444 | |
| 445 | /** |
| 446 | * Creates a new table in the database. |
| 447 | * |
| 448 | * @param string $nameWithoutPrefix The name of the table without any piwik prefix. |
| 449 | * @param string $createDefinition The table create definition, see the "MySQL CREATE TABLE" specification for |
| 450 | * more information. |
| 451 | * @throws \Exception |
| 452 | */ |
| 453 | public function createTable($nameWithoutPrefix, $createDefinition) |
| 454 | { |
| 455 | $statement = sprintf("CREATE TABLE IF NOT EXISTS `%s` ( %s ) ENGINE=%s DEFAULT CHARSET=utf8 ;", |
| 456 | Common::prefixTable($nameWithoutPrefix), |
| 457 | $createDefinition, |
| 458 | $this->getTableEngine()); |
| 459 | |
| 460 | try { |
| 461 | Db::exec($statement); |
| 462 | } catch (Exception $e) { |
| 463 | // mysql code error 1050:table already exists |
| 464 | // see bug #153 https://github.com/piwik/piwik/issues/153 |
| 465 | if (!$this->getDb()->isErrNo($e, '1050')) { |
| 466 | throw $e; |
| 467 | } |
| 468 | } |
| 469 | } |
| 470 | |
| 471 | /** |
| 472 | * Drop database |
| 473 | */ |
| 474 | public function dropDatabase($dbName = null) |
| 475 | { |
| 476 | $dbName = $dbName ?: $this->getDbName(); |
| 477 | $dbName = str_replace('`', '', $dbName); |
| 478 | Db::exec("DROP DATABASE IF EXISTS `" . $dbName . "`"); |
| 479 | } |
| 480 | |
| 481 | /** |
| 482 | * Create all tables |
| 483 | */ |
| 484 | public function createTables() |
| 485 | { |
| 486 | $db = $this->getDb(); |
| 487 | $prefixTables = $this->getTablePrefix(); |
| 488 | |
| 489 | $tablesAlreadyInstalled = $this->getTablesInstalled(); |
| 490 | $tablesToCreate = $this->getTablesCreateSql(); |
| 491 | unset($tablesToCreate['archive_blob']); |
| 492 | unset($tablesToCreate['archive_numeric']); |
| 493 | |
| 494 | foreach ($tablesToCreate as $tableName => $tableSql) { |
| 495 | $tableName = $prefixTables . $tableName; |
| 496 | if (!in_array($tableName, $tablesAlreadyInstalled)) { |
| 497 | $db->query($tableSql); |
| 498 | } |
| 499 | } |
| 500 | } |
| 501 | |
| 502 | /** |
| 503 | * Creates an entry in the User table for the "anonymous" user. |
| 504 | */ |
| 505 | public function createAnonymousUser() |
| 506 | { |
| 507 | $now = Date::factory('now')->getDatetime(); |
| 508 | |
| 509 | // The anonymous user is the user that is assigned by default |
| 510 | // note that the token_auth value is anonymous, which is assigned by default as well in the Login plugin |
| 511 | $db = $this->getDb(); |
| 512 | $db->query("INSERT IGNORE INTO " . Common::prefixTable("user") . " |
| 513 | VALUES ( 'anonymous', '', 'anonymous', 'anonymous@example.org', '', 'anonymous', 0, '$now', '$now' );"); |
| 514 | } |
| 515 | |
| 516 | /** |
| 517 | * Records the Matomo version a user used when installing this Matomo for the first time |
| 518 | */ |
| 519 | public function recordInstallVersion() |
| 520 | { |
| 521 | if (!self::getInstallVersion()) { |
| 522 | Option::set(self::OPTION_NAME_MATOMO_INSTALL_VERSION, Version::VERSION); |
| 523 | } |
| 524 | } |
| 525 | |
| 526 | /** |
| 527 | * Returns which Matomo version was used to install this Matomo for the first time. |
| 528 | */ |
| 529 | public function getInstallVersion() |
| 530 | { |
| 531 | Option::clearCachedOption(self::OPTION_NAME_MATOMO_INSTALL_VERSION); |
| 532 | $version = Option::get(self::OPTION_NAME_MATOMO_INSTALL_VERSION); |
| 533 | if (!empty($version)) { |
| 534 | return $version; |
| 535 | } |
| 536 | } |
| 537 | |
| 538 | /** |
| 539 | * Truncate all tables |
| 540 | */ |
| 541 | public function truncateAllTables() |
| 542 | { |
| 543 | $tables = $this->getAllExistingTables(); |
| 544 | foreach ($tables as $table) { |
| 545 | Db::query("TRUNCATE `$table`"); |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | private function getTablePrefix() |
| 550 | { |
| 551 | return $this->getDbSettings()->getTablePrefix(); |
| 552 | } |
| 553 | |
| 554 | private function getTableEngine() |
| 555 | { |
| 556 | return $this->getDbSettings()->getEngine(); |
| 557 | } |
| 558 | |
| 559 | private function getDb() |
| 560 | { |
| 561 | return Db::get(); |
| 562 | } |
| 563 | |
| 564 | private function getDbSettings() |
| 565 | { |
| 566 | return new Db\Settings(); |
| 567 | } |
| 568 | |
| 569 | private function getDbName() |
| 570 | { |
| 571 | return $this->getDbSettings()->getDbName(); |
| 572 | } |
| 573 | |
| 574 | private function getAllExistingTables($prefixTables = false) |
| 575 | { |
| 576 | if (empty($prefixTables)) { |
| 577 | $prefixTables = $this->getTablePrefixEscaped(); |
| 578 | } |
| 579 | |
| 580 | return Db::get()->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "%'"); |
| 581 | } |
| 582 | |
| 583 | private function getTablePrefixEscaped() |
| 584 | { |
| 585 | $prefixTables = $this->getTablePrefix(); |
| 586 | // '_' matches any character; force it to be literal |
| 587 | $prefixTables = str_replace('_', '\_', $prefixTables); |
| 588 | return $prefixTables; |
| 589 | } |
| 590 | } |
| 591 |