Permalink
Browse files

Add MSSQL support for Database#foreign_key_list

  • Loading branch information...
1 parent 56bd06d commit 4f30f84fd6422a1c36b0e8a3f94c1b30f6803537 mluu committed Oct 1, 2012
Showing with 120 additions and 0 deletions.
  1. +42 −0 lib/sequel/adapters/shared/mssql.rb
  2. +78 −0 spec/adapters/mssql_spec.rb
@@ -13,6 +13,7 @@ module DatabaseMethods
SQL_ROLLBACK_TO_SAVEPOINT = 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION autopoint_%d'.freeze
SQL_SAVEPOINT = 'SAVE TRANSACTION autopoint_%d'.freeze
MSSQL_DEFAULT_RE = /\A(?:\(N?('.*')\)|\(\((-?\d+(?:\.\d+)?)\)\))\z/
+ FOREIGN_KEY_ACTION_MAP = {0 => :no_action, 1 => :cascade, 2 => :set_null, 3 => :set_default}.freeze
include Sequel::Database::SplitAlterTable
@@ -36,6 +37,47 @@ def global_index_namespace?
false
end
+ # Return foreign key information using the system views, including
+ # :name, :on_delete, and :on_update entries in the hashes.
+ def foreign_key_list(table, opts={})
+ m = output_identifier_meth
+ im = input_identifier_meth
+ schema, table = schema_and_table(table)
+ current_schema = m.call(get(Sequel.function('schema_name')))
+ fk_action_map = FOREIGN_KEY_ACTION_MAP
+ ds = metadata_dataset.from(:sys__foreign_keys___fk).
+ join(:sys__foreign_key_columns___fkc, :constraint_object_id => :object_id).
+ join(:sys__all_columns___pc, :object_id => :fkc__parent_object_id, :column_id => :fkc__parent_column_id).
+ join(:sys__all_columns___rc, :object_id => :fkc__referenced_object_id, :column_id => :fkc__referenced_column_id).
+ where{{object_schema_name(:fk__parent_object_id) => im.call(schema || current_schema)}}.
+ where{{object_name(:fk__parent_object_id) => im.call(table)}}.
+ select{[:fk__name,
+ :fk__delete_referential_action,
+ :fk__update_referential_action,
+ :pc__name___column,
+ :rc__name___referenced_column,
+ object_schema_name(:fk__referenced_object_id).as(:schema),
+ object_name(:fk__referenced_object_id).as(:table)]}.
+ order(:name)
+ h = {}
+ ds.each do |row|
+ if r = h[row[:name]]
+ r[:columns] << m.call(row[:column])
+ r[:key] << m.call(row[:referenced_column])
+ else
+ referenced_schema = m.call(row[:schema])
+ referenced_table = m.call(row[:table])
+ h[row[:name]] = { :name => m.call(row[:name]),
+ :table => (referenced_schema == current_schema) ? referenced_table : :"#{referenced_schema}__#{referenced_table}",
+ :columns => [m.call(row[:column])],
+ :key => [m.call(row[:referenced_column])],
+ :on_update => fk_action_map[row[:update_referential_action]],
+ :on_delete => fk_action_map[row[:delete_referential_action]] }
+ end
+ end
+ h.values
+ end
+
# Use the system tables to get index information
def indexes(table, opts={})
m = output_identifier_meth
@@ -568,3 +568,81 @@ def transaction(opts={})
MSSQL_DB[:test__items].columns.should == [:id]
end
end
+
+describe "Database#foreign_key_list" do
+ before(:all) do
+ MSSQL_DB.create_table! :items do
+ primary_key :id
+ integer :sku
+ end
+ MSSQL_DB.create_table! :prices do
+ integer :item_id
+ datetime :valid_from
+ float :price
+ primary_key [:item_id, :valid_from]
+ foreign_key [:item_id], :items, :key => :id, :name => :fk_prices_items
+ end
+ MSSQL_DB.create_table! :sales do
+ integer :id
+ integer :price_item_id
+ datetime :price_valid_from
+ foreign_key [:price_item_id, :price_valid_from], :prices, :key => [:item_id, :valid_from], :name => :fk_sales_prices, :on_delete => :cascade
+ end
+ end
+ after(:all) do
+ MSSQL_DB.drop_table :sales
+ MSSQL_DB.drop_table :prices
+ MSSQL_DB.drop_table :items
+ end
+ it "should support typical foreign keys" do
+ MSSQL_DB.foreign_key_list(:prices).should == [{:name => :fk_prices_items,
+ :table => :items,
+ :columns => [:item_id],
+ :key => [:id],
+ :on_update => :no_action,
+ :on_delete => :no_action }]
+ end
+ it "should support a foreign key with multiple columns" do
+ MSSQL_DB.foreign_key_list(:sales).should == [{:name => :fk_sales_prices,
+ :table => :prices,
+ :columns => [:price_item_id, :price_valid_from],
+ :key => [:item_id, :valid_from],
+ :on_update => :no_action,
+ :on_delete => :cascade }]
+ end
+
+ context "with multiple schemas" do
+ before(:all) do
+ MSSQL_DB.execute_ddl "create schema vendor"
+ MSSQL_DB.create_table! :vendor__vendors do
+ primary_key :id
+ varchar :name
+ end
+ MSSQL_DB.create_table! :vendor__mapping do
+ integer :vendor_id
+ integer :item_id
+ foreign_key [:vendor_id], :vendor__vendors, :name => :fk_mapping_vendor
+ foreign_key [:item_id], :items, :name => :fk_mapping_item
+ end
+ end
+ after(:all) do
+ MSSQL_DB.drop_table :vendor__mapping
+ MSSQL_DB.drop_table :vendor__vendors
+ MSSQL_DB.execute_ddl "drop schema vendor"
+ end
+ it "should support mixed schema bound tables" do
+ MSSQL_DB.foreign_key_list(:vendor__mapping).should == [{:name => :fk_mapping_item,
+ :table => :items,
+ :columns => [:item_id],
+ :key => [:id],
+ :on_update => :no_action,
+ :on_delete => :no_action },
+ {:name => :fk_mapping_vendor,
+ :table => :vendor__vendors,
+ :columns => [:vendor_id],
+ :key => [:id],
+ :on_update => :no_action,
+ :on_delete => :no_action }]
+ end
+ end
+end

0 comments on commit 4f30f84

Please sign in to comment.