added japanese language
[openemr.git] / phpmyadmin / libraries / Tracker.class.php
blob072f97e9a433890db90ed37a947aded46db01330
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * Tracking changes on databases, tables and views
6 * @package PhpMyAdmin
7 */
8 if (! defined('PHPMYADMIN')) {
9 exit;
12 /**
13 * This class tracks changes on databases, tables and views.
15 * @package PhpMyAdmin
17 * @todo use stristr instead of strstr
19 class PMA_Tracker
21 /**
22 * Whether tracking is ready.
24 static protected $enabled = false;
26 /**
27 * Defines the internal PMA table which contains tracking data.
29 * @access protected
30 * @var string
32 static protected $pma_table;
34 /**
35 * Defines the usage of DROP TABLE statment in SQL dumps.
37 * @access protected
38 * @var boolean
40 static protected $add_drop_table;
42 /**
43 * Defines the usage of DROP VIEW statment in SQL dumps.
45 * @access protected
46 * @var boolean
48 static protected $add_drop_view;
50 /**
51 * Defines the usage of DROP DATABASE statment in SQL dumps.
53 * @access protected
54 * @var boolean
56 static protected $add_drop_database;
58 /**
59 * Defines auto-creation of tracking versions.
61 * @var boolean
63 static protected $version_auto_create;
65 /**
66 * Defines the default set of tracked statements.
68 * @var string
70 static protected $default_tracking_set;
72 /**
73 * Flags copied from `tracking` column definition in `pma_tracking` table.
74 * Used for column type conversion in Drizzle.
76 * @var array
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'
86 /**
87 * Initializes settings.
89 * @static
91 * @return void
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
99 = $GLOBALS['cfg']['Server']['tracking_add_drop_table'];
101 self::$add_drop_view
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.
118 * @static
120 * @return void
122 static public function enable()
124 self::$enabled = true;
128 * Gets the on/off value of the Tracker module, starts initialization.
130 * @static
132 * @return boolean (true=on|false=off)
134 static public function isActive()
136 if (! self::$enabled) {
137 return false;
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']) {
147 return false;
149 self::init();
151 if (isset(self::$pma_table)) {
152 return true;
153 } else {
154 return false;
159 * Parses the name of a table from a SQL statement substring.
161 * @param string $string part of SQL statement
163 * @static
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];
172 } else {
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);
183 return $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
193 * @static
195 * @return boolean true or false
197 static public function isTracked($dbname, $tablename)
199 if (! self::$enabled) {
200 return false;
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']) {
210 return false;
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) {
221 return true;
222 } else {
223 return false;
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
249 * @static
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(
265 "export",
266 "sql",
267 'libraries/plugins/export/',
268 array(
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;
296 $create_sql = "";
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", "");
312 // Save version
314 $sql_query = "/*NOTRACK*/\n" .
315 "INSERT INTO" . self::$pma_table . " (" .
316 "db_name, " .
317 "table_name, " .
318 "version, " .
319 "date_created, " .
320 "date_updated, " .
321 "schema_snapshot, " .
322 "schema_sql, " .
323 "data_sql, " .
324 "tracking " .
325 ") " .
326 "values (
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))
336 . "' )";
338 $result = PMA_queryAsControlUser($sql_query);
340 if ($result) {
341 // Deactivate previous version
342 self::deactivateTracking($dbname, $tablename, ($version - 1));
345 return $result;
350 * Removes all tracking data for a table
352 * @param string $dbname name of database
353 * @param string $tablename name of table
355 * @static
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);
369 return $result;
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
381 * @static
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;
394 $create_sql = "";
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;
403 // Save version
404 $sql_query = "/*NOTRACK*/\n" .
405 "INSERT INTO" . self::$pma_table . " (" .
406 "db_name, " .
407 "table_name, " .
408 "version, " .
409 "date_created, " .
410 "date_updated, " .
411 "schema_snapshot, " .
412 "schema_sql, " .
413 "data_sql, " .
414 "tracking " .
415 ") " .
416 "values (
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))
426 . "' )";
428 $result = PMA_queryAsControlUser($sql_query);
430 return $result;
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
443 * @static
445 * @return int result of SQL query
447 static private function _changeTracking($dbname, $tablename,
448 $version, $new_state
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);
459 return $result;
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
471 * @static
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';
482 } else {
483 return false;
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";
493 } else {
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);
505 return $result;
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
515 * @static
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
532 * @static
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
550 * @static
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 != "") {
561 if (PMA_DRIZZLE) {
562 $sql_query .= ' AND tracking & '
563 . self::_transformTrackingSet($statement) . ' <> 0';
564 } else {
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])
571 ? $row[0]
572 : -1;
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
583 * @static
585 * @return mixed record DDM log, DDL log, structure snapshot, tracked
586 * statements.
588 static public function getTrackedData($dbname, $tablename, $version)
590 if (! isset(self::$pma_table)) {
591 self::init();
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));
604 // Parse log
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');
610 $ddlog = array();
611 $i = 0;
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);
619 if ($i == 0) {
620 $ddl_date_from = $date;
622 $statement = rtrim(strstr($log_entry, "\n"));
624 $ddlog[] = array( 'date' => $date,
625 'username'=> $username,
626 'statement' => $statement );
627 $i++;
631 $date_from = $ddl_date_from;
632 $ddl_date_to = $date;
634 $dml_date_from = $date_from;
636 $dmlog = array();
637 $i = 0;
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);
645 if ($i == 0) {
646 $dml_date_from = $date;
648 $statement = rtrim(strstr($log_entry, "\n"));
650 $dmlog[] = array( 'date' => $date,
651 'username' => $username,
652 'statement' => $statement );
653 $i++;
657 $dml_date_to = $date;
659 // Define begin and end of date range for both logs
660 $data = array();
661 if (strtotime($ddl_date_from) <= strtotime($dml_date_from)) {
662 $data['date_from'] = $ddl_date_from;
663 } else {
664 $data['date_from'] = $dml_date_from;
666 if (strtotime($ddl_date_to) >= strtotime($dml_date_to)) {
667 $data['date_to'] = $ddl_date_to;
668 } else {
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'];
676 return $data;
681 * Parses a query. Gets
682 * - statement identifier (UPDATE, ALTER TABLE, ...)
683 * - type of statement, is it part of DDL or DML ?
684 * - tablename
686 * @param string $query query
688 * @static
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]);
722 * DDL statements
725 $result = array();
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 ) {
767 $str = $prefix[1];
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 ) {
837 $str = $prefix[1];
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]);
875 * DML statements
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]);
920 return $result;
925 * Analyzes a given SQL statement and saves tracking data.
927 * @param string $query a SQL query
929 * @static
931 * @return void
933 static public function handleQuery($query)
935 // If query is marked as untouchable, leave
936 if (strstr($query, "/*NOTRACK*/")) {
937 return;
940 if (! (substr($query, -1) == ';')) {
941 $query = $query . ";\n";
943 // Get some information about query
944 $result = self::parseQuery($query);
946 // Get database name
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)) {
951 return;
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
963 && $version == -1
965 // Create the version
967 switch ($result['identifier']) {
968 case 'CREATE TABLE':
969 self::createVersion($dbname, $result['tablename'], '1');
970 break;
971 case 'CREATE VIEW':
972 self::createVersion(
973 $dbname, $result['tablename'], '1', '', true
975 break;
976 case 'CREATE DATABASE':
977 self::createDatabaseVersion($dbname, '1', $query);
978 break;
979 } // end switch
982 // If version exists
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';
988 } else {
989 $save_to = '';
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'])
1012 . '\' ';
1015 // Save the tracking information only for
1016 // 1. the database
1017 // 2. the table / view
1018 // 3. the statements
1019 // we want to track
1020 $sql_query .=
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)
1043 if (!PMA_DRIZZLE) {
1044 return $tracking_set;
1047 // init conversion array (key 3 doesn't exist in calculated array)
1048 if (isset(self::$_tracking_set_flags[3])) {
1049 // initialize flags
1050 $set = self::$_tracking_set_flags;
1051 $array = array();
1052 for ($i = 0, $nb = count($set); $i < $nb; $i++) {
1053 $flag = 1 << $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
1062 $aflags = array();
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++) {
1066 $flag = 1 << $i;
1067 if ($tracking_set & $flag) {
1068 $aflags[] = self::$_tracking_set_flags[$flag];
1071 $flags = implode(',', $aflags);
1072 } else {
1073 // string > int conversion
1074 $flags = 0;
1075 foreach (explode(',', $tracking_set) as $strflag) {
1076 if ($strflag == '') {
1077 continue;
1079 $flags |= self::$_tracking_set_flags[$strflag];
1083 return $flags;