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 * Flags copied from `tracking` column definition in `pma_tracking` table.
28 * Used for column type conversion in Drizzle.
32 static private $_tracking_set_flags = array(
33 'UPDATE','REPLACE','INSERT','DELETE','TRUNCATE','CREATE DATABASE',
34 'ALTER DATABASE','DROP DATABASE','CREATE TABLE','ALTER TABLE',
35 'RENAME TABLE','DROP TABLE','CREATE INDEX','DROP INDEX',
36 'CREATE VIEW','ALTER VIEW','DROP VIEW'
40 * Actually enables tracking. This needs to be done after all
41 * underlaying code is initialized.
47 static public function enable()
49 self
::$enabled = true;
53 * Gets the on/off value of the Tracker module, starts initialization.
57 * @return boolean (true=on|false=off)
59 static public function isActive()
61 if (! self
::$enabled) {
64 /* We need to avoid attempt to track any queries
65 * from PMA_getRelationsParam
67 self
::$enabled = false;
68 $cfgRelation = PMA_getRelationsParam();
69 /* Restore original state */
70 self
::$enabled = true;
71 if (! $cfgRelation['trackingwork']) {
75 $pma_table = self
::_getTrackingTable();
76 if (isset($pma_table)) {
84 * Parses the name of a table from a SQL statement substring.
86 * @param string $string part of SQL statement
90 * @return string the name of table
92 static protected function getTableName($string)
94 if (/*overload*/mb_strstr($string, '.')) {
95 $temp = explode('.', $string);
96 $tablename = $temp[1];
101 $str = explode("\n", $tablename);
102 $tablename = $str[0];
104 $tablename = str_replace(';', '', $tablename);
105 $tablename = str_replace('`', '', $tablename);
106 $tablename = trim($tablename);
113 * Gets the tracking status of a table, is it active or deactive ?
115 * @param string $dbname name of database
116 * @param string $tablename name of table
120 * @return boolean true or false
122 static public function isTracked($dbname, $tablename)
124 if (! self
::$enabled) {
127 /* We need to avoid attempt to track any queries
128 * from PMA_getRelationsParam
130 self
::$enabled = false;
131 $cfgRelation = PMA_getRelationsParam();
132 /* Restore original state */
133 self
::$enabled = true;
134 if (! $cfgRelation['trackingwork']) {
138 $sql_query = " SELECT tracking_active FROM " . self
::_getTrackingTable() .
139 " WHERE db_name = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
140 " AND table_name = '" . PMA_Util
::sqlAddSlashes($tablename) . "' " .
141 " ORDER BY version DESC";
143 $row = $GLOBALS['dbi']->fetchArray(PMA_queryAsControlUser($sql_query));
145 if (isset($row['tracking_active']) && $row['tracking_active'] == 1) {
153 * Returns the comment line for the log.
155 * @return string Comment, contains date and username
157 static public function getLogComment()
159 $date = date('Y-m-d H:i:s');
161 return "# log " . $date . " " . $GLOBALS['cfg']['Server']['user'] . "\n";
165 * Creates tracking version of a table / view
166 * (in other words: create a job to track future changes on the table).
168 * @param string $dbname name of database
169 * @param string $tablename name of table
170 * @param string $version version
171 * @param string $tracking_set set of tracking statements
172 * @param bool $is_view if table is a view
176 * @return int result of version insertion
178 static public function createVersion($dbname, $tablename, $version,
179 $tracking_set = '', $is_view = false
181 global $sql_backquotes, $export_type;
183 if ($tracking_set == '') {
185 = $GLOBALS['cfg']['Server']['tracking_default_statements'];
188 // get Export SQL instance
189 include_once "libraries/plugin_interface.lib.php";
190 $export_sql_plugin = PMA_getPlugin(
193 'libraries/plugins/export/',
195 'export_type' => $export_type,
196 'single_table' => false,
200 $sql_backquotes = true;
202 $date = date('Y-m-d H:i:s');
204 // Get data definition snapshot of table
206 $columns = $GLOBALS['dbi']->getColumns($dbname, $tablename, null, true);
207 // int indices to reduce size
208 $columns = array_values($columns);
209 // remove Privileges to reduce size
210 for ($i = 0, $nb = count($columns); $i < $nb; $i++
) {
211 unset($columns[$i]['Privileges']);
214 $indexes = $GLOBALS['dbi']->getTableIndexes($dbname, $tablename);
216 $snapshot = array('COLUMNS' => $columns, 'INDEXES' => $indexes);
217 $snapshot = serialize($snapshot);
219 // Get DROP TABLE / DROP VIEW and CREATE TABLE SQL statements
220 $sql_backquotes = true;
224 if ($GLOBALS['cfg']['Server']['tracking_add_drop_table'] == true
227 $create_sql .= self
::getLogComment()
228 . 'DROP TABLE IF EXISTS ' . PMA_Util
::backquote($tablename) . ";\n";
232 if ($GLOBALS['cfg']['Server']['tracking_add_drop_view'] == true
235 $create_sql .= self
::getLogComment()
236 . 'DROP VIEW IF EXISTS ' . PMA_Util
::backquote($tablename) . ";\n";
239 $create_sql .= self
::getLogComment() .
240 $export_sql_plugin->getTableDef($dbname, $tablename, "\n", "");
244 $sql_query = "/*NOTRACK*/\n" .
245 "INSERT INTO " . self
::_getTrackingTable() . " (" .
251 "schema_snapshot, " .
257 '" . PMA_Util
::sqlAddSlashes($dbname) . "',
258 '" . PMA_Util
::sqlAddSlashes($tablename) . "',
259 '" . PMA_Util
::sqlAddSlashes($version) . "',
260 '" . PMA_Util
::sqlAddSlashes($date) . "',
261 '" . PMA_Util
::sqlAddSlashes($date) . "',
262 '" . PMA_Util
::sqlAddSlashes($snapshot) . "',
263 '" . PMA_Util
::sqlAddSlashes($create_sql) . "',
264 '" . PMA_Util
::sqlAddSlashes("\n") . "',
265 '" . PMA_Util
::sqlAddSlashes(self
::_transformTrackingSet($tracking_set))
268 $result = PMA_queryAsControlUser($sql_query);
271 // Deactivate previous version
272 self
::deactivateTracking($dbname, $tablename, ($version - 1));
280 * Removes all tracking data for a table or a version of a table
282 * @param string $dbname name of database
283 * @param string $tablename name of table
284 * @param string $version version
288 * @return int result of version insertion
290 static public function deleteTracking($dbname, $tablename, $version = '')
292 $sql_query = "/*NOTRACK*/\n"
293 . "DELETE FROM " . self
::_getTrackingTable()
294 . " WHERE `db_name` = '"
295 . PMA_Util
::sqlAddSlashes($dbname) . "'"
296 . " AND `table_name` = '"
297 . PMA_Util
::sqlAddSlashes($tablename) . "'";
299 $sql_query .= " AND `version` = '"
300 . PMA_Util
::sqlAddSlashes($version) . "'";
302 $result = PMA_queryAsControlUser($sql_query);
308 * Creates tracking version of a database
309 * (in other words: create a job to track future changes on the database).
311 * @param string $dbname name of database
312 * @param string $version version
313 * @param string $query query
314 * @param string $tracking_set set of tracking statements
318 * @return int result of version insertion
320 static public function createDatabaseVersion($dbname, $version, $query,
321 $tracking_set = 'CREATE DATABASE,ALTER DATABASE,DROP DATABASE'
323 $date = date('Y-m-d H:i:s');
325 if ($tracking_set == '') {
327 = $GLOBALS['cfg']['Server']['tracking_default_statements'];
332 if ($GLOBALS['cfg']['Server']['tracking_add_drop_database'] == true) {
333 $create_sql .= self
::getLogComment()
334 . 'DROP DATABASE IF EXISTS ' . PMA_Util
::backquote($dbname) . ";\n";
337 $create_sql .= self
::getLogComment() . $query;
340 $sql_query = "/*NOTRACK*/\n" .
341 "INSERT INTO " . self
::_getTrackingTable() . " (" .
347 "schema_snapshot, " .
353 '" . PMA_Util
::sqlAddSlashes($dbname) . "',
354 '" . PMA_Util
::sqlAddSlashes('') . "',
355 '" . PMA_Util
::sqlAddSlashes($version) . "',
356 '" . PMA_Util
::sqlAddSlashes($date) . "',
357 '" . PMA_Util
::sqlAddSlashes($date) . "',
358 '" . PMA_Util
::sqlAddSlashes('') . "',
359 '" . PMA_Util
::sqlAddSlashes($create_sql) . "',
360 '" . PMA_Util
::sqlAddSlashes("\n") . "',
361 '" . PMA_Util
::sqlAddSlashes(self
::_transformTrackingSet($tracking_set))
364 $result = PMA_queryAsControlUser($sql_query);
372 * Changes tracking of a table.
374 * @param string $dbname name of database
375 * @param string $tablename name of table
376 * @param string $version version
377 * @param integer $new_state the new state of tracking
381 * @return int result of SQL query
383 static private function _changeTracking($dbname, $tablename,
387 $sql_query = " UPDATE " . self
::_getTrackingTable() .
388 " SET `tracking_active` = '" . $new_state . "' " .
389 " WHERE `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
390 " AND `table_name` = '" . PMA_Util
::sqlAddSlashes($tablename) . "' " .
391 " AND `version` = '" . PMA_Util
::sqlAddSlashes($version) . "' ";
393 $result = PMA_queryAsControlUser($sql_query);
399 * Changes tracking data of a table.
401 * @param string $dbname name of database
402 * @param string $tablename name of table
403 * @param string $version version
404 * @param string $type type of data(DDL || DML)
405 * @param string|array $new_data the new tracking data
409 * @return bool result of change
411 static public function changeTrackingData($dbname, $tablename,
412 $version, $type, $new_data
414 if ($type == 'DDL') {
415 $save_to = 'schema_sql';
416 } elseif ($type == 'DML') {
417 $save_to = 'data_sql';
421 $date = date('Y-m-d H:i:s');
423 $new_data_processed = '';
424 if (is_array($new_data)) {
425 foreach ($new_data as $data) {
426 $new_data_processed .= '# log ' . $date . ' ' . $data['username']
427 . PMA_Util
::sqlAddSlashes($data['statement']) . "\n";
430 $new_data_processed = $new_data;
433 $sql_query = " UPDATE " . self
::_getTrackingTable() .
434 " SET `" . $save_to . "` = '" . $new_data_processed . "' " .
435 " WHERE `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
436 " AND `table_name` = '" . PMA_Util
::sqlAddSlashes($tablename) . "' " .
437 " AND `version` = '" . PMA_Util
::sqlAddSlashes($version) . "' ";
439 $result = PMA_queryAsControlUser($sql_query);
445 * Activates tracking of a table.
447 * @param string $dbname name of database
448 * @param string $tablename name of table
449 * @param string $version version
453 * @return int result of SQL query
455 static public function activateTracking($dbname, $tablename, $version)
457 return self
::_changeTracking($dbname, $tablename, $version, 1);
462 * Deactivates tracking of a table.
464 * @param string $dbname name of database
465 * @param string $tablename name of table
466 * @param string $version version
470 * @return int result of SQL query
472 static public function deactivateTracking($dbname, $tablename, $version)
474 return self
::_changeTracking($dbname, $tablename, $version, 0);
479 * Gets the newest version of a tracking job
480 * (in other words: gets the HEAD version).
482 * @param string $dbname name of database
483 * @param string $tablename name of table
484 * @param string $statement tracked statement
488 * @return int (-1 if no version exists | > 0 if a version exists)
490 static public function getVersion($dbname, $tablename, $statement = null)
492 $sql_query = " SELECT MAX(version) FROM " . self
::_getTrackingTable() .
493 " WHERE `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
494 " AND `table_name` = '" . PMA_Util
::sqlAddSlashes($tablename) . "' ";
496 if ($statement != "") {
498 $sql_query .= ' AND tracking & '
499 . self
::_transformTrackingSet($statement) . ' <> 0';
501 $sql_query .= " AND FIND_IN_SET('"
502 . $statement . "',tracking) > 0" ;
505 $row = $GLOBALS['dbi']->fetchArray(PMA_queryAsControlUser($sql_query));
506 return isset($row[0])
513 * Gets the record of a tracking job.
515 * @param string $dbname name of database
516 * @param string $tablename name of table
517 * @param string $version version number
521 * @return mixed record DDM log, DDL log, structure snapshot, tracked
524 static public function getTrackedData($dbname, $tablename, $version)
526 $sql_query = " SELECT * FROM " . self
::_getTrackingTable() .
527 " WHERE `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' ";
528 if (! empty($tablename)) {
529 $sql_query .= " AND `table_name` = '"
530 . PMA_Util
::sqlAddSlashes($tablename) . "' ";
532 $sql_query .= " AND `version` = '" . PMA_Util
::sqlAddSlashes($version)
533 . "' " . " ORDER BY `version` DESC LIMIT 1";
535 $mixed = $GLOBALS['dbi']->fetchAssoc(PMA_queryAsControlUser($sql_query));
538 $log_schema_entries = explode('# log ', $mixed['schema_sql']);
539 $log_data_entries = explode('# log ', $mixed['data_sql']);
541 $ddl_date_from = $date = date('Y-m-d H:i:s');
546 // Iterate tracked data definition statements
547 // For each log entry we want to get date, username and statement
548 foreach ($log_schema_entries as $log_entry) {
549 if (trim($log_entry) != '') {
550 $date = /*overload*/mb_substr($log_entry, 0, 19);
551 $username = /*overload*/mb_substr(
552 $log_entry, 20, /*overload*/mb_strpos($log_entry, "\n") - 20
555 $ddl_date_from = $date;
557 $statement = rtrim(/*overload*/mb_strstr($log_entry, "\n"));
559 $ddlog[] = array( 'date' => $date,
560 'username'=> $username,
561 'statement' => $statement );
566 $date_from = $ddl_date_from;
567 $ddl_date_to = $date;
569 $dml_date_from = $date_from;
574 // Iterate tracked data manipulation statements
575 // For each log entry we want to get date, username and statement
576 foreach ($log_data_entries as $log_entry) {
577 if (trim($log_entry) != '') {
578 $date = /*overload*/mb_substr($log_entry, 0, 19);
579 $username = /*overload*/mb_substr(
580 $log_entry, 20, /*overload*/mb_strpos($log_entry, "\n") - 20
583 $dml_date_from = $date;
585 $statement = rtrim(/*overload*/mb_strstr($log_entry, "\n"));
587 $dmlog[] = array( 'date' => $date,
588 'username' => $username,
589 'statement' => $statement );
594 $dml_date_to = $date;
596 // Define begin and end of date range for both logs
598 if (strtotime($ddl_date_from) <= strtotime($dml_date_from)) {
599 $data['date_from'] = $ddl_date_from;
601 $data['date_from'] = $dml_date_from;
603 if (strtotime($ddl_date_to) >= strtotime($dml_date_to)) {
604 $data['date_to'] = $ddl_date_to;
606 $data['date_to'] = $dml_date_to;
608 $data['ddlog'] = $ddlog;
609 $data['dmlog'] = $dmlog;
610 $data['tracking'] = self
::_transformTrackingSet($mixed['tracking']);
611 $data['schema_snapshot'] = $mixed['schema_snapshot'];
618 * Parses a query. Gets
619 * - statement identifier (UPDATE, ALTER TABLE, ...)
620 * - type of statement, is it part of DDL or DML ?
623 * @param string $query query
626 * @todo: using PMA SQL Parser when possible
627 * @todo: support multi-table/view drops
629 * @return mixed Array containing identifier, type and tablename.
632 static public function parseQuery($query)
634 // Usage of PMA_SQP does not work here
636 // require_once("libraries/sqlparser.lib.php");
637 // $parsed_sql = PMA_SQP_parse($query);
638 // $sql_info = PMA_SQP_analyze($parsed_sql);
640 $query = str_replace("\n", " ", $query);
641 $query = str_replace("\r", " ", $query);
643 $query = trim($query);
644 $query = trim($query, ' -');
646 $tokens = explode(" ", $query);
647 foreach ($tokens as $key => $value) {
648 $tokens[$key] = /*overload*/mb_strtoupper($value);
651 // Parse USE statement, need it for SQL dump imports
652 if (/*overload*/mb_substr($query, 0, 4) == 'USE ') {
653 $prefix = explode('USE ', $query);
654 $GLOBALS['db'] = self
::getTableName($prefix[1]);
662 $result['type'] = 'DDL';
664 // Parse CREATE VIEW statement
665 if (in_array('CREATE', $tokens) == true
666 && in_array('VIEW', $tokens) == true
667 && in_array('AS', $tokens) == true
669 $result['identifier'] = 'CREATE VIEW';
671 $index = array_search('VIEW', $tokens);
673 $result['tablename'] = /*overload*/mb_strtolower(
674 self
::getTableName($tokens[$index +
1])
678 // Parse ALTER VIEW statement
679 if (in_array('ALTER', $tokens) == true
680 && in_array('VIEW', $tokens) == true
681 && in_array('AS', $tokens) == true
682 && ! isset($result['identifier'])
684 $result['identifier'] = 'ALTER VIEW';
686 $index = array_search('VIEW', $tokens);
688 $result['tablename'] = /*overload*/mb_strtolower(
689 self
::getTableName($tokens[$index +
1])
693 // Parse DROP VIEW statement
694 if (! isset($result['identifier'])
695 && substr($query, 0, 10) == 'DROP VIEW '
697 $result['identifier'] = 'DROP VIEW';
699 $prefix = explode('DROP VIEW ', $query);
700 $str = /*overload*/mb_strstr($prefix[1], 'IF EXISTS');
702 if ($str == false ) {
705 $result['tablename'] = self
::getTableName($str);
708 // Parse CREATE DATABASE statement
709 if (! isset($result['identifier'])
710 && substr($query, 0, 15) == 'CREATE DATABASE'
712 $result['identifier'] = 'CREATE DATABASE';
713 $str = str_replace('CREATE DATABASE', '', $query);
714 $str = str_replace('IF NOT EXISTS', '', $str);
716 $prefix = explode('DEFAULT ', $str);
718 $result['tablename'] = '';
719 $GLOBALS['db'] = self
::getTableName($prefix[0]);
722 // Parse ALTER DATABASE statement
723 if (! isset($result['identifier'])
724 && substr($query, 0, 14) == 'ALTER DATABASE'
726 $result['identifier'] = 'ALTER DATABASE';
727 $result['tablename'] = '';
730 // Parse DROP DATABASE statement
731 if (! isset($result['identifier'])
732 && substr($query, 0, 13) == 'DROP DATABASE'
734 $result['identifier'] = 'DROP DATABASE';
735 $str = str_replace('DROP DATABASE', '', $query);
736 $str = str_replace('IF EXISTS', '', $str);
737 $GLOBALS['db'] = self
::getTableName($str);
738 $result['tablename'] = '';
741 // Parse CREATE TABLE statement
742 if (! isset($result['identifier'])
743 && substr($query, 0, 12) == 'CREATE TABLE'
745 $result['identifier'] = 'CREATE TABLE';
746 $query = str_replace('IF NOT EXISTS', '', $query);
747 $prefix = explode('CREATE TABLE ', $query);
748 $suffix = explode('(', $prefix[1]);
749 $result['tablename'] = self
::getTableName($suffix[0]);
752 // Parse ALTER TABLE statement
753 if (! isset($result['identifier'])
754 && substr($query, 0, 12) == 'ALTER TABLE '
756 $result['identifier'] = 'ALTER TABLE';
758 $prefix = explode('ALTER TABLE ', $query);
759 $suffix = explode(' ', $prefix[1]);
760 $result['tablename'] = self
::getTableName($suffix[0]);
763 // Parse DROP TABLE statement
764 if (! isset($result['identifier'])
765 && substr($query, 0, 11) == 'DROP TABLE '
767 $result['identifier'] = 'DROP TABLE';
769 $prefix = explode('DROP TABLE ', $query);
770 $str = /*overload*/mb_strstr($prefix[1], 'IF EXISTS');
772 if ($str == false ) {
775 $result['tablename'] = self
::getTableName($str);
778 // Parse CREATE INDEX statement
779 if (! isset($result['identifier'])
780 && (substr($query, 0, 12) == 'CREATE INDEX'
781 ||
substr($query, 0, 19) == 'CREATE UNIQUE INDEX'
782 ||
substr($query, 0, 20) == 'CREATE SPATIAL INDEX')
784 $result['identifier'] = 'CREATE INDEX';
785 $prefix = explode('ON ', $query);
786 $suffix = explode('(', $prefix[1]);
787 $result['tablename'] = self
::getTableName($suffix[0]);
790 // Parse DROP INDEX statement
791 if (! isset($result['identifier'])
792 && substr($query, 0, 10) == 'DROP INDEX'
794 $result['identifier'] = 'DROP INDEX';
795 $prefix = explode('ON ', $query);
796 $result['tablename'] = self
::getTableName($prefix[1]);
799 // Parse RENAME TABLE statement
800 if (! isset($result['identifier'])
801 && substr($query, 0, 13) == 'RENAME TABLE '
803 $result['identifier'] = 'RENAME TABLE';
804 $prefix = explode('RENAME TABLE ', $query);
805 $names = explode(' TO ', $prefix[1]);
806 $result['tablename'] = self
::getTableName($names[0]);
807 $result["tablename_after_rename"] = self
::getTableName($names[1]);
814 if (! isset($result['identifier'])) {
815 $result["type"] = 'DML';
817 // Parse UPDATE statement
818 if (! isset($result['identifier'])
819 && substr($query, 0, 6) == 'UPDATE'
821 $result['identifier'] = 'UPDATE';
822 $prefix = explode('UPDATE ', $query);
823 $suffix = explode(' ', $prefix[1]);
824 $result['tablename'] = self
::getTableName($suffix[0]);
827 // Parse INSERT INTO statement
828 if (! isset($result['identifier'])
829 && substr($query, 0, 11) == 'INSERT INTO'
831 $result['identifier'] = 'INSERT';
832 $prefix = explode('INSERT INTO', $query);
833 $suffix = explode('(', $prefix[1]);
834 $result['tablename'] = self
::getTableName($suffix[0]);
837 // Parse DELETE statement
838 if (! isset($result['identifier'])
839 && substr($query, 0, 6) == 'DELETE'
841 $result['identifier'] = 'DELETE';
842 $prefix = explode('FROM ', $query);
843 $suffix = explode(' ', $prefix[1]);
844 $result['tablename'] = self
::getTableName($suffix[0]);
847 // Parse TRUNCATE statement
848 if (! isset($result['identifier'])
849 && substr($query, 0, 8) == 'TRUNCATE'
851 $result['identifier'] = 'TRUNCATE';
852 $prefix = explode('TRUNCATE', $query);
853 $result['tablename'] = self
::getTableName($prefix[1]);
861 * Analyzes a given SQL statement and saves tracking data.
863 * @param string $query a SQL query
869 static public function handleQuery($query)
871 // If query is marked as untouchable, leave
872 if (/*overload*/mb_strstr($query, "/*NOTRACK*/")) {
876 if (! (substr($query, -1) == ';')) {
877 $query = $query . ";\n";
879 // Get some information about query
880 $result = self
::parseQuery($query);
883 $dbname = trim(isset($GLOBALS['db']) ?
$GLOBALS['db'] : '', '`');
884 // $dbname can be empty, for example when coming from Synchronize
885 // and this is a query for the remote server
886 if (empty($dbname)) {
890 // If we found a valid statement
891 if (isset($result['identifier'])) {
892 $version = self
::getVersion(
893 $dbname, $result['tablename'], $result['identifier']
896 // If version not exists and auto-creation is enabled
897 if ($GLOBALS['cfg']['Server']['tracking_version_auto_create'] == true
898 && self
::isTracked($dbname, $result['tablename']) == false
901 // Create the version
903 switch ($result['identifier']) {
905 self
::createVersion($dbname, $result['tablename'], '1');
909 $dbname, $result['tablename'], '1', '', true
912 case 'CREATE DATABASE':
913 self
::createDatabaseVersion($dbname, '1', $query);
919 if (self
::isTracked($dbname, $result['tablename']) && $version != -1) {
920 if ($result['type'] == 'DDL') {
921 $save_to = 'schema_sql';
922 } elseif ($result['type'] == 'DML') {
923 $save_to = 'data_sql';
927 $date = date('Y-m-d H:i:s');
929 // Cut off `dbname`. from query
930 $query = preg_replace(
931 '/`' . preg_quote($dbname) . '`\s?\./',
936 // Add log information
937 $query = self
::getLogComment() . $query ;
939 // Mark it as untouchable
940 $sql_query = " /*NOTRACK*/\n"
941 . " UPDATE " . self
::_getTrackingTable()
942 . " SET " . PMA_Util
::backquote($save_to)
943 . " = CONCAT( " . PMA_Util
::backquote($save_to) . ",'\n"
944 . PMA_Util
::sqlAddSlashes($query) . "') ,"
945 . " `date_updated` = '" . $date . "' ";
947 // If table was renamed we have to change
948 // the tablename attribute in pma_tracking too
949 if ($result['identifier'] == 'RENAME TABLE') {
950 $sql_query .= ', `table_name` = \''
951 . PMA_Util
::sqlAddSlashes($result['tablename_after_rename'])
955 // Save the tracking information only for
957 // 2. the table / view
961 " WHERE FIND_IN_SET('" . $result['identifier'] . "',tracking) > 0" .
962 " AND `db_name` = '" . PMA_Util
::sqlAddSlashes($dbname) . "' " .
963 " AND `table_name` = '"
964 . PMA_Util
::sqlAddSlashes($result['tablename']) . "' " .
965 " AND `version` = '" . PMA_Util
::sqlAddSlashes($version) . "' ";
967 PMA_queryAsControlUser($sql_query);
973 * Transforms tracking set for Drizzle, which has no SET type
975 * Converts int<>string for Drizzle, does nothing for MySQL
977 * @param int|string $tracking_set Set to convert
981 static private function _transformTrackingSet($tracking_set)
984 return $tracking_set;
987 // init conversion array (key 3 doesn't exist in calculated array)
988 if (isset(self
::$_tracking_set_flags[3])) {
990 $set = self
::$_tracking_set_flags;
992 for ($i = 0, $nb = count($set); $i < $nb; $i++
) {
994 $array[$flag] = $set[$i];
995 $array[$set[$i]] = $flag;
997 self
::$_tracking_set_flags = $array;
1000 if (is_numeric($tracking_set)) {
1001 // int > string conversion
1003 // count/2 - conversion table has both int > string
1004 // and string > int values
1005 for ($i = 0, $nb = count(self
::$_tracking_set_flags)/2; $i < $nb; $i++
) {
1007 if ($tracking_set & $flag) {
1008 $aflags[] = self
::$_tracking_set_flags[$flag];
1011 $flags = implode(',', $aflags);
1013 // string > int conversion
1015 foreach (explode(',', $tracking_set) as $strflag) {
1016 if ($strflag == '') {
1019 $flags |
= self
::$_tracking_set_flags[$strflag];
1027 * Returns the tracking table
1029 * @return string tracking table
1031 private static function _getTrackingTable()
1033 $cfgRelation = PMA_getRelationsParam();
1034 return PMA_Util
::backquote($cfgRelation['db'])
1035 . '.' . PMA_Util
::backquote($cfgRelation['tracking']);