2 /* vim: set expandtab sw=4 ts=4 sts=4: */
4 * Tracking changes on databases, tables and views
8 if (! defined('PHPMYADMIN')) {
13 * This class tracks changes on databases, tables and views.
17 * @todo use stristr instead of strstr
22 * Whether tracking is ready.
24 static protected $enabled = false;
27 * Defines the internal PMA table which contains tracking data.
32 static protected $pma_table;
35 * Defines the usage of DROP TABLE statment in SQL dumps.
40 static protected $add_drop_table;
43 * Defines the usage of DROP VIEW statment in SQL dumps.
48 static protected $add_drop_view;
51 * Defines the usage of DROP DATABASE statment in SQL dumps.
56 static protected $add_drop_database;
59 * Defines auto-creation of tracking versions.
63 static protected $version_auto_create;
66 * Defines the default set of tracked statements.
70 static protected $default_tracking_set;
73 * Flags copied from `tracking` column definition in `pma_tracking` table.
74 * Used for column type conversion in Drizzle.
78 static private $_tracking_set_flags = array(
79 'UPDATE','REPLACE','INSERT','DELETE','TRUNCATE','CREATE DATABASE',
80 'ALTER DATABASE','DROP DATABASE','CREATE TABLE','ALTER TABLE',
81 'RENAME TABLE','DROP TABLE','CREATE INDEX','DROP INDEX',
82 'CREATE VIEW','ALTER VIEW','DROP VIEW'
87 * Initializes settings.
93 static protected function init()
95 self
::$pma_table = PMA_Util
::backquote($GLOBALS['cfg']['Server']['pmadb'])
96 . '.' . PMA_Util
::backquote($GLOBALS['cfg']['Server']['tracking']);
99 = $GLOBALS['cfg']['Server']['tracking_add_drop_table'];
102 = $GLOBALS['cfg']['Server']['tracking_add_drop_view'];
104 self
::$add_drop_database
105 = $GLOBALS['cfg']['Server']['tracking_add_drop_database'];
107 self
::$default_tracking_set
108 = $GLOBALS['cfg']['Server']['tracking_default_statements'];
110 self
::$version_auto_create
111 = $GLOBALS['cfg']['Server']['tracking_version_auto_create'];
115 * Actually enables tracking. This needs to be done after all
116 * underlaying code is initialized.
122 static public function enable()
124 self
::$enabled = true;
128 * Gets the on/off value of the Tracker module, starts initialization.
132 * @return boolean (true=on|false=off)
134 static public function isActive()
136 if (! self
::$enabled) {
139 /* We need to avoid attempt to track any queries
140 * from PMA_getRelationsParam
142 self
::$enabled = false;
143 $cfgRelation = PMA_getRelationsParam();
144 /* Restore original state */
145 self
::$enabled = true;
146 if (! $cfgRelation['trackingwork']) {
151 if (isset(self
::$pma_table)) {
159 * Parses the name of a table from a SQL statement substring.
161 * @param string $string part of SQL statement
165 * @return string the name of table
167 static protected function getTableName($string)
169 if (strstr($string, '.')) {
170 $temp = explode('.', $string);
171 $tablename = $temp[1];
173 $tablename = $string;
176 $str = explode("\n", $tablename);
177 $tablename = $str[0];
179 $tablename = str_replace(';', '', $tablename);
180 $tablename = str_replace('`', '', $tablename);
181 $tablename = trim($tablename);
188 * Gets the tracking status of a table, is it active or deactive ?
190 * @param string $dbname name of database
191 * @param string $tablename name of table
195 * @return boolean true or false
197 static public function isTracked($dbname, $tablename)
199 if (! self
::$enabled) {
202 /* We need to avoid attempt to track any queries
203 * from PMA_getRelationsParam
205 self
::$enabled = false;
206 $cfgRelation = PMA_getRelationsParam();
207 /* Restore original state */
208 self
::$enabled = true;
209 if (! $cfgRelation['trackingwork']) {
213 $sql_query = " SELECT tracking_active FROM " . self
::$pma_table .
214 " WHERE db_name = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
215 " AND table_name = '" . PMA_Util
::sqlAddSlashes($tablename) . "' " .
216 " ORDER BY version DESC";
218 $row = $GLOBALS['dbi']->fetchArray(PMA_queryAsControlUser($sql_query));
220 if (isset($row['tracking_active']) && $row['tracking_active'] == 1) {
228 * Returns the comment line for the log.
230 * @return string Comment, contains date and username
232 static public function getLogComment()
234 $date = date('Y-m-d H:i:s');
236 return "# log " . $date . " " . $GLOBALS['cfg']['Server']['user'] . "\n";
240 * Creates tracking version of a table / view
241 * (in other words: create a job to track future changes on the table).
243 * @param string $dbname name of database
244 * @param string $tablename name of table
245 * @param string $version version
246 * @param string $tracking_set set of tracking statements
247 * @param bool $is_view if table is a view
251 * @return int result of version insertion
253 static public function createVersion($dbname, $tablename, $version,
254 $tracking_set = '', $is_view = false
256 global $sql_backquotes, $export_type;
258 if ($tracking_set == '') {
259 $tracking_set = self
::$default_tracking_set;
262 // get Export SQL instance
263 include_once "libraries/plugin_interface.lib.php";
264 $export_sql_plugin = PMA_getPlugin(
267 'libraries/plugins/export/',
269 'export_type' => $export_type,
270 'single_table' => false,
274 $sql_backquotes = true;
276 $date = date('Y-m-d H:i:s');
278 // Get data definition snapshot of table
280 $columns = $GLOBALS['dbi']->getColumns($dbname, $tablename, null, true);
281 // int indices to reduce size
282 $columns = array_values($columns);
283 // remove Privileges to reduce size
284 for ($i = 0, $nb = count($columns); $i < $nb; $i++
) {
285 unset($columns[$i]['Privileges']);
288 $indexes = $GLOBALS['dbi']->getTableIndexes($dbname, $tablename);
290 $snapshot = array('COLUMNS' => $columns, 'INDEXES' => $indexes);
291 $snapshot = serialize($snapshot);
293 // Get DROP TABLE / DROP VIEW and CREATE TABLE SQL statements
294 $sql_backquotes = true;
298 if (self
::$add_drop_table == true && $is_view == false) {
299 $create_sql .= self
::getLogComment()
300 . 'DROP TABLE IF EXISTS ' . PMA_Util
::backquote($tablename) . ";\n";
304 if (self
::$add_drop_view == true && $is_view == true) {
305 $create_sql .= self
::getLogComment()
306 . 'DROP VIEW IF EXISTS ' . PMA_Util
::backquote($tablename) . ";\n";
309 $create_sql .= self
::getLogComment() .
310 $export_sql_plugin->getTableDef($dbname, $tablename, "\n", "");
314 $sql_query = "/*NOTRACK*/\n" .
315 "INSERT INTO" . self
::$pma_table . " (" .
321 "schema_snapshot, " .
327 '" . PMA_Util
::sqlAddSlashes($dbname) . "',
328 '" . PMA_Util
::sqlAddSlashes($tablename) . "',
329 '" . PMA_Util
::sqlAddSlashes($version) . "',
330 '" . PMA_Util
::sqlAddSlashes($date) . "',
331 '" . PMA_Util
::sqlAddSlashes($date) . "',
332 '" . PMA_Util
::sqlAddSlashes($snapshot) . "',
333 '" . PMA_Util
::sqlAddSlashes($create_sql) . "',
334 '" . PMA_Util
::sqlAddSlashes("\n") . "',
335 '" . PMA_Util
::sqlAddSlashes(self
::_transformTrackingSet($tracking_set))
338 $result = PMA_queryAsControlUser($sql_query);
341 // Deactivate previous version
342 self
::deactivateTracking($dbname, $tablename, ($version - 1));
350 * Removes all tracking data for a table
352 * @param string $dbname name of database
353 * @param string $tablename name of table
357 * @return int result of version insertion
359 static public function deleteTracking($dbname, $tablename)
361 $sql_query = "/*NOTRACK*/\n"
362 . "DELETE FROM " . self
::$pma_table
363 . " WHERE `db_name` = '"
364 . PMA_Util
::sqlAddSlashes($dbname) . "'"
365 . " AND `table_name` = '"
366 . PMA_Util
::sqlAddSlashes($tablename) . "'";
367 $result = PMA_queryAsControlUser($sql_query);
373 * Creates tracking version of a database
374 * (in other words: create a job to track future changes on the database).
376 * @param string $dbname name of database
377 * @param string $version version
378 * @param string $query query
379 * @param string $tracking_set set of tracking statements
383 * @return int result of version insertion
385 static public function createDatabaseVersion($dbname, $version, $query,
386 $tracking_set = 'CREATE DATABASE,ALTER DATABASE,DROP DATABASE'
388 $date = date('Y-m-d H:i:s');
390 if ($tracking_set == '') {
391 $tracking_set = self
::$default_tracking_set;
396 if (self
::$add_drop_database == true) {
397 $create_sql .= self
::getLogComment()
398 . 'DROP DATABASE IF EXISTS ' . PMA_Util
::backquote($dbname) . ";\n";
401 $create_sql .= self
::getLogComment() . $query;
404 $sql_query = "/*NOTRACK*/\n" .
405 "INSERT INTO" . self
::$pma_table . " (" .
411 "schema_snapshot, " .
417 '" . PMA_Util
::sqlAddSlashes($dbname) . "',
418 '" . PMA_Util
::sqlAddSlashes('') . "',
419 '" . PMA_Util
::sqlAddSlashes($version) . "',
420 '" . PMA_Util
::sqlAddSlashes($date) . "',
421 '" . PMA_Util
::sqlAddSlashes($date) . "',
422 '" . PMA_Util
::sqlAddSlashes('') . "',
423 '" . PMA_Util
::sqlAddSlashes($create_sql) . "',
424 '" . PMA_Util
::sqlAddSlashes("\n") . "',
425 '" . PMA_Util
::sqlAddSlashes(self
::_transformTrackingSet($tracking_set))
428 $result = PMA_queryAsControlUser($sql_query);
436 * Changes tracking of a table.
438 * @param string $dbname name of database
439 * @param string $tablename name of table
440 * @param string $version version
441 * @param integer $new_state the new state of tracking
445 * @return int result of SQL query
447 static private function _changeTracking($dbname, $tablename,
451 $sql_query = " UPDATE " . self
::$pma_table .
452 " SET `tracking_active` = '" . $new_state . "' " .
453 " WHERE `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
454 " AND `table_name` = '" . PMA_Util
::sqlAddSlashes($tablename) . "' " .
455 " AND `version` = '" . PMA_Util
::sqlAddSlashes($version) . "' ";
457 $result = PMA_queryAsControlUser($sql_query);
463 * Changes tracking data of a table.
465 * @param string $dbname name of database
466 * @param string $tablename name of table
467 * @param string $version version
468 * @param string $type type of data(DDL || DML)
469 * @param string|array $new_data the new tracking data
473 * @return bool result of change
475 static public function changeTrackingData($dbname, $tablename,
476 $version, $type, $new_data
478 if ($type == 'DDL') {
479 $save_to = 'schema_sql';
480 } elseif ($type == 'DML') {
481 $save_to = 'data_sql';
485 $date = date('Y-m-d H:i:s');
487 $new_data_processed = '';
488 if (is_array($new_data)) {
489 foreach ($new_data as $data) {
490 $new_data_processed .= '# log ' . $date . ' ' . $data['username']
491 . PMA_Util
::sqlAddSlashes($data['statement']) . "\n";
494 $new_data_processed = $new_data;
497 $sql_query = " UPDATE " . self
::$pma_table .
498 " SET `" . $save_to . "` = '" . $new_data_processed . "' " .
499 " WHERE `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
500 " AND `table_name` = '" . PMA_Util
::sqlAddSlashes($tablename) . "' " .
501 " AND `version` = '" . PMA_Util
::sqlAddSlashes($version) . "' ";
503 $result = PMA_queryAsControlUser($sql_query);
509 * Activates tracking of a table.
511 * @param string $dbname name of database
512 * @param string $tablename name of table
513 * @param string $version version
517 * @return int result of SQL query
519 static public function activateTracking($dbname, $tablename, $version)
521 return self
::_changeTracking($dbname, $tablename, $version, 1);
526 * Deactivates tracking of a table.
528 * @param string $dbname name of database
529 * @param string $tablename name of table
530 * @param string $version version
534 * @return int result of SQL query
536 static public function deactivateTracking($dbname, $tablename, $version)
538 return self
::_changeTracking($dbname, $tablename, $version, 0);
543 * Gets the newest version of a tracking job
544 * (in other words: gets the HEAD version).
546 * @param string $dbname name of database
547 * @param string $tablename name of table
548 * @param string $statement tracked statement
552 * @return int (-1 if no version exists | > 0 if a version exists)
554 static public function getVersion($dbname, $tablename, $statement = null)
556 $sql_query = " SELECT MAX(version) FROM " . self
::$pma_table .
557 " WHERE `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
558 " AND `table_name` = '" . PMA_Util
::sqlAddSlashes($tablename) . "' ";
560 if ($statement != "") {
562 $sql_query .= ' AND tracking & '
563 . self
::_transformTrackingSet($statement) . ' <> 0';
565 $sql_query .= " AND FIND_IN_SET('"
566 . $statement . "',tracking) > 0" ;
569 $row = $GLOBALS['dbi']->fetchArray(PMA_queryAsControlUser($sql_query));
570 return isset($row[0])
577 * Gets the record of a tracking job.
579 * @param string $dbname name of database
580 * @param string $tablename name of table
581 * @param string $version version number
585 * @return mixed record DDM log, DDL log, structure snapshot, tracked
588 static public function getTrackedData($dbname, $tablename, $version)
590 if (! isset(self
::$pma_table)) {
593 $sql_query = " SELECT * FROM " . self
::$pma_table .
594 " WHERE `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' ";
595 if (! empty($tablename)) {
596 $sql_query .= " AND `table_name` = '"
597 . PMA_Util
::sqlAddSlashes($tablename) . "' ";
599 $sql_query .= " AND `version` = '" . PMA_Util
::sqlAddSlashes($version)
600 . "' " . " ORDER BY `version` DESC LIMIT 1";
602 $mixed = $GLOBALS['dbi']->fetchAssoc(PMA_queryAsControlUser($sql_query));
605 $log_schema_entries = explode('# log ', $mixed['schema_sql']);
606 $log_data_entries = explode('# log ', $mixed['data_sql']);
608 $ddl_date_from = $date = date('Y-m-d H:i:s');
613 // Iterate tracked data definition statements
614 // For each log entry we want to get date, username and statement
615 foreach ($log_schema_entries as $log_entry) {
616 if (trim($log_entry) != '') {
617 $date = substr($log_entry, 0, 19);
618 $username = substr($log_entry, 20, strpos($log_entry, "\n") - 20);
620 $ddl_date_from = $date;
622 $statement = rtrim(strstr($log_entry, "\n"));
624 $ddlog[] = array( 'date' => $date,
625 'username'=> $username,
626 'statement' => $statement );
631 $date_from = $ddl_date_from;
632 $ddl_date_to = $date;
634 $dml_date_from = $date_from;
639 // Iterate tracked data manipulation statements
640 // For each log entry we want to get date, username and statement
641 foreach ($log_data_entries as $log_entry) {
642 if (trim($log_entry) != '') {
643 $date = substr($log_entry, 0, 19);
644 $username = substr($log_entry, 20, strpos($log_entry, "\n") - 20);
646 $dml_date_from = $date;
648 $statement = rtrim(strstr($log_entry, "\n"));
650 $dmlog[] = array( 'date' => $date,
651 'username' => $username,
652 'statement' => $statement );
657 $dml_date_to = $date;
659 // Define begin and end of date range for both logs
661 if (strtotime($ddl_date_from) <= strtotime($dml_date_from)) {
662 $data['date_from'] = $ddl_date_from;
664 $data['date_from'] = $dml_date_from;
666 if (strtotime($ddl_date_to) >= strtotime($dml_date_to)) {
667 $data['date_to'] = $ddl_date_to;
669 $data['date_to'] = $dml_date_to;
671 $data['ddlog'] = $ddlog;
672 $data['dmlog'] = $dmlog;
673 $data['tracking'] = self
::_transformTrackingSet($mixed['tracking']);
674 $data['schema_snapshot'] = $mixed['schema_snapshot'];
681 * Parses a query. Gets
682 * - statement identifier (UPDATE, ALTER TABLE, ...)
683 * - type of statement, is it part of DDL or DML ?
686 * @param string $query query
689 * @todo: using PMA SQL Parser when possible
690 * @todo: support multi-table/view drops
692 * @return mixed Array containing identifier, type and tablename.
695 static public function parseQuery($query)
698 // Usage of PMA_SQP does not work here
700 // require_once("libraries/sqlparser.lib.php");
701 // $parsed_sql = PMA_SQP_parse($query);
702 // $sql_info = PMA_SQP_analyze($parsed_sql);
704 $query = str_replace("\n", " ", $query);
705 $query = str_replace("\r", " ", $query);
707 $query = trim($query);
708 $query = trim($query, ' -');
710 $tokens = explode(" ", $query);
711 foreach ($tokens as $key => $value) {
712 $tokens[$key] = strtoupper($value);
715 // Parse USE statement, need it for SQL dump imports
716 if (substr($query, 0, 4) == 'USE ') {
717 $prefix = explode('USE ', $query);
718 $GLOBALS['db'] = self
::getTableName($prefix[1]);
726 $result['type'] = 'DDL';
728 // Parse CREATE VIEW statement
729 if (in_array('CREATE', $tokens) == true
730 && in_array('VIEW', $tokens) == true
731 && in_array('AS', $tokens) == true
733 $result['identifier'] = 'CREATE VIEW';
735 $index = array_search('VIEW', $tokens);
737 $result['tablename'] = strtolower(
738 self
::getTableName($tokens[$index +
1])
742 // Parse ALTER VIEW statement
743 if (in_array('ALTER', $tokens) == true
744 && in_array('VIEW', $tokens) == true
745 && in_array('AS', $tokens) == true
746 && ! isset($result['identifier'])
748 $result['identifier'] = 'ALTER VIEW';
750 $index = array_search('VIEW', $tokens);
752 $result['tablename'] = strtolower(
753 self
::getTableName($tokens[$index +
1])
757 // Parse DROP VIEW statement
758 if (! isset($result['identifier'])
759 && substr($query, 0, 10) == 'DROP VIEW '
761 $result['identifier'] = 'DROP VIEW';
763 $prefix = explode('DROP VIEW ', $query);
764 $str = strstr($prefix[1], 'IF EXISTS');
766 if ($str == false ) {
769 $result['tablename'] = self
::getTableName($str);
772 // Parse CREATE DATABASE statement
773 if (! isset($result['identifier'])
774 && substr($query, 0, 15) == 'CREATE DATABASE'
776 $result['identifier'] = 'CREATE DATABASE';
777 $str = str_replace('CREATE DATABASE', '', $query);
778 $str = str_replace('IF NOT EXISTS', '', $str);
780 $prefix = explode('DEFAULT ', $str);
782 $result['tablename'] = '';
783 $GLOBALS['db'] = self
::getTableName($prefix[0]);
786 // Parse ALTER DATABASE statement
787 if (! isset($result['identifier'])
788 && substr($query, 0, 14) == 'ALTER DATABASE'
790 $result['identifier'] = 'ALTER DATABASE';
791 $result['tablename'] = '';
794 // Parse DROP DATABASE statement
795 if (! isset($result['identifier'])
796 && substr($query, 0, 13) == 'DROP DATABASE'
798 $result['identifier'] = 'DROP DATABASE';
799 $str = str_replace('DROP DATABASE', '', $query);
800 $str = str_replace('IF EXISTS', '', $str);
801 $GLOBALS['db'] = self
::getTableName($str);
802 $result['tablename'] = '';
805 // Parse CREATE TABLE statement
806 if (! isset($result['identifier'])
807 && substr($query, 0, 12) == 'CREATE TABLE'
809 $result['identifier'] = 'CREATE TABLE';
810 $query = str_replace('IF NOT EXISTS', '', $query);
811 $prefix = explode('CREATE TABLE ', $query);
812 $suffix = explode('(', $prefix[1]);
813 $result['tablename'] = self
::getTableName($suffix[0]);
816 // Parse ALTER TABLE statement
817 if (! isset($result['identifier'])
818 && substr($query, 0, 12) == 'ALTER TABLE '
820 $result['identifier'] = 'ALTER TABLE';
822 $prefix = explode('ALTER TABLE ', $query);
823 $suffix = explode(' ', $prefix[1]);
824 $result['tablename'] = self
::getTableName($suffix[0]);
827 // Parse DROP TABLE statement
828 if (! isset($result['identifier'])
829 && substr($query, 0, 11) == 'DROP TABLE '
831 $result['identifier'] = 'DROP TABLE';
833 $prefix = explode('DROP TABLE ', $query);
834 $str = strstr($prefix[1], 'IF EXISTS');
836 if ($str == false ) {
839 $result['tablename'] = self
::getTableName($str);
842 // Parse CREATE INDEX statement
843 if (! isset($result['identifier'])
844 && (substr($query, 0, 12) == 'CREATE INDEX'
845 ||
substr($query, 0, 19) == 'CREATE UNIQUE INDEX'
846 ||
substr($query, 0, 20) == 'CREATE SPATIAL INDEX')
848 $result['identifier'] = 'CREATE INDEX';
849 $prefix = explode('ON ', $query);
850 $suffix = explode('(', $prefix[1]);
851 $result['tablename'] = self
::getTableName($suffix[0]);
854 // Parse DROP INDEX statement
855 if (! isset($result['identifier'])
856 && substr($query, 0, 10) == 'DROP INDEX'
858 $result['identifier'] = 'DROP INDEX';
859 $prefix = explode('ON ', $query);
860 $result['tablename'] = self
::getTableName($prefix[1]);
863 // Parse RENAME TABLE statement
864 if (! isset($result['identifier'])
865 && substr($query, 0, 13) == 'RENAME TABLE '
867 $result['identifier'] = 'RENAME TABLE';
868 $prefix = explode('RENAME TABLE ', $query);
869 $names = explode(' TO ', $prefix[1]);
870 $result['tablename'] = self
::getTableName($names[0]);
871 $result["tablename_after_rename"] = self
::getTableName($names[1]);
878 if (! isset($result['identifier'])) {
879 $result["type"] = 'DML';
881 // Parse UPDATE statement
882 if (! isset($result['identifier'])
883 && substr($query, 0, 6) == 'UPDATE'
885 $result['identifier'] = 'UPDATE';
886 $prefix = explode('UPDATE ', $query);
887 $suffix = explode(' ', $prefix[1]);
888 $result['tablename'] = self
::getTableName($suffix[0]);
891 // Parse INSERT INTO statement
892 if (! isset($result['identifier'])
893 && substr($query, 0, 11) == 'INSERT INTO'
895 $result['identifier'] = 'INSERT';
896 $prefix = explode('INSERT INTO', $query);
897 $suffix = explode('(', $prefix[1]);
898 $result['tablename'] = self
::getTableName($suffix[0]);
901 // Parse DELETE statement
902 if (! isset($result['identifier'])
903 && substr($query, 0, 6) == 'DELETE'
905 $result['identifier'] = 'DELETE';
906 $prefix = explode('FROM ', $query);
907 $suffix = explode(' ', $prefix[1]);
908 $result['tablename'] = self
::getTableName($suffix[0]);
911 // Parse TRUNCATE statement
912 if (! isset($result['identifier'])
913 && substr($query, 0, 8) == 'TRUNCATE'
915 $result['identifier'] = 'TRUNCATE';
916 $prefix = explode('TRUNCATE', $query);
917 $result['tablename'] = self
::getTableName($prefix[1]);
925 * Analyzes a given SQL statement and saves tracking data.
927 * @param string $query a SQL query
933 static public function handleQuery($query)
935 // If query is marked as untouchable, leave
936 if (strstr($query, "/*NOTRACK*/")) {
940 if (! (substr($query, -1) == ';')) {
941 $query = $query . ";\n";
943 // Get some information about query
944 $result = self
::parseQuery($query);
947 $dbname = trim(isset($GLOBALS['db']) ?
$GLOBALS['db'] : '', '`');
948 // $dbname can be empty, for example when coming from Synchronize
949 // and this is a query for the remote server
950 if (empty($dbname)) {
954 // If we found a valid statement
955 if (isset($result['identifier'])) {
956 $version = self
::getVersion(
957 $dbname, $result['tablename'], $result['identifier']
960 // If version not exists and auto-creation is enabled
961 if (self
::$version_auto_create == true
962 && self
::isTracked($dbname, $result['tablename']) == false
965 // Create the version
967 switch ($result['identifier']) {
969 self
::createVersion($dbname, $result['tablename'], '1');
973 $dbname, $result['tablename'], '1', '', true
976 case 'CREATE DATABASE':
977 self
::createDatabaseVersion($dbname, '1', $query);
983 if (self
::isTracked($dbname, $result['tablename']) && $version != -1) {
984 if ($result['type'] == 'DDL') {
985 $save_to = 'schema_sql';
986 } elseif ($result['type'] == 'DML') {
987 $save_to = 'data_sql';
991 $date = date('Y-m-d H:i:s');
993 // Cut off `dbname`. from query
994 $query = preg_replace('/`' . preg_quote($dbname) . '`\s?\./', '', $query);
996 // Add log information
997 $query = self
::getLogComment() . $query ;
999 // Mark it as untouchable
1000 $sql_query = " /*NOTRACK*/\n"
1001 . " UPDATE " . self
::$pma_table
1002 . " SET " . PMA_Util
::backquote($save_to)
1003 . " = CONCAT( " . PMA_Util
::backquote($save_to) . ",'\n"
1004 . PMA_Util
::sqlAddSlashes($query) . "') ,"
1005 . " `date_updated` = '" . $date . "' ";
1007 // If table was renamed we have to change
1008 // the tablename attribute in pma_tracking too
1009 if ($result['identifier'] == 'RENAME TABLE') {
1010 $sql_query .= ', `table_name` = \''
1011 . PMA_Util
::sqlAddSlashes($result['tablename_after_rename'])
1015 // Save the tracking information only for
1017 // 2. the table / view
1018 // 3. the statements
1021 " WHERE FIND_IN_SET('" . $result['identifier'] . "',tracking) > 0" .
1022 " AND `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
1023 " AND `table_name` = '"
1024 . PMA_Util
::sqlAddSlashes($result['tablename']) . "' " .
1025 " AND `version` = '" . PMA_Util
::sqlAddSlashes($version) . "' ";
1027 $result = PMA_queryAsControlUser($sql_query);
1033 * Transforms tracking set for Drizzle, which has no SET type
1035 * Converts int<>string for Drizzle, does nothing for MySQL
1037 * @param int|string $tracking_set Set to convert
1039 * @return int|string
1041 static private function _transformTrackingSet($tracking_set)
1044 return $tracking_set;
1047 // init conversion array (key 3 doesn't exist in calculated array)
1048 if (isset(self
::$_tracking_set_flags[3])) {
1050 $set = self
::$_tracking_set_flags;
1052 for ($i = 0, $nb = count($set); $i < $nb; $i++
) {
1054 $array[$flag] = $set[$i];
1055 $array[$set[$i]] = $flag;
1057 self
::$_tracking_set_flags = $array;
1060 if (is_numeric($tracking_set)) {
1061 // int > string conversion
1063 // count/2 - conversion table has both int > string
1064 // and string > int values
1065 for ($i = 0, $nb = count(self
::$_tracking_set_flags)/2; $i < $nb; $i++
) {
1067 if ($tracking_set & $flag) {
1068 $aflags[] = self
::$_tracking_set_flags[$flag];
1071 $flags = implode(',', $aflags);
1073 // string > int conversion
1075 foreach (explode(',', $tracking_set) as $strflag) {
1076 if ($strflag == '') {
1079 $flags |
= self
::$_tracking_set_flags[$strflag];