PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/phpmyadmin/libraries/Tracker.class.php

https://bitbucket.org/adarshj/convenient_website
PHP | 1001 lines | 544 code | 148 blank | 309 comment | 146 complexity | c648c8ed75aec1b5a5f12ca4493cc676 MD5 | raw file
Possible License(s): Apache-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-2-Clause, GPL-2.0, LGPL-3.0
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. *
  5. * @package PhpMyAdmin
  6. */
  7. /**
  8. * This class tracks changes on databases, tables and views.
  9. * For more information please see phpMyAdmin/Documentation.html
  10. *
  11. * @package PhpMyAdmin
  12. *
  13. * @todo use stristr instead of strstr
  14. */
  15. class PMA_Tracker
  16. {
  17. /**
  18. * Whether tracking is ready.
  19. */
  20. static protected $enabled = false;
  21. /**
  22. * Defines the internal PMA table which contains tracking data.
  23. *
  24. * @access protected
  25. * @var string
  26. */
  27. static protected $pma_table;
  28. /**
  29. * Defines the usage of DROP TABLE statment in SQL dumps.
  30. *
  31. * @access protected
  32. * @var boolean
  33. */
  34. static protected $add_drop_table;
  35. /**
  36. * Defines the usage of DROP VIEW statment in SQL dumps.
  37. *
  38. * @access protected
  39. * @var boolean
  40. */
  41. static protected $add_drop_view;
  42. /**
  43. * Defines the usage of DROP DATABASE statment in SQL dumps.
  44. *
  45. * @access protected
  46. * @var boolean
  47. */
  48. static protected $add_drop_database;
  49. /**
  50. * Defines auto-creation of tracking versions.
  51. *
  52. * @var boolean
  53. */
  54. static protected $version_auto_create;
  55. /**
  56. * Defines the default set of tracked statements.
  57. *
  58. * @var string
  59. */
  60. static protected $default_tracking_set;
  61. /**
  62. * Flags copied from `tracking` column definition in `pma_tracking` table.
  63. * Used for column type conversion in Drizzle.
  64. *
  65. * @var array
  66. */
  67. static private $tracking_set_flags = array('UPDATE','REPLACE','INSERT','DELETE','TRUNCATE','CREATE DATABASE',
  68. 'ALTER DATABASE','DROP DATABASE','CREATE TABLE','ALTER TABLE','RENAME TABLE','DROP TABLE','CREATE INDEX',
  69. 'DROP INDEX','CREATE VIEW','ALTER VIEW','DROP VIEW');
  70. /**
  71. * Initializes settings. See phpMyAdmin/Documentation.html.
  72. *
  73. * @static
  74. *
  75. * @return nothing
  76. */
  77. static protected function init()
  78. {
  79. self::$pma_table = PMA_backquote($GLOBALS['cfg']['Server']['pmadb']) .".".
  80. PMA_backquote($GLOBALS['cfg']['Server']['tracking']);
  81. self::$add_drop_table = $GLOBALS['cfg']['Server']['tracking_add_drop_table'];
  82. self::$add_drop_view = $GLOBALS['cfg']['Server']['tracking_add_drop_view'];
  83. self::$add_drop_database = $GLOBALS['cfg']['Server']['tracking_add_drop_database'];
  84. self::$default_tracking_set = $GLOBALS['cfg']['Server']['tracking_default_statements'];
  85. self::$version_auto_create = $GLOBALS['cfg']['Server']['tracking_version_auto_create'];
  86. }
  87. /**
  88. * Actually enables tracking. This needs to be done after all
  89. * underlaying code is initialized.
  90. *
  91. * @static
  92. *
  93. * @return nothing
  94. */
  95. static public function enable()
  96. {
  97. self::$enabled = true;
  98. }
  99. /**
  100. * Gets the on/off value of the Tracker module, starts initialization.
  101. *
  102. * @static
  103. *
  104. * @return boolean (true=on|false=off)
  105. */
  106. static public function isActive()
  107. {
  108. if (! self::$enabled) {
  109. return false;
  110. }
  111. /* We need to avoid attempt to track any queries from PMA_getRelationsParam */
  112. self::$enabled = false;
  113. $cfgRelation = PMA_getRelationsParam();
  114. /* Restore original state */
  115. self::$enabled = true;
  116. if (! $cfgRelation['trackingwork']) {
  117. return false;
  118. }
  119. self::init();
  120. if (isset(self::$pma_table)) {
  121. return true;
  122. } else {
  123. return false;
  124. }
  125. }
  126. /**
  127. * Parses the name of a table from a SQL statement substring.
  128. *
  129. * @param string $string part of SQL statement
  130. *
  131. * @static
  132. *
  133. * @return string the name of table
  134. */
  135. static protected function getTableName($string)
  136. {
  137. if (strstr($string, '.')) {
  138. $temp = explode('.', $string);
  139. $tablename = $temp[1];
  140. } else {
  141. $tablename = $string;
  142. }
  143. $str = explode("\n", $tablename);
  144. $tablename = $str[0];
  145. $tablename = str_replace(';', '', $tablename);
  146. $tablename = str_replace('`', '', $tablename);
  147. $tablename = trim($tablename);
  148. return $tablename;
  149. }
  150. /**
  151. * Gets the tracking status of a table, is it active or deactive ?
  152. *
  153. * @param string $dbname name of database
  154. * @param string $tablename name of table
  155. *
  156. * @static
  157. *
  158. * @return boolean true or false
  159. */
  160. static public function isTracked($dbname, $tablename)
  161. {
  162. if (! self::$enabled) {
  163. return false;
  164. }
  165. /* We need to avoid attempt to track any queries from PMA_getRelationsParam */
  166. self::$enabled = false;
  167. $cfgRelation = PMA_getRelationsParam();
  168. /* Restore original state */
  169. self::$enabled = true;
  170. if (! $cfgRelation['trackingwork']) {
  171. return false;
  172. }
  173. $sql_query = " SELECT tracking_active FROM " . self::$pma_table .
  174. " WHERE db_name = '" . PMA_sqlAddSlashes($dbname) . "' " .
  175. " AND table_name = '" . PMA_sqlAddSlashes($tablename) . "' " .
  176. " ORDER BY version DESC";
  177. $row = PMA_DBI_fetch_array(PMA_query_as_controluser($sql_query));
  178. if (isset($row['tracking_active']) && $row['tracking_active'] == 1) {
  179. return true;
  180. } else {
  181. return false;
  182. }
  183. }
  184. /**
  185. * Returns the comment line for the log.
  186. *
  187. * @return string Comment, contains date and username
  188. */
  189. static public function getLogComment()
  190. {
  191. $date = date('Y-m-d H:i:s');
  192. return "# log " . $date . " " . $GLOBALS['cfg']['Server']['user'] . "\n";
  193. }
  194. /**
  195. * Creates tracking version of a table / view
  196. * (in other words: create a job to track future changes on the table).
  197. *
  198. * @param string $dbname name of database
  199. * @param string $tablename name of table
  200. * @param string $version version
  201. * @param string $tracking_set set of tracking statements
  202. * @param bool $is_view if table is a view
  203. *
  204. * @static
  205. *
  206. * @return int result of version insertion
  207. */
  208. static public function createVersion($dbname, $tablename, $version, $tracking_set = '', $is_view = false)
  209. {
  210. global $sql_backquotes;
  211. if ($tracking_set == '') {
  212. $tracking_set = self::$default_tracking_set;
  213. }
  214. include_once './libraries/export/sql.php';
  215. $sql_backquotes = true;
  216. $date = date('Y-m-d H:i:s');
  217. // Get data definition snapshot of table
  218. $columns = PMA_DBI_get_columns($dbname, $tablename, null, true);
  219. // int indices to reduce size
  220. $columns = array_values($columns);
  221. // remove Privileges to reduce size
  222. for ($i = 0; $i < count($columns); $i++) {
  223. unset($columns[$i]['Privileges']);
  224. }
  225. $indexes = PMA_DBI_get_table_indexes($dbname, $tablename);
  226. $snapshot = array('COLUMNS' => $columns, 'INDEXES' => $indexes);
  227. $snapshot = serialize($snapshot);
  228. // Get DROP TABLE / DROP VIEW and CREATE TABLE SQL statements
  229. $sql_backquotes = true;
  230. $create_sql = "";
  231. if (self::$add_drop_table == true && $is_view == false) {
  232. $create_sql .= self::getLogComment() .
  233. 'DROP TABLE IF EXISTS ' . PMA_backquote($tablename) . ";\n";
  234. }
  235. if (self::$add_drop_view == true && $is_view == true) {
  236. $create_sql .= self::getLogComment() .
  237. 'DROP VIEW IF EXISTS ' . PMA_backquote($tablename) . ";\n";
  238. }
  239. $create_sql .= self::getLogComment() .
  240. PMA_getTableDef($dbname, $tablename, "\n", "");
  241. // Save version
  242. $sql_query = "/*NOTRACK*/\n" .
  243. "INSERT INTO" . self::$pma_table . " (" .
  244. "db_name, " .
  245. "table_name, " .
  246. "version, " .
  247. "date_created, " .
  248. "date_updated, " .
  249. "schema_snapshot, " .
  250. "schema_sql, " .
  251. "data_sql, " .
  252. "tracking " .
  253. ") " .
  254. "values (
  255. '" . PMA_sqlAddSlashes($dbname) . "',
  256. '" . PMA_sqlAddSlashes($tablename) . "',
  257. '" . PMA_sqlAddSlashes($version) . "',
  258. '" . PMA_sqlAddSlashes($date) . "',
  259. '" . PMA_sqlAddSlashes($date) . "',
  260. '" . PMA_sqlAddSlashes($snapshot) . "',
  261. '" . PMA_sqlAddSlashes($create_sql) . "',
  262. '" . PMA_sqlAddSlashes("\n") . "',
  263. '" . PMA_sqlAddSlashes(self::transformTrackingSet($tracking_set)) . "' )";
  264. $result = PMA_query_as_controluser($sql_query);
  265. if ($result) {
  266. // Deactivate previous version
  267. self::deactivateTracking($dbname, $tablename, ($version - 1));
  268. }
  269. return $result;
  270. }
  271. /**
  272. * Removes all tracking data for a table
  273. *
  274. * @param string $dbname name of database
  275. * @param string $tablename name of table
  276. *
  277. * @static
  278. *
  279. * @return int result of version insertion
  280. */
  281. static public function deleteTracking($dbname, $tablename)
  282. {
  283. $sql_query = "/*NOTRACK*/\n" .
  284. "DELETE FROM " . self::$pma_table . " WHERE `db_name` = '" . PMA_sqlAddSlashes($dbname) . "' AND `table_name` = '" . PMA_sqlAddSlashes($tablename) . "'";
  285. $result = PMA_query_as_controluser($sql_query);
  286. return $result;
  287. }
  288. /**
  289. * Creates tracking version of a database
  290. * (in other words: create a job to track future changes on the database).
  291. *
  292. * @param string $dbname name of database
  293. * @param string $version version
  294. * @param string $query query
  295. * @param string $tracking_set set of tracking statements
  296. *
  297. * @static
  298. *
  299. * @return int result of version insertion
  300. */
  301. static public function createDatabaseVersion($dbname, $version, $query, $tracking_set = 'CREATE DATABASE,ALTER DATABASE,DROP DATABASE')
  302. {
  303. $date = date('Y-m-d H:i:s');
  304. if ($tracking_set == '') {
  305. $tracking_set = self::$default_tracking_set;
  306. }
  307. include_once './libraries/export/sql.php';
  308. $create_sql = "";
  309. if (self::$add_drop_database == true) {
  310. $create_sql .= self::getLogComment() .
  311. 'DROP DATABASE IF EXISTS ' . PMA_backquote($dbname) . ";\n";
  312. }
  313. $create_sql .= self::getLogComment() . $query;
  314. // Save version
  315. $sql_query = "/*NOTRACK*/\n" .
  316. "INSERT INTO" . self::$pma_table . " (" .
  317. "db_name, " .
  318. "table_name, " .
  319. "version, " .
  320. "date_created, " .
  321. "date_updated, " .
  322. "schema_snapshot, " .
  323. "schema_sql, " .
  324. "data_sql, " .
  325. "tracking " .
  326. ") " .
  327. "values (
  328. '" . PMA_sqlAddSlashes($dbname) . "',
  329. '" . PMA_sqlAddSlashes('') . "',
  330. '" . PMA_sqlAddSlashes($version) . "',
  331. '" . PMA_sqlAddSlashes($date) . "',
  332. '" . PMA_sqlAddSlashes($date) . "',
  333. '" . PMA_sqlAddSlashes('') . "',
  334. '" . PMA_sqlAddSlashes($create_sql) . "',
  335. '" . PMA_sqlAddSlashes("\n") . "',
  336. '" . PMA_sqlAddSlashes(self::transformTrackingSet($tracking_set)) . "' )";
  337. $result = PMA_query_as_controluser($sql_query);
  338. return $result;
  339. }
  340. /**
  341. * Changes tracking of a table.
  342. *
  343. * @param string $dbname name of database
  344. * @param string $tablename name of table
  345. * @param string $version version
  346. * @param integer $new_state the new state of tracking
  347. *
  348. * @static
  349. *
  350. * @return int result of SQL query
  351. */
  352. static private function _changeTracking($dbname, $tablename, $version, $new_state)
  353. {
  354. $sql_query = " UPDATE " . self::$pma_table .
  355. " SET `tracking_active` = '" . $new_state . "' " .
  356. " WHERE `db_name` = '" . PMA_sqlAddSlashes($dbname) . "' " .
  357. " AND `table_name` = '" . PMA_sqlAddSlashes($tablename) . "' " .
  358. " AND `version` = '" . PMA_sqlAddSlashes($version) . "' ";
  359. $result = PMA_query_as_controluser($sql_query);
  360. return $result;
  361. }
  362. /**
  363. * Changes tracking data of a table.
  364. *
  365. * @param string $dbname name of database
  366. * @param string $tablename name of table
  367. * @param string $version version
  368. * @param string $type type of data(DDL || DML)
  369. * @param string|array $new_data the new tracking data
  370. *
  371. * @static
  372. *
  373. * @return bool result of change
  374. */
  375. static public function changeTrackingData($dbname, $tablename, $version, $type, $new_data)
  376. {
  377. if ($type == 'DDL') {
  378. $save_to = 'schema_sql';
  379. } elseif ($type == 'DML') {
  380. $save_to = 'data_sql';
  381. } else {
  382. return false;
  383. }
  384. $date = date('Y-m-d H:i:s');
  385. $new_data_processed = '';
  386. if (is_array($new_data)) {
  387. foreach ($new_data as $data) {
  388. $new_data_processed .= '# log ' . $date . ' ' . $data['username']
  389. . PMA_sqlAddSlashes($data['statement']) . "\n";
  390. }
  391. } else {
  392. $new_data_processed = $new_data;
  393. }
  394. $sql_query = " UPDATE " . self::$pma_table .
  395. " SET `" . $save_to . "` = '" . $new_data_processed . "' " .
  396. " WHERE `db_name` = '" . PMA_sqlAddSlashes($dbname) . "' " .
  397. " AND `table_name` = '" . PMA_sqlAddSlashes($tablename) . "' " .
  398. " AND `version` = '" . PMA_sqlAddSlashes($version) . "' ";
  399. $result = PMA_query_as_controluser($sql_query);
  400. return $result;
  401. }
  402. /**
  403. * Activates tracking of a table.
  404. *
  405. * @param string $dbname name of database
  406. * @param string $tablename name of table
  407. * @param string $version version
  408. *
  409. * @static
  410. *
  411. * @return int result of SQL query
  412. */
  413. static public function activateTracking($dbname, $tablename, $version)
  414. {
  415. return self::_changeTracking($dbname, $tablename, $version, 1);
  416. }
  417. /**
  418. * Deactivates tracking of a table.
  419. *
  420. * @param string $dbname name of database
  421. * @param string $tablename name of table
  422. * @param string $version version
  423. *
  424. * @static
  425. *
  426. * @return int result of SQL query
  427. */
  428. static public function deactivateTracking($dbname, $tablename, $version)
  429. {
  430. return self::_changeTracking($dbname, $tablename, $version, 0);
  431. }
  432. /**
  433. * Gets the newest version of a tracking job
  434. * (in other words: gets the HEAD version).
  435. *
  436. * @param string $dbname name of database
  437. * @param string $tablename name of table
  438. * @param string $statement tracked statement
  439. *
  440. * @static
  441. *
  442. * @return int (-1 if no version exists | > 0 if a version exists)
  443. */
  444. static public function getVersion($dbname, $tablename, $statement = null)
  445. {
  446. $sql_query = " SELECT MAX(version) FROM " . self::$pma_table .
  447. " WHERE `db_name` = '" . PMA_sqlAddSlashes($dbname) . "' " .
  448. " AND `table_name` = '" . PMA_sqlAddSlashes($tablename) . "' ";
  449. if ($statement != "") {
  450. $sql_query .= PMA_DRIZZLE
  451. ? ' AND tracking & ' . self::transformTrackingSet($statement) . ' <> 0'
  452. : " AND FIND_IN_SET('" . $statement . "',tracking) > 0" ;
  453. }
  454. $row = PMA_DBI_fetch_array(PMA_query_as_controluser($sql_query));
  455. return isset($row[0])
  456. ? $row[0]
  457. : -1;
  458. }
  459. /**
  460. * Gets the record of a tracking job.
  461. *
  462. * @param string $dbname name of database
  463. * @param string $tablename name of table
  464. * @param string $version version number
  465. *
  466. * @static
  467. *
  468. * @return mixed record DDM log, DDL log, structure snapshot, tracked statements.
  469. */
  470. static public function getTrackedData($dbname, $tablename, $version)
  471. {
  472. if (! isset(self::$pma_table)) {
  473. self::init();
  474. }
  475. $sql_query = " SELECT * FROM " . self::$pma_table .
  476. " WHERE `db_name` = '" . PMA_sqlAddSlashes($dbname) . "' ";
  477. if (! empty($tablename)) {
  478. $sql_query .= " AND `table_name` = '" . PMA_sqlAddSlashes($tablename) ."' ";
  479. }
  480. $sql_query .= " AND `version` = '" . PMA_sqlAddSlashes($version) ."' ".
  481. " ORDER BY `version` DESC LIMIT 1";
  482. $mixed = PMA_DBI_fetch_assoc(PMA_query_as_controluser($sql_query));
  483. // Parse log
  484. $log_schema_entries = explode('# log ', $mixed['schema_sql']);
  485. $log_data_entries = explode('# log ', $mixed['data_sql']);
  486. $ddl_date_from = $date = date('Y-m-d H:i:s');
  487. $ddlog = array();
  488. $i = 0;
  489. // Iterate tracked data definition statements
  490. // For each log entry we want to get date, username and statement
  491. foreach ($log_schema_entries as $log_entry) {
  492. if (trim($log_entry) != '') {
  493. $date = substr($log_entry, 0, 19);
  494. $username = substr($log_entry, 20, strpos($log_entry, "\n") - 20);
  495. if ($i == 0) {
  496. $ddl_date_from = $date;
  497. }
  498. $statement = rtrim(strstr($log_entry, "\n"));
  499. $ddlog[] = array( 'date' => $date,
  500. 'username'=> $username,
  501. 'statement' => $statement );
  502. $i++;
  503. }
  504. }
  505. $date_from = $ddl_date_from;
  506. $date_to = $ddl_date_to = $date;
  507. $dml_date_from = $date_from;
  508. $dmlog = array();
  509. $i = 0;
  510. // Iterate tracked data manipulation statements
  511. // For each log entry we want to get date, username and statement
  512. foreach ($log_data_entries as $log_entry) {
  513. if (trim($log_entry) != '') {
  514. $date = substr($log_entry, 0, 19);
  515. $username = substr($log_entry, 20, strpos($log_entry, "\n") - 20);
  516. if ($i == 0) {
  517. $dml_date_from = $date;
  518. }
  519. $statement = rtrim(strstr($log_entry, "\n"));
  520. $dmlog[] = array( 'date' => $date,
  521. 'username' => $username,
  522. 'statement' => $statement );
  523. $i++;
  524. }
  525. }
  526. $dml_date_to = $date;
  527. // Define begin and end of date range for both logs
  528. if (strtotime($ddl_date_from) <= strtotime($dml_date_from)) {
  529. $data['date_from'] = $ddl_date_from;
  530. } else {
  531. $data['date_from'] = $dml_date_from;
  532. }
  533. if (strtotime($ddl_date_to) >= strtotime($dml_date_to)) {
  534. $data['date_to'] = $ddl_date_to;
  535. } else {
  536. $data['date_to'] = $dml_date_to;
  537. }
  538. $data['ddlog'] = $ddlog;
  539. $data['dmlog'] = $dmlog;
  540. $data['tracking'] = self::transformTrackingSet($mixed['tracking']);
  541. $data['schema_snapshot'] = $mixed['schema_snapshot'];
  542. return $data;
  543. }
  544. /**
  545. * Parses a query. Gets
  546. * - statement identifier (UPDATE, ALTER TABLE, ...)
  547. * - type of statement, is it part of DDL or DML ?
  548. * - tablename
  549. *
  550. * @param string $query query
  551. *
  552. * @static
  553. * @todo: using PMA SQL Parser when possible
  554. * @todo: support multi-table/view drops
  555. *
  556. * @return mixed Array containing identifier, type and tablename.
  557. *
  558. */
  559. static public function parseQuery($query)
  560. {
  561. // Usage of PMA_SQP does not work here
  562. //
  563. // require_once("libraries/sqlparser.lib.php");
  564. // $parsed_sql = PMA_SQP_parse($query);
  565. // $sql_info = PMA_SQP_analyze($parsed_sql);
  566. $query = str_replace("\n", " ", $query);
  567. $query = str_replace("\r", " ", $query);
  568. $query = trim($query);
  569. $query = trim($query, ' -');
  570. $tokens = explode(" ", $query);
  571. foreach ($tokens as $key => $value) {
  572. $tokens[$key] = strtoupper($value);
  573. }
  574. // Parse USE statement, need it for SQL dump imports
  575. if (substr($query, 0, 4) == 'USE ') {
  576. $prefix = explode('USE ', $query);
  577. $GLOBALS['db'] = self::getTableName($prefix[1]);
  578. }
  579. /*
  580. * DDL statements
  581. */
  582. $result['type'] = 'DDL';
  583. // Parse CREATE VIEW statement
  584. if (in_array('CREATE', $tokens) == true
  585. && in_array('VIEW', $tokens) == true
  586. && in_array('AS', $tokens) == true
  587. ) {
  588. $result['identifier'] = 'CREATE VIEW';
  589. $index = array_search('VIEW', $tokens);
  590. $result['tablename'] = strtolower(self::getTableName($tokens[$index + 1]));
  591. }
  592. // Parse ALTER VIEW statement
  593. if (in_array('ALTER', $tokens) == true
  594. && in_array('VIEW', $tokens) == true
  595. && in_array('AS', $tokens) == true
  596. && ! isset($result['identifier'])
  597. ) {
  598. $result['identifier'] = 'ALTER VIEW';
  599. $index = array_search('VIEW', $tokens);
  600. $result['tablename'] = strtolower(self::getTableName($tokens[$index + 1]));
  601. }
  602. // Parse DROP VIEW statement
  603. if (! isset($result['identifier']) && substr($query, 0, 10) == 'DROP VIEW ') {
  604. $result['identifier'] = 'DROP VIEW';
  605. $prefix = explode('DROP VIEW ', $query);
  606. $str = strstr($prefix[1], 'IF EXISTS');
  607. if ($str == false ) {
  608. $str = $prefix[1];
  609. }
  610. $result['tablename'] = self::getTableName($str);
  611. }
  612. // Parse CREATE DATABASE statement
  613. if (! isset($result['identifier']) && substr($query, 0, 15) == 'CREATE DATABASE') {
  614. $result['identifier'] = 'CREATE DATABASE';
  615. $str = str_replace('CREATE DATABASE', '', $query);
  616. $str = str_replace('IF NOT EXISTS', '', $str);
  617. $prefix = explode('DEFAULT ', $str);
  618. $result['tablename'] = '';
  619. $GLOBALS['db'] = self::getTableName($prefix[0]);
  620. }
  621. // Parse ALTER DATABASE statement
  622. if (! isset($result['identifier']) && substr($query, 0, 14) == 'ALTER DATABASE') {
  623. $result['identifier'] = 'ALTER DATABASE';
  624. $result['tablename'] = '';
  625. }
  626. // Parse DROP DATABASE statement
  627. if (! isset($result['identifier']) && substr($query, 0, 13) == 'DROP DATABASE') {
  628. $result['identifier'] = 'DROP DATABASE';
  629. $str = str_replace('DROP DATABASE', '', $query);
  630. $str = str_replace('IF EXISTS', '', $str);
  631. $GLOBALS['db'] = self::getTableName($str);
  632. $result['tablename'] = '';
  633. }
  634. // Parse CREATE TABLE statement
  635. if (! isset($result['identifier']) && substr($query, 0, 12) == 'CREATE TABLE' ) {
  636. $result['identifier'] = 'CREATE TABLE';
  637. $query = str_replace('IF NOT EXISTS', '', $query);
  638. $prefix = explode('CREATE TABLE ', $query);
  639. $suffix = explode('(', $prefix[1]);
  640. $result['tablename'] = self::getTableName($suffix[0]);
  641. }
  642. // Parse ALTER TABLE statement
  643. if (! isset($result['identifier']) && substr($query, 0, 12) == 'ALTER TABLE ') {
  644. $result['identifier'] = 'ALTER TABLE';
  645. $prefix = explode('ALTER TABLE ', $query);
  646. $suffix = explode(' ', $prefix[1]);
  647. $result['tablename'] = self::getTableName($suffix[0]);
  648. }
  649. // Parse DROP TABLE statement
  650. if (! isset($result['identifier']) && substr($query, 0, 11) == 'DROP TABLE ') {
  651. $result['identifier'] = 'DROP TABLE';
  652. $prefix = explode('DROP TABLE ', $query);
  653. $str = strstr($prefix[1], 'IF EXISTS');
  654. if ($str == false ) {
  655. $str = $prefix[1];
  656. }
  657. $result['tablename'] = self::getTableName($str);
  658. }
  659. // Parse CREATE INDEX statement
  660. if (! isset($result['identifier'])
  661. && (substr($query, 0, 12) == 'CREATE INDEX'
  662. || substr($query, 0, 19) == 'CREATE UNIQUE INDEX'
  663. || substr($query, 0, 20) == 'CREATE SPATIAL INDEX')
  664. ) {
  665. $result['identifier'] = 'CREATE INDEX';
  666. $prefix = explode('ON ', $query);
  667. $suffix = explode('(', $prefix[1]);
  668. $result['tablename'] = self::getTableName($suffix[0]);
  669. }
  670. // Parse DROP INDEX statement
  671. if (! isset($result['identifier']) && substr($query, 0, 10) == 'DROP INDEX') {
  672. $result['identifier'] = 'DROP INDEX';
  673. $prefix = explode('ON ', $query);
  674. $result['tablename'] = self::getTableName($prefix[1]);
  675. }
  676. // Parse RENAME TABLE statement
  677. if (! isset($result['identifier']) && substr($query, 0, 13) == 'RENAME TABLE ') {
  678. $result['identifier'] = 'RENAME TABLE';
  679. $prefix = explode('RENAME TABLE ', $query);
  680. $names = explode(' TO ', $prefix[1]);
  681. $result['tablename'] = self::getTableName($names[0]);
  682. $result["tablename_after_rename"] = self::getTableName($names[1]);
  683. }
  684. /*
  685. * DML statements
  686. */
  687. if (! isset($result['identifier'])) {
  688. $result["type"] = 'DML';
  689. }
  690. // Parse UPDATE statement
  691. if (! isset($result['identifier']) && substr($query, 0, 6) == 'UPDATE') {
  692. $result['identifier'] = 'UPDATE';
  693. $prefix = explode('UPDATE ', $query);
  694. $suffix = explode(' ', $prefix[1]);
  695. $result['tablename'] = self::getTableName($suffix[0]);
  696. }
  697. // Parse INSERT INTO statement
  698. if (! isset($result['identifier']) && substr($query, 0, 11) == 'INSERT INTO') {
  699. $result['identifier'] = 'INSERT';
  700. $prefix = explode('INSERT INTO', $query);
  701. $suffix = explode('(', $prefix[1]);
  702. $result['tablename'] = self::getTableName($suffix[0]);
  703. }
  704. // Parse DELETE statement
  705. if (! isset($result['identifier']) && substr($query, 0, 6) == 'DELETE') {
  706. $result['identifier'] = 'DELETE';
  707. $prefix = explode('FROM ', $query);
  708. $suffix = explode(' ', $prefix[1]);
  709. $result['tablename'] = self::getTableName($suffix[0]);
  710. }
  711. // Parse TRUNCATE statement
  712. if (! isset($result['identifier']) && substr($query, 0, 8) == 'TRUNCATE') {
  713. $result['identifier'] = 'TRUNCATE';
  714. $prefix = explode('TRUNCATE', $query);
  715. $result['tablename'] = self::getTableName($prefix[1]);
  716. }
  717. return $result;
  718. }
  719. /**
  720. * Analyzes a given SQL statement and saves tracking data.
  721. *
  722. * @param string $query a SQL query
  723. *
  724. * @static
  725. *
  726. * @return nothing
  727. */
  728. static public function handleQuery($query)
  729. {
  730. // If query is marked as untouchable, leave
  731. if (strstr($query, "/*NOTRACK*/")) {
  732. return;
  733. }
  734. if (! (substr($query, -1) == ';')) {
  735. $query = $query . ";\n";
  736. }
  737. // Get some information about query
  738. $result = self::parseQuery($query);
  739. // Get database name
  740. $dbname = trim($GLOBALS['db'], '`');
  741. // $dbname can be empty, for example when coming from Synchronize
  742. // and this is a query for the remote server
  743. if (empty($dbname)) {
  744. return;
  745. }
  746. // If we found a valid statement
  747. if (isset($result['identifier'])) {
  748. $version = self::getVersion($dbname, $result['tablename'], $result['identifier']);
  749. // If version not exists and auto-creation is enabled
  750. if (self::$version_auto_create == true
  751. && self::isTracked($dbname, $result['tablename']) == false
  752. && $version == -1
  753. ) {
  754. // Create the version
  755. switch ($result['identifier']) {
  756. case 'CREATE TABLE':
  757. self::createVersion($dbname, $result['tablename'], '1');
  758. break;
  759. case 'CREATE VIEW':
  760. self::createVersion($dbname, $result['tablename'], '1', '', true);
  761. break;
  762. case 'CREATE DATABASE':
  763. self::createDatabaseVersion($dbname, '1', $query);
  764. break;
  765. } // end switch
  766. }
  767. // If version exists
  768. if (self::isTracked($dbname, $result['tablename']) && $version != -1) {
  769. if ($result['type'] == 'DDL') {
  770. $save_to = 'schema_sql';
  771. } elseif ($result['type'] == 'DML') {
  772. $save_to = 'data_sql';
  773. } else {
  774. $save_to = '';
  775. }
  776. $date = date('Y-m-d H:i:s');
  777. // Cut off `dbname`. from query
  778. $query = preg_replace('/`' . $dbname . '`\s?\./', '', $query);
  779. // Add log information
  780. $query = self::getLogComment() . $query ;
  781. // Mark it as untouchable
  782. $sql_query = " /*NOTRACK*/\n" .
  783. " UPDATE " . self::$pma_table .
  784. " SET " . PMA_backquote($save_to) ." = CONCAT( " . PMA_backquote($save_to) . ",'\n"
  785. . PMA_sqlAddSlashes($query) . "') ," . " `date_updated` = '" . $date . "' ";
  786. // If table was renamed we have to change the tablename attribute in pma_tracking too
  787. if ($result['identifier'] == 'RENAME TABLE') {
  788. $sql_query .= ', `table_name` = \'' . PMA_sqlAddSlashes($result['tablename_after_rename']) . '\' ';
  789. }
  790. // Save the tracking information only for
  791. // 1. the database
  792. // 2. the table / view
  793. // 3. the statements
  794. // we want to track
  795. $sql_query .=
  796. " WHERE FIND_IN_SET('" . $result['identifier'] . "',tracking) > 0" .
  797. " AND `db_name` = '" . PMA_sqlAddSlashes($dbname) . "' " .
  798. " AND `table_name` = '" . PMA_sqlAddSlashes($result['tablename']) . "' " .
  799. " AND `version` = '" . PMA_sqlAddSlashes($version) . "' ";
  800. $result = PMA_query_as_controluser($sql_query);
  801. }
  802. }
  803. }
  804. /**
  805. * Transforms tracking set for Drizzle, which has no SET type
  806. *
  807. * Converts int<>string for Drizzle, does nothing for MySQL
  808. *
  809. * @param int|string $tracking_set
  810. * @return int|string
  811. */
  812. static private function transformTrackingSet($tracking_set)
  813. {
  814. if (!PMA_DRIZZLE) {
  815. return $tracking_set;
  816. }
  817. // init conversion array (key 3 doesn't exist in calculated array)
  818. if (isset(self::$tracking_set_flags[3])) {
  819. // initialize flags
  820. $set = self::$tracking_set_flags;
  821. $array = array();
  822. for ($i = 0; $i < count($set); $i++) {
  823. $flag = 1 << $i;
  824. $array[$flag] = $set[$i];
  825. $array[$set[$i]] = $flag;
  826. }
  827. self::$tracking_set_flags = $array;
  828. }
  829. if (is_numeric($tracking_set)) {
  830. // int > string conversion
  831. $aflags = array();
  832. // count/2 - conversion table has both int > string and string > int values
  833. for ($i = 0; $i < count(self::$tracking_set_flags)/2; $i++) {
  834. $flag = 1 << $i;
  835. if ($tracking_set & $flag) {
  836. $aflags[] = self::$tracking_set_flags[$flag];
  837. }
  838. }
  839. $flags = implode(',', $aflags);
  840. } else {
  841. // string > int conversion
  842. $flags = 0;
  843. foreach (explode(',', $tracking_set) as $strflag) {
  844. if ($strflag == '') {
  845. continue;
  846. }
  847. $flags |= self::$tracking_set_flags[$strflag];
  848. }
  849. }
  850. return $flags;
  851. }
  852. }
  853. ?>