Skip to content

Commit

Permalink
Add MSSQL support for Database#foreign_key_list
Browse files Browse the repository at this point in the history
  • Loading branch information
mluu committed Oct 2, 2012
1 parent 56bd06d commit 4f30f84
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 0 deletions.
42 changes: 42 additions & 0 deletions lib/sequel/adapters/shared/mssql.rb
Expand Up @@ -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

Expand All @@ -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
Expand Down
78 changes: 78 additions & 0 deletions spec/adapters/mssql_spec.rb
Expand Up @@ -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.