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