PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.13.3
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.13.3
5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Db / Schema / Mysql.php
matomo / app / core / Db / Schema Last commit date
Mysql.php 3 years ago
Mysql.php
681 lines
1 <?php
2 /**
3 * Matomo - 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\Piwik;
20 use Piwik\Plugin\Manager;
21 use Piwik\Plugins\UsersManager\Model;
22 use Piwik\Version;
23
24 /**
25 * MySQL schema
26 */
27 class Mysql implements SchemaInterface
28 {
29 const OPTION_NAME_MATOMO_INSTALL_VERSION = 'install_version';
30 const MAX_TABLE_NAME_LENGTH = 64;
31
32 private $tablesInstalled = null;
33
34 /**
35 * Get the SQL to create Piwik tables
36 *
37 * @return array array of strings containing SQL
38 */
39 public function getTablesCreateSql()
40 {
41 $engine = $this->getTableEngine();
42 $prefixTables = $this->getTablePrefix();
43 $dbSettings = new Db\Settings();
44 $charset = $dbSettings->getUsedCharset();
45
46 $tables = array(
47 'user' => "CREATE TABLE {$prefixTables}user (
48 login VARCHAR(100) NOT NULL,
49 password VARCHAR(255) NOT NULL,
50 email VARCHAR(100) NOT NULL,
51 twofactor_secret VARCHAR(40) NOT NULL DEFAULT '',
52 superuser_access TINYINT(2) unsigned NOT NULL DEFAULT '0',
53 date_registered TIMESTAMP NULL,
54 ts_password_modified TIMESTAMP NULL,
55 idchange_last_viewed INTEGER UNSIGNED NULL,
56 invited_by VARCHAR(100) NULL,
57 invite_token VARCHAR(191) NULL,
58 invite_link_token VARCHAR(191) NULL,
59 invite_expired_at TIMESTAMP NULL,
60 invite_accept_at TIMESTAMP NULL,
61 PRIMARY KEY(login),
62 UNIQUE INDEX `uniq_email` (`email`)
63 ) ENGINE=$engine DEFAULT CHARSET=$charset
64 ",
65 'user_token_auth' => "CREATE TABLE {$prefixTables}user_token_auth (
66 idusertokenauth BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
67 login VARCHAR(100) NOT NULL,
68 description VARCHAR(".Model::MAX_LENGTH_TOKEN_DESCRIPTION.") NOT NULL,
69 password VARCHAR(191) NOT NULL,
70 hash_algo VARCHAR(30) NOT NULL,
71 system_token TINYINT(1) NOT NULL DEFAULT 0,
72 last_used DATETIME NULL,
73 date_created DATETIME NOT NULL,
74 date_expired DATETIME NULL,
75 PRIMARY KEY(idusertokenauth),
76 UNIQUE KEY uniq_password(password)
77 ) ENGINE=$engine DEFAULT CHARSET=$charset
78 ",
79
80 'twofactor_recovery_code' => "CREATE TABLE {$prefixTables}twofactor_recovery_code (
81 idrecoverycode BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
82 login VARCHAR(100) NOT NULL,
83 recovery_code VARCHAR(40) NOT NULL,
84 PRIMARY KEY(idrecoverycode)
85 ) ENGINE=$engine DEFAULT CHARSET=$charset
86 ",
87
88 'access' => "CREATE TABLE {$prefixTables}access (
89 idaccess INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
90 login VARCHAR(100) NOT NULL,
91 idsite INTEGER UNSIGNED NOT NULL,
92 access VARCHAR(50) NULL,
93 PRIMARY KEY(idaccess),
94 INDEX index_loginidsite (login, idsite)
95 ) ENGINE=$engine DEFAULT CHARSET=$charset
96 ",
97
98 'site' => "CREATE TABLE {$prefixTables}site (
99 idsite INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
100 name VARCHAR(90) NOT NULL,
101 main_url VARCHAR(255) NOT NULL,
102 ts_created TIMESTAMP NULL,
103 ecommerce TINYINT DEFAULT 0,
104 sitesearch TINYINT DEFAULT 1,
105 sitesearch_keyword_parameters TEXT NOT NULL,
106 sitesearch_category_parameters TEXT NOT NULL,
107 timezone VARCHAR( 50 ) NOT NULL,
108 currency CHAR( 3 ) NOT NULL,
109 exclude_unknown_urls TINYINT(1) DEFAULT 0,
110 excluded_ips TEXT NOT NULL,
111 excluded_parameters TEXT NOT NULL,
112 excluded_user_agents TEXT NOT NULL,
113 excluded_referrers TEXT NOT NULL,
114 `group` VARCHAR(250) NOT NULL,
115 `type` VARCHAR(255) NOT NULL,
116 keep_url_fragment TINYINT NOT NULL DEFAULT 0,
117 creator_login VARCHAR(100) NULL,
118 PRIMARY KEY(idsite)
119 ) ENGINE=$engine DEFAULT CHARSET=$charset
120 ",
121
122 'plugin_setting' => "CREATE TABLE {$prefixTables}plugin_setting (
123 `plugin_name` VARCHAR(60) NOT NULL,
124 `setting_name` VARCHAR(255) NOT NULL,
125 `setting_value` LONGTEXT NOT NULL,
126 `json_encoded` TINYINT UNSIGNED NOT NULL DEFAULT 0,
127 `user_login` VARCHAR(100) NOT NULL DEFAULT '',
128 `idplugin_setting` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
129 PRIMARY KEY (idplugin_setting),
130 INDEX(plugin_name, user_login)
131 ) ENGINE=$engine DEFAULT CHARSET=$charset
132 ",
133
134 'site_setting' => "CREATE TABLE {$prefixTables}site_setting (
135 idsite INTEGER(10) UNSIGNED NOT NULL,
136 `plugin_name` VARCHAR(60) NOT NULL,
137 `setting_name` VARCHAR(255) NOT NULL,
138 `setting_value` LONGTEXT NOT NULL,
139 `json_encoded` TINYINT UNSIGNED NOT NULL DEFAULT 0,
140 `idsite_setting` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
141 PRIMARY KEY (idsite_setting),
142 INDEX(idsite, plugin_name)
143 ) ENGINE=$engine DEFAULT CHARSET=$charset
144 ",
145
146 'site_url' => "CREATE TABLE {$prefixTables}site_url (
147 idsite INTEGER(10) UNSIGNED NOT NULL,
148 url VARCHAR(190) NOT NULL,
149 PRIMARY KEY(idsite, url)
150 ) ENGINE=$engine DEFAULT CHARSET=$charset
151 ",
152
153 'goal' => "CREATE TABLE `{$prefixTables}goal` (
154 `idsite` int(11) NOT NULL,
155 `idgoal` int(11) NOT NULL,
156 `name` varchar(50) NOT NULL,
157 `description` varchar(255) NOT NULL DEFAULT '',
158 `match_attribute` varchar(20) NOT NULL,
159 `pattern` varchar(255) NOT NULL,
160 `pattern_type` varchar(25) NOT NULL,
161 `case_sensitive` tinyint(4) NOT NULL,
162 `allow_multiple` tinyint(4) NOT NULL,
163 `revenue` DOUBLE NOT NULL,
164 `deleted` tinyint(4) NOT NULL default '0',
165 `event_value_as_revenue` tinyint(4) NOT NULL default '0',
166 PRIMARY KEY (`idsite`,`idgoal`)
167 ) ENGINE=$engine DEFAULT CHARSET=$charset
168 ",
169
170 'logger_message' => "CREATE TABLE {$prefixTables}logger_message (
171 idlogger_message INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
172 tag VARCHAR(50) NULL,
173 timestamp TIMESTAMP NULL,
174 level VARCHAR(16) NULL,
175 message TEXT NULL,
176 PRIMARY KEY(idlogger_message)
177 ) ENGINE=$engine DEFAULT CHARSET=$charset
178 ",
179
180 'log_action' => "CREATE TABLE {$prefixTables}log_action (
181 idaction INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
182 name VARCHAR(4096),
183 hash INTEGER(10) UNSIGNED NOT NULL,
184 type TINYINT UNSIGNED NULL,
185 url_prefix TINYINT(2) NULL,
186 PRIMARY KEY(idaction),
187 INDEX index_type_hash (type, hash)
188 ) ENGINE=$engine DEFAULT CHARSET=$charset
189 ",
190
191 'log_visit' => "CREATE TABLE {$prefixTables}log_visit (
192 idvisit BIGINT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
193 idsite INTEGER(10) UNSIGNED NOT NULL,
194 idvisitor BINARY(8) NOT NULL,
195 visit_last_action_time DATETIME NOT NULL,
196 config_id BINARY(8) NOT NULL,
197 location_ip VARBINARY(16) NOT NULL,
198 PRIMARY KEY(idvisit),
199 INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time),
200 INDEX index_idsite_datetime (idsite, visit_last_action_time),
201 INDEX index_idsite_idvisitor (idsite, idvisitor, visit_last_action_time DESC)
202 ) ENGINE=$engine DEFAULT CHARSET=$charset
203 ",
204
205 'log_conversion_item' => "CREATE TABLE `{$prefixTables}log_conversion_item` (
206 idsite int(10) UNSIGNED NOT NULL,
207 idvisitor BINARY(8) NOT NULL,
208 server_time DATETIME NOT NULL,
209 idvisit BIGINT(10) UNSIGNED NOT NULL,
210 idorder varchar(100) NOT NULL,
211 idaction_sku INTEGER(10) UNSIGNED NOT NULL,
212 idaction_name INTEGER(10) UNSIGNED NOT NULL,
213 idaction_category INTEGER(10) UNSIGNED NOT NULL,
214 idaction_category2 INTEGER(10) UNSIGNED NOT NULL,
215 idaction_category3 INTEGER(10) UNSIGNED NOT NULL,
216 idaction_category4 INTEGER(10) UNSIGNED NOT NULL,
217 idaction_category5 INTEGER(10) UNSIGNED NOT NULL,
218 price DOUBLE NOT NULL,
219 quantity INTEGER(10) UNSIGNED NOT NULL,
220 deleted TINYINT(1) UNSIGNED NOT NULL,
221 PRIMARY KEY(idvisit, idorder, idaction_sku),
222 INDEX index_idsite_servertime ( idsite, server_time )
223 ) ENGINE=$engine DEFAULT CHARSET=$charset
224 ",
225
226 'log_conversion' => "CREATE TABLE `{$prefixTables}log_conversion` (
227 idvisit BIGINT(10) unsigned NOT NULL,
228 idsite int(10) unsigned NOT NULL,
229 idvisitor BINARY(8) NOT NULL,
230 server_time datetime NOT NULL,
231 idaction_url INTEGER(10) UNSIGNED default NULL,
232 idlink_va BIGINT(10) UNSIGNED default NULL,
233 idgoal int(10) NOT NULL,
234 buster int unsigned NOT NULL,
235 idorder varchar(100) default NULL,
236 items SMALLINT UNSIGNED DEFAULT NULL,
237 url VARCHAR(4096) NOT NULL,
238 revenue DOUBLE default NULL,
239 revenue_shipping DOUBLE default NULL,
240 revenue_subtotal DOUBLE default NULL,
241 revenue_tax DOUBLE default NULL,
242 revenue_discount DOUBLE default NULL,
243 PRIMARY KEY (idvisit, idgoal, buster),
244 UNIQUE KEY unique_idsite_idorder (idsite, idorder),
245 INDEX index_idsite_datetime ( idsite, server_time )
246 ) ENGINE=$engine DEFAULT CHARSET=$charset
247 ",
248
249 'log_link_visit_action' => "CREATE TABLE {$prefixTables}log_link_visit_action (
250 idlink_va BIGINT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
251 idsite int(10) UNSIGNED NOT NULL,
252 idvisitor BINARY(8) NOT NULL,
253 idvisit BIGINT(10) UNSIGNED NOT NULL,
254 idaction_url_ref INTEGER(10) UNSIGNED NULL DEFAULT 0,
255 idaction_name_ref INTEGER(10) UNSIGNED NULL,
256 custom_float DOUBLE NULL DEFAULT NULL,
257 pageview_position MEDIUMINT UNSIGNED DEFAULT NULL,
258 PRIMARY KEY(idlink_va),
259 INDEX index_idvisit(idvisit)
260 ) ENGINE=$engine DEFAULT CHARSET=$charset
261 ",
262
263 'log_profiling' => "CREATE TABLE {$prefixTables}log_profiling (
264 query TEXT NOT NULL,
265 count INTEGER UNSIGNED NULL,
266 sum_time_ms FLOAT NULL,
267 idprofiling BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
268 PRIMARY KEY (idprofiling),
269 UNIQUE KEY query(query(100))
270 ) ENGINE=$engine DEFAULT CHARSET=$charset
271 ",
272
273 'option' => "CREATE TABLE `{$prefixTables}option` (
274 option_name VARCHAR( 191 ) NOT NULL,
275 option_value LONGTEXT NOT NULL,
276 autoload TINYINT NOT NULL DEFAULT '1',
277 PRIMARY KEY ( option_name ),
278 INDEX autoload( autoload )
279 ) ENGINE=$engine DEFAULT CHARSET=$charset
280 ",
281
282 'session' => "CREATE TABLE {$prefixTables}session (
283 id VARCHAR( 191 ) NOT NULL,
284 modified INTEGER,
285 lifetime INTEGER,
286 data MEDIUMTEXT,
287 PRIMARY KEY ( id )
288 ) ENGINE=$engine DEFAULT CHARSET=$charset
289 ",
290
291 'archive_numeric' => "CREATE TABLE {$prefixTables}archive_numeric (
292 idarchive INTEGER UNSIGNED NOT NULL,
293 name VARCHAR(190) NOT NULL,
294 idsite INTEGER UNSIGNED NULL,
295 date1 DATE NULL,
296 date2 DATE NULL,
297 period TINYINT UNSIGNED NULL,
298 ts_archived DATETIME NULL,
299 value DOUBLE NULL,
300 PRIMARY KEY(idarchive, name),
301 INDEX index_idsite_dates_period(idsite, date1, date2, period, ts_archived),
302 INDEX index_period_archived(period, ts_archived)
303 ) ENGINE=$engine DEFAULT CHARSET=$charset
304 ",
305
306 'archive_blob' => "CREATE TABLE {$prefixTables}archive_blob (
307 idarchive INTEGER UNSIGNED NOT NULL,
308 name VARCHAR(190) NOT NULL,
309 idsite INTEGER UNSIGNED NULL,
310 date1 DATE NULL,
311 date2 DATE NULL,
312 period TINYINT UNSIGNED NULL,
313 ts_archived DATETIME NULL,
314 value MEDIUMBLOB NULL,
315 PRIMARY KEY(idarchive, name),
316 INDEX index_period_archived(period, ts_archived)
317 ) ENGINE=$engine DEFAULT CHARSET=$charset
318 ",
319
320 'archive_invalidations' => "CREATE TABLE `{$prefixTables}archive_invalidations` (
321 idinvalidation BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
322 idarchive INTEGER UNSIGNED NULL,
323 name VARCHAR(255) NOT NULL,
324 idsite INTEGER UNSIGNED NOT NULL,
325 date1 DATE NOT NULL,
326 date2 DATE NOT NULL,
327 period TINYINT UNSIGNED NOT NULL,
328 ts_invalidated DATETIME NULL,
329 ts_started DATETIME NULL,
330 status TINYINT(1) UNSIGNED DEFAULT 0,
331 `report` VARCHAR(255) NULL,
332 PRIMARY KEY(idinvalidation),
333 INDEX index_idsite_dates_period_name(idsite, date1, period)
334 ) ENGINE=$engine DEFAULT CHARSET=$charset
335 ",
336
337 'sequence' => "CREATE TABLE {$prefixTables}sequence (
338 `name` VARCHAR(120) NOT NULL,
339 `value` BIGINT(20) UNSIGNED NOT NULL ,
340 PRIMARY KEY(`name`)
341 ) ENGINE=$engine DEFAULT CHARSET=$charset
342 ",
343
344 'brute_force_log' => "CREATE TABLE {$prefixTables}brute_force_log (
345 `id_brute_force_log` bigint(11) NOT NULL AUTO_INCREMENT,
346 `ip_address` VARCHAR(60) DEFAULT NULL,
347 `attempted_at` datetime NOT NULL,
348 `login` VARCHAR(100) NULL,
349 INDEX index_ip_address(ip_address),
350 PRIMARY KEY(`id_brute_force_log`)
351 ) ENGINE=$engine DEFAULT CHARSET=$charset
352 ",
353
354 'tracking_failure' => "CREATE TABLE {$prefixTables}tracking_failure (
355 `idsite` BIGINT(20) UNSIGNED NOT NULL ,
356 `idfailure` SMALLINT UNSIGNED NOT NULL ,
357 `date_first_occurred` DATETIME NOT NULL ,
358 `request_url` MEDIUMTEXT NOT NULL ,
359 PRIMARY KEY(`idsite`, `idfailure`)
360 ) ENGINE=$engine DEFAULT CHARSET=$charset
361 ",
362 'locks' => "CREATE TABLE `{$prefixTables}locks` (
363 `key` VARCHAR(".Lock::MAX_KEY_LEN.") NOT NULL,
364 `value` VARCHAR(255) NULL DEFAULT NULL,
365 `expiry_time` BIGINT UNSIGNED DEFAULT 9999999999,
366 PRIMARY KEY (`key`)
367 ) ENGINE=$engine DEFAULT CHARSET=$charset
368 ",
369 'changes' => "CREATE TABLE `{$prefixTables}changes` (
370 `idchange` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
371 `created_time` DATETIME NOT NULL,
372 `plugin_name` VARCHAR(60) NOT NULL,
373 `version` VARCHAR(20) NOT NULL,
374 `title` VARCHAR(255) NOT NULL,
375 `description` TEXT NULL,
376 `link_name` VARCHAR(255) NULL,
377 `link` VARCHAR(255) NULL,
378 PRIMARY KEY(`idchange`),
379 UNIQUE KEY unique_plugin_version_title (`plugin_name`, `version`, `title`(100))
380 ) ENGINE=$engine DEFAULT CHARSET=$charset
381 ",
382 );
383
384 return $tables;
385 }
386
387 /**
388 * Get the SQL to create a specific Piwik table
389 *
390 * @param string $tableName
391 * @throws Exception
392 * @return string SQL
393 */
394 public function getTableCreateSql($tableName)
395 {
396 $tables = DbHelper::getTablesCreateSql();
397
398 if (!isset($tables[$tableName])) {
399 throw new Exception("The table '$tableName' SQL creation code couldn't be found.");
400 }
401
402 return $tables[$tableName];
403 }
404
405 /**
406 * Names of all the prefixed tables in piwik
407 * Doesn't use the DB
408 *
409 * @return array Table names
410 */
411 public function getTablesNames()
412 {
413 $aTables = array_keys($this->getTablesCreateSql());
414 $prefixTables = $this->getTablePrefix();
415
416 $return = array();
417 foreach ($aTables as $table) {
418 $return[] = $prefixTables . $table;
419 }
420
421 return $return;
422 }
423
424 /**
425 * Get list of installed columns in a table
426 *
427 * @param string $tableName The name of a table.
428 *
429 * @return array Installed columns indexed by the column name.
430 */
431 public function getTableColumns($tableName)
432 {
433 $db = $this->getDb();
434
435 $allColumns = $db->fetchAll("SHOW COLUMNS FROM " . $tableName);
436
437 $fields = array();
438 foreach ($allColumns as $column) {
439 $fields[trim($column['Field'])] = $column;
440 }
441
442 return $fields;
443 }
444
445 /**
446 * Get list of tables installed (including tables defined by deactivated plugins)
447 *
448 * @param bool $forceReload Invalidate cache
449 * @return array installed Tables
450 */
451 public function getTablesInstalled($forceReload = true)
452 {
453 if (is_null($this->tablesInstalled)
454 || $forceReload === true
455 ) {
456 $db = $this->getDb();
457 $prefixTables = $this->getTablePrefixEscaped();
458
459 $allTables = $this->getAllExistingTables($prefixTables);
460
461 // all the tables to be installed
462 $allMyTables = $this->getTablesNames();
463
464 /**
465 * Triggered when detecting which tables have already been created by Matomo.
466 * This should be used by plugins to define it's database tables. Table names need to be added prefixed.
467 *
468 * **Example**
469 *
470 * Piwik::addAction('Db.getTablesInstalled', function(&$allTablesInstalled) {
471 * $allTablesInstalled = 'log_custom';
472 * });
473 * @param array $result
474 */
475 if (count($allTables) && empty($GLOBALS['DISABLE_GET_TABLES_INSTALLED_EVENTS_FOR_TEST'])) {
476 Manager::getInstance()->loadPlugins(Manager::getAllPluginsNames());
477 Piwik::postEvent('Db.getTablesInstalled', [&$allMyTables]);
478 Manager::getInstance()->unloadPlugins();
479 Manager::getInstance()->loadActivatedPlugins();
480 }
481
482 // we get the intersection between all the tables in the DB and the tables to be installed
483 $tablesInstalled = array_intersect($allMyTables, $allTables);
484
485 // at this point we have the static list of core tables, but let's add the monthly archive tables
486 $allArchiveNumeric = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_numeric%'");
487 $allArchiveBlob = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_blob%'");
488
489 $allTablesReallyInstalled = array_merge($tablesInstalled, $allArchiveNumeric, $allArchiveBlob);
490
491 $allTablesReallyInstalled = array_unique($allTablesReallyInstalled);
492
493 $this->tablesInstalled = $allTablesReallyInstalled;
494 }
495
496 return $this->tablesInstalled;
497 }
498
499 /**
500 * Checks whether any table exists
501 *
502 * @return bool True if tables exist; false otherwise
503 */
504 public function hasTables()
505 {
506 return count($this->getTablesInstalled()) != 0;
507 }
508
509 /**
510 * Create database
511 *
512 * @param string $dbName Name of the database to create
513 */
514 public function createDatabase($dbName = null)
515 {
516 if (is_null($dbName)) {
517 $dbName = $this->getDbName();
518 }
519
520 $dbName = str_replace('`', '', $dbName);
521 $charset = DbHelper::getDefaultCharset();
522
523 Db::exec("CREATE DATABASE IF NOT EXISTS `" . $dbName . "` DEFAULT CHARACTER SET ".$charset);
524 }
525
526 /**
527 * Creates a new table in the database.
528 *
529 * @param string $nameWithoutPrefix The name of the table without any piwik prefix.
530 * @param string $createDefinition The table create definition, see the "MySQL CREATE TABLE" specification for
531 * more information.
532 * @throws \Exception
533 */
534 public function createTable($nameWithoutPrefix, $createDefinition)
535 {
536 $dbSettings = new Db\Settings();
537 $charset = $dbSettings->getUsedCharset();
538
539 $statement = sprintf("CREATE TABLE IF NOT EXISTS `%s` ( %s ) ENGINE=%s DEFAULT CHARSET=%s %s;",
540 Common::prefixTable($nameWithoutPrefix),
541 $createDefinition,
542 $this->getTableEngine(),
543 $charset,
544 $dbSettings->getRowFormat());
545
546 try {
547 Db::exec($statement);
548 } catch (Exception $e) {
549 // mysql code error 1050:table already exists
550 // see bug #153 https://github.com/piwik/piwik/issues/153
551 if (!$this->getDb()->isErrNo($e, '1050')) {
552 throw $e;
553 }
554 }
555 }
556
557 /**
558 * Drop database
559 */
560 public function dropDatabase($dbName = null)
561 {
562 $dbName = $dbName ?: $this->getDbName();
563 $dbName = str_replace('`', '', $dbName);
564 Db::exec("DROP DATABASE IF EXISTS `" . $dbName . "`");
565 }
566
567 /**
568 * Create all tables
569 */
570 public function createTables()
571 {
572 $db = $this->getDb();
573 $prefixTables = $this->getTablePrefix();
574
575 $tablesAlreadyInstalled = $this->getAllExistingTables($prefixTables);
576 $tablesToCreate = $this->getTablesCreateSql();
577 unset($tablesToCreate['archive_blob']);
578 unset($tablesToCreate['archive_numeric']);
579
580 foreach ($tablesToCreate as $tableName => $tableSql) {
581 $tableName = $prefixTables . $tableName;
582 if (!in_array($tableName, $tablesAlreadyInstalled)) {
583 $db->query($tableSql);
584 }
585 }
586 }
587
588 /**
589 * Creates an entry in the User table for the "anonymous" user.
590 */
591 public function createAnonymousUser()
592 {
593 $now = Date::factory('now')->getDatetime();
594 // The anonymous user is the user that is assigned by default
595 // note that the token_auth value is anonymous, which is assigned by default as well in the Login plugin
596 $db = $this->getDb();
597 $db->query("INSERT IGNORE INTO " . Common::prefixTable("user") . "
598 (`login`, `password`, `email`, `twofactor_secret`, `superuser_access`, `date_registered`, `ts_password_modified`,
599 `idchange_last_viewed`)
600 VALUES ( 'anonymous', '', 'anonymous@example.org', '', 0, '$now', '$now' , NULL);");
601
602 $model = new Model();
603 $model->addTokenAuth('anonymous', 'anonymous', 'anonymous default token', $now);
604 }
605
606 /**
607 * Records the Matomo version a user used when installing this Matomo for the first time
608 */
609 public function recordInstallVersion()
610 {
611 if (!self::getInstallVersion()) {
612 Option::set(self::OPTION_NAME_MATOMO_INSTALL_VERSION, Version::VERSION);
613 }
614 }
615
616 /**
617 * Returns which Matomo version was used to install this Matomo for the first time.
618 */
619 public function getInstallVersion()
620 {
621 Option::clearCachedOption(self::OPTION_NAME_MATOMO_INSTALL_VERSION);
622 $version = Option::get(self::OPTION_NAME_MATOMO_INSTALL_VERSION);
623 if (!empty($version)) {
624 return $version;
625 }
626 }
627
628 /**
629 * Truncate all tables
630 */
631 public function truncateAllTables()
632 {
633 $tables = $this->getAllExistingTables();
634 foreach ($tables as $table) {
635 Db::query("TRUNCATE `$table`");
636 }
637 }
638
639 private function getTablePrefix()
640 {
641 return $this->getDbSettings()->getTablePrefix();
642 }
643
644 private function getTableEngine()
645 {
646 return $this->getDbSettings()->getEngine();
647 }
648
649 private function getDb()
650 {
651 return Db::get();
652 }
653
654 private function getDbSettings()
655 {
656 return new Db\Settings();
657 }
658
659 private function getDbName()
660 {
661 return $this->getDbSettings()->getDbName();
662 }
663
664 private function getAllExistingTables($prefixTables = false)
665 {
666 if (empty($prefixTables)) {
667 $prefixTables = $this->getTablePrefixEscaped();
668 }
669
670 return Db::get()->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "%'");
671 }
672
673 private function getTablePrefixEscaped()
674 {
675 $prefixTables = $this->getTablePrefix();
676 // '_' matches any character; force it to be literal
677 $prefixTables = str_replace('_', '\_', $prefixTables);
678 return $prefixTables;
679 }
680 }
681