PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.14.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.14.1
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
682 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 pageviews_before SMALLINT UNSIGNED DEFAULT NULL,
244 PRIMARY KEY (idvisit, idgoal, buster),
245 UNIQUE KEY unique_idsite_idorder (idsite, idorder),
246 INDEX index_idsite_datetime ( idsite, server_time )
247 ) ENGINE=$engine DEFAULT CHARSET=$charset
248 ",
249
250 'log_link_visit_action' => "CREATE TABLE {$prefixTables}log_link_visit_action (
251 idlink_va BIGINT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
252 idsite int(10) UNSIGNED NOT NULL,
253 idvisitor BINARY(8) NOT NULL,
254 idvisit BIGINT(10) UNSIGNED NOT NULL,
255 idaction_url_ref INTEGER(10) UNSIGNED NULL DEFAULT 0,
256 idaction_name_ref INTEGER(10) UNSIGNED NULL,
257 custom_float DOUBLE NULL DEFAULT NULL,
258 pageview_position MEDIUMINT UNSIGNED DEFAULT NULL,
259 PRIMARY KEY(idlink_va),
260 INDEX index_idvisit(idvisit)
261 ) ENGINE=$engine DEFAULT CHARSET=$charset
262 ",
263
264 'log_profiling' => "CREATE TABLE {$prefixTables}log_profiling (
265 query TEXT NOT NULL,
266 count INTEGER UNSIGNED NULL,
267 sum_time_ms FLOAT NULL,
268 idprofiling BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
269 PRIMARY KEY (idprofiling),
270 UNIQUE KEY query(query(100))
271 ) ENGINE=$engine DEFAULT CHARSET=$charset
272 ",
273
274 'option' => "CREATE TABLE `{$prefixTables}option` (
275 option_name VARCHAR( 191 ) NOT NULL,
276 option_value LONGTEXT NOT NULL,
277 autoload TINYINT NOT NULL DEFAULT '1',
278 PRIMARY KEY ( option_name ),
279 INDEX autoload( autoload )
280 ) ENGINE=$engine DEFAULT CHARSET=$charset
281 ",
282
283 'session' => "CREATE TABLE {$prefixTables}session (
284 id VARCHAR( 191 ) NOT NULL,
285 modified INTEGER,
286 lifetime INTEGER,
287 data MEDIUMTEXT,
288 PRIMARY KEY ( id )
289 ) ENGINE=$engine DEFAULT CHARSET=$charset
290 ",
291
292 'archive_numeric' => "CREATE TABLE {$prefixTables}archive_numeric (
293 idarchive INTEGER UNSIGNED NOT NULL,
294 name VARCHAR(190) NOT NULL,
295 idsite INTEGER UNSIGNED NULL,
296 date1 DATE NULL,
297 date2 DATE NULL,
298 period TINYINT UNSIGNED NULL,
299 ts_archived DATETIME NULL,
300 value DOUBLE NULL,
301 PRIMARY KEY(idarchive, name),
302 INDEX index_idsite_dates_period(idsite, date1, date2, period, ts_archived),
303 INDEX index_period_archived(period, ts_archived)
304 ) ENGINE=$engine DEFAULT CHARSET=$charset
305 ",
306
307 'archive_blob' => "CREATE TABLE {$prefixTables}archive_blob (
308 idarchive INTEGER UNSIGNED NOT NULL,
309 name VARCHAR(190) NOT NULL,
310 idsite INTEGER UNSIGNED NULL,
311 date1 DATE NULL,
312 date2 DATE NULL,
313 period TINYINT UNSIGNED NULL,
314 ts_archived DATETIME NULL,
315 value MEDIUMBLOB NULL,
316 PRIMARY KEY(idarchive, name),
317 INDEX index_period_archived(period, ts_archived)
318 ) ENGINE=$engine DEFAULT CHARSET=$charset
319 ",
320
321 'archive_invalidations' => "CREATE TABLE `{$prefixTables}archive_invalidations` (
322 idinvalidation BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
323 idarchive INTEGER UNSIGNED NULL,
324 name VARCHAR(255) NOT NULL,
325 idsite INTEGER UNSIGNED NOT NULL,
326 date1 DATE NOT NULL,
327 date2 DATE NOT NULL,
328 period TINYINT UNSIGNED NOT NULL,
329 ts_invalidated DATETIME NULL,
330 ts_started DATETIME NULL,
331 status TINYINT(1) UNSIGNED DEFAULT 0,
332 `report` VARCHAR(255) NULL,
333 PRIMARY KEY(idinvalidation),
334 INDEX index_idsite_dates_period_name(idsite, date1, period)
335 ) ENGINE=$engine DEFAULT CHARSET=$charset
336 ",
337
338 'sequence' => "CREATE TABLE {$prefixTables}sequence (
339 `name` VARCHAR(120) NOT NULL,
340 `value` BIGINT(20) UNSIGNED NOT NULL ,
341 PRIMARY KEY(`name`)
342 ) ENGINE=$engine DEFAULT CHARSET=$charset
343 ",
344
345 'brute_force_log' => "CREATE TABLE {$prefixTables}brute_force_log (
346 `id_brute_force_log` bigint(11) NOT NULL AUTO_INCREMENT,
347 `ip_address` VARCHAR(60) DEFAULT NULL,
348 `attempted_at` datetime NOT NULL,
349 `login` VARCHAR(100) NULL,
350 INDEX index_ip_address(ip_address),
351 PRIMARY KEY(`id_brute_force_log`)
352 ) ENGINE=$engine DEFAULT CHARSET=$charset
353 ",
354
355 'tracking_failure' => "CREATE TABLE {$prefixTables}tracking_failure (
356 `idsite` BIGINT(20) UNSIGNED NOT NULL ,
357 `idfailure` SMALLINT UNSIGNED NOT NULL ,
358 `date_first_occurred` DATETIME NOT NULL ,
359 `request_url` MEDIUMTEXT NOT NULL ,
360 PRIMARY KEY(`idsite`, `idfailure`)
361 ) ENGINE=$engine DEFAULT CHARSET=$charset
362 ",
363 'locks' => "CREATE TABLE `{$prefixTables}locks` (
364 `key` VARCHAR(".Lock::MAX_KEY_LEN.") NOT NULL,
365 `value` VARCHAR(255) NULL DEFAULT NULL,
366 `expiry_time` BIGINT UNSIGNED DEFAULT 9999999999,
367 PRIMARY KEY (`key`)
368 ) ENGINE=$engine DEFAULT CHARSET=$charset
369 ",
370 'changes' => "CREATE TABLE `{$prefixTables}changes` (
371 `idchange` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
372 `created_time` DATETIME NOT NULL,
373 `plugin_name` VARCHAR(60) NOT NULL,
374 `version` VARCHAR(20) NOT NULL,
375 `title` VARCHAR(255) NOT NULL,
376 `description` TEXT NULL,
377 `link_name` VARCHAR(255) NULL,
378 `link` VARCHAR(255) NULL,
379 PRIMARY KEY(`idchange`),
380 UNIQUE KEY unique_plugin_version_title (`plugin_name`, `version`, `title`(100))
381 ) ENGINE=$engine DEFAULT CHARSET=$charset
382 ",
383 );
384
385 return $tables;
386 }
387
388 /**
389 * Get the SQL to create a specific Piwik table
390 *
391 * @param string $tableName
392 * @throws Exception
393 * @return string SQL
394 */
395 public function getTableCreateSql($tableName)
396 {
397 $tables = DbHelper::getTablesCreateSql();
398
399 if (!isset($tables[$tableName])) {
400 throw new Exception("The table '$tableName' SQL creation code couldn't be found.");
401 }
402
403 return $tables[$tableName];
404 }
405
406 /**
407 * Names of all the prefixed tables in piwik
408 * Doesn't use the DB
409 *
410 * @return array Table names
411 */
412 public function getTablesNames()
413 {
414 $aTables = array_keys($this->getTablesCreateSql());
415 $prefixTables = $this->getTablePrefix();
416
417 $return = array();
418 foreach ($aTables as $table) {
419 $return[] = $prefixTables . $table;
420 }
421
422 return $return;
423 }
424
425 /**
426 * Get list of installed columns in a table
427 *
428 * @param string $tableName The name of a table.
429 *
430 * @return array Installed columns indexed by the column name.
431 */
432 public function getTableColumns($tableName)
433 {
434 $db = $this->getDb();
435
436 $allColumns = $db->fetchAll("SHOW COLUMNS FROM " . $tableName);
437
438 $fields = array();
439 foreach ($allColumns as $column) {
440 $fields[trim($column['Field'])] = $column;
441 }
442
443 return $fields;
444 }
445
446 /**
447 * Get list of tables installed (including tables defined by deactivated plugins)
448 *
449 * @param bool $forceReload Invalidate cache
450 * @return array installed Tables
451 */
452 public function getTablesInstalled($forceReload = true)
453 {
454 if (is_null($this->tablesInstalled)
455 || $forceReload === true
456 ) {
457 $db = $this->getDb();
458 $prefixTables = $this->getTablePrefixEscaped();
459
460 $allTables = $this->getAllExistingTables($prefixTables);
461
462 // all the tables to be installed
463 $allMyTables = $this->getTablesNames();
464
465 /**
466 * Triggered when detecting which tables have already been created by Matomo.
467 * This should be used by plugins to define it's database tables. Table names need to be added prefixed.
468 *
469 * **Example**
470 *
471 * Piwik::addAction('Db.getTablesInstalled', function(&$allTablesInstalled) {
472 * $allTablesInstalled = 'log_custom';
473 * });
474 * @param array $result
475 */
476 if (count($allTables) && empty($GLOBALS['DISABLE_GET_TABLES_INSTALLED_EVENTS_FOR_TEST'])) {
477 Manager::getInstance()->loadPlugins(Manager::getAllPluginsNames());
478 Piwik::postEvent('Db.getTablesInstalled', [&$allMyTables]);
479 Manager::getInstance()->unloadPlugins();
480 Manager::getInstance()->loadActivatedPlugins();
481 }
482
483 // we get the intersection between all the tables in the DB and the tables to be installed
484 $tablesInstalled = array_intersect($allMyTables, $allTables);
485
486 // at this point we have the static list of core tables, but let's add the monthly archive tables
487 $allArchiveNumeric = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_numeric%'");
488 $allArchiveBlob = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_blob%'");
489
490 $allTablesReallyInstalled = array_merge($tablesInstalled, $allArchiveNumeric, $allArchiveBlob);
491
492 $allTablesReallyInstalled = array_unique($allTablesReallyInstalled);
493
494 $this->tablesInstalled = $allTablesReallyInstalled;
495 }
496
497 return $this->tablesInstalled;
498 }
499
500 /**
501 * Checks whether any table exists
502 *
503 * @return bool True if tables exist; false otherwise
504 */
505 public function hasTables()
506 {
507 return count($this->getTablesInstalled()) != 0;
508 }
509
510 /**
511 * Create database
512 *
513 * @param string $dbName Name of the database to create
514 */
515 public function createDatabase($dbName = null)
516 {
517 if (is_null($dbName)) {
518 $dbName = $this->getDbName();
519 }
520
521 $dbName = str_replace('`', '', $dbName);
522 $charset = DbHelper::getDefaultCharset();
523
524 Db::exec("CREATE DATABASE IF NOT EXISTS `" . $dbName . "` DEFAULT CHARACTER SET ".$charset);
525 }
526
527 /**
528 * Creates a new table in the database.
529 *
530 * @param string $nameWithoutPrefix The name of the table without any piwik prefix.
531 * @param string $createDefinition The table create definition, see the "MySQL CREATE TABLE" specification for
532 * more information.
533 * @throws \Exception
534 */
535 public function createTable($nameWithoutPrefix, $createDefinition)
536 {
537 $dbSettings = new Db\Settings();
538 $charset = $dbSettings->getUsedCharset();
539
540 $statement = sprintf("CREATE TABLE IF NOT EXISTS `%s` ( %s ) ENGINE=%s DEFAULT CHARSET=%s %s;",
541 Common::prefixTable($nameWithoutPrefix),
542 $createDefinition,
543 $this->getTableEngine(),
544 $charset,
545 $dbSettings->getRowFormat());
546
547 try {
548 Db::exec($statement);
549 } catch (Exception $e) {
550 // mysql code error 1050:table already exists
551 // see bug #153 https://github.com/piwik/piwik/issues/153
552 if (!$this->getDb()->isErrNo($e, '1050')) {
553 throw $e;
554 }
555 }
556 }
557
558 /**
559 * Drop database
560 */
561 public function dropDatabase($dbName = null)
562 {
563 $dbName = $dbName ?: $this->getDbName();
564 $dbName = str_replace('`', '', $dbName);
565 Db::exec("DROP DATABASE IF EXISTS `" . $dbName . "`");
566 }
567
568 /**
569 * Create all tables
570 */
571 public function createTables()
572 {
573 $db = $this->getDb();
574 $prefixTables = $this->getTablePrefix();
575
576 $tablesAlreadyInstalled = $this->getAllExistingTables($prefixTables);
577 $tablesToCreate = $this->getTablesCreateSql();
578 unset($tablesToCreate['archive_blob']);
579 unset($tablesToCreate['archive_numeric']);
580
581 foreach ($tablesToCreate as $tableName => $tableSql) {
582 $tableName = $prefixTables . $tableName;
583 if (!in_array($tableName, $tablesAlreadyInstalled)) {
584 $db->query($tableSql);
585 }
586 }
587 }
588
589 /**
590 * Creates an entry in the User table for the "anonymous" user.
591 */
592 public function createAnonymousUser()
593 {
594 $now = Date::factory('now')->getDatetime();
595 // The anonymous user is the user that is assigned by default
596 // note that the token_auth value is anonymous, which is assigned by default as well in the Login plugin
597 $db = $this->getDb();
598 $db->query("INSERT IGNORE INTO " . Common::prefixTable("user") . "
599 (`login`, `password`, `email`, `twofactor_secret`, `superuser_access`, `date_registered`, `ts_password_modified`,
600 `idchange_last_viewed`)
601 VALUES ( 'anonymous', '', 'anonymous@example.org', '', 0, '$now', '$now' , NULL);");
602
603 $model = new Model();
604 $model->addTokenAuth('anonymous', 'anonymous', 'anonymous default token', $now);
605 }
606
607 /**
608 * Records the Matomo version a user used when installing this Matomo for the first time
609 */
610 public function recordInstallVersion()
611 {
612 if (!self::getInstallVersion()) {
613 Option::set(self::OPTION_NAME_MATOMO_INSTALL_VERSION, Version::VERSION);
614 }
615 }
616
617 /**
618 * Returns which Matomo version was used to install this Matomo for the first time.
619 */
620 public function getInstallVersion()
621 {
622 Option::clearCachedOption(self::OPTION_NAME_MATOMO_INSTALL_VERSION);
623 $version = Option::get(self::OPTION_NAME_MATOMO_INSTALL_VERSION);
624 if (!empty($version)) {
625 return $version;
626 }
627 }
628
629 /**
630 * Truncate all tables
631 */
632 public function truncateAllTables()
633 {
634 $tables = $this->getAllExistingTables();
635 foreach ($tables as $table) {
636 Db::query("TRUNCATE `$table`");
637 }
638 }
639
640 private function getTablePrefix()
641 {
642 return $this->getDbSettings()->getTablePrefix();
643 }
644
645 private function getTableEngine()
646 {
647 return $this->getDbSettings()->getEngine();
648 }
649
650 private function getDb()
651 {
652 return Db::get();
653 }
654
655 private function getDbSettings()
656 {
657 return new Db\Settings();
658 }
659
660 private function getDbName()
661 {
662 return $this->getDbSettings()->getDbName();
663 }
664
665 private function getAllExistingTables($prefixTables = false)
666 {
667 if (empty($prefixTables)) {
668 $prefixTables = $this->getTablePrefixEscaped();
669 }
670
671 return Db::get()->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "%'");
672 }
673
674 private function getTablePrefixEscaped()
675 {
676 $prefixTables = $this->getTablePrefix();
677 // '_' matches any character; force it to be literal
678 $prefixTables = str_replace('_', '\_', $prefixTables);
679 return $prefixTables;
680 }
681 }
682