From 33ec743c4d6244da80b4d8e5aeb339a09d9be374 Mon Sep 17 00:00:00 2001 From: Matthew Higgins Date: Sun, 6 Sep 2009 21:51:58 -0700 Subject: [PATCH] - Do not load the adapter unless it is the current one in use - Extract mysql foreign key definitions into the sql_2003 specification - Add postgressql support, sans schema dumping. I will add foreign_keys to schema.rb soon. --- lib/foreigner.rb | 11 +- .../connection_adapters/mysql_adapter.rb | 112 ++++++------------ .../connection_adapters/postgresql_adapter.rb | 21 ++++ lib/foreigner/connection_adapters/sql_2003.rb | 57 +++++++++ 4 files changed, 121 insertions(+), 80 deletions(-) create mode 100644 lib/foreigner/connection_adapters/postgresql_adapter.rb create mode 100644 lib/foreigner/connection_adapters/sql_2003.rb diff --git a/lib/foreigner.rb b/lib/foreigner.rb index 2927451..02bb407 100644 --- a/lib/foreigner.rb +++ b/lib/foreigner.rb @@ -1,6 +1,5 @@ require 'foreigner/connection_adapters/abstract/schema_statements' require 'foreigner/connection_adapters/abstract/schema_definitions' -require 'foreigner/connection_adapters/mysql_adapter' require 'foreigner/schema_dumper' module ActiveRecord @@ -8,10 +7,6 @@ module ConnectionAdapters AbstractAdapter.class_eval do include Foreigner::AdapterMethods end - - MysqlAdapter.class_eval do - include Foreigner::MysqlAdapter - end TableDefinition.class_eval do include Foreigner::TableDefinition @@ -25,4 +20,10 @@ module ConnectionAdapters SchemaDumper.class_eval do include Foreigner::SchemaDumper end + + Base.class_eval do + if ['MySQL', 'PostgreSQL'].include? connection.adapter_name + require "foreigner/connection_adapters/#{connection.adapter_name.downcase}_adapter" + end + end end \ No newline at end of file diff --git a/lib/foreigner/connection_adapters/mysql_adapter.rb b/lib/foreigner/connection_adapters/mysql_adapter.rb index 5591a94..7cea6ef 100644 --- a/lib/foreigner/connection_adapters/mysql_adapter.rb +++ b/lib/foreigner/connection_adapters/mysql_adapter.rb @@ -1,84 +1,46 @@ -module Foreigner - module MysqlAdapter - def supports_foreign_keys? - true - end - - def add_foreign_key(from_table, to_table, options = {}) - column = options[:column] || "#{to_table.to_s.singularize}_id" - foreign_key_name = foreign_key_name(from_table, column, options) +require 'foreigner/connection_adapters/sql_2003' - sql = - "ALTER TABLE #{quote_table_name(from_table)} " + - "ADD CONSTRAINT #{quote_column_name(foreign_key_name)} " + - foreign_key_definition(to_table, options) +module Foreigner + module ConnectionAdapters + module MysqlAdapter + include Foreigner::ConnectionAdapters::Sql2003 - execute(sql) - end - - def foreign_key_definition(to_table, options = {}) - column = options[:column] || "#{to_table.to_s.singularize}_id" - dependency = dependency_sql(options[:dependent]) - - sql = "FOREIGN KEY (#{quote_column_name(column)}) REFERENCES #{quote_table_name(to_table)}(id)" - sql << " #{dependency}" unless dependency.blank? - sql - end - - def remove_foreign_key(table, options) - if Hash === options - foreign_key_name = foreign_key_name(table, options[:column], options) - else - foreign_key_name = foreign_key_name(table, "#{options.to_s.singularize}_id") - end - - execute "ALTER TABLE #{quote_table_name(table)} DROP FOREIGN KEY #{quote_column_name(foreign_key_name)}" - end - - def foreign_keys(table_name) - foreign_keys = [] - fk_info = select_all %{ - SELECT fk.referenced_table_name as 'to_table' - ,fk.column_name as 'column' - ,fk.constraint_name as 'name' - FROM information_schema.key_column_usage fk - WHERE fk.referenced_column_name is not null - AND fk.table_schema = '#{@config[:database]}' - AND fk.table_name = '#{table_name}' - } - - create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"] - - fk_info.each do |row| - options = {:column => row['column'], :name => row['name']} - if create_table_info =~ /CONSTRAINT #{quote_column_name(row['name'])} FOREIGN KEY .* REFERENCES .* ON DELETE (CASCADE|SET NULL)/ - if $1 == 'CASCADE' - options[:dependent] = :delete - elsif $1 == 'SET NULL' - options[:dependent] = :nullify + def foreign_keys(table_name) + foreign_keys = [] + fk_info = select_all %{ + SELECT fk.referenced_table_name as 'to_table' + ,fk.column_name as 'column' + ,fk.constraint_name as 'name' + FROM information_schema.key_column_usage fk + WHERE fk.referenced_column_name is not null + AND fk.table_schema = '#{@config[:database]}' + AND fk.table_name = '#{table_name}' + } + + create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"] + + fk_info.each do |row| + options = {:column => row['column'], :name => row['name']} + if create_table_info =~ /CONSTRAINT #{quote_column_name(row['name'])} FOREIGN KEY .* REFERENCES .* ON DELETE (CASCADE|SET NULL)/ + if $1 == 'CASCADE' + options[:dependent] = :delete + elsif $1 == 'SET NULL' + options[:dependent] = :nullify + end end + foreign_keys << ForeignKeyDefinition.new(table_name, row['to_table'], options) end - foreign_keys << ForeignKeyDefinition.new(table_name, row['to_table'], options) - end - foreign_keys - end - - private - def foreign_key_name(table, column, options = {}) - if options[:name] - options[:name] - else - "#{table}_#{column}_fk" - end + foreign_keys end + end + end +end - def dependency_sql(dependency) - case dependency - when :nullify then "ON DELETE SET NULL" - when :delete then "ON DELETE CASCADE" - else "" - end - end +module ActiveRecord + module ConnectionAdapters + MysqlAdapter.class_eval do + include Foreigner::ConnectionAdapters::MysqlAdapter + end end end diff --git a/lib/foreigner/connection_adapters/postgresql_adapter.rb b/lib/foreigner/connection_adapters/postgresql_adapter.rb new file mode 100644 index 0000000..6c7a92a --- /dev/null +++ b/lib/foreigner/connection_adapters/postgresql_adapter.rb @@ -0,0 +1,21 @@ +require 'foreigner/connection_adapters/sql_2003' + +module Foreigner + module ConnectionAdapters + module PostgreSQLAdapter + include Foreigner::ConnectionAdapters::Sql2003 + + def foreign_keys(table_name) + + end + end + end +end + +module ActiveRecord + module ConnectionAdapters + PostgreSQLAdapter.class_eval do + include Foreigner::ConnectionAdapters::PostgreSQLAdapter + end + end +end \ No newline at end of file diff --git a/lib/foreigner/connection_adapters/sql_2003.rb b/lib/foreigner/connection_adapters/sql_2003.rb new file mode 100644 index 0000000..7ff64d9 --- /dev/null +++ b/lib/foreigner/connection_adapters/sql_2003.rb @@ -0,0 +1,57 @@ +module Foreigner + module ConnectionAdapters + module Sql2003 + def supports_foreign_keys? + true + end + + def add_foreign_key(from_table, to_table, options = {}) + column = options[:column] || "#{to_table.to_s.singularize}_id" + foreign_key_name = foreign_key_name(from_table, column, options) + + sql = + "ALTER TABLE #{quote_table_name(from_table)} " + + "ADD CONSTRAINT #{quote_column_name(foreign_key_name)} " + + foreign_key_definition(to_table, options) + + execute(sql) + end + + def foreign_key_definition(to_table, options = {}) + column = options[:column] || "#{to_table.to_s.singularize}_id" + dependency = dependency_sql(options[:dependent]) + + sql = "FOREIGN KEY (#{quote_column_name(column)}) REFERENCES #{quote_table_name(to_table)}(id)" + sql << " #{dependency}" unless dependency.blank? + sql + end + + def remove_foreign_key(table, options) + if Hash === options + foreign_key_name = foreign_key_name(table, options[:column], options) + else + foreign_key_name = foreign_key_name(table, "#{options.to_s.singularize}_id") + end + + execute "ALTER TABLE #{quote_table_name(table)} DROP FOREIGN KEY #{quote_column_name(foreign_key_name)}" + end + + private + def foreign_key_name(table, column, options = {}) + if options[:name] + options[:name] + else + "#{table}_#{column}_fk" + end + end + + def dependency_sql(dependency) + case dependency + when :nullify then "ON DELETE SET NULL" + when :delete then "ON DELETE CASCADE" + else "" + end + end + end + end +end \ No newline at end of file