schema_plus 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +360 -0
- data/lib/schema_plus/active_record/column_options_handler.rb +9 -8
- data/lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb +25 -47
- data/lib/schema_plus/active_record/connection_adapters/column.rb +6 -0
- data/lib/schema_plus/active_record/connection_adapters/foreign_key_definition.rb +15 -5
- data/lib/schema_plus/active_record/connection_adapters/mysql_adapter.rb +35 -4
- data/lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb +23 -11
- data/lib/schema_plus/active_record/connection_adapters/schema_statements.rb +3 -0
- data/lib/schema_plus/active_record/connection_adapters/sqlite3_adapter.rb +9 -3
- data/lib/schema_plus/version.rb +1 -1
- data/runspecs +1 -1
- data/spec/column_definition_spec.rb +2 -2
- data/spec/column_spec.rb +11 -1
- data/spec/foreign_key_definition_spec.rb +11 -1
- data/spec/migration_spec.rb +6 -6
- data/spec/{multiple_schemas_spec.rb → named_schemas_spec.rb} +92 -0
- data/spec/schema_spec.rb +0 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/support/matchers/reference.rb +7 -0
- data/spec/views_spec.rb +1 -1
- metadata +6 -6
- data/README.rdoc +0 -347
@@ -51,6 +51,12 @@ module SchemaPlus
|
|
51
51
|
:update
|
52
52
|
end
|
53
53
|
end
|
54
|
+
|
55
|
+
# The default as_jon includes all instance variables. but
|
56
|
+
# @connection can't be dumped (it contains circular references)
|
57
|
+
def as_json(options=nil)
|
58
|
+
instance_values.except "connection"
|
59
|
+
end
|
54
60
|
end
|
55
61
|
end
|
56
62
|
end
|
@@ -9,6 +9,10 @@ module SchemaPlus
|
|
9
9
|
# :set_null
|
10
10
|
# :set_default
|
11
11
|
# :no_action
|
12
|
+
#
|
13
|
+
# The deferrable attribute can take on the following values:
|
14
|
+
# true
|
15
|
+
# :initially_deferred
|
12
16
|
class ForeignKeyDefinition
|
13
17
|
|
14
18
|
# The name of the foreign key constraint
|
@@ -65,9 +69,9 @@ module SchemaPlus
|
|
65
69
|
dump = (opts[:inline] ? " t.foreign_key" : "add_foreign_key #{table_name.inspect},")
|
66
70
|
dump << " [#{Array(column_names).collect{ |name| name.inspect }.join(', ')}]"
|
67
71
|
dump << ", #{references_table_name.inspect}, [#{Array(references_column_names).collect{ |name| name.inspect }.join(', ')}]"
|
68
|
-
dump << ", :on_update =>
|
69
|
-
dump << ", :on_delete =>
|
70
|
-
dump << ", :deferrable => #{deferrable}" if deferrable
|
72
|
+
dump << ", :on_update => #{on_update.inspect}" if on_update
|
73
|
+
dump << ", :on_delete => #{on_delete.inspect}" if on_delete
|
74
|
+
dump << ", :deferrable => #{deferrable.inspect}" if deferrable
|
71
75
|
dump << ", :name => #{name.inspect}" if name
|
72
76
|
dump << "\n"
|
73
77
|
dump
|
@@ -79,6 +83,7 @@ module SchemaPlus
|
|
79
83
|
sql << " ON UPDATE #{ACTIONS[on_update]}" if on_update
|
80
84
|
sql << " ON DELETE #{ACTIONS[on_delete]}" if on_delete
|
81
85
|
sql << " DEFERRABLE" if deferrable
|
86
|
+
sql << " INITIALLY DEFERRED" if deferrable == :initially_deferred
|
82
87
|
sql
|
83
88
|
end
|
84
89
|
|
@@ -107,11 +112,16 @@ module SchemaPlus
|
|
107
112
|
end
|
108
113
|
|
109
114
|
def self.default_name(table_name, column_names)
|
110
|
-
"fk_#{table_name}_#{Array.wrap(column_names).join('_and_')}"
|
115
|
+
"fk_#{fixup_schema_name(table_name)}_#{Array.wrap(column_names).join('_and_')}"
|
111
116
|
end
|
112
117
|
|
113
118
|
def self.auto_index_name(table_name, column_name)
|
114
|
-
"fk__#{table_name}_#{Array.wrap(column_name).join('_and_')}"
|
119
|
+
"fk__#{fixup_schema_name(table_name)}_#{Array.wrap(column_name).join('_and_')}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.fixup_schema_name(table_name)
|
123
|
+
# replace . with _
|
124
|
+
table_name.to_s.gsub(/[.]/, '_')
|
115
125
|
end
|
116
126
|
|
117
127
|
end
|
@@ -47,6 +47,12 @@ module SchemaPlus
|
|
47
47
|
exec_stmt_without_schema_plus(sql, name, binds, &block)
|
48
48
|
end
|
49
49
|
|
50
|
+
# implement cascade by removing foreign keys
|
51
|
+
def drop_table(name, options={})
|
52
|
+
reverse_foreign_keys(name).each{ |foreign_key| remove_foreign_key(foreign_key.table_name, foreign_key.name) } if options[:cascade]
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
50
56
|
def remove_foreign_key(table_name, foreign_key_name, options = {})
|
51
57
|
execute "ALTER TABLE #{quote_table_name(table_name)} DROP FOREIGN KEY #{foreign_key_name}"
|
52
58
|
end
|
@@ -54,6 +60,9 @@ module SchemaPlus
|
|
54
60
|
def foreign_keys(table_name, name = nil)
|
55
61
|
results = execute("SHOW CREATE TABLE #{quote_table_name(table_name)}", name)
|
56
62
|
|
63
|
+
table_name = table_name.to_s
|
64
|
+
namespace_prefix = table_namespace_prefix(table_name)
|
65
|
+
|
57
66
|
foreign_keys = []
|
58
67
|
|
59
68
|
results.each do |row|
|
@@ -62,6 +71,7 @@ module SchemaPlus
|
|
62
71
|
name = $1
|
63
72
|
column_names = $2
|
64
73
|
references_table_name = $3
|
74
|
+
references_table_name = namespace_prefix + references_table_name if table_namespace_prefix(references_table_name).blank?
|
65
75
|
references_column_names = $4
|
66
76
|
on_update = $8
|
67
77
|
on_delete = $6
|
@@ -69,7 +79,7 @@ module SchemaPlus
|
|
69
79
|
on_delete = on_delete ? on_delete.downcase.gsub(' ', '_').to_sym : :restrict
|
70
80
|
|
71
81
|
foreign_keys << ForeignKeyDefinition.new(name,
|
72
|
-
table_name, column_names.gsub('`', '').split(', '),
|
82
|
+
namespace_prefix + table_name, column_names.gsub('`', '').split(', '),
|
73
83
|
references_table_name, references_column_names.gsub('`', '').split(', '),
|
74
84
|
on_update, on_delete)
|
75
85
|
end
|
@@ -83,17 +93,23 @@ module SchemaPlus
|
|
83
93
|
results = execute(<<-SQL, name)
|
84
94
|
SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
|
85
95
|
FROM information_schema.key_column_usage
|
86
|
-
WHERE table_schema =
|
96
|
+
WHERE table_schema = #{table_schema_sql(table_name)}
|
87
97
|
AND referenced_table_schema = table_schema
|
88
98
|
ORDER BY constraint_name, ordinal_position;
|
89
99
|
SQL
|
90
100
|
current_foreign_key = nil
|
91
101
|
foreign_keys = []
|
92
102
|
|
103
|
+
namespace_prefix = table_namespace_prefix(table_name)
|
104
|
+
|
93
105
|
results.each do |row|
|
94
|
-
next unless table_name.casecmp(row[3]) == 0
|
106
|
+
next unless table_name_without_namespace(table_name).casecmp(row[3]) == 0
|
95
107
|
if current_foreign_key != row[0]
|
96
|
-
|
108
|
+
referenced_table_name = row[1]
|
109
|
+
referenced_table_name = namespace_prefix + referenced_table_name if table_namespace_prefix(referenced_table_name).blank?
|
110
|
+
references_table_name = row[3]
|
111
|
+
references_table_name = namespace_prefix + references_table_name if table_namespace_prefix(references_table_name).blank?
|
112
|
+
foreign_keys << ForeignKeyDefinition.new(row[0], referenced_table_name, [], references_table_name, [])
|
97
113
|
current_foreign_key = row[0]
|
98
114
|
end
|
99
115
|
|
@@ -134,6 +150,21 @@ module SchemaPlus
|
|
134
150
|
when :now then 'CURRENT_TIMESTAMP'
|
135
151
|
end
|
136
152
|
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def table_namespace_prefix(table_name)
|
157
|
+
table_name.to_s =~ /(.*[.])/ ? $1 : ""
|
158
|
+
end
|
159
|
+
|
160
|
+
def table_schema_sql(table_name)
|
161
|
+
table_name.to_s =~ /(.*)[.]/ ? "'#{$1}'" : "SCHEMA()"
|
162
|
+
end
|
163
|
+
|
164
|
+
def table_name_without_namespace(table_name)
|
165
|
+
table_name.to_s.sub /.*[.]/, ''
|
166
|
+
end
|
167
|
+
|
137
168
|
end
|
138
169
|
end
|
139
170
|
end
|
@@ -53,6 +53,7 @@ module SchemaPlus
|
|
53
53
|
alias_method_chain :rename_table, :schema_plus
|
54
54
|
alias_method_chain :exec_cache, :schema_plus
|
55
55
|
end
|
56
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLColumn.send(:include, PostgreSQLColumn) unless ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn.include?(PostgreSQLColumn)
|
56
57
|
end
|
57
58
|
|
58
59
|
# SchemaPlus provides the following extra options for Postgres
|
@@ -126,8 +127,8 @@ module SchemaPlus
|
|
126
127
|
INNER JOIN pg_am m ON i.relam = m.oid
|
127
128
|
WHERE i.relkind = 'i'
|
128
129
|
AND d.indisprimary = 'f'
|
129
|
-
AND t.relname = '#{table_name}'
|
130
|
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname =
|
130
|
+
AND t.relname = '#{table_name_without_namespace(table_name)}'
|
131
|
+
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = #{namespace_sql(table_name)} )
|
131
132
|
ORDER BY i.relname
|
132
133
|
SQL
|
133
134
|
|
@@ -200,8 +201,8 @@ module SchemaPlus
|
|
200
201
|
FROM pg_class t, pg_constraint f
|
201
202
|
WHERE f.conrelid = t.oid
|
202
203
|
AND f.contype = 'f'
|
203
|
-
AND t.relname = '#{table_name}'
|
204
|
-
AND t.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname =
|
204
|
+
AND t.relname = '#{table_name_without_namespace(table_name)}'
|
205
|
+
AND t.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = #{namespace_sql(table_name)} )
|
205
206
|
SQL
|
206
207
|
end
|
207
208
|
|
@@ -212,17 +213,19 @@ module SchemaPlus
|
|
212
213
|
WHERE f.confrelid = t.oid
|
213
214
|
AND f.conrelid = t2.oid
|
214
215
|
AND f.contype = 'f'
|
215
|
-
AND t.relname = '#{table_name}'
|
216
|
-
AND t.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname =
|
216
|
+
AND t.relname = '#{table_name_without_namespace(table_name)}'
|
217
|
+
AND t.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = #{namespace_sql(table_name)} )
|
217
218
|
SQL
|
218
219
|
end
|
219
220
|
|
220
221
|
def views(name = nil) #:nodoc:
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
222
|
+
sql = <<-SQL
|
223
|
+
SELECT viewname
|
224
|
+
FROM pg_views
|
225
|
+
WHERE schemaname = ANY (current_schemas(false))
|
225
226
|
SQL
|
227
|
+
sql += " AND schemaname != 'postgis'" if adapter_name == 'PostGIS'
|
228
|
+
query(sql, name).map { |row| row[0] }
|
226
229
|
end
|
227
230
|
|
228
231
|
def view_definition(view_name, name = nil) #:nodoc:
|
@@ -238,11 +241,19 @@ module SchemaPlus
|
|
238
241
|
|
239
242
|
private
|
240
243
|
|
244
|
+
def namespace_sql(table_name)
|
245
|
+
(table_name.to_s =~ /(.*)[.]/) ? "'#{$1}'" : "ANY (current_schemas(false))"
|
246
|
+
end
|
247
|
+
|
248
|
+
def table_name_without_namespace(table_name)
|
249
|
+
table_name.to_s.sub /.*[.]/, ''
|
250
|
+
end
|
251
|
+
|
241
252
|
def load_foreign_keys(sql, name = nil) #:nodoc:
|
242
253
|
foreign_keys = []
|
243
254
|
|
244
255
|
query(sql, name).each do |row|
|
245
|
-
if row[1] =~ /^FOREIGN KEY \((.+?)\) REFERENCES (.+?)\((.+?)\)( ON UPDATE (.+?))?( ON DELETE (.+?))?( (DEFERRABLE|NOT DEFERRABLE))?$/
|
256
|
+
if row[1] =~ /^FOREIGN KEY \((.+?)\) REFERENCES (.+?)\((.+?)\)( ON UPDATE (.+?))?( ON DELETE (.+?))?( (DEFERRABLE|NOT DEFERRABLE)( (INITIALLY DEFERRED|INITIALLY IMMEDIATE))?)?$/
|
246
257
|
name = row[0]
|
247
258
|
from_table_name = row[2]
|
248
259
|
column_names = $1
|
@@ -251,6 +262,7 @@ module SchemaPlus
|
|
251
262
|
on_update = $5
|
252
263
|
on_delete = $7
|
253
264
|
deferrable = $9 == "DEFERRABLE"
|
265
|
+
deferrable = :initially_deferred if ($11 == "INITIALLY DEFERRED" )
|
254
266
|
on_update = on_update ? on_update.downcase.gsub(' ', '_').to_sym : :no_action
|
255
267
|
on_delete = on_delete ? on_delete.downcase.gsub(' ', '_').to_sym : :no_action
|
256
268
|
|
@@ -22,6 +22,9 @@ module SchemaPlus::ActiveRecord::ConnectionAdapters
|
|
22
22
|
config_options = {}
|
23
23
|
options.keys.each { |key| config_options[key] = options.delete(key) if SchemaPlus.config.class.attributes.include? key }
|
24
24
|
|
25
|
+
# override rails' :force to cascade
|
26
|
+
drop_table(table, if_exists: true, cascade: true) if options.delete(:force)
|
27
|
+
|
25
28
|
indexes = []
|
26
29
|
create_table_without_schema_plus(table, options) do |table_definition|
|
27
30
|
table_definition.schema_plus_config = SchemaPlus.config.merge(config_options)
|
@@ -21,6 +21,7 @@ module SchemaPlus
|
|
21
21
|
alias_method_chain :indexes, :schema_plus
|
22
22
|
alias_method_chain :rename_table, :schema_plus
|
23
23
|
end
|
24
|
+
::ActiveRecord::ConnectionAdapters::SQLiteColumn.send(:include, SQLiteColumn) unless ::ActiveRecord::ConnectionAdapters::SQLiteColumn.include?(SQLiteColumn)
|
24
25
|
end
|
25
26
|
|
26
27
|
def indexes_with_schema_plus(table_name, name = nil)
|
@@ -39,7 +40,6 @@ module SchemaPlus
|
|
39
40
|
rename_indexes_and_foreign_keys(oldname, newname)
|
40
41
|
end
|
41
42
|
|
42
|
-
|
43
43
|
def add_foreign_key(table_name, column_names, references_table_name, references_column_names, options = {})
|
44
44
|
raise NotImplementedError, "Sqlite3 does not support altering a table to add foreign key constraints (table #{table_name.inspect} column #{column_names.inspect})"
|
45
45
|
end
|
@@ -48,6 +48,10 @@ module SchemaPlus
|
|
48
48
|
raise NotImplementedError, "Sqlite3 does not support altering a table to remove foreign key constraints (table #{table_name.inspect} constraint #{foreign_key_name.inspect})"
|
49
49
|
end
|
50
50
|
|
51
|
+
def drop_table(name, options={})
|
52
|
+
super(name, options.except(:cascade))
|
53
|
+
end
|
54
|
+
|
51
55
|
def foreign_keys(table_name, name = nil)
|
52
56
|
get_foreign_keys(table_name, name)
|
53
57
|
end
|
@@ -83,22 +87,24 @@ module SchemaPlus
|
|
83
87
|
\s*REFERENCES\s*[`"](.+?)[`"]\s*\((.+?)\)
|
84
88
|
(\s+ON\s+UPDATE\s+(.+?))?
|
85
89
|
(\s*ON\s+DELETE\s+(.+?))?
|
90
|
+
(\s*DEFERRABLE(\s+INITIALLY\s+DEFERRED)?)?
|
86
91
|
\s*[,)]
|
87
92
|
]x
|
88
93
|
|
89
94
|
foreign_keys = []
|
90
95
|
results.each do |row|
|
91
96
|
table_name = row["name"]
|
92
|
-
row["sql"].scan(re).each do |d0, name, column_names, references_table_name, references_column_names, d1, on_update, d2, on_delete|
|
97
|
+
row["sql"].scan(re).each do |d0, name, column_names, references_table_name, references_column_names, d1, on_update, d2, on_delete, deferrable, initially_deferred|
|
93
98
|
column_names = column_names.gsub('`', '').split(', ')
|
94
99
|
|
95
100
|
references_column_names = references_column_names.gsub('`"', '').split(', ')
|
96
101
|
on_update = on_update ? on_update.downcase.gsub(' ', '_').to_sym : :no_action
|
97
102
|
on_delete = on_delete ? on_delete.downcase.gsub(' ', '_').to_sym : :no_action
|
103
|
+
deferrable = deferrable ? (initially_deferred ? :initially_deferred : true) : false
|
98
104
|
foreign_keys << ForeignKeyDefinition.new(name,
|
99
105
|
table_name, column_names,
|
100
106
|
references_table_name, references_column_names,
|
101
|
-
on_update, on_delete)
|
107
|
+
on_update, on_delete, deferrable)
|
102
108
|
end
|
103
109
|
end
|
104
110
|
|
data/lib/schema_plus/version.rb
CHANGED
data/runspecs
CHANGED
@@ -97,7 +97,7 @@ combos.each_with_index do |combo, n|
|
|
97
97
|
Tempfile.open('runspecs') do |file|
|
98
98
|
system("(#{command}) 2>&1 | tee #{file.path}")
|
99
99
|
file.rewind
|
100
|
-
errs << "ruby #{ruby}, rails #{rails}#{db_adapter && ", db_adapter #{db_adapter}"}" if file.readlines.grep(
|
100
|
+
errs << "ruby #{ruby}, rails #{rails}#{db_adapter && ", db_adapter #{db_adapter}"}" if file.readlines.grep(/(^Failed examples)|(rake aborted)/).any?
|
101
101
|
end
|
102
102
|
end
|
103
103
|
puts errs.any? ? "\n*** #{errs.size} failures:\n\t#{errs.join("\n\t")}" : "\n*** #{combos.size > 1 ? 'all versions' : 'spec'} succeeded ***" unless o.dry_run
|
@@ -133,7 +133,7 @@ describe "Column definition" do
|
|
133
133
|
subject { @sql}
|
134
134
|
|
135
135
|
it "should give the default as false" do
|
136
|
-
should
|
136
|
+
should =~ /boolean DEFAULT (\'f\'|0)/
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
@@ -145,7 +145,7 @@ describe "Column definition" do
|
|
145
145
|
subject { @sql}
|
146
146
|
|
147
147
|
it "should give the default as true" do
|
148
|
-
should
|
148
|
+
should =~ /boolean DEFAULT (\'t\'|1)/
|
149
149
|
end
|
150
150
|
end
|
151
151
|
end
|
data/spec/column_spec.rb
CHANGED
@@ -8,6 +8,16 @@ describe "Column" do
|
|
8
8
|
|
9
9
|
let(:migration) { ::ActiveRecord::Migration }
|
10
10
|
|
11
|
+
context "JSON serialization" do
|
12
|
+
before(:each) do
|
13
|
+
create_table(User, :login => { :index => true})
|
14
|
+
@login = User.columns.find{|column| column.name == "login"}
|
15
|
+
end
|
16
|
+
it "works properly" do
|
17
|
+
JSON.parse(@login.to_json).should include("name" => "login", "type" => "string")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
11
21
|
context "regarding indexes" do
|
12
22
|
|
13
23
|
context "if not unique" do
|
@@ -78,7 +88,7 @@ describe "Column" do
|
|
78
88
|
User.columns.find{|column| column.name == "login"}.required_on.should == :save
|
79
89
|
end
|
80
90
|
|
81
|
-
it "must have a value on :
|
91
|
+
it "must have a value on :update if there's default" do
|
82
92
|
create_table(User, :login => { :null => false, :default => "foo" })
|
83
93
|
User.columns.find{|column| column.name == "login"}.required_on.should == :update
|
84
94
|
end
|
@@ -4,10 +4,20 @@ describe "Foreign Key definition" do
|
|
4
4
|
|
5
5
|
let(:definition) { SchemaPlus::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new("posts_user_fkey", :posts, :user, :users, :id) }
|
6
6
|
|
7
|
-
it "
|
7
|
+
it "dumps to sql with quoted values" do
|
8
8
|
definition.to_sql.should == %Q{CONSTRAINT posts_user_fkey FOREIGN KEY (#{quote_column_name('user')}) REFERENCES #{quote_table_name('users')} (#{quote_column_name('id')})}
|
9
9
|
end
|
10
10
|
|
11
|
+
it "dumps to sql with deferrable values" do
|
12
|
+
deferred_definition = SchemaPlus::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new("posts_user_fkey", :posts, :user, :users, :id, nil, nil, true)
|
13
|
+
deferred_definition.to_sql.should == %Q{CONSTRAINT posts_user_fkey FOREIGN KEY (#{quote_column_name('user')}) REFERENCES #{quote_table_name('users')} (#{quote_column_name('id')}) DEFERRABLE}
|
14
|
+
end
|
15
|
+
|
16
|
+
it "dumps to sql with initially deferrable values" do
|
17
|
+
initially_deferred_definition = SchemaPlus::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new("posts_user_fkey", :posts, :user, :users, :id, nil, nil, :initially_deferred)
|
18
|
+
initially_deferred_definition.to_sql.should == %Q{CONSTRAINT posts_user_fkey FOREIGN KEY (#{quote_column_name('user')}) REFERENCES #{quote_table_name('users')} (#{quote_column_name('id')}) DEFERRABLE INITIALLY DEFERRED}
|
19
|
+
end
|
20
|
+
|
11
21
|
def quote_table_name(table)
|
12
22
|
ActiveRecord::Base.connection.quote_table_name(table)
|
13
23
|
end
|
data/spec/migration_spec.rb
CHANGED
@@ -4,7 +4,7 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
4
4
|
describe ActiveRecord::Migration do
|
5
5
|
include SchemaPlusHelpers
|
6
6
|
|
7
|
-
before(:
|
7
|
+
before(:each) do
|
8
8
|
define_schema(:auto_create => true) do
|
9
9
|
|
10
10
|
create_table :users, :force => true do |t|
|
@@ -293,14 +293,14 @@ describe ActiveRecord::Migration do
|
|
293
293
|
end
|
294
294
|
end
|
295
295
|
|
296
|
-
|
297
|
-
|
296
|
+
[false, true, :initially_deferred].each do |status|
|
297
|
+
it "should create and detect deferrable #{status.inspect}" do
|
298
298
|
recreate_table @model do |t|
|
299
|
-
t.integer :user_id
|
299
|
+
t.integer :user_id, :on_delete => :cascade, :deferrable => status
|
300
300
|
end
|
301
|
-
@model.should reference.on(:user_id).
|
301
|
+
@model.should reference.on(:user_id).deferrable(status)
|
302
302
|
end
|
303
|
-
end
|
303
|
+
end unless SchemaPlusHelpers.mysql?
|
304
304
|
|
305
305
|
it "should use default on_delete action" do
|
306
306
|
with_fk_config(:on_delete => :cascade) do
|
@@ -121,6 +121,98 @@ describe "with multiple schemas" do
|
|
121
121
|
|
122
122
|
end
|
123
123
|
|
124
|
+
context "foreign key migrations" do
|
125
|
+
before(:each) do
|
126
|
+
define_schema do
|
127
|
+
create_table "items", :force => true do |t|
|
128
|
+
end
|
129
|
+
create_table "schema_plus_test2.groups", :force => true do |t|
|
130
|
+
end
|
131
|
+
create_table "schema_plus_test2.members", :force => true do |t|
|
132
|
+
t.integer :item_id, :foreign_key => true unless SchemaPlusHelpers.mysql?
|
133
|
+
t.integer :group_id, :foreign_key => { references: "schema_plus_test2.groups" }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
class Group < ::ActiveRecord::Base
|
137
|
+
self.table_name = "schema_plus_test2.groups"
|
138
|
+
end
|
139
|
+
class Item < ::ActiveRecord::Base
|
140
|
+
self.table_name = "items"
|
141
|
+
end
|
142
|
+
class Member < ::ActiveRecord::Base
|
143
|
+
self.table_name = "schema_plus_test2.members"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
around(:each) do |example|
|
148
|
+
begin
|
149
|
+
example.run
|
150
|
+
ensure
|
151
|
+
connection.execute 'DROP TABLE IF EXISTS schema_plus_test2.members'
|
152
|
+
connection.execute 'DROP TABLE IF EXISTS schema_plus_test2.groups'
|
153
|
+
connection.execute 'DROP TABLE IF EXISTS items'
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should find foreign keys" do
|
158
|
+
Member.foreign_keys.should_not be_empty
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should find reverse foreign keys" do
|
162
|
+
Group.reverse_foreign_keys.should_not be_empty
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should reference table in same schema" do
|
166
|
+
Member.foreign_keys.map(&:references_table_name).should include "schema_plus_test2.groups"
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should reference table in default schema" do
|
170
|
+
Member.foreign_keys.map(&:references_table_name).should include "items"
|
171
|
+
end unless SchemaPlusHelpers.mysql?
|
172
|
+
|
173
|
+
it "should include the schema in the constraint name" do
|
174
|
+
expected_names = ["fk_schema_plus_test2_members_group_id"]
|
175
|
+
expected_names << "fk_schema_plus_test2_members_item_id" unless SchemaPlusHelpers.mysql?
|
176
|
+
Member.foreign_keys.map(&:name).should =~ expected_names
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
if SchemaPlusHelpers.postgresql?
|
181
|
+
context "when using PostGIS" do
|
182
|
+
before(:all) do
|
183
|
+
begin
|
184
|
+
connection.execute "CREATE SCHEMA postgis"
|
185
|
+
rescue ActiveRecord::StatementInvalid => e
|
186
|
+
raise unless e.message =~ /already exists/
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
around (:each) do |example|
|
191
|
+
begin
|
192
|
+
connection.execute "SET search_path to '$user','public','postgis'"
|
193
|
+
example.run
|
194
|
+
ensure
|
195
|
+
connection.execute "SET search_path to '$user','public'"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
before(:each) do
|
200
|
+
connection.stub :adapter_name => 'PostGIS'
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should hide views in postgis schema" do
|
204
|
+
begin
|
205
|
+
connection.create_view "postgis.hidden", "select 1", :force => true
|
206
|
+
connection.create_view :myview, "select 2", :force => true
|
207
|
+
connection.views.should == ["myview"]
|
208
|
+
ensure
|
209
|
+
connection.execute 'DROP VIEW postgis.hidden' rescue nil
|
210
|
+
connection.execute 'DROP VIEW myview' rescue nil
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
124
216
|
end
|
125
217
|
|
126
218
|
|