PageRenderTime 50ms CodeModel.GetById 25ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

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