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']);
98 self
::$add_drop_table = $GLOBALS['cfg']['Server']['tracking_add_drop_table'];
100 self
::$add_drop_view = $GLOBALS['cfg']['Server']['tracking_add_drop_view'];
102 self
::$add_drop_database
103 = $GLOBALS['cfg']['Server']['tracking_add_drop_database'];
105 self
::$default_tracking_set
106 = $GLOBALS['cfg']['Server']['tracking_default_statements'];
108 self
::$version_auto_create
109 = $GLOBALS['cfg']['Server']['tracking_version_auto_create'];
113 * Actually enables tracking. This needs to be done after all
114 * underlaying code is initialized.
120 static public function enable()
122 self
::$enabled = true;
126 * Gets the on/off value of the Tracker module, starts initialization.
130 * @return boolean (true=on|false=off)
132 static public function isActive()
134 if (! self
::$enabled) {
137 /* We need to avoid attempt to track any queries
138 * from PMA_getRelationsParam
140 self
::$enabled = false;
141 $cfgRelation = PMA_getRelationsParam();
142 /* Restore original state */
143 self
::$enabled = true;
144 if (! $cfgRelation['trackingwork']) {
149 if (isset(self
::$pma_table)) {
157 * Parses the name of a table from a SQL statement substring.
159 * @param string $string part of SQL statement
163 * @return string the name of table
165 static protected function getTableName($string)
167 if (strstr($string, '.')) {
168 $temp = explode('.', $string);
169 $tablename = $temp[1];
171 $tablename = $string;
174 $str = explode("\n", $tablename);
175 $tablename = $str[0];
177 $tablename = str_replace(';', '', $tablename);
178 $tablename = str_replace('`', '', $tablename);
179 $tablename = trim($tablename);
186 * Gets the tracking status of a table, is it active or deactive ?
188 * @param string $dbname name of database
189 * @param string $tablename name of table
193 * @return boolean true or false
195 static public function isTracked($dbname, $tablename)
197 if (! self
::$enabled) {
200 /* We need to avoid attempt to track any queries
201 * from PMA_getRelationsParam
203 self
::$enabled = false;
204 $cfgRelation = PMA_getRelationsParam();
205 /* Restore original state */
206 self
::$enabled = true;
207 if (! $cfgRelation['trackingwork']) {
211 $sql_query = " SELECT tracking_active FROM " . self
::$pma_table .
212 " WHERE db_name = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
213 " AND table_name = '" . PMA_Util
::sqlAddSlashes($tablename) . "' " .
214 " ORDER BY version DESC";
216 $row = PMA_DBI_fetch_array(PMA_queryAsControlUser($sql_query));
218 if (isset($row['tracking_active']) && $row['tracking_active'] == 1) {
226 * Returns the comment line for the log.
228 * @return string Comment, contains date and username
230 static public function getLogComment()
232 $date = date('Y-m-d H:i:s');
234 return "# log " . $date . " " . $GLOBALS['cfg']['Server']['user'] . "\n";
238 * Creates tracking version of a table / view
239 * (in other words: create a job to track future changes on the table).
241 * @param string $dbname name of database
242 * @param string $tablename name of table
243 * @param string $version version
244 * @param string $tracking_set set of tracking statements
245 * @param bool $is_view if table is a view
249 * @return int result of version insertion
251 static public function createVersion($dbname, $tablename, $version,
252 $tracking_set = '', $is_view = false
254 global $sql_backquotes, $export_type;
256 if ($tracking_set == '') {
257 $tracking_set = self
::$default_tracking_set;
260 // get Export SQL instance
261 include_once "libraries/plugin_interface.lib.php";
262 $export_sql_plugin = PMA_getPlugin(
265 'libraries/plugins/export/',
267 'export_type' => $export_type,
268 'single_table' => isset($single_table)
272 $sql_backquotes = true;
274 $date = date('Y-m-d H:i:s');
276 // Get data definition snapshot of table
278 $columns = PMA_DBI_get_columns($dbname, $tablename, null, true);
279 // int indices to reduce size
280 $columns = array_values($columns);
281 // remove Privileges to reduce size
282 for ($i = 0; $i < count($columns); $i++
) {
283 unset($columns[$i]['Privileges']);
286 $indexes = PMA_DBI_get_table_indexes($dbname, $tablename);
288 $snapshot = array('COLUMNS' => $columns, 'INDEXES' => $indexes);
289 $snapshot = serialize($snapshot);
291 // Get DROP TABLE / DROP VIEW and CREATE TABLE SQL statements
292 $sql_backquotes = true;
296 if (self
::$add_drop_table == true && $is_view == false) {
297 $create_sql .= self
::getLogComment()
298 . 'DROP TABLE IF EXISTS ' . PMA_Util
::backquote($tablename) . ";\n";
302 if (self
::$add_drop_view == true && $is_view == true) {
303 $create_sql .= self
::getLogComment()
304 . 'DROP VIEW IF EXISTS ' . PMA_Util
::backquote($tablename) . ";\n";
307 $create_sql .= self
::getLogComment() .
308 $export_sql_plugin->getTableDef($dbname, $tablename, "\n", "");
312 $sql_query = "/*NOTRACK*/\n" .
313 "INSERT INTO" . self
::$pma_table . " (" .
319 "schema_snapshot, " .
325 '" . PMA_Util
::sqlAddSlashes($dbname) . "',
326 '" . PMA_Util
::sqlAddSlashes($tablename) . "',
327 '" . PMA_Util
::sqlAddSlashes($version) . "',
328 '" . PMA_Util
::sqlAddSlashes($date) . "',
329 '" . PMA_Util
::sqlAddSlashes($date) . "',
330 '" . PMA_Util
::sqlAddSlashes($snapshot) . "',
331 '" . PMA_Util
::sqlAddSlashes($create_sql) . "',
332 '" . PMA_Util
::sqlAddSlashes("\n") . "',
333 '" . PMA_Util
::sqlAddSlashes(self
::_transformTrackingSet($tracking_set)) . "' )";
335 $result = PMA_queryAsControlUser($sql_query);
338 // Deactivate previous version
339 self
::deactivateTracking($dbname, $tablename, ($version - 1));
347 * Removes all tracking data for a table
349 * @param string $dbname name of database
350 * @param string $tablename name of table
354 * @return int result of version insertion
356 static public function deleteTracking($dbname, $tablename)
358 $sql_query = "/*NOTRACK*/\n"
359 . "DELETE FROM " . self
::$pma_table
360 . " WHERE `db_name` = '"
361 . PMA_Util
::sqlAddSlashes($dbname) . "'"
362 . " AND `table_name` = '"
363 . PMA_Util
::sqlAddSlashes($tablename) . "'";
364 $result = PMA_queryAsControlUser($sql_query);
370 * Creates tracking version of a database
371 * (in other words: create a job to track future changes on the database).
373 * @param string $dbname name of database
374 * @param string $version version
375 * @param string $query query
376 * @param string $tracking_set set of tracking statements
380 * @return int result of version insertion
382 static public function createDatabaseVersion($dbname, $version, $query,
383 $tracking_set = 'CREATE DATABASE,ALTER DATABASE,DROP DATABASE'
385 $date = date('Y-m-d H:i:s');
387 if ($tracking_set == '') {
388 $tracking_set = self
::$default_tracking_set;
391 include_once './libraries/export/sql.php';
395 if (self
::$add_drop_database == true) {
396 $create_sql .= self
::getLogComment()
397 . 'DROP DATABASE IF EXISTS ' . PMA_Util
::backquote($dbname) . ";\n";
400 $create_sql .= self
::getLogComment() . $query;
403 $sql_query = "/*NOTRACK*/\n" .
404 "INSERT INTO" . self
::$pma_table . " (" .
410 "schema_snapshot, " .
416 '" . PMA_Util
::sqlAddSlashes($dbname) . "',
417 '" . PMA_Util
::sqlAddSlashes('') . "',
418 '" . PMA_Util
::sqlAddSlashes($version) . "',
419 '" . PMA_Util
::sqlAddSlashes($date) . "',
420 '" . PMA_Util
::sqlAddSlashes($date) . "',
421 '" . PMA_Util
::sqlAddSlashes('') . "',
422 '" . PMA_Util
::sqlAddSlashes($create_sql) . "',
423 '" . PMA_Util
::sqlAddSlashes("\n") . "',
424 '" . PMA_Util
::sqlAddSlashes(self
::_transformTrackingSet($tracking_set)) . "' )";
426 $result = PMA_queryAsControlUser($sql_query);
434 * Changes tracking of a table.
436 * @param string $dbname name of database
437 * @param string $tablename name of table
438 * @param string $version version
439 * @param integer $new_state the new state of tracking
443 * @return int result of SQL query
445 static private function _changeTracking($dbname, $tablename,
449 $sql_query = " UPDATE " . self
::$pma_table .
450 " SET `tracking_active` = '" . $new_state . "' " .
451 " WHERE `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
452 " AND `table_name` = '" . PMA_Util
::sqlAddSlashes($tablename) . "' " .
453 " AND `version` = '" . PMA_Util
::sqlAddSlashes($version) . "' ";
455 $result = PMA_queryAsControlUser($sql_query);
461 * Changes tracking data of a table.
463 * @param string $dbname name of database
464 * @param string $tablename name of table
465 * @param string $version version
466 * @param string $type type of data(DDL || DML)
467 * @param string|array $new_data the new tracking data
471 * @return bool result of change
473 static public function changeTrackingData($dbname, $tablename,
474 $version, $type, $new_data
476 if ($type == 'DDL') {
477 $save_to = 'schema_sql';
478 } elseif ($type == 'DML') {
479 $save_to = 'data_sql';
483 $date = date('Y-m-d H:i:s');
485 $new_data_processed = '';
486 if (is_array($new_data)) {
487 foreach ($new_data as $data) {
488 $new_data_processed .= '# log ' . $date . ' ' . $data['username']
489 . PMA_Util
::sqlAddSlashes($data['statement']) . "\n";
492 $new_data_processed = $new_data;
495 $sql_query = " UPDATE " . self
::$pma_table .
496 " SET `" . $save_to . "` = '" . $new_data_processed . "' " .
497 " WHERE `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
498 " AND `table_name` = '" . PMA_Util
::sqlAddSlashes($tablename) . "' " .
499 " AND `version` = '" . PMA_Util
::sqlAddSlashes($version) . "' ";
501 $result = PMA_queryAsControlUser($sql_query);
507 * Activates tracking of a table.
509 * @param string $dbname name of database
510 * @param string $tablename name of table
511 * @param string $version version
515 * @return int result of SQL query
517 static public function activateTracking($dbname, $tablename, $version)
519 return self
::_changeTracking($dbname, $tablename, $version, 1);
524 * Deactivates tracking of a table.
526 * @param string $dbname name of database
527 * @param string $tablename name of table
528 * @param string $version version
532 * @return int result of SQL query
534 static public function deactivateTracking($dbname, $tablename, $version)
536 return self
::_changeTracking($dbname, $tablename, $version, 0);
541 * Gets the newest version of a tracking job
542 * (in other words: gets the HEAD version).
544 * @param string $dbname name of database
545 * @param string $tablename name of table
546 * @param string $statement tracked statement
550 * @return int (-1 if no version exists | > 0 if a version exists)
552 static public function getVersion($dbname, $tablename, $statement = null)
554 $sql_query = " SELECT MAX(version) FROM " . self
::$pma_table .
555 " WHERE `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
556 " AND `table_name` = '" . PMA_Util
::sqlAddSlashes($tablename) . "' ";
558 if ($statement != "") {
559 $sql_query .= PMA_DRIZZLE
560 ?
' AND tracking & ' . self
::_transformTrackingSet($statement) . ' <> 0'
561 : " AND FIND_IN_SET('" . $statement . "',tracking) > 0" ;
563 $row = PMA_DBI_fetch_array(PMA_queryAsControlUser($sql_query));
564 return isset($row[0])
571 * Gets the record of a tracking job.
573 * @param string $dbname name of database
574 * @param string $tablename name of table
575 * @param string $version version number
579 * @return mixed record DDM log, DDL log, structure snapshot, tracked statements.
581 static public function getTrackedData($dbname, $tablename, $version)
583 if (! isset(self
::$pma_table)) {
586 $sql_query = " SELECT * FROM " . self
::$pma_table .
587 " WHERE `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' ";
588 if (! empty($tablename)) {
589 $sql_query .= " AND `table_name` = '"
590 . PMA_Util
::sqlAddSlashes($tablename) ."' ";
592 $sql_query .= " AND `version` = '" . PMA_Util
::sqlAddSlashes($version) ."' ".
593 " ORDER BY `version` DESC LIMIT 1";
595 $mixed = PMA_DBI_fetch_assoc(PMA_queryAsControlUser($sql_query));
598 $log_schema_entries = explode('# log ', $mixed['schema_sql']);
599 $log_data_entries = explode('# log ', $mixed['data_sql']);
601 $ddl_date_from = $date = date('Y-m-d H:i:s');
606 // Iterate tracked data definition statements
607 // For each log entry we want to get date, username and statement
608 foreach ($log_schema_entries as $log_entry) {
609 if (trim($log_entry) != '') {
610 $date = substr($log_entry, 0, 19);
611 $username = substr($log_entry, 20, strpos($log_entry, "\n") - 20);
613 $ddl_date_from = $date;
615 $statement = rtrim(strstr($log_entry, "\n"));
617 $ddlog[] = array( 'date' => $date,
618 'username'=> $username,
619 'statement' => $statement );
624 $date_from = $ddl_date_from;
625 $date_to = $ddl_date_to = $date;
627 $dml_date_from = $date_from;
632 // Iterate tracked data manipulation statements
633 // For each log entry we want to get date, username and statement
634 foreach ($log_data_entries as $log_entry) {
635 if (trim($log_entry) != '') {
636 $date = substr($log_entry, 0, 19);
637 $username = substr($log_entry, 20, strpos($log_entry, "\n") - 20);
639 $dml_date_from = $date;
641 $statement = rtrim(strstr($log_entry, "\n"));
643 $dmlog[] = array( 'date' => $date,
644 'username' => $username,
645 'statement' => $statement );
650 $dml_date_to = $date;
652 // Define begin and end of date range for both logs
653 if (strtotime($ddl_date_from) <= strtotime($dml_date_from)) {
654 $data['date_from'] = $ddl_date_from;
656 $data['date_from'] = $dml_date_from;
658 if (strtotime($ddl_date_to) >= strtotime($dml_date_to)) {
659 $data['date_to'] = $ddl_date_to;
661 $data['date_to'] = $dml_date_to;
663 $data['ddlog'] = $ddlog;
664 $data['dmlog'] = $dmlog;
665 $data['tracking'] = self
::_transformTrackingSet($mixed['tracking']);
666 $data['schema_snapshot'] = $mixed['schema_snapshot'];
673 * Parses a query. Gets
674 * - statement identifier (UPDATE, ALTER TABLE, ...)
675 * - type of statement, is it part of DDL or DML ?
678 * @param string $query query
681 * @todo: using PMA SQL Parser when possible
682 * @todo: support multi-table/view drops
684 * @return mixed Array containing identifier, type and tablename.
687 static public function parseQuery($query)
690 // Usage of PMA_SQP does not work here
692 // require_once("libraries/sqlparser.lib.php");
693 // $parsed_sql = PMA_SQP_parse($query);
694 // $sql_info = PMA_SQP_analyze($parsed_sql);
696 $query = str_replace("\n", " ", $query);
697 $query = str_replace("\r", " ", $query);
699 $query = trim($query);
700 $query = trim($query, ' -');
702 $tokens = explode(" ", $query);
703 foreach ($tokens as $key => $value) {
704 $tokens[$key] = strtoupper($value);
707 // Parse USE statement, need it for SQL dump imports
708 if (substr($query, 0, 4) == 'USE ') {
709 $prefix = explode('USE ', $query);
710 $GLOBALS['db'] = self
::getTableName($prefix[1]);
717 $result['type'] = 'DDL';
719 // Parse CREATE VIEW statement
720 if (in_array('CREATE', $tokens) == true
721 && in_array('VIEW', $tokens) == true
722 && in_array('AS', $tokens) == true
724 $result['identifier'] = 'CREATE VIEW';
726 $index = array_search('VIEW', $tokens);
728 $result['tablename'] = strtolower(
729 self
::getTableName($tokens[$index +
1])
733 // Parse ALTER VIEW statement
734 if (in_array('ALTER', $tokens) == true
735 && in_array('VIEW', $tokens) == true
736 && in_array('AS', $tokens) == true
737 && ! isset($result['identifier'])
739 $result['identifier'] = 'ALTER VIEW';
741 $index = array_search('VIEW', $tokens);
743 $result['tablename'] = strtolower(
744 self
::getTableName($tokens[$index +
1])
748 // Parse DROP VIEW statement
749 if (! isset($result['identifier'])
750 && substr($query, 0, 10) == 'DROP VIEW '
752 $result['identifier'] = 'DROP VIEW';
754 $prefix = explode('DROP VIEW ', $query);
755 $str = strstr($prefix[1], 'IF EXISTS');
757 if ($str == false ) {
760 $result['tablename'] = self
::getTableName($str);
763 // Parse CREATE DATABASE statement
764 if (! isset($result['identifier'])
765 && substr($query, 0, 15) == 'CREATE DATABASE'
767 $result['identifier'] = 'CREATE DATABASE';
768 $str = str_replace('CREATE DATABASE', '', $query);
769 $str = str_replace('IF NOT EXISTS', '', $str);
771 $prefix = explode('DEFAULT ', $str);
773 $result['tablename'] = '';
774 $GLOBALS['db'] = self
::getTableName($prefix[0]);
777 // Parse ALTER DATABASE statement
778 if (! isset($result['identifier'])
779 && substr($query, 0, 14) == 'ALTER DATABASE'
781 $result['identifier'] = 'ALTER DATABASE';
782 $result['tablename'] = '';
785 // Parse DROP DATABASE statement
786 if (! isset($result['identifier'])
787 && substr($query, 0, 13) == 'DROP DATABASE'
789 $result['identifier'] = 'DROP DATABASE';
790 $str = str_replace('DROP DATABASE', '', $query);
791 $str = str_replace('IF EXISTS', '', $str);
792 $GLOBALS['db'] = self
::getTableName($str);
793 $result['tablename'] = '';
796 // Parse CREATE TABLE statement
797 if (! isset($result['identifier'])
798 && substr($query, 0, 12) == 'CREATE TABLE'
800 $result['identifier'] = 'CREATE TABLE';
801 $query = str_replace('IF NOT EXISTS', '', $query);
802 $prefix = explode('CREATE TABLE ', $query);
803 $suffix = explode('(', $prefix[1]);
804 $result['tablename'] = self
::getTableName($suffix[0]);
807 // Parse ALTER TABLE statement
808 if (! isset($result['identifier'])
809 && substr($query, 0, 12) == 'ALTER TABLE '
811 $result['identifier'] = 'ALTER TABLE';
813 $prefix = explode('ALTER TABLE ', $query);
814 $suffix = explode(' ', $prefix[1]);
815 $result['tablename'] = self
::getTableName($suffix[0]);
818 // Parse DROP TABLE statement
819 if (! isset($result['identifier'])
820 && substr($query, 0, 11) == 'DROP TABLE '
822 $result['identifier'] = 'DROP TABLE';
824 $prefix = explode('DROP TABLE ', $query);
825 $str = strstr($prefix[1], 'IF EXISTS');
827 if ($str == false ) {
830 $result['tablename'] = self
::getTableName($str);
833 // Parse CREATE INDEX statement
834 if (! isset($result['identifier'])
835 && (substr($query, 0, 12) == 'CREATE INDEX'
836 ||
substr($query, 0, 19) == 'CREATE UNIQUE INDEX'
837 ||
substr($query, 0, 20) == 'CREATE SPATIAL INDEX')
839 $result['identifier'] = 'CREATE INDEX';
840 $prefix = explode('ON ', $query);
841 $suffix = explode('(', $prefix[1]);
842 $result['tablename'] = self
::getTableName($suffix[0]);
845 // Parse DROP INDEX statement
846 if (! isset($result['identifier'])
847 && substr($query, 0, 10) == 'DROP INDEX'
849 $result['identifier'] = 'DROP INDEX';
850 $prefix = explode('ON ', $query);
851 $result['tablename'] = self
::getTableName($prefix[1]);
854 // Parse RENAME TABLE statement
855 if (! isset($result['identifier'])
856 && substr($query, 0, 13) == 'RENAME TABLE '
858 $result['identifier'] = 'RENAME TABLE';
859 $prefix = explode('RENAME TABLE ', $query);
860 $names = explode(' TO ', $prefix[1]);
861 $result['tablename'] = self
::getTableName($names[0]);
862 $result["tablename_after_rename"] = self
::getTableName($names[1]);
869 if (! isset($result['identifier'])) {
870 $result["type"] = 'DML';
872 // Parse UPDATE statement
873 if (! isset($result['identifier'])
874 && substr($query, 0, 6) == 'UPDATE'
876 $result['identifier'] = 'UPDATE';
877 $prefix = explode('UPDATE ', $query);
878 $suffix = explode(' ', $prefix[1]);
879 $result['tablename'] = self
::getTableName($suffix[0]);
882 // Parse INSERT INTO statement
883 if (! isset($result['identifier'])
884 && substr($query, 0, 11) == 'INSERT INTO'
886 $result['identifier'] = 'INSERT';
887 $prefix = explode('INSERT INTO', $query);
888 $suffix = explode('(', $prefix[1]);
889 $result['tablename'] = self
::getTableName($suffix[0]);
892 // Parse DELETE statement
893 if (! isset($result['identifier'])
894 && substr($query, 0, 6) == 'DELETE'
896 $result['identifier'] = 'DELETE';
897 $prefix = explode('FROM ', $query);
898 $suffix = explode(' ', $prefix[1]);
899 $result['tablename'] = self
::getTableName($suffix[0]);
902 // Parse TRUNCATE statement
903 if (! isset($result['identifier'])
904 && substr($query, 0, 8) == 'TRUNCATE'
906 $result['identifier'] = 'TRUNCATE';
907 $prefix = explode('TRUNCATE', $query);
908 $result['tablename'] = self
::getTableName($prefix[1]);
916 * Analyzes a given SQL statement and saves tracking data.
918 * @param string $query a SQL query
924 static public function handleQuery($query)
926 // If query is marked as untouchable, leave
927 if (strstr($query, "/*NOTRACK*/")) {
931 if (! (substr($query, -1) == ';')) {
932 $query = $query . ";\n";
934 // Get some information about query
935 $result = self
::parseQuery($query);
938 $dbname = trim(isset($GLOBALS['db']) ?
$GLOBALS['db'] : '', '`');
939 // $dbname can be empty, for example when coming from Synchronize
940 // and this is a query for the remote server
941 if (empty($dbname)) {
944 // Remove null bytes (preg_replace() is vulnerable in some
946 $dbname = str_replace("\0", "", $dbname);
948 // If we found a valid statement
949 if (isset($result['identifier'])) {
950 $version = self
::getVersion(
951 $dbname, $result['tablename'], $result['identifier']
954 // If version not exists and auto-creation is enabled
955 if (self
::$version_auto_create == true
956 && self
::isTracked($dbname, $result['tablename']) == false
959 // Create the version
961 switch ($result['identifier']) {
963 self
::createVersion($dbname, $result['tablename'], '1');
967 $dbname, $result['tablename'], '1', '', true
970 case 'CREATE DATABASE':
971 self
::createDatabaseVersion($dbname, '1', $query);
977 if (self
::isTracked($dbname, $result['tablename']) && $version != -1) {
978 if ($result['type'] == 'DDL') {
979 $save_to = 'schema_sql';
980 } elseif ($result['type'] == 'DML') {
981 $save_to = 'data_sql';
985 $date = date('Y-m-d H:i:s');
987 // Cut off `dbname`. from query
988 $query = preg_replace('/`' . $dbname . '`\s?\./', '', $query);
990 // Add log information
991 $query = self
::getLogComment() . $query ;
993 // Mark it as untouchable
994 $sql_query = " /*NOTRACK*/\n"
995 . " UPDATE " . self
::$pma_table
996 . " SET " . PMA_Util
::backquote($save_to)
997 . " = CONCAT( " . PMA_Util
::backquote($save_to) . ",'\n"
998 . PMA_Util
::sqlAddSlashes($query) . "') ,"
999 . " `date_updated` = '" . $date . "' ";
1001 // If table was renamed we have to change
1002 // the tablename attribute in pma_tracking too
1003 if ($result['identifier'] == 'RENAME TABLE') {
1004 $sql_query .= ', `table_name` = \''
1005 . PMA_Util
::sqlAddSlashes($result['tablename_after_rename'])
1009 // Save the tracking information only for
1011 // 2. the table / view
1012 // 3. the statements
1015 " WHERE FIND_IN_SET('" . $result['identifier'] . "',tracking) > 0" .
1016 " AND `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
1017 " AND `table_name` = '" . PMA_Util
::sqlAddSlashes($result['tablename']) . "' " .
1018 " AND `version` = '" . PMA_Util
::sqlAddSlashes($version) . "' ";
1020 $result = PMA_queryAsControlUser($sql_query);
1026 * Transforms tracking set for Drizzle, which has no SET type
1028 * Converts int<>string for Drizzle, does nothing for MySQL
1030 * @param int|string $tracking_set
1032 * @return int|string
1034 static private function _transformTrackingSet($tracking_set)
1037 return $tracking_set;
1040 // init conversion array (key 3 doesn't exist in calculated array)
1041 if (isset(self
::$_tracking_set_flags[3])) {
1043 $set = self
::$_tracking_set_flags;
1045 for ($i = 0; $i < count($set); $i++
) {
1047 $array[$flag] = $set[$i];
1048 $array[$set[$i]] = $flag;
1050 self
::$_tracking_set_flags = $array;
1053 if (is_numeric($tracking_set)) {
1054 // int > string conversion
1056 // count/2 - conversion table has both int > string
1057 // and string > int values
1058 for ($i = 0; $i < count(self
::$_tracking_set_flags)/2; $i++
) {
1060 if ($tracking_set & $flag) {
1061 $aflags[] = self
::$_tracking_set_flags[$flag];
1064 $flags = implode(',', $aflags);
1066 // string > int conversion
1068 foreach (explode(',', $tracking_set) as $strflag) {
1069 if ($strflag == '') {
1072 $flags |
= self
::$_tracking_set_flags[$strflag];