/** * Adds commands for modification of columns to the list of * commands. * * @param commands list of commands * @param old_table original table * @param new_table new table * @param drop_defaults_columns list for storing columns for which default value should be dropped */ private static function add_modify_table_columns(&$commands, $old_table, $new_schema, $new_table, &$drop_defaults_columns) { $case_sensitive = dbsteward::$quote_all_names || dbsteward::$quote_column_names; foreach (dbx::get_table_columns($new_table) as $new_column) { if (!pgsql8_table::contains_column($old_table, $new_column['name'], $case_sensitive)) { continue; } if (!dbsteward::$ignore_oldnames && pgsql8_diff_tables::is_renamed_column($old_table, $new_table, $new_column)) { // oldColumnName renamed column ? skip definition diffing on it, it is being renamed continue; } $old_column = pgsql8_table::get_column_by_name($old_table, $new_column['name'], $case_sensitive); $new_column_name = pgsql8::get_quoted_column_name($new_column['name']); $old_column_type = null; if ($old_column) { $old_column_type = pgsql8_column::column_type(dbsteward::$old_database, $new_schema, $old_table, $old_column, $foreign); } $new_column_type = pgsql8_column::column_type(dbsteward::$new_database, $new_schema, $new_table, $new_column, $foreign); if (preg_match(pgsql8::PATTERN_TABLE_LINKED_TYPES, $new_column_type) > 0 && $old_column_type !== null && preg_match(pgsql8::PATTERN_TABLE_LINKED_TYPES, $old_column_type) == 0) { throw new Exception("Table " . $new_schema['name'] . "." . $new_table['name'] . " column " . $new_column['name'] . " has linked type " . $new_column_type . " -- Column types cannot be altered to serial. If this column cannot be recreated as part of database change control, a user defined serial should be created, and corresponding nextval() defined as the default for the column."); } if (strcmp($old_column_type, $new_column_type) != 0) { // ALTER TYPE .. USING support by looking up the new type in the xml definition $type_using = ''; $type_using_comment = ''; if (isset($new_column['convertUsing'])) { $type_using = ' USING ' . $new_column['convertUsing'] . ' '; $type_using_comment = '- found XML convertUsing: ' . $new_column['convertUsing'] . ' '; } $commands[] = array('stage' => '1', 'command' => "\tALTER COLUMN " . $new_column_name . " TYPE " . $new_column_type . $type_using . " /* TYPE change - table: " . $new_table['name'] . " original: " . $old_column_type . " new: " . $new_column_type . ' ' . $type_using_comment . '*/'); } $old_default = isset($old_column['default']) ? $old_column['default'] : ''; $new_default = isset($new_column['default']) ? $new_column['default'] : ''; if (strcmp($old_default, $new_default) != 0) { if (strlen($new_default) == 0) { $commands[] = array('stage' => '1', 'command' => "\tALTER COLUMN " . $new_column_name . " DROP DEFAULT"); } else { $commands[] = array('stage' => '1', 'command' => "\tALTER COLUMN " . $new_column_name . " SET DEFAULT " . $new_default); } } if (strcasecmp($old_column['null'], $new_column['null']) != 0) { if (pgsql8_column::null_allowed($new_table, $new_column)) { $commands[] = array('stage' => '1', 'command' => "\tALTER COLUMN " . $new_column_name . " DROP NOT NULL"); } else { if (pgsql8_diff::$add_defaults) { $default_value = pgsql8_column::get_default_value($new_column_type); if ($default_value != null) { $commands[] = array('stage' => '1', 'command' => "\tALTER COLUMN " . $new_column_name . " SET DEFAULT " . $default_value); $drop_defaults_columns[] = $new_column; } } // if the default value is defined in the dbsteward XML // set the value of the column to the default in end of stage 1 so that NOT NULL can be applied in stage 3 // this way custom <sql> tags can be avoided for upgrade generation if defaults are specified if (strlen($new_column['default']) > 0) { $commands[] = array('stage' => 'AFTER1', 'command' => "UPDATE " . pgsql8::get_quoted_schema_name($new_schema['name']) . "." . pgsql8::get_quoted_table_name($new_table['name']) . " SET " . $new_column_name . " = " . $new_column['default'] . " WHERE " . $new_column_name . " IS NULL; -- has_default_now: make modified column that is null the default value before NOT NULL hits"); } $commands[] = array('stage' => '3', 'command' => "\tALTER COLUMN " . $new_column_name . " SET NOT NULL"); } } // drop sequence and default if converting from *serial to *int if (preg_match('/serial$/', $old_column['type']) > 0 && ($new_column['type'] == 'int' || $new_column['type'] == 'bigint')) { $commands[] = array('stage' => 'BEFORE3', 'command' => "DROP SEQUENCE IF EXISTS " . pgsql8::get_quoted_schema_name($new_schema['name']) . '.' . pgsql8::get_quoted_table_name(pgsql8::identifier_name($new_schema['name'], $new_table['name'], $new_column['name'], '_seq')) . ";"); $commands[] = array('stage' => '1', 'command' => "\tALTER COLUMN " . $new_column_name . " DROP DEFAULT"); } } }
/** * Generate column defaults from column definitions, returns FALSE if * no defaults were defined, otherwise return the * ALTER TABLE ALTER COLUMN SET statements needed. * * Don't know if this would work with functions referenced as function(argument1 ... argumentN) * * @param type $node_schema * @param type $node_table * @param type $node_column * @param type $add_defaults * @param type $include_null_definition * @param type $include_default_nextval * @return boolean|string */ public static function set_column_defaults($node_schema, $node_table, $node_column, $add_defaults, $include_null_definition = true, $include_default_nextval = TRUE) { $fq_table_name = pgsql8::get_fully_qualified_table_name($node_schema['name'], $node_table['name']); $base_sql = "ALTER TABLE " . $fq_table_name . " ALTER COLUMN " . pgsql8::get_quoted_column_name($node_column['name']) . " SET"; $sql = $base_sql; $changes = FALSE; if (strlen($node_column['default']) > 0) { if (!$include_default_nextval && static::has_default_nextval($node_table, $node_column)) { // if the default is a nextval expression, don't specify it in the regular full definition // because if the sequence has not been defined yet, // the nextval expression will be evaluated inline and fail dbsteward::info("Skipping " . $node_column['name'] . " default expression \"" . $node_column['default'] . "\" - this default expression will be applied after all sequences have been created"); return $changes; } else { $sql .= " DEFAULT " . $node_column['default']; $changes = TRUE; } } else { if (!pgsql8_column::null_allowed($node_table, $node_column) && $add_defaults) { $default_col_value = pgsql8_column::get_default_value($node_column['type']); if ($default_col_value != null) { $sql .= " DEFAULT " . $default_col_value; $changes = TRUE; } } } if ($include_null_definition && !pgsql8_column::null_allowed($node_table, $node_column)) { if ($changes) { $sql .= ";\n"; $sql .= $base_sql . " NOT NULL"; } else { $sql .= " NOT NULL"; $changes = TRUE; } } // no changes? we don't have a default for this column... keep going pls if (!$changes) { return $changes; } $sql .= ";\n"; return $sql; }
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; }