Skip to content

Commit

Permalink
Merge pull request #93 from austinhyde/foreign-key-element
Browse files Browse the repository at this point in the history
Foreign key element support
  • Loading branch information
nkiraly committed Aug 17, 2015
2 parents 5995180 + e8d3d6b commit f7e63ec
Show file tree
Hide file tree
Showing 11 changed files with 356 additions and 215 deletions.
10 changes: 9 additions & 1 deletion lib/DBSteward/dbsteward.dtd
Expand Up @@ -68,7 +68,7 @@
<!ATTLIST schema description CDATA #IMPLIED>
<!ATTLIST schema slonySetId CDATA #IMPLIED>

<!ELEMENT table (tablePartition?, tableOption*, column+, index*, constraint*, grant*, rows?)>
<!ELEMENT table (tablePartition?, tableOption*, column+, index*, constraint*, foreignKey*, grant*, rows?)>
<!ATTLIST table name CDATA #REQUIRED>
<!ATTLIST table primaryKey CDATA #REQUIRED>
<!ATTLIST table primaryKeyName CDATA #IMPLIED>
Expand Down Expand Up @@ -142,6 +142,14 @@
<!ATTLIST constraint foreignSchema CDATA #IMPLIED>
<!ATTLIST constraint foreignTable CDATA #IMPLIED>

<!ELEMENT foreignKey EMPTY>
<!ATTLIST foreignKey columns CDATA #REQUIRED>
<!ATTLIST foreignKey foreignSchema CDATA #IMPLIED>
<!ATTLIST foreignKey foreignTable CDATA #REQUIRED>
<!ATTLIST foreignKey foreignColumns CDATA #IMPLIED>
<!ATTLIST foreignKey constraintName CDATA #REQUIRED>
<!ATTLIST foreignKey indexName CDATA #IMPLIED>

<!ELEMENT rows (row+)>
<!ATTLIST rows columns CDATA #REQUIRED>
<!ELEMENT row (col+)>
Expand Down
165 changes: 0 additions & 165 deletions lib/DBSteward/dbx.php
Expand Up @@ -220,177 +220,12 @@ public static function &get_table_rows(&$node_table, $create_if_not_exist = FALS
return $node_rows;
}

/**
* @NOTE: because this gets the defintion from the composite list returned by get_table_constraints
* the constraint is not returned by reference as it is not modifiable like other get functions in this class
* when saving changes to constraints, need to lookup the child where they would come from explicitly
*/
public static function get_table_constraint($db_doc, $node_schema, $node_table, $name) {
$constraints = self::get_table_constraints($db_doc, $node_schema, $node_table, 'all');
$return_constraint = NULL;
foreach ($constraints AS $constraint) {
if (strcasecmp($constraint['name'], $name) == 0) {
if ($return_constraint == NULL) {
$return_constraint = $constraint;
}
else {
var_dump($constraints);
throw new exception("more than one table " . $node_schema['name'] . '.' . $node_table['name'] . " constraint called " . $name . " found");
}
}
}
return $return_constraint;
}

public function &create_table_constraint(&$node_table, $name) {
$node_constraint = $node_table->addChild('constraint');
$node_constraint->addAttribute('name', $name);
return $node_constraint;
}

/**
* return collection of arrays representing all of the constraints on a table
* this is more than just the <constraint> discret children of a table element
* this is also primary key, inline column foreign keys, and inline column unique constraints
* everything comparing the constraints of a table should be calling this
*/
public static function get_table_constraints($db_doc, $node_schema, $node_table, $type = 'all') {
if ( !is_object($node_table) ) {
var_dump($node_table);
throw new exception("node_table is not an object, check trace for bad table pointer");
}
switch ($type) {
case 'all':
case 'primaryKey':
case 'constraint':
case 'foreignKey':
break;
default:
throw new exception("unknown type " . $type . " encountered");
}
$constraints = array();
if ($type == 'all' || $type == 'primaryKey') {
if (isset($node_table['primaryKey'])) {
if (isset($node_table['primaryKeyName'])
&& strlen($node_table['primaryKeyName']) > 0) {
$primary_key_name = dbsteward::string_cast($node_table['primaryKeyName']);
}
else {
$primary_key_name = pgsql8_index::index_name($node_table['name'], NULL, 'pkey');
}

// quoted column name processing for primary key definitions
$primary_key_columns = preg_split("/[\,\s]+/", $node_table['primaryKey'], -1, PREG_SPLIT_NO_EMPTY);
$primary_key_list = '';
foreach ($primary_key_columns AS $primary_key_column) {
$primary_key_list .= pgsql8::get_quoted_column_name($primary_key_column) . ', ';
}
$primary_key_list = substr($primary_key_list, 0, -2);

$constraints[] = array(
'name' => $primary_key_name,
'schema_name' => (string)$node_schema['name'],
'table_name' => (string)$node_table['name'],
'type' => 'PRIMARY KEY',
'definition' => '(' . $primary_key_list . ')'
);
}
}
if ($type == 'all'
|| $type == 'constraint'
|| $type == 'foreignKey' ) {
$nodes = $node_table->xpath("constraint");
foreach ($nodes AS $node_constraint) {
// sanity check node definition constraint types
switch ((string)$node_constraint['type']) {
case 'CHECK':
case 'FOREIGN KEY':
case 'PRIMARY KEY':
case 'UNIQUE':
break;
default:
throw new exception('unknown constraint type ' . $node_constraint['type'] . ' encountered');
break;
}

if ( $type == 'foreignKey' && strcasecmp($node_constraint['type'], 'FOREIGN KEY') != 0 ) {
// requested type is foreignKey yet node type is not FOREIGN KEY, continue on
continue;
}

$constraints[] = array(
'name' => (string)$node_constraint['name'],
'schema_name' => (string)$node_schema['name'],
'table_name' => (string)$node_table['name'],
'type' => (string)$node_constraint['type'],
'definition' => (string)$node_constraint['definition']
);
}
}

if ($type == 'all'
|| $type == 'constraint'
|| $type == 'foreignKey' ) {
foreach ($node_table->column AS $column) {
// add column foreign key constraints to the list
if (isset($column['foreignSchema']) || isset($column['foreignTable'])) {
if (strlen($column['foreignTable']) == 0) {
throw new exception("Invalid foreignTable for " . dbsteward::string_cast($node_schema['name']) . "." . dbsteward::string_cast($node_table['name']) . "." . dbsteward::string_cast($column['name']));
}
if (isset($column['type'])
|| strlen($column['type']) > 0) {
throw new exception("Foreign-Keyed columns should not specify a type for " . dbsteward::string_cast($node_schema['name']) . "." . dbsteward::string_cast($node_table['name']) . "." . dbsteward::string_cast($column['name']));
}

$foreign = array();
dbx::foreign_key($db_doc, $node_schema, $node_table, $column, $foreign);
if (isset($column['foreignKeyName'])
&& strlen($column['foreignKeyName']) > 0) {
// explicitly name the foreign key if specified in the node
$foreign['name'] = (string)$column['foreignKeyName'];
}

$column_fkey_constraint = array(
'name' => (string)$foreign['name'],
'schema_name' => (string)$node_schema['name'],
'table_name' => (string)$node_table['name'],
'type' => 'FOREIGN KEY',
'definition' => '(' . dbsteward::quote_column_name($column['name']) . ') REFERENCES ' . $foreign['references'],
'foreign_key_data' => $foreign
);

if (isset($column['foreignOnDelete']) && strlen($column['foreignOnDelete'])) {
$column_fkey_constraint['foreignOnDelete'] = (string)$column['foreignOnDelete'];
}
if (isset($column['foreignOnUpdate']) && strlen($column['foreignOnUpdate'])) {
$column_fkey_constraint['foreignOnUpdate'] = (string)$column['foreignOnUpdate'];
}

$constraints[] = $column_fkey_constraint;
}
}
}

if ($type == 'all'
|| $type == 'constraint'
|| $type == 'check' ) {
foreach ($node_table->column AS $column) {
// add column check constraints to the list
if ( isset($column['check']) ) {
$column_check_constraint = array(
'name' => $column['name'] . '_check',
'schema_name' => (string)$node_schema['name'],
'table_name' => (string)$node_table['name'],
'type' => 'CHECK',
'definition' => $column['check']
);
$constraints[] = $column_check_constraint;
}
}
}
return $constraints;
}

/**
* return the constraints of other tables that refer to the table specified
*
Expand Down
12 changes: 12 additions & 0 deletions lib/DBSteward/sql_format/mssql10/mssql10_constraint.php
@@ -0,0 +1,12 @@
<?php
/**
* MSSQL10-flavored constraints
*
* @package DBSteward
* @subpackage mssql10
* @license http://www.opensource.org/licenses/bsd-license.php Simplified BSD License
* @author Austin Hyde <austin109@gmail.com>
*/

class mssql10_constraint extends sql99_constraint {
}
18 changes: 9 additions & 9 deletions lib/DBSteward/sql_format/mssql10/mssql10_diff_tables.php
Expand Up @@ -337,7 +337,7 @@ private static function add_modify_table_columns(&$commands, $old_table, $new_sc
}

// before altering column, remove any constraint that would stop us from doing so
foreach(dbx::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, 'constraint') as $constraint) {
foreach(mssql10_constraint::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, 'constraint') as $constraint) {
if (preg_match('/' . $new_column['name'] . '[\s,=)]/', $constraint['definition']) > 0) {
$commands[] = array(
'stage' => '3',
Expand All @@ -352,7 +352,7 @@ private static function add_modify_table_columns(&$commands, $old_table, $new_sc
);

// add the constraint back on
foreach(dbx::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, 'constraint') as $constraint) {
foreach(mssql10_constraint::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, 'constraint') as $constraint) {
if (preg_match('/' . $new_column['name'] . '[\s,=\)]/', $constraint['definition']) > 0) {
$commands[] = array(
'stage' => '3',
Expand Down Expand Up @@ -977,7 +977,7 @@ public static function diff_constraints_table($ofs, $old_schema, $old_table, $ne
else {
// if it is a renamed table, remove all constraints and recreate with new table name conventions
if ( mssql10_diff_tables::is_renamed_table($new_schema, $new_table) ) {
foreach(dbx::get_table_constraints(dbsteward::$old_database, $old_schema, $old_table, $type) as $constraint) {
foreach(mssql10_constraint::get_table_constraints(dbsteward::$old_database, $old_schema, $old_table, $type) as $constraint) {
// rewrite the constraint definer to refer to the new table name
// so the constraint by the old name, but part of the new table
// will be referenced properly in the drop statement
Expand All @@ -986,7 +986,7 @@ public static function diff_constraints_table($ofs, $old_schema, $old_table, $ne
}

// add all defined constraints back to the new table
foreach(dbx::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, $type) as $constraint) {
foreach(mssql10_constraint::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, $type) as $constraint) {
$ofs->write(mssql10_table::get_constraint_sql($constraint) . "\n");
}
return;
Expand Down Expand Up @@ -1019,8 +1019,8 @@ protected static function get_drop_constraints($old_schema, $old_table, $new_sch
if ($old_table->getName() != 'table') {
throw new exception("Unexpected element type: " . $old_table->getName() . " panicing");
}
foreach (dbx::get_table_constraints(dbsteward::$old_database, $old_schema, $old_table, $type) as $constraint) {
$new_constraint = dbx::get_table_constraint(dbsteward::$new_database, $new_schema, $new_table, $constraint['name']);
foreach (mssql10_constraint::get_table_constraints(dbsteward::$old_database, $old_schema, $old_table, $type) as $constraint) {
$new_constraint = mssql10_constraint::get_table_constraint(dbsteward::$new_database, $new_schema, $new_table, $constraint['name']);

if (!mssql10_table::contains_constraint(dbsteward::$new_database, $new_schema, $new_table, $constraint['name'])
|| !mssql10_table::constraint_equals($new_constraint, $constraint)) {
Expand All @@ -1046,13 +1046,13 @@ protected static function get_new_constraints($old_schema, $old_table, $new_sche

if ($new_table != NULL) {
if ($old_table == NULL) {
foreach (dbx::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, $type) as $constraint) {
foreach (mssql10_constraint::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, $type) as $constraint) {
$list[] = $constraint;
}
}
else {
foreach (dbx::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, $type) as $constraint) {
$old_constraint = dbx::get_table_constraint(dbsteward::$old_database, $old_schema, $old_table, $constraint['name']);
foreach (mssql10_constraint::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, $type) as $constraint) {
$old_constraint = mssql10_constraint::get_table_constraint(dbsteward::$old_database, $old_schema, $old_table, $constraint['name']);

if (!mssql10_table::contains_constraint(dbsteward::$old_database, $old_schema, $old_table, $constraint['name'])
|| !mssql10_table::constraint_equals($old_constraint, $constraint)) {
Expand Down
14 changes: 7 additions & 7 deletions lib/DBSteward/sql_format/pgsql8/pgsql8_diff_tables.php
Expand Up @@ -1107,7 +1107,7 @@ public static function diff_constraints_table($ofs, $old_schema, $old_table, $ne
// if it is a renamed table
if ( pgsql8_diff_tables::is_renamed_table($new_schema, $new_table) ) {
// remove all constraints and recreate with new table name conventions
foreach(dbx::get_table_constraints(dbsteward::$old_database, $old_schema, $old_table, $type) as $constraint) {
foreach(pgsql8_constraint::get_table_constraints(dbsteward::$old_database, $old_schema, $old_table, $type) as $constraint) {
// rewrite the constraint definer to refer to the new table name
// so the constraint by the old name, but part of the new table
// will be referenced properly in the drop statement
Expand All @@ -1117,7 +1117,7 @@ public static function diff_constraints_table($ofs, $old_schema, $old_table, $ne
}

// add all still-define constraints back and any new ones to the table
foreach(dbx::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, $type) as $constraint) {
foreach(pgsql8_constraint::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, $type) as $constraint) {
$ofs->write(pgsql8_table::get_constraint_sql($constraint) . "\n");
}
// this gets any new constraints as well.
Expand Down Expand Up @@ -1153,8 +1153,8 @@ private static function get_drop_constraints($old_schema, $old_table, $new_schem
if ( $old_table->getName() != 'table' ) {
throw new exception("Unexpected element type: " . $old_table->getName() . " panicing");
}
foreach(dbx::get_table_constraints(dbsteward::$old_database, $old_schema, $old_table, $type) as $constraint) {
$new_constraint = dbx::get_table_constraint(dbsteward::$new_database, $new_schema, $new_table, $constraint['name']);
foreach(pgsql8_constraint::get_table_constraints(dbsteward::$old_database, $old_schema, $old_table, $type) as $constraint) {
$new_constraint = pgsql8_constraint::get_table_constraint(dbsteward::$new_database, $new_schema, $new_table, $constraint['name']);

if ( !pgsql8_table::contains_constraint(dbsteward::$new_database, $new_schema, $new_table, $constraint['name'])
|| !pgsql8_table::constraint_equals($new_constraint, $constraint)
Expand Down Expand Up @@ -1182,12 +1182,12 @@ private static function get_new_constraints($old_schema, $old_table, $new_schema

if ($new_table != null) {
if ($old_table == null) {
foreach(dbx::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, $type) as $constraint) {
foreach(pgsql8_constraint::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, $type) as $constraint) {
$list[] = $constraint;
}
} else {
foreach(dbx::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, $type) as $constraint) {
$old_constraint = dbx::get_table_constraint(dbsteward::$old_database, $old_schema, $old_table, $constraint['name']);
foreach(pgsql8_constraint::get_table_constraints(dbsteward::$new_database, $new_schema, $new_table, $type) as $constraint) {
$old_constraint = pgsql8_constraint::get_table_constraint(dbsteward::$old_database, $old_schema, $old_table, $constraint['name']);

if ( !pgsql8_table::contains_constraint(dbsteward::$old_database, $old_schema, $old_table, $constraint['name'])
|| !pgsql8_table::constraint_equals($old_constraint, $constraint)
Expand Down
6 changes: 3 additions & 3 deletions lib/DBSteward/sql_format/pgsql8/pgsql8_parser_alter_table.php
Expand Up @@ -130,15 +130,15 @@ private static function parse_rows(&$db_doc, &$node_schema, &$node_table, $comma
if (preg_match(self::PATTERN_ADD_CONSTRAINT_FOREIGN_KEY, $subCommand, $matches) > 0) {
$column_name = trim($matches[3]);
$constraint_name = trim($matches[1]);
$node_constraint = &dbx::get_table_constraint($db_doc, $node_table, $constraint_name, true);
$node_constraint = pgsql8_constraint::get_table_constraint($db_doc, $node_table, $constraint_name, true);
dbx::set_attribute($node_constraint, 'definition', trim($matches[2]));
$subCommand = "";
}
}

if (preg_match(self::PATTERN_ADD_CONSTRAINT, $subCommand, $matches) > 0) {
$constraint_name = trim($matches[1]);
$node_constraint = &dbx::get_table_constraint($db_doc, $node_table, $constraint_name, true);
$node_constraint = pgsql8_constraint::get_table_constraint($db_doc, $node_table, $constraint_name, true);
dbx::set_attribute($node_constraint, 'definition', trim($matches[2]));
$subCommand = "";
}
Expand All @@ -157,7 +157,7 @@ private static function parse_rows(&$db_doc, &$node_schema, &$node_table, $comma
if (preg_match(self::PATTERN_ADD_FOREIGN_KEY, $subCommand, $matches) > 0) {
$column_name = trim($matches[2]);
$constraint_name = pgsql8::identifier_name($node_schema['name'], $node_table['name'], $column_name, '_fkey');
$node_constraint = &dbx::get_table_constraint($db_doc, $node_table, $constraint_name, true);
$node_constraint = pgsql8_constraint::get_table_constraint($db_doc, $node_table, $constraint_name, true);
dbx::set_attribute($node_constraint, 'definition', trim($matches[1]));
$subCommand = "";
}
Expand Down

0 comments on commit f7e63ec

Please sign in to comment.