Example #1
0
 /**
  * Returns the tables in the current database
  *
  * @param  boolean|string  $creation_order  `TRUE` to return in a valid table creation order, or a table name to return that table and any tables that depend on it, in table creation order
  * @return array  The tables in the current database, all converted to lowercase
  */
 public function getTables($creation_order = NULL)
 {
     if ($creation_order) {
         return $this->determineTableCreationOrder(is_bool($creation_order) ? NULL : $creation_order);
     }
     if ($this->tables !== NULL) {
         return $this->tables;
     }
     switch ($this->database->getType()) {
         case 'db2':
             $sql = "SELECT\n\t\t\t\t\t\t\tLOWER(RTRIM(TABSCHEMA)) AS \"schema\",\n\t\t\t\t\t\t\tLOWER(TABNAME) AS \"table\"\n\t\t\t\t\t\tFROM\n\t\t\t\t\t\t\tSYSCAT.TABLES\n\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\tTYPE = 'T' AND\n\t\t\t\t\t\t\tTABSCHEMA != 'SYSIBM' AND\n\t\t\t\t\t\t\tDEFINER != 'SYSIBM' AND\n\t\t\t\t\t\t\tTABSCHEMA != 'SYSTOOLS' AND\n\t\t\t\t\t\t\tDEFINER != 'SYSTOOLS'\n\t\t\t\t\t\tORDER BY\n\t\t\t\t\t\t\tLOWER(TABNAME)";
             break;
         case 'mssql':
             $sql = "SELECT\n\t\t\t\t\t\t\tTABLE_SCHEMA AS \"schema\",\n\t\t\t\t\t\t\tTABLE_NAME AS \"table\"\n\t\t\t\t\t\tFROM\n\t\t\t\t\t\t\tINFORMATION_SCHEMA.TABLES\n\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\tTABLE_NAME != 'sysdiagrams'\n\t\t\t\t\t\tORDER BY\n\t\t\t\t\t\t\tLOWER(TABLE_NAME)";
             break;
         case 'mysql':
             if (version_compare($this->database->getVersion(), 5, '<')) {
                 $sql = 'SHOW TABLES';
             } else {
                 $sql = "SHOW FULL TABLES WHERE table_type = 'BASE TABLE'";
             }
             break;
         case 'oracle':
             $sql = "SELECT\n\t\t\t\t\t\t\tLOWER(OWNER) AS \"SCHEMA\",\n\t\t\t\t\t\t\tLOWER(TABLE_NAME) AS \"TABLE\"\n\t\t\t\t\t\tFROM\n\t\t\t\t\t\t\tALL_TABLES\n\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\tOWNER NOT IN (\n\t\t\t\t\t\t\t\t'SYS',\n\t\t\t\t\t\t\t\t'SYSTEM',\n\t\t\t\t\t\t\t\t'OUTLN',\n\t\t\t\t\t\t\t\t'ANONYMOUS',\n\t\t\t\t\t\t\t\t'AURORA\$ORB\$UNAUTHENTICATED',\n\t\t\t\t\t\t\t\t'AWR_STAGE',\n\t\t\t\t\t\t\t\t'CSMIG',\n\t\t\t\t\t\t\t\t'CTXSYS',\n\t\t\t\t\t\t\t\t'DBSNMP',\n\t\t\t\t\t\t\t\t'DIP',\n\t\t\t\t\t\t\t\t'DMSYS',\n\t\t\t\t\t\t\t\t'DSSYS',\n\t\t\t\t\t\t\t\t'EXFSYS',\n\t\t\t\t\t\t\t\t'FLOWS_020100',\n\t\t\t\t\t\t\t\t'FLOWS_FILES',\n\t\t\t\t\t\t\t\t'LBACSYS',\n\t\t\t\t\t\t\t\t'MDSYS',\n\t\t\t\t\t\t\t\t'ORACLE_OCM',\n\t\t\t\t\t\t\t\t'ORDPLUGINS',\n\t\t\t\t\t\t\t\t'ORDSYS',\n\t\t\t\t\t\t\t\t'PERFSTAT',\n\t\t\t\t\t\t\t\t'TRACESVR',\n\t\t\t\t\t\t\t\t'TSMSYS',\n\t\t\t\t\t\t\t\t'XDB'\n\t\t\t\t\t\t\t) AND\n\t\t\t\t\t\t\tDROPPED = 'NO'\n\t\t\t\t\t\tORDER BY\n\t\t\t\t\t\t\tTABLE_NAME ASC";
             break;
         case 'postgresql':
             $sql = "SELECT\n\t\t\t\t\t\t\t schemaname AS \"schema\",\n\t\t\t\t\t\t\t tablename as \"table\"\n\t\t\t\t\t\tFROM\n\t\t\t\t\t\t\t pg_tables\n\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t tablename !~ '^(pg|sql)_'\n\t\t\t\t\t\tORDER BY\n\t\t\t\t\t\t\tLOWER(tablename)";
             break;
         case 'sqlite':
             $sql = "SELECT\n\t\t\t\t\t\t\tname\n\t\t\t\t\t\tFROM\n\t\t\t\t\t\t\tsqlite_master\n\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\ttype = 'table' AND\n\t\t\t\t\t\t\tname NOT LIKE 'sqlite_%'\n\t\t\t\t\t\tORDER BY\n\t\t\t\t\t\t\tname ASC";
             break;
     }
     $result = $this->database->query($sql);
     $this->tables = array();
     // For databases with schemas we only include the schema
     // name if there are conflicting table names
     if (!in_array($this->database->getType(), array('mysql', 'sqlite'))) {
         $default_schema_map = array('db2' => strtolower($this->database->getUsername()), 'mssql' => 'dbo', 'oracle' => strtolower($this->database->getUsername()), 'postgresql' => 'public');
         $default_schema = $default_schema_map[$this->database->getType()];
         foreach ($result as $row) {
             if ($row['schema'] == $default_schema) {
                 $this->tables[] = strtolower($row['table']);
             } else {
                 $this->tables[] = strtolower($row['schema'] . '.' . $row['table']);
             }
         }
         // SQLite and MySQL don't support schemas
     } else {
         foreach ($result as $row) {
             $keys = array_keys($row);
             $this->tables[] = strtolower($row[$keys[0]]);
         }
     }
     sort($this->tables);
     if ($this->cache) {
         $this->cache->set($this->makeCachePrefix() . 'tables', $this->tables);
     }
     return $this->tables;
 }
 /**
  * Translates Flourish SQL `ALTER TABLE * RENAME TO` statements to the appropriate
  * statements for SQLite
  *
  * @param string $sql                The SQL statements that will be executed against the database
  * @param array  &$extra_statements  Any extra SQL statements required for SQLite
  * @param array  $data               Data parsed from the `ALTER TABLE` statement
  * @return string  The modified SQL statement
  */
 private function translateSQLiteRenameTableStatements($sql, &$extra_statements, $data)
 {
     $tables = $this->getSQLiteTables();
     if (in_array($data['new_table_name'], $tables)) {
         $this->throwException(self::compose('A table with the name "%1$s" already exists', $data['new_table_name']), $sql);
     }
     if (!in_array($data['table'], $tables)) {
         $this->throwException(self::compose('The table specified, "%1$s", does not exist', $data['table']), $sql);
     }
     // We start by dropping all references to this table
     $foreign_keys = $this->getSQLiteForeignKeys($data['table']);
     foreach ($foreign_keys as $foreign_key) {
         $extra_statements = array_merge($extra_statements, $this->database->preprocess("ALTER TABLE %r DROP FOREIGN KEY (%r)", array($foreign_key['table'], $foreign_key['column']), TRUE));
     }
     // SQLite 2 does not natively support renaming tables, so we have to do
     // it by creating a new table name and copying all data and indexes
     if (version_compare($this->database->getVersion(), 3, '<')) {
         $renamed_create_sql = preg_replace('#^\\s*CREATE\\s+TABLE\\s+["\\[`\']?\\w+["\\]`\']?\\s+#i', 'CREATE TABLE "' . $data['new_table_name'] . '" ', $this->getSQLiteCreateTable($data['table']));
         $this->addSQLiteTable($data['new_table_name'], $renamed_create_sql);
         // We rename string placeholders to prevent confusion with
         // string placeholders that are added by call to fDatabase
         $renamed_create_sql = str_replace(':string_', ':sub_string_', $renamed_create_sql);
         $create_statements = str_replace(':sub_string_', ':string_', $this->database->preprocess($renamed_create_sql, array(), TRUE));
         $extra_statements[] = array_shift($create_statements);
         // Recreate the indexes on the new table
         $indexes = $this->getSQLiteIndexes($data['table']);
         foreach ($indexes as $name => $index) {
             $create_sql = $index['sql'];
             preg_match('#^\\s*CREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+(?:[\'"`\\[]?\\w+[\'"`\\]]?\\.)?[\'"`\\[]?\\w+[\'"`\\]]?\\s+(ON\\s+[\'"`\\[]?\\w+[\'"`\\]]?)\\s*(\\((\\s*(?:\\s*[\'"`\\[]?\\w+[\'"`\\]]?\\s*,\\s*)*[\'"`\\[]?\\w+[\'"`\\]]?\\s*)\\))\\s*$#Di', $create_sql, $match);
             // Fix the table name to the new table
             $create_sql = str_replace($match[1], preg_replace('#(?:`|\'|"|\\[|\\b)?' . preg_quote($data['table'], '#') . '(?:`|\'|"|\\]|\\b)?#i', '"' . $data['new_table_name'] . '"', $match[1]), $create_sql);
             // We change the name of the index to keep it in sync
             // with the new table name
             $new_name = preg_replace('#^' . preg_quote($data['table'], '#') . '_#i', $data['new_table_name'] . '_', $name);
             $create_sql = preg_replace('#[\'"`\\[]?' . preg_quote($name, '#') . '[\'"`\\]]?(\\s+ON\\s+)#i', '"' . $new_name . '"\\1', $create_sql);
             $extra_statements[] = $create_sql;
             $this->addSQLiteIndex($new_name, $data['new_table_name'], $create_sql);
         }
         $column_names = $this->getSQLiteColumns($data['table']);
         $extra_statements[] = $this->database->escape("INSERT INTO %r (%r) SELECT %r FROM %r", $data['new_table_name'], $column_names, $column_names, $data['table']);
         $extra_statements = array_merge($extra_statements, $create_statements);
         $extra_statements = array_merge($extra_statements, $this->database->preprocess("DROP TABLE %r", array($data['table']), TRUE));
         // SQLite 3 natively supports renaming tables, but it does not fix
         // references to the old table name inside of trigger bodies
     } else {
         // We add the rename SQL in the middle so it happens after we drop the
         // foreign key constraints and before we re-add them
         $extra_statements[] = $sql;
         $this->addSQLiteTable($data['new_table_name'], preg_replace('#^\\s*CREATE\\s+TABLE\\s+[\'"\\[`]?\\w+[\'"\\]`]?\\s+#i', 'CREATE TABLE "' . $data['new_table_name'] . '" ', $this->getSQLiteCreateTable($data['table'])));
         $this->removeSQLiteTable($data['table']);
         // Copy the trigger definitions to the new table name
         foreach ($this->getSQLiteTriggers() as $name => $trigger) {
             if ($trigger['table'] == $data['table']) {
                 $this->addSQLiteTrigger($name, $data['new_table_name'], $trigger['sql']);
             }
         }
         // Move the index definitions to the new table name
         foreach ($this->getSQLiteIndexes($data['table']) as $name => $index) {
             $this->addSQLiteIndex($name, $data['new_table_name'], preg_replace('#(\\s+ON\\s+)["\'`\\[]?\\w+["\'`\\]]?#', '\\1"' . preg_quote($data['new_table_name'], '#') . '"', $index['sql']));
         }
         foreach ($this->getSQLiteTriggers() as $name => $trigger) {
             $create_sql = $trigger['sql'];
             $create_sql = preg_replace('#( on table )"' . $data['table'] . '"#i', '\\1"' . $data['new_table_name'] . '"', $create_sql);
             $create_sql = preg_replace('#(\\s+FROM\\s+)(`' . $data['table'] . '`|"' . $data['table'] . '"|\'' . $data['table'] . '\'|' . $data['table'] . '|\\[' . $data['table'] . '\\])#i', '\\1"' . $data['new_table_name'] . '"', $create_sql);
             if ($create_sql != $trigger['sql']) {
                 $extra_statements[] = $this->database->escape("DROP TRIGGER %r", $name);
                 $this->removeSQLiteTrigger($name);
                 $this->addSQLiteTrigger($name, $data['new_table_name'], $create_sql);
                 $extra_statements[] = $create_sql;
             }
         }
     }
     // Here we recreate the references that we dropped at the beginning
     foreach ($foreign_keys as $foreign_key) {
         $extra_statements = array_merge($extra_statements, $this->database->preprocess("ALTER TABLE %r ADD FOREIGN KEY (%r) REFERENCES %r(%r) ON UPDATE " . $foreign_key['on_update'] . " ON DELETE " . $foreign_key['on_delete'], array($foreign_key['table'], $foreign_key['column'], $data['new_table_name'], $foreign_key['foreign_column']), TRUE));
     }
     // Remove any nested transactions
     $extra_statements = array_diff($extra_statements, array("BEGIN", "COMMIT"));
     // Since the actual rename or create/drop has to happen after adjusting
     // foreign keys, we previously added it in the appropriate place and
     // now need to provide the first statement to be run
     return array_shift($extra_statements);
 }
Example #3
0
 /**
  * Translates basic syntax differences of the current database
  * 
  * @param  string $sql  The SQL to translate
  * @return string  The translated SQL
  */
 private function translateBasicSyntax($sql)
 {
     if ($this->database->getType() == 'db2') {
         $regex = array('#\\brandom\\(#i' => 'RAND(', '#\\bceil\\(#i' => 'CEILING(', '#\\btrue\\b#i' => "'1'", '#\\bfalse\\b#i' => "'0'", '#\\bpi\\(\\)#i' => '3.14159265358979', '#\\bcot\\(\\s*((?>[^()\\s]+|\\(((?:[^()]+|\\((?2)\\))*)\\))+)\\s*\\)#i' => '(1/TAN(\\1))', '#\\blength\\(\\s*((?>[^()\\s]+|\\(((?:[^()]+|\\((?2)\\))*)\\))+)\\s*\\)#i' => 'CHARACTER_LENGTH(\\1, CODEUNITS32)', '#\\bsubstr\\(\\s*((?>[^(),]+|\\((?1)(?:,(?1))?\\)|\\(\\))+)\\s*,\\s*((?>[^(),]+|\\((?2)(?:,(?2))?\\)|\\(\\))+)\\s*,\\s*((?>[^(),]+|\\((?3)(?:,(?3))?\\)|\\(\\))+)\\s*\\)#i' => 'SUBSTRING(\\1, \\2, \\3, CODEUNITS32)', '#(?:\\b|^)((?>[^()%\\s]+|\\(((?:[^()]+|\\((?2)\\))*)\\))+)\\s*%(?!\\d+\\$[lbdfristp]\\b)\\s*((?>[^()\\s]+|\\(((?:[^()]+|\\((?4)\\))*)\\))+)(?:\\b|$)#i' => 'MOD(\\1, \\3)', '#(?<!["\\w.])((?>[^()\\s]+|\\(((?:[^()]+|\\((?2)\\))*)\\))+)\\s+(NOT\\s+)?LIKE\\s+((?>[^()\\s]+|\\(((?:[^()]+|\\((?4)\\))*)\\))+)(?:\\b|$)#i' => 'LOWER(\\1) \\3LIKE LOWER(\\4)', '#\\blog\\(\\s*((?>[^(),]+|\\((?1)(?:,(?1))?\\)|\\(\\))+)\\s*,\\s*((?>[^(),]+|\\((?2)(?:,(?2))?\\)|\\(\\))+)\\s*\\)#i' => '(LN(\\2)/LN(\\1))');
     } elseif ($this->database->getType() == 'mssql') {
         $regex = array('#\\bbegin\\s*(?!tran)#i' => 'BEGIN TRANSACTION ', '#\\brandom\\(#i' => 'RAND(', '#\\batan2\\(#i' => 'ATN2(', '#\\bceil\\(#i' => 'CEILING(', '#\\bln\\(#i' => 'LOG(', '#\\blength\\(#i' => 'LEN(', '#\\bsubstr\\(#i' => 'SUBSTRING(', '#\\btrue\\b#i' => "'1'", '#\\bfalse\\b#i' => "'0'", '#\\|\\|#i' => '+', '#\\btrim\\(\\s*((?>[^(),]+|\\((?1)\\)|\\(\\))+)\\s*\\)#i' => 'RTRIM(LTRIM(\\1))', '#\\bround\\(\\s*((?>[^(),]+|\\((?1)\\)|\\(\\))+)\\s*\\)#i' => 'round(\\1, 0)', '#\\blog\\(\\s*((?>[^(),]+|\\((?1)(?:,(?1))?\\)|\\(\\))+)\\s*,\\s*((?>[^(),]+|\\((?2)(?:,(?2))?\\)|\\(\\))+)\\s*\\)#i' => '(LOG(\\2)/LOG(\\1))');
     } elseif ($this->database->getType() == 'mysql') {
         $regex = array('#\\brandom\\(#i' => 'rand(', '#\\bpi\\(\\)#i' => '(pi()+0.0000000000000)', '#\\blength\\(#i' => 'CHAR_LENGTH(');
     } elseif ($this->database->getType() == 'oracle') {
         $regex = array('#\\btrue\\b#i' => '1', '#\\bfalse\\b#i' => '0', '#\\bceiling\\(#i' => 'CEIL(', '#\\brandom\\(\\)#i' => '(ABS(DBMS_RANDOM.RANDOM)/2147483647)', '#\\bpi\\(\\)#i' => '3.14159265358979', '#\\bcot\\(\\s*((?>[^()\\s]+|\\(((?:[^()]+|\\((?2)\\))*)\\))+)\\s*\\)#i' => '(1/TAN(\\1))', '#\\bdegrees\\(\\s*((?>[^()\\s]+|\\(((?:[^()]+|\\((?2)\\))*)\\))+)\\s*\\)#i' => '(\\1 * 57.295779513083)', '#\\bradians\\(\\s*((?>[^()\\s]+|\\(((?:[^()]+|\\((?2)\\))*)\\))+)\\s*\\)#i' => '(\\1 * 0.017453292519943)', '#(?:\\b|^)((?>[^()%\\s]+|\\(((?:[^()]+|\\((?2)\\))*)\\))+)\\s*%(?!\\d+\\$[lbdfristp]\\b)\\s*((?>[^()\\s]+|\\(((?:[^()]+|\\((?4)\\))*)\\))+)(?:\\b|$)#i' => 'MOD(\\1, \\3)', '#(?<!["\\w.])((?>[^()\\s]+|\\(((?:[^()]+|\\((?2)\\))*)\\))+)\\s+(NOT\\s+)?LIKE\\s+((?>[^()\\s]+|\\(((?:[^()]+|\\((?4)\\))*)\\))+)(?:\\b|$)#i' => 'LOWER(\\1) \\3LIKE LOWER(\\4)');
     } elseif ($this->database->getType() == 'postgresql') {
         $regex = array('#(?<!["\\w.])(["\\w.]+)\\s+(not\\s+)?like\\b#i' => 'CAST(\\1 AS VARCHAR) \\2ILIKE', '#\\blower\\(\\s*(?<!["\\w.])(["\\w.]+)\\s*\\)#i' => 'LOWER(CAST(\\1 AS VARCHAR))', '#\\blike\\b#i' => 'ILIKE', '#\\b(INSERT\\s+INTO\\s+(?:\\w+|"[^"]+")\\s+)\\(\\s*\\)\\s+VALUES\\s+\\(\\s*\\)#i' => '\\1DEFAULT VALUES');
     } elseif ($this->database->getType() == 'sqlite') {
         if (version_compare($this->database->getVersion(), 3, '>=')) {
             $regex = array('#\\bcurrent_timestamp\\b#i' => "datetime(CURRENT_TIMESTAMP, 'localtime')", '#\\btrue\\b#i' => "'1'", '#\\bfalse\\b#i' => "'0'", '#\\brandom\\(\\)#i' => '(ABS(RANDOM())/9223372036854775807)');
         } else {
             $regex = array('#\\bcurrent_timestamp\\b#i' => "CURRENT_TIMESTAMP()", '#\\bcurrent_time\\b#i' => "CURRENT_TIME()", '#\\bcurrent_date\\b#i' => "CURRENT_DATE()", '#\\btrue\\b#i' => "'1'", '#\\bfalse\\b#i' => "'0'", '#\\brandom\\(\\)#i' => '(ABS(RANDOM())/9223372036854775807)', '#\\bcast\\(\\s*((?:[^()\\s]+|\\(((?:[^()]+|\\((?2)\\))*)\\))+)\\s+as\\s+(?:[^()\\s]+|\\(((?:[^()]+|\\((?3)\\))*)\\))+\\s*\\)#i' => '\\1');
         }
     }
     return preg_replace(array_keys($regex), array_values($regex), $sql);
 }