public function testCreateTable()
    {
        $xml = <<<XML
<schema name="public" owner="NOBODY">
  <table name="test" primaryKey="id" description="test description">
    <tableOption sqlFormat="pgsql8" name="tablespace" value="schmableschpace"/>
    <tableOption sqlFormat="pgsql8" name="with" value="(oids=true,fillfactor=70)"/>
    <tableOption sqlFormat="mysql5" name="auto_increment" value="5"/>
    <column name="id" type="int"/>
    <column name="foo" type="int"/>
  </table>
</schema>
XML;
        $schema = new SimpleXMLElement($xml);
        $expected = <<<SQL
CREATE TABLE "public"."test" (
\t"id" int,
\t"foo" int
)
WITH (oids=true,fillfactor=70)
TABLESPACE schmableschpace;
COMMENT ON TABLE "public"."test" IS 'test description';
SQL;
        $actual = pgsql8_table::get_creation_sql($schema, $schema->table);
        $this->assertEquals($expected, trim($actual));
    }
 /**
  * Does this table constrain against a renamed table?
  * 
  * @param object $db_doc
  * @param object $schema
  * @param object $table
  * return boolean
  */
 public static function constrains_against_renamed_table($db_doc, $schema, $table)
 {
     foreach (format_constraint::get_table_constraints($db_doc, $schema, $table, 'constraint') as $constraint) {
         if (pgsql8_table::constraint_depends_on_renamed_table($db_doc, $constraint)) {
             dbsteward::info("NOTICE: " . $schema['name'] . "." . $table['name'] . " constrains against a renamed table with constraint " . $constraint['name']);
             return TRUE;
         }
     }
     return FALSE;
 }
 /**
  * Parses DELETE FROM command.
  *
  * @param database database
  * @param command DELETE FROM command
  *
  */
 public static function parse($database, $command)
 {
     if (preg_match(self::PATTERN_DELETE_FROM, $command, $matches) > 0) {
         $line = $command;
         $table_name = $matches[1];
         $where_clause = $matches[2];
         $table_name = sql_parser::get_schema_name($table_name, $database) . '.' . sql_parser::get_object_name($table_name);
         $schema = $database->get_schema(sql_parser::get_schema_name($table_name, $database));
         if ($schema == null) {
             throw new exception("Failed to find schema for data delete: " . sql_parser::get_schema_name($table_name, $database));
         }
         $table = $schema->get_table(sql_parser::get_object_name($table_name));
         if ($table == null) {
             throw new exception("Failed to find table for data delete: " . $table_name);
         }
         pgsql8_table::delete_data_row($table, $where_clause);
     } else {
         throw new exception("Cannot parse command: " . $command);
     }
 }
Esempio n. 4
0
    public function testQuoteFKeyTable()
    {
        $xml = <<<XML
<dbsteward>
<schema name="test">
  <table name="test1">
    <column name="col1" type="int" />
  </table>
  <table name="test2">
    <column name="col1" foreignSchema="test" foreignTable="test1" foreignColumn="col1"/>
  </table>
</schema>
</dbsteward>
XML;
        $db_doc = simplexml_load_string($xml);
        $constraints = pgsql8_constraint::get_table_constraints($db_doc, $db_doc->schema, $db_doc->schema->table[1], 'foreignKey');
        $sql = trim(pgsql8_table::get_constraint_sql_change_statement($constraints[0]));
        $expected = 'ADD CONSTRAINT "test2_col1_fkey" FOREIGN KEY ("col1") REFERENCES "test"."test1" ("col1")';
        $this->assertEquals($expected, $sql);
    }
 public static function parse($database, $command)
 {
     if (preg_match(self::PATTERN_INSERT_INTO, $command, $matches) > 0) {
         $line = $command;
         $table_name = $matches[1];
         $columns_signature = md5($matches[2]);
         if (!isset(self::$columns_cache[$columns_signature])) {
             self::$columns_cache[$columns_signature] = $columns = self::column_split($matches[2], array('"'));
         } else {
             $columns = self::$columns_cache[$columns_signature];
         }
         $data_row = self::column_split($matches[3], array("'"));
         if (count($columns) != count($data_row)) {
             throw new exception("column count " . count($columns) . " does not match data_row count " . count($data_row));
         }
         // merge together as an alpha index row
         for ($i = 0; $i < count($columns); $i++) {
             $row[$columns[$i]] = $data_row[$i];
         }
         $node_schema =& dbx::get_schema($database, sql_parser::get_schema_name($table_name, $database));
         if ($node_schema == null) {
             throw new exception("Failed to find schema for data append: " . sql_parser::get_schema_name($table_name, $database));
         }
         $node_table =& dbx::get_table($node_schema, sql_parser::get_object_name($table_name));
         if ($node_table == null) {
             throw new exception("Failed to find table for data append: " . $table_name);
         }
         try {
             pgsql8_table::add_data_row($node_table, $row);
         } catch (Exception $e) {
             var_dump($command);
             throw $e;
         }
     } else {
         throw new exception("Cannot parse command: " . $command);
     }
 }
Esempio n. 6
0
 public static function build_schema($db_doc, $ofs, $table_depends)
 {
     // schema creation
     foreach ($db_doc->schema as $schema) {
         $ofs->write(pgsql8_schema::get_creation_sql($schema));
         // schema grants
         if (isset($schema->grant)) {
             foreach ($schema->grant as $grant) {
                 $ofs->write(pgsql8_permission::get_sql($db_doc, $schema, $schema, $grant) . "\n");
             }
         }
     }
     // types: enumerated list, etc
     foreach ($db_doc->schema as $schema) {
         foreach ($schema->type as $type) {
             $ofs->write(pgsql8_type::get_creation_sql($schema, $type) . "\n");
         }
     }
     // table structure creation
     foreach ($db_doc->schema as $schema) {
         // create defined tables
         pgsql8_table::$include_column_default_nextval_in_create_sql = FALSE;
         foreach ($schema->table as $table) {
             // table definition
             $ofs->write(pgsql8_table::get_creation_sql($schema, $table) . "\n");
             // table indexes
             pgsql8_diff_indexes::diff_indexes_table($ofs, NULL, NULL, $schema, $table);
             // table grants
             if (isset($table->grant)) {
                 foreach ($table->grant as $grant) {
                     $ofs->write(pgsql8_permission::get_sql($db_doc, $schema, $table, $grant) . "\n");
                 }
             }
             $ofs->write("\n");
         }
         pgsql8_table::$include_column_default_nextval_in_create_sql = TRUE;
         // sequences contained in the schema
         if (isset($schema->sequence)) {
             foreach ($schema->sequence as $sequence) {
                 $ofs->write(pgsql8_sequence::get_creation_sql($schema, $sequence));
                 // sequence permission grants
                 if (isset($sequence->grant)) {
                     foreach ($sequence->grant as $grant) {
                         $ofs->write(pgsql8_permission::get_sql($db_doc, $schema, $sequence, $grant) . "\n");
                     }
                 }
             }
         }
         // add table nextvals that were omitted
         foreach ($schema->table as $table) {
             if (pgsql8_table::has_default_nextval($table)) {
                 $ofs->write(pgsql8_table::get_default_nextval_sql($schema, $table) . "\n");
             }
         }
     }
     $ofs->write("\n");
     // function definitions
     foreach ($db_doc->schema as $schema) {
         foreach ($schema->function as $function) {
             if (pgsql8_function::has_definition($function)) {
                 $ofs->write(pgsql8_function::get_creation_sql($schema, $function));
                 // when pg:build_schema() is doing its thing for straight builds, include function permissions
                 // they are not included in pg_function::get_creation_sql()
                 foreach (dbx::get_permissions($function) as $function_permission) {
                     $ofs->write(pgsql8_permission::get_sql($db_doc, $schema, $function, $function_permission) . "\n");
                 }
             }
         }
     }
     $ofs->write("\n");
     // maybe move this but here we're defining column defaults fo realz
     foreach ($db_doc->schema as $schema) {
         foreach ($schema->table as $table) {
             $ofs->write(pgsql8_table::define_table_column_defaults($schema, $table));
         }
     }
     // define table primary keys before foreign keys so unique requirements are always met for FOREIGN KEY constraints
     foreach ($db_doc->schema as $schema) {
         foreach ($schema->table as $table) {
             pgsql8_diff_tables::diff_constraints_table($ofs, NULL, NULL, $schema, $table, 'primaryKey', FALSE);
         }
     }
     $ofs->write("\n");
     // foreign key references
     // use the dependency order to specify foreign keys in an order that will satisfy nested foreign keys and etc
     for ($i = 0; $i < count($table_depends); $i++) {
         $schema = $table_depends[$i]['schema'];
         $table = $table_depends[$i]['table'];
         if ($table['name'] === dbsteward::TABLE_DEPENDENCY_IGNORABLE_NAME) {
             // don't do anything with this table, it is a magic internal DBSteward value
             continue;
         }
         pgsql8_diff_tables::diff_constraints_table($ofs, NULL, NULL, $schema, $table, 'constraint', FALSE);
     }
     $ofs->write("\n");
     // trigger definitions
     foreach ($db_doc->schema as $schema) {
         foreach ($schema->trigger as $trigger) {
             // only do triggers set to the current sql format
             if (strcasecmp($trigger['sqlFormat'], dbsteward::get_sql_format()) == 0) {
                 $ofs->write(pgsql8_trigger::get_creation_sql($schema, $trigger));
             }
         }
     }
     $ofs->write("\n");
     pgsql8_diff_views::create_views_ordered($ofs, null, $db_doc);
     // view permission grants
     foreach ($db_doc->schema as $schema) {
         foreach ($schema->view as $view) {
             if (isset($view->grant)) {
                 foreach ($view->grant as $grant) {
                     $ofs->write(pgsql8_permission::get_sql($db_doc, $schema, $view, $grant) . "\n");
                 }
             }
         }
     }
     $ofs->write("\n");
     // use pgdiff to add any configurationParameters that are defined
     pgsql8_diff::update_database_config_parameters($ofs, null, $db_doc);
 }
Esempio n. 7
0
 /**
  * @group pgsql8
  */
 public function testTableColumnTypeQuotingPgsql8()
 {
     dbsteward::set_sql_format('pgsql8');
     dbsteward::$quote_all_names = TRUE;
     dbsteward::$single_stage_upgrade = TRUE;
     $doc_empty = simplexml_load_string($this->xml_empty);
     $doc_empty = xml_parser::composite_doc(FALSE, $doc_empty);
     dbsteward::$old_database = $doc_empty;
     $doc = simplexml_load_string($this->xml);
     $doc = xml_parser::composite_doc(FALSE, $doc);
     dbsteward::$new_database = $doc;
     $table_dependency = xml_parser::table_dependency_order($doc);
     //var_dump(xml_parser::format_xml($doc_empty->saveXML()));
     //var_dump(xml_parser::format_xml($doc->saveXML()));
     $schema = $doc->schema;
     $table = $schema->table;
     // make sure the type is named with quoting as part of a definition build
     $expected = "CREATE TYPE \"schema1\".\"enumCamelCaseType\" AS ENUM ('Read','Write','Delete');";
     $mofs = new mock_output_file_segmenter();
     pgsql8::build_schema($doc, $mofs, $table_dependency);
     $actual = trim($mofs->_get_output());
     $this->assertContains($expected, $actual);
     // make sure the type is referred to with quoting in a table creation as part of a definition build
     $expected_column = '"table_shable_mode" "enumCamelCaseType"';
     $this->assertContains($expected_column, $actual);
     // make sure the type is referred to with quoting when generating table create statements
     $expected = '"table_shable_mode" "enumCamelCaseType"';
     $sql = pgsql8_table::get_creation_sql($schema, $table);
     $this->assertContains($expected, $sql);
     // make sure create table quotes the type name
     $expected = '"table_shable_mode" "enumCamelCaseType"';
     $mofs = new mock_output_file_segmenter();
     var_dump(dbx::get_tables($schema));
     pgsql8_diff_tables::diff_tables($mofs, $mofs, NULL, $schema);
     $actual = trim($mofs->_get_output());
     $this->assertContains($expected, $actual);
     // make sure insert statements are made that match the XML definition
     $expected = "INSERT INTO \"schema1\".\"table_shable\" (\"table_shable_id\", \"table_shable_value\", \"table_shable_mode\") VALUES (1, E'shim sham', BETA);";
     $actual = trim(pgsql8_diff_tables::get_data_sql(NULL, NULL, $schema, $table, FALSE));
     $this->assertContains($expected, $actual);
 }
Esempio n. 8
0
 /**
  * Updates data in table definitions
  *
  * @param ofs output file segmenter
  * @param $old_database original database
  * @param $new_database new database
  */
 private static function update_data($ofs, $delete_mode = false)
 {
     if (self::$new_table_dependency != null && count(self::$new_table_dependency) > 0) {
         for ($i = 0; $i < count(self::$new_table_dependency); $i++) {
             // go in reverse when in delete mode
             if ($delete_mode) {
                 $item = self::$new_table_dependency[count(self::$new_table_dependency) - 1 - $i];
             } else {
                 $item = self::$new_table_dependency[$i];
             }
             if ($item['table']['name'] === dbsteward::TABLE_DEPENDENCY_IGNORABLE_NAME) {
                 // don't do anything with this table, it is a magic internal DBSteward value
                 continue;
             }
             $old_schema = dbx::get_schema(dbsteward::$old_database, $item['schema']['name']);
             $old_table = null;
             if ($old_schema != null) {
                 $old_table = dbx::get_table($old_schema, $item['table']['name']);
             }
             $new_schema = dbx::get_schema(dbsteward::$new_database, $item['schema']['name']);
             if ($new_schema == null) {
                 throw new exception("schema " . $item['schema']['name'] . " not found in new database");
             }
             $new_table = dbx::get_table($new_schema, $item['table']['name']);
             if ($new_table == null) {
                 throw new exception("table " . $item['table']['name'] . " not found in new database schema " . $new_schema['name']);
             }
             pgsql8::set_context_replica_set_id($new_schema);
             // if the table was renamed, get old definition pointers for comparison
             if (pgsql8_diff_tables::is_renamed_table($new_schema, $new_table)) {
                 dbsteward::info("NOTICE: " . $new_schema['name'] . "." . $new_table['name'] . " used to be called " . $new_table['oldTableName'] . " -- will diff data against that definition");
                 $old_schema = pgsql8_table::get_old_table_schema($new_schema, $new_table);
                 $old_table = pgsql8_table::get_old_table($new_schema, $new_table);
             }
             $ofs->write(pgsql8_diff_tables::get_data_sql($old_schema, $old_table, $new_schema, $new_table, $delete_mode));
         }
     } else {
         // dependency order unknown, hit them in natural order
         foreach (dbx::get_schemas(dbsteward::$new_database) as $new_schema) {
             pgsql8::set_context_replica_set_id($new_schema);
             $old_schema = dbx::get_schema(dbsteward::$old_database, $new_schema['name']);
             pgsql8_diff_tables::diff_data($ofs, $old_schema, $new_schema);
         }
     }
 }
    public static function apply_table_options_diff($ofs1, $ofs3, $schema, $table, $alter_options, $create_options, $drop_options)
    {
        $schema_name = (string) $schema['name'];
        $table_name = (string) $table['name'];
        $fq_name = pgsql8::get_fully_qualified_table_name($schema_name, $table_name);
        $actions = array();
        $sql = "";
        // create and alter have the same syntax:
        $create_alter = array_merge($create_options, $alter_options);
        foreach ($create_alter as $name => $value) {
            switch (strtolower($name)) {
                case 'with':
                    // ALTER TABLE ... SET (params) doesn't accept oids=true/false, unlike CREATE TABLE
                    // only WITH OIDS or WITHOUT OIDS
                    $params = pgsql8_table::parse_storage_params($value);
                    if (array_key_exists('oids', $params)) {
                        $oids = $params['oids'];
                        unset($params['oids']);
                        if (strcasecmp($oids, 'true') === 0) {
                            $actions[] = "SET WITH OIDS";
                        } else {
                            $actions[] = "SET WITHOUT OIDS";
                        }
                    } else {
                        // we might have gotten rid of the oids param
                        $actions[] = "SET WITHOUT OIDS";
                    }
                    // set the rest of the params normally
                    $params = pgsql8_table::compose_storage_params($params);
                    $actions[] = "SET {$params}";
                    break;
                case 'tablespace':
                    $tbsp = (string) $value;
                    $actions[] = "SET TABLESPACE " . pgsql8::get_quoted_object_name($tbsp);
                    $sql .= <<<SQL
CREATE FUNCTION __dbsteward_migrate_move_index_tablespace(TEXT,TEXT,TEXT) RETURNS void AS \$\$
  DECLARE idx RECORD;
BEGIN
  -- need to move the tablespace of the indexes as well
  FOR idx IN SELECT index_pgc.relname FROM pg_index
               INNER JOIN pg_class index_pgc ON index_pgc.oid = pg_index.indexrelid
               INNER JOIN pg_class table_pgc ON table_pgc.oid = pg_index.indrelid AND table_pgc.relname=\$2
               INNER JOIN pg_namespace ON pg_namespace.oid = table_pgc.relnamespace AND pg_namespace.nspname=\$1 LOOP
    EXECUTE 'ALTER INDEX ' || quote_ident(\$1) || '.' || quote_ident(idx.relname) || ' SET TABLESPACE ' || quote_ident(\$3) || ';';
  END LOOP;
END \$\$ LANGUAGE plpgsql;
SELECT __dbsteward_migrate_move_index_tablespace('{$schema_name}','{$table_name}','{$tbsp}');
DROP FUNCTION __dbsteward_migrate_move_index_tablespace(TEXT,TEXT,TEXT);

SQL;
                    break;
            }
        }
        foreach ($drop_options as $name => $value) {
            switch (strtolower($name)) {
                case 'with':
                    $params = pgsql8_table::parse_storage_params($value);
                    // handle oids separately, since pgsql doesn't recognise it as
                    // a storage parameter in an ALTER TABLE statement
                    if (array_key_exists('oids', $params)) {
                        $oids = $params['oids'];
                        unset($params['oids']);
                        $actions[] = "SET WITHOUT OIDS";
                    }
                    $names = '(' . implode(',', array_keys($params)) . ')';
                    $actions[] = "RESET {$names}";
                    break;
                case 'tablespace':
                    // the only way to switch table and index to an unknown-beforehand value
                    // is with a function that's immediately executed
                    $sql .= <<<SQL
CREATE OR REPLACE FUNCTION __dbsteward_migrate_reset_tablespace(TEXT,TEXT) RETURNS void AS \$\$
  DECLARE tbsp TEXT;
  DECLARE idx RECORD;
BEGIN
  SELECT setting FROM pg_settings WHERE name='default_tablespace' INTO tbsp;

  IF tbsp = '' THEN
    tbsp := 'pg_default';
  END IF;

  EXECUTE 'ALTER TABLE ' || quote_ident(\$1) || '.' || quote_ident(\$2) || ' SET TABLESPACE ' || quote_ident(tbsp) || ';';

  -- need to move the tablespace of the indexes as well
  FOR idx IN SELECT index_pgc.relname FROM pg_index
               INNER JOIN pg_class index_pgc ON index_pgc.oid = pg_index.indexrelid
               INNER JOIN pg_class table_pgc ON table_pgc.oid = pg_index.indrelid AND table_pgc.relname=\$2
               INNER JOIN pg_namespace ON pg_namespace.oid = table_pgc.relnamespace AND pg_namespace.nspname=\$1 LOOP
    EXECUTE 'ALTER INDEX ' || quote_ident(\$1) || '.' || quote_ident(idx.relname) || ' SET TABLESPACE ' || quote_ident(tbsp) || ';';
  END LOOP;
END \$\$ LANGUAGE plpgsql;
SELECT __dbsteward_migrate_reset_tablespace('{$schema_name}','{$table_name}');
DROP FUNCTION __dbsteward_migrate_reset_tablespace(TEXT,TEXT);

SQL;
            }
        }
        if (!empty($actions)) {
            $sql .= "\nALTER TABLE {$fq_name}\n  " . implode(",\n  ", $actions) . ";";
        }
        if (!empty($sql)) {
            $ofs1->write($sql . "\n");
        }
    }
 /**
  * Returns list of indexes that should be added.
  *
  * @param old_table original table
  * @param new_table new table
  *
  * @return list of indexes that should be added
  */
 public static function get_new_indexes($old_schema, $old_table, $new_schema, $new_table)
 {
     $list = array();
     if ($new_table != null) {
         if ($old_table == null) {
             foreach (format_index::get_table_indexes($new_schema, $new_table) as $index) {
                 $list[] = $index;
             }
         } else {
             foreach (format_index::get_table_indexes($new_schema, $new_table) as $index) {
                 $old_index = dbx::get_table_index($old_schema, $old_table, $index['name']);
                 if (!pgsql8_table::contains_index($old_schema, $old_table, $index['name'])) {
                     $list[] = $index;
                 } else {
                     if (!pgsql8_index::equals($old_index, $index)) {
                         $list[] = $index;
                     }
                 }
             }
         }
     }
     return $list;
 }
Esempio n. 11
0
 public static function null_allowed($node_table, $node_column)
 {
     $null_allowed = parent::null_allowed($node_table, $node_column);
     if ($null_allowed) {
         // if the column is in the primary_key list, make it NOT NULL anyway
         // mssql will not implicitly make the column NOT NULL like postgresql
         $primary_keys = pgsql8_table::primary_key_columns($node_table);
         if (in_array((string) $node_column['name'], $primary_keys)) {
             $null_allowed = FALSE;
         }
     }
     return $null_allowed;
 }