aspgems-redhillonrails_core 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/CHANGELOG +194 -0
  2. data/Gemfile +3 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README +161 -0
  5. data/Rakefile +59 -0
  6. data/init.rb +1 -0
  7. data/lib/redhillonrails_core.rb +46 -0
  8. data/lib/redhillonrails_core/active_record/base.rb +45 -0
  9. data/lib/redhillonrails_core/active_record/connection_adapters/abstract/column.rb +14 -0
  10. data/lib/redhillonrails_core/active_record/connection_adapters/abstract/foreign_key_definition.rb +63 -0
  11. data/lib/redhillonrails_core/active_record/connection_adapters/abstract/index_definition.rb +11 -0
  12. data/lib/redhillonrails_core/active_record/connection_adapters/abstract/schema_statements.rb +23 -0
  13. data/lib/redhillonrails_core/active_record/connection_adapters/abstract/table_definition.rb +27 -0
  14. data/lib/redhillonrails_core/active_record/connection_adapters/abstract_adapter.rb +84 -0
  15. data/lib/redhillonrails_core/active_record/connection_adapters/mysql_adapter.rb +131 -0
  16. data/lib/redhillonrails_core/active_record/connection_adapters/mysql_column.rb +8 -0
  17. data/lib/redhillonrails_core/active_record/connection_adapters/postgresql_adapter.rb +131 -0
  18. data/lib/redhillonrails_core/active_record/connection_adapters/sqlite3_adapter.rb +115 -0
  19. data/lib/redhillonrails_core/active_record/schema.rb +25 -0
  20. data/lib/redhillonrails_core/active_record/schema_dumper.rb +75 -0
  21. data/lib/redhillonrails_core/version.rb +3 -0
  22. data/lib/tasks/db/comments.rake +9 -0
  23. data/redhillonrails_core.gemspec +27 -0
  24. data/spec/connections/mysql/connection.rb +18 -0
  25. data/spec/connections/mysql2/connection.rb +18 -0
  26. data/spec/connections/postgresql/connection.rb +15 -0
  27. data/spec/connections/sqlite3/connection.rb +14 -0
  28. data/spec/foreign_key_definition_spec.rb +21 -0
  29. data/spec/foreign_key_spec.rb +100 -0
  30. data/spec/index_definition_spec.rb +145 -0
  31. data/spec/index_spec.rb +67 -0
  32. data/spec/models/comment.rb +5 -0
  33. data/spec/models/post.rb +6 -0
  34. data/spec/models/user.rb +5 -0
  35. data/spec/schema/schema.rb +21 -0
  36. data/spec/schema_dumper_spec.rb +138 -0
  37. data/spec/spec_helper.rb +16 -0
  38. data/spec/support/reference.rb +66 -0
  39. metadata +159 -0
@@ -0,0 +1,45 @@
1
+ module RedhillonrailsCore::ActiveRecord
2
+ module Base
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def self.extended(base)
9
+ class << base
10
+ alias_method_chain :abstract_class?, :redhillonrails_core
11
+ alias_method_chain :reset_column_information, :redhillonrails_core
12
+ end
13
+ end
14
+
15
+ def base_class?
16
+ self == base_class
17
+ end
18
+
19
+ def abstract_class_with_redhillonrails_core?
20
+ abstract_class_without_redhillonrails_core? || !(name =~ /^Abstract/).nil?
21
+ end
22
+
23
+ def reset_column_information_with_redhillonrails_core
24
+ reset_column_information_without_redhillonrails_core
25
+ @indexes = @foreign_keys = nil
26
+ end
27
+
28
+ def pluralized_table_name(table_name)
29
+ ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name
30
+ end
31
+
32
+ def indexes
33
+ @indexes ||= connection.indexes(table_name, "#{name} Indexes")
34
+ end
35
+
36
+ def foreign_keys
37
+ @foreign_keys ||= connection.foreign_keys(table_name, "#{name} Foreign Keys")
38
+ end
39
+
40
+ def reverse_foreign_keys
41
+ connection.reverse_foreign_keys(table_name, "#{name} Reverse Foreign Keys")
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,14 @@
1
+ module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
2
+ module Column
3
+
4
+ def required_on
5
+ if null
6
+ nil
7
+ elsif default.nil?
8
+ :save
9
+ else
10
+ :update
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,63 @@
1
+ module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
2
+ class ForeignKeyDefinition < Struct.new(:name, :table_name, :column_names, :references_table_name, :references_column_names, :on_update, :on_delete, :deferrable)
3
+ ACTIONS = {:cascade => "CASCADE", :restrict => "RESTRICT", :set_null => "SET NULL", :set_default => "SET DEFAULT", :no_action => "NO ACTION"}.freeze
4
+
5
+ def initialize(name, table_name, column_names, references_table_name, references_column_names, on_update = nil, on_delete = nil, deferrable = nil)
6
+ super(name, unquote(table_name), unquote(column_names), unquote(references_table_name), unquote(references_column_names), on_update, on_delete, deferrable)
7
+ end
8
+
9
+ def to_dump
10
+ dump = "add_foreign_key"
11
+ dump << " #{table_name.inspect}, [#{Array(column_names).collect { |name| name.inspect }.join(', ')}]"
12
+ dump << ", #{references_table_name.inspect}, [#{Array(references_column_names).collect { |name| name.inspect }.join(', ')}]"
13
+ dump << ", :on_update => :#{on_update}" if on_update
14
+ dump << ", :on_delete => :#{on_delete}" if on_delete
15
+ dump << ", :deferrable => #{deferrable}" if deferrable
16
+ dump << ", :name => #{name.inspect}" if name
17
+ dump
18
+ end
19
+
20
+ def to_sql
21
+ sql = name ? "CONSTRAINT #{name} " : ""
22
+ sql << "FOREIGN KEY (#{Array(quoted_column_names).join(", ")}) REFERENCES #{quoted_references_table_name} (#{Array(quoted_references_column_names).join(", ")})"
23
+ sql << " ON UPDATE #{ACTIONS[on_update]}" if on_update
24
+ sql << " ON DELETE #{ACTIONS[on_delete]}" if on_delete
25
+ sql << " DEFERRABLE" if deferrable
26
+ sql
27
+ end
28
+
29
+ alias :to_s :to_sql
30
+
31
+ def quoted_column_names
32
+ Array(column_names).collect { |name| ::ActiveRecord::Base.connection.quote_column_name(name) }
33
+ end
34
+
35
+ def quoted_references_column_names
36
+ Array(references_column_names).collect { |name| ::ActiveRecord::Base.connection.quote_column_name(name) }
37
+ end
38
+
39
+ # def quoted_table_name
40
+ # ::ActiveRecord::Base.connection.quote_table_name(table_name)
41
+ # end
42
+ #
43
+ def quoted_references_table_name
44
+ ::ActiveRecord::Base.connection.quote_table_name(references_table_name)
45
+ end
46
+
47
+ def quote(name)
48
+ ::ActiveRecord::Base.connection.quote(name)
49
+ end
50
+
51
+ def unquote(names)
52
+ if names.is_a?(Array)
53
+ names.collect { |name| __unqoute(name) }
54
+ else
55
+ __unqoute(names)
56
+ end
57
+ end
58
+
59
+ def __unqoute(value)
60
+ value.to_s.sub(/^["`](.*)["`]$/, '\1')
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,11 @@
1
+ module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
2
+ module IndexDefinition
3
+ def case_sensitive?
4
+ @case_sensitive.nil? ? true : @case_sensitive
5
+ end
6
+
7
+ def case_sensitive=(case_sensitive)
8
+ @case_sensitive = case_sensitive
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
2
+ module SchemaStatements
3
+ def self.included(base)
4
+ base.module_eval do
5
+ alias_method_chain :create_table, :redhillonrails_core
6
+ end
7
+ end
8
+
9
+ def create_table_with_redhillonrails_core(name, options = {})
10
+ if options.include?(:comment)
11
+ options = options.dup
12
+ comment = options.delete(:comment)
13
+ end
14
+
15
+ create_table_without_redhillonrails_core(name, options) do |table_defintion|
16
+ table_defintion.name = name
17
+ yield table_defintion if block_given?
18
+ end
19
+
20
+ set_table_comment(name, comment) if comment
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
2
+ module TableDefinition
3
+ def self.included(base)
4
+ base.class_eval do
5
+ attr_accessor :name
6
+ alias_method_chain :initialize, :redhillonrails_core
7
+ alias_method_chain :to_sql, :redhillonrails_core
8
+ end
9
+ end
10
+
11
+ def initialize_with_redhillonrails_core(*args)
12
+ initialize_without_redhillonrails_core(*args)
13
+ @foreign_keys = []
14
+ end
15
+
16
+ def foreign_key(column_names, references_table_name, references_column_names, options = {})
17
+ @foreign_keys << ForeignKeyDefinition.new(options[:name], nil, column_names, ActiveRecord::Migrator.proper_table_name(references_table_name), references_column_names, options[:on_update], options[:on_delete], options[:deferrable])
18
+ self
19
+ end
20
+
21
+ def to_sql_with_redhillonrails_core
22
+ sql = to_sql_without_redhillonrails_core
23
+ sql << ', ' << @foreign_keys * ', ' unless @foreign_keys.empty? || ActiveRecord::Schema.defining?
24
+ sql
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,84 @@
1
+ module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
2
+ module AbstractAdapter
3
+ def self.included(base)
4
+ base.module_eval do
5
+ alias_method_chain :initialize, :redhillonrails_core
6
+ alias_method_chain :drop_table, :redhillonrails_core
7
+ end
8
+ end
9
+
10
+ def initialize_with_redhillonrails_core(*args)
11
+ initialize_without_redhillonrails_core(*args)
12
+ adapter = nil
13
+ case adapter_name
14
+ # name of MySQL adapter depends on mysql gem
15
+ # * with mysql gem adapter is named MySQL
16
+ # * with mysql2 gem adapter is named Mysql2
17
+ # Here we handle this and hopefully futher adapter names
18
+ when /^MySQL/i
19
+ adapter = 'MysqlAdapter'
20
+ when 'PostgreSQL'
21
+ adapter = 'PostgresqlAdapter'
22
+ when 'SQLite'
23
+ adapter = 'Sqlite3Adapter'
24
+ end
25
+ if adapter
26
+ adapter_module = RedhillonrailsCore::ActiveRecord::ConnectionAdapters.const_get(adapter)
27
+ self.class.send(:include, adapter_module) unless self.class.include?(adapter_module)
28
+ end
29
+ # Needed from mysql2 >= 0.2.7
30
+ if adapter_name =~ /^Mysql2/i && defined?(ActiveRecord::ConnectionAdapters::Mysql2IndexDefinition)
31
+ index_definition_module = RedhillonrailsCore::ActiveRecord::ConnectionAdapters::IndexDefinition
32
+ ActiveRecord::ConnectionAdapters::Mysql2IndexDefinition.send(:include, index_definition_module) unless ActiveRecord::ConnectionAdapters::Mysql2IndexDefinition.include?(index_definition_module)
33
+ end
34
+ end
35
+
36
+ def create_view(view_name, definition)
37
+ execute "CREATE VIEW #{view_name} AS #{definition}"
38
+ end
39
+
40
+ def drop_view(view_name)
41
+ execute "DROP VIEW #{view_name}"
42
+ end
43
+
44
+ def views(name = nil)
45
+ []
46
+ end
47
+
48
+ def view_definition(view_name, name = nil)
49
+ end
50
+
51
+ def foreign_keys(table_name, name = nil)
52
+ []
53
+ end
54
+
55
+ def reverse_foreign_keys(table_name, name = nil)
56
+ []
57
+ end
58
+
59
+ def add_foreign_key(table_name, column_names, references_table_name, references_column_names, options = {})
60
+ foreign_key = ForeignKeyDefinition.new(options[:name], table_name, column_names, ActiveRecord::Migrator.proper_table_name(references_table_name), references_column_names, options[:on_update], options[:on_delete], options[:deferrable])
61
+ execute "ALTER TABLE #{table_name} ADD #{foreign_key}"
62
+ end
63
+
64
+ def remove_foreign_key(table_name, foreign_key_name)
65
+ execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{foreign_key_name}"
66
+ end
67
+
68
+ def drop_table_with_redhillonrails_core(name, options = {})
69
+ reverse_foreign_keys(name).each do |foreign_key|
70
+ begin
71
+ remove_foreign_key(foreign_key.table_name, foreign_key.name)
72
+ rescue Exception => e
73
+ # TODO spec this
74
+ #there is a problem when using rollback if two tables have
75
+ #similar names. In that case, the plugin will try to remove a
76
+ #non-existing column and raise an exception. I rescue the
77
+ #exception so the migration can proceed
78
+ nil
79
+ end
80
+ end
81
+ drop_table_without_redhillonrails_core(name, options)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,131 @@
1
+ module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
2
+ module MysqlAdapter
3
+ def self.included(base)
4
+ base.class_eval do
5
+ alias_method_chain :remove_column, :redhillonrails_core
6
+ alias_method_chain :remove_columns, :redhillonrails_core
7
+ end
8
+ end
9
+
10
+ def set_table_comment(table_name, comment)
11
+ execute "ALTER TABLE #{table_name} COMMENT='#{quote_string(comment)}'"
12
+ end
13
+
14
+ def clear_table_comment(table_name)
15
+ execute "ALTER TABLE #{table_name} COMMENT=''"
16
+ end
17
+
18
+ def remove_foreign_key(table_name, foreign_key_name)
19
+ execute "ALTER TABLE #{table_name} DROP FOREIGN KEY #{foreign_key_name}"
20
+ end
21
+
22
+ def remove_column_with_redhillonrails_core(table_name, *column_names)
23
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
24
+ column_names.flatten.each do |column_name|
25
+ foreign_keys(table_name).select { |foreign_key| foreign_key.column_names.include?(column_name.to_s) }.each do |foreign_key|
26
+ remove_foreign_key(table_name, foreign_key.name)
27
+ end
28
+ remove_column_without_redhillonrails_core(table_name, column_name)
29
+ end
30
+ end
31
+ alias :remove_columns_with_redhillonrails_core :remove_column_with_redhillonrails_core
32
+
33
+ def foreign_keys(table_name, name = nil)
34
+ results = execute("SHOW CREATE TABLE #{quote_table_name(table_name)}", name)
35
+
36
+ foreign_keys = []
37
+
38
+ results.each do |row|
39
+ row[1].each_line do |line|
40
+ if line =~ /^ CONSTRAINT [`"](.+?)[`"] FOREIGN KEY \([`"](.+?)[`"]\) REFERENCES [`"](.+?)[`"] \((.+?)\)( ON DELETE (.+?))?( ON UPDATE (.+?))?,?$/
41
+ name = $1
42
+ column_names = $2
43
+ references_table_name = $3
44
+ references_column_names = $4
45
+ on_update = $8
46
+ on_delete = $6
47
+ on_update = on_update.downcase.gsub(' ', '_').to_sym if on_update
48
+ on_delete = on_delete.downcase.gsub(' ', '_').to_sym if on_delete
49
+
50
+ foreign_keys << ForeignKeyDefinition.new(name,
51
+ table_name, column_names.gsub('`', '').split(', '),
52
+ references_table_name, references_column_names.gsub('`', '').split(', '),
53
+ on_update, on_delete)
54
+ end
55
+ end
56
+ end
57
+
58
+ foreign_keys
59
+ end
60
+
61
+ # def reverse_foreign_keys(table_name, name = nil)
62
+ # results = execute(<<-SQL, name)
63
+ # SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
64
+ # FROM information_schema.key_column_usage
65
+ # WHERE table_schema = SCHEMA()
66
+ # AND referenced_table_schema = table_schema
67
+ # ORDER BY constraint_name, ordinal_position;
68
+ # SQL
69
+ # current_foreign_key = nil
70
+ # foreign_keys = []
71
+ #
72
+ # results.each do |row|
73
+ # next unless table_name.casecmp(row[3]) == 0
74
+ # if current_foreign_key != row[0]
75
+ # foreign_keys << ForeignKeyDefinition.new(row[0], row[1], [], row[3], [])
76
+ # current_foreign_key = row[0]
77
+ # end
78
+ #
79
+ # foreign_keys.last.column_names << row[2]
80
+ # foreign_keys.last.references_column_names << row[4]
81
+ # end
82
+ #
83
+ # foreign_keys
84
+ # end
85
+
86
+ def reverse_foreign_keys(table_name, name = nil)
87
+ # @@schema ||= nil
88
+ # @@schema_version ||= 0
89
+ # current_version = ActiveRecord::Migrator.current_version
90
+ # if @@schema.nil? || @@schema_version != current_version
91
+ # @@schema_version = current_version
92
+ # ans = execute(<<-SQL, name)
93
+ # SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
94
+ # FROM information_schema.key_column_usage
95
+ # WHERE table_schema = SCHEMA()
96
+ # AND referenced_table_schema = table_schema
97
+ # ORDER BY constraint_name, ordinal_position;
98
+ # SQL
99
+ # @@schema = []
100
+ # ans.each do | row |
101
+ # @@schema << [row[0], row[1], row[2], row[3], row[4]]
102
+ # end
103
+ # end
104
+ # results = @@schema
105
+
106
+ results = execute(<<-SQL, name)
107
+ SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
108
+ FROM information_schema.key_column_usage
109
+ WHERE table_schema = SCHEMA()
110
+ AND referenced_table_schema = table_schema
111
+ ORDER BY constraint_name, ordinal_position;
112
+ SQL
113
+
114
+ current_foreign_key = nil
115
+ foreign_keys = []
116
+
117
+ results.each do |row|
118
+ next if row[3] != table_name
119
+ if current_foreign_key != row[0]
120
+ foreign_keys << ForeignKeyDefinition.new(row[0], row[1], [], row[3], [])
121
+ current_foreign_key = row[0]
122
+ end
123
+
124
+ foreign_keys.last.column_names << row[2]
125
+ foreign_keys.last.references_column_names << row[4]
126
+ end
127
+
128
+ foreign_keys
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,8 @@
1
+ module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
2
+ module MysqlColumn
3
+ def initialize(name, default, sql_type = nil, null = true)
4
+ default = nil if !null && default.blank?
5
+ super
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,131 @@
1
+ module RedhillonrailsCore::ActiveRecord::ConnectionAdapters
2
+ module PostgresqlAdapter
3
+ def self.included(base)
4
+ base.class_eval do
5
+ alias_method_chain :indexes, :redhillonrails_core
6
+ end
7
+ end
8
+
9
+ def set_table_comment(table_name, comment)
10
+ execute "COMMENT ON TABLE #{table_name} IS '#{quote_string(comment)}'"
11
+ end
12
+
13
+ def clear_table_comment(table_name)
14
+ execute "COMMENT ON TABLE #{table_name} IS NULL"
15
+ end
16
+
17
+ def add_index(table_name, column_name, options = {})
18
+ column_names = Array(column_name)
19
+ index_name = index_name(table_name, :column => column_names)
20
+
21
+ if Hash === options # legacy support, since this param was a string
22
+ index_type = options[:unique] ? "UNIQUE" : ""
23
+ index_name = options[:name] || index_name
24
+ else
25
+ index_type = options
26
+ end
27
+
28
+ quoted_column_names = column_names.map { |e| options[:case_sensitive] == false && e.to_s !~ /_id$/ ? "LOWER(#{quote_column_name(e)})" : quote_column_name(e) }
29
+
30
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{table_name} (#{quoted_column_names.join(", ")})"
31
+ end
32
+
33
+ def indexes_with_redhillonrails_core(table_name, name = nil)
34
+ indexes = indexes_without_redhillonrails_core(table_name, name)
35
+ result = query(<<-SQL, name)
36
+ SELECT c2.relname, i.indisunique, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true)
37
+ FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
38
+ WHERE c.relname = '#{table_name}'
39
+ AND c.oid = i.indrelid AND i.indexrelid = c2.oid
40
+ AND i.indisprimary = 'f'
41
+ AND i.indexprs IS NOT NULL
42
+ ORDER BY 1
43
+ SQL
44
+
45
+ result.each do |row|
46
+ if row[2]=~ /\((.*LOWER\([^:]+(::text)?\).*)\)$/i
47
+ indexes.delete_if { |index| index.name == row[0] }
48
+ column_names = $1.split(", ").map do |name|
49
+ name = $1 if name =~ /^LOWER\(([^:]+)(::text)?\)$/i
50
+ name = $1 if name =~ /^"(.*)"$/
51
+ name
52
+ end
53
+ index = ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, row[0], row[1] == "t", column_names)
54
+ index.case_sensitive = false
55
+ indexes << index
56
+ end
57
+ end
58
+
59
+ indexes
60
+ end
61
+
62
+ def foreign_keys(table_name, name = nil)
63
+ load_foreign_keys(<<-SQL, name)
64
+ SELECT f.conname, pg_get_constraintdef(f.oid), t.relname
65
+ FROM pg_class t, pg_constraint f
66
+ WHERE f.conrelid = t.oid
67
+ AND f.contype = 'f'
68
+ AND t.relname = '#{table_name}'
69
+ SQL
70
+ end
71
+
72
+ def reverse_foreign_keys(table_name, name = nil)
73
+ load_foreign_keys(<<-SQL, name)
74
+ SELECT f.conname, pg_get_constraintdef(f.oid), t2.relname
75
+ FROM pg_class t, pg_class t2, pg_constraint f
76
+ WHERE f.confrelid = t.oid
77
+ AND f.conrelid = t2.oid
78
+ AND f.contype = 'f'
79
+ AND t.relname = '#{table_name}'
80
+ SQL
81
+ end
82
+
83
+ def views(name = nil)
84
+ schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
85
+ query(<<-SQL, name).map { |row| row[0] }
86
+ SELECT viewname
87
+ FROM pg_views
88
+ WHERE schemaname IN (#{schemas})
89
+ SQL
90
+ end
91
+
92
+ def view_definition(view_name, name = nil)
93
+ result = query(<<-SQL, name)
94
+ SELECT pg_get_viewdef(oid)
95
+ FROM pg_class
96
+ WHERE relkind = 'v'
97
+ AND relname = '#{view_name}'
98
+ SQL
99
+ row = result.first
100
+ row.first unless row.nil?
101
+ end
102
+
103
+ private
104
+
105
+ def load_foreign_keys(sql, name = nil)
106
+ foreign_keys = []
107
+
108
+ query(sql, name).each do |row|
109
+ if row[1] =~ /^FOREIGN KEY \((.+?)\) REFERENCES (.+?)\((.+?)\)( ON UPDATE (.+?))?( ON DELETE (.+?))?( (DEFERRABLE|NOT DEFERRABLE))?$/
110
+ name = row[0]
111
+ from_table_name = row[2]
112
+ column_names = $1
113
+ references_table_name = $2
114
+ references_column_names = $3
115
+ on_update = $5
116
+ on_delete = $7
117
+ deferrable = $9 == "DEFERRABLE"
118
+ on_update = on_update.downcase.gsub(' ', '_').to_sym if on_update
119
+ on_delete = on_delete.downcase.gsub(' ', '_').to_sym if on_delete
120
+
121
+ foreign_keys << ForeignKeyDefinition.new(name,
122
+ from_table_name, column_names.split(', '),
123
+ references_table_name.sub(/^"(.*)"$/, '\1'), references_column_names.split(', '),
124
+ on_update, on_delete, deferrable)
125
+ end
126
+ end
127
+
128
+ foreign_keys
129
+ end
130
+ end
131
+ end