activerecord-postgresql-extensions 0.0.7
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/MIT-LICENSE +23 -0
- data/README.rdoc +32 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/lib/activerecord-postgresql-extensions.rb +30 -0
- data/lib/postgresql_extensions/foreign_key_associations.rb +367 -0
- data/lib/postgresql_extensions/postgresql_adapter_extensions.rb +646 -0
- data/lib/postgresql_extensions/postgresql_constraints.rb +579 -0
- data/lib/postgresql_extensions/postgresql_functions.rb +345 -0
- data/lib/postgresql_extensions/postgresql_geometry.rb +212 -0
- data/lib/postgresql_extensions/postgresql_indexes.rb +219 -0
- data/lib/postgresql_extensions/postgresql_languages.rb +80 -0
- data/lib/postgresql_extensions/postgresql_permissions.rb +322 -0
- data/lib/postgresql_extensions/postgresql_rules.rb +112 -0
- data/lib/postgresql_extensions/postgresql_schemas.rb +49 -0
- data/lib/postgresql_extensions/postgresql_sequences.rb +222 -0
- data/lib/postgresql_extensions/postgresql_tables.rb +308 -0
- data/lib/postgresql_extensions/postgresql_triggers.rb +131 -0
- data/lib/postgresql_extensions/postgresql_types.rb +17 -0
- data/lib/postgresql_extensions/postgresql_views.rb +103 -0
- data/postgresql-extensions.gemspec +50 -0
- data/test/adapter_test.rb +45 -0
- data/test/constraints_test.rb +98 -0
- data/test/functions_test.rb +112 -0
- data/test/geometry_test.rb +43 -0
- data/test/index_test.rb +68 -0
- data/test/languages_test.rb +48 -0
- data/test/permissions_test.rb +163 -0
- data/test/rules_test.rb +32 -0
- data/test/schemas_test.rb +43 -0
- data/test/sequences_test.rb +90 -0
- data/test/tables_test.rb +49 -0
- data/test/test_helper.rb +64 -0
- metadata +97 -0
@@ -0,0 +1,579 @@
|
|
1
|
+
|
2
|
+
module ActiveRecord
|
3
|
+
class InvalidForeignKeyAction < ActiveRecordError #:nodoc:
|
4
|
+
def initialize(action)
|
5
|
+
super("Invalid foreign key action - #{action}")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class InvalidMatchType < ActiveRecordError #:nodoc:
|
10
|
+
def initialize(type)
|
11
|
+
super("Invalid MATCH type - #{type}")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class InvalidDeferrableOption < ActiveRecordError #:nodoc:
|
16
|
+
def initialize(option)
|
17
|
+
super("Invalid DEFERRABLE option - #{option}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class InvalidConstraintDependencyAction < ActiveRecordError #:nodoc:
|
22
|
+
def initialize(option)
|
23
|
+
super("Invalid constraint dependency action - #{option}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ConnectionAdapters
|
28
|
+
class PostgreSQLAdapter < AbstractAdapter
|
29
|
+
# Adds a CHECK constraint to the table. See
|
30
|
+
# PostgreSQLCheckConstraint for usage.
|
31
|
+
def add_check_constraint(table, expression, options = {})
|
32
|
+
sql = "ALTER TABLE #{quote_table_name(table)} ADD "
|
33
|
+
sql << PostgreSQLCheckConstraint.new(self, expression, options).to_s
|
34
|
+
execute sql
|
35
|
+
end
|
36
|
+
|
37
|
+
# Adds a UNIQUE constraint to the table. See
|
38
|
+
# PostgreSQLUniqueConstraint for details.
|
39
|
+
def add_unique_constraint(table, columns, options = {})
|
40
|
+
sql = "ALTER TABLE #{quote_table_name(table)} ADD "
|
41
|
+
sql << PostgreSQLUniqueConstraint.new(self, columns, options).to_s
|
42
|
+
execute sql
|
43
|
+
end
|
44
|
+
|
45
|
+
# Adds a FOREIGN KEY constraint to the table. See
|
46
|
+
# PostgreSQLForeignKeyConstraint for details.
|
47
|
+
def add_foreign_key(table, columns, ref_table, *args)
|
48
|
+
sql = "ALTER TABLE #{quote_table_name(table)} ADD "
|
49
|
+
sql << PostgreSQLForeignKeyConstraint.new(self, columns, ref_table, *args).to_s
|
50
|
+
execute sql
|
51
|
+
end
|
52
|
+
|
53
|
+
# Drops a constraint from the table. Use this to drop CHECK,
|
54
|
+
# UNIQUE and FOREIGN KEY constraints from a table.
|
55
|
+
#
|
56
|
+
# Options:
|
57
|
+
#
|
58
|
+
# * <tt>:cascade</tt> - set to true to add a CASCADE clause to
|
59
|
+
# the command.
|
60
|
+
def drop_constraint(table, name, options = {})
|
61
|
+
sql = "ALTER TABLE #{quote_table_name(table)} DROP CONSTRAINT #{quote_generic(name)}"
|
62
|
+
sql << ' CASCADE' if options[:cascade]
|
63
|
+
execute sql
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# This is a base class for other PostgreSQL constraint classes. It
|
68
|
+
# isn't really meant to be used directly.
|
69
|
+
class PostgreSQLConstraint
|
70
|
+
attr_accessor :base, :options
|
71
|
+
|
72
|
+
def initialize(base, options) #:nodoc:
|
73
|
+
@base, @options = base, options
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
DEFERRABLE_TYPES = [ 'true', 'false', 'immediate', 'deferred' ].freeze
|
78
|
+
def assert_valid_deferrable_option option
|
79
|
+
if !DEFERRABLE_TYPES.include? option.to_s.downcase
|
80
|
+
raise ActiveRecord::InvalidDeferrableOption.new(option)
|
81
|
+
end unless option.nil?
|
82
|
+
end
|
83
|
+
|
84
|
+
def deferrable
|
85
|
+
case options[:deferrable]
|
86
|
+
when true
|
87
|
+
' DEFERRABLE'
|
88
|
+
when false
|
89
|
+
' NOT DEFERRABLE'
|
90
|
+
when nil
|
91
|
+
''
|
92
|
+
else
|
93
|
+
" DEFERRABLE INITIALLY #{options[:deferrable].to_s.upcase}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def constraint_name
|
98
|
+
if options[:name]
|
99
|
+
"CONSTRAINT #{base.quote_generic(options[:name])} "
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Creates CHECK constraints for PostgreSQL tables.
|
105
|
+
#
|
106
|
+
# This class is meant to be used by PostgreSQL column and table
|
107
|
+
# definition and manipulation methods. There are several ways to create
|
108
|
+
# a CHECK constraint:
|
109
|
+
#
|
110
|
+
# * on a column definition
|
111
|
+
# * on a table definition
|
112
|
+
# * when altering a table
|
113
|
+
#
|
114
|
+
# === Column Definition
|
115
|
+
#
|
116
|
+
# When creating a new table via PostgreSQLAdapter#create_table, you
|
117
|
+
# can specify CHECK constraints on individual columns during
|
118
|
+
# definition.
|
119
|
+
#
|
120
|
+
# ==== Example
|
121
|
+
#
|
122
|
+
# ### ruby
|
123
|
+
# create_table(:foo) do |t|
|
124
|
+
# t.integer :fancy_id, :check => "fancy_id != 10"
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
# # Produces:
|
128
|
+
# #
|
129
|
+
# # CREATE TABLE "foo" (
|
130
|
+
# # "id" serial primary key,
|
131
|
+
# # "fancy_id" integer DEFAULT NULL NULL,
|
132
|
+
# # CHECK (fancy_id != 10)
|
133
|
+
# # );
|
134
|
+
#
|
135
|
+
# You can also provide an Array to <tt>:check</tt> with multiple CHECK
|
136
|
+
# constraints. Each CHECK constraint can be either a String containing
|
137
|
+
# the CHECK expression or a Hash containing <tt>:name</tt> and
|
138
|
+
# <tt>:expression</tt> values if you want to provide a specific name
|
139
|
+
# for the constraint. Otherwise, PostgreSQL will provide a name
|
140
|
+
# automatically. Thus, the following is equivalent to the example
|
141
|
+
# above:
|
142
|
+
#
|
143
|
+
# ### ruby
|
144
|
+
# create_table(:foo) do |t|
|
145
|
+
# t.integer :fancy_id, :check => [ { :expression => "fancy_id != 10" } ]
|
146
|
+
# end
|
147
|
+
#
|
148
|
+
# See below for additional options.
|
149
|
+
#
|
150
|
+
# === Table Definition
|
151
|
+
#
|
152
|
+
# CHECK constraints can also be applied to the table directly rather
|
153
|
+
# than on a column definition.
|
154
|
+
#
|
155
|
+
# ==== Examples
|
156
|
+
#
|
157
|
+
# ### ruby
|
158
|
+
# create_table(:foo) do |t|
|
159
|
+
# t.integer :fancy_id
|
160
|
+
# t.integer :another_fancy_id
|
161
|
+
# t.check_constraint 'fancy_id != another_fancy_id'
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# # Produces:
|
165
|
+
# #
|
166
|
+
# # CREATE TABLE "foo" (
|
167
|
+
# # "id" serial primary key,
|
168
|
+
# # "fancy_id" integer DEFAULT NULL NULL,
|
169
|
+
# # "another_fancy_id" integer DEFAULT NULL NULL,
|
170
|
+
# # CHECK (fancy_id != another_fancy_id)
|
171
|
+
# # );
|
172
|
+
#
|
173
|
+
# create_table(:foo) do |t|
|
174
|
+
# t.integer :fancy_id
|
175
|
+
# t.integer :another_fancy_id
|
176
|
+
# t.check_constraint 'fancy_id != another_fancy_id', :name => 'my_constraint'
|
177
|
+
# end
|
178
|
+
#
|
179
|
+
# # Produces:
|
180
|
+
# #
|
181
|
+
# # CREATE TABLE "foo" (
|
182
|
+
# # "id" serial primary key,
|
183
|
+
# # "fancy_id" integer DEFAULT NULL NULL,
|
184
|
+
# # "another_fancy_id" integer DEFAULT NULL NULL,
|
185
|
+
# # CONSTRAINT "my_constraint" CHECK (fancy_id != another_fancy_id)
|
186
|
+
# # );
|
187
|
+
#
|
188
|
+
# See below for additional options.
|
189
|
+
#
|
190
|
+
# === Table Manipulation
|
191
|
+
#
|
192
|
+
# You can also create new CHECK constraints outside of a table
|
193
|
+
# definition using PostgreSQLAdapter#add_check_constraint.
|
194
|
+
#
|
195
|
+
# ==== Example
|
196
|
+
#
|
197
|
+
# ### ruby
|
198
|
+
# add_check_constraint(:foo, 'fancy_id != 10')
|
199
|
+
#
|
200
|
+
# # Produces:
|
201
|
+
# #
|
202
|
+
# # ALTER TABLE "foo" ADD CHECK (fancy_id != 10);
|
203
|
+
#
|
204
|
+
# See below for additional options.
|
205
|
+
#
|
206
|
+
# === CHECK Constraint Options
|
207
|
+
#
|
208
|
+
# * <tt>:name</tt> - specifies a name for the constraint.
|
209
|
+
# * <tt>:expression</tt> - when creating a column definition, you can
|
210
|
+
# supply either a String containing the expression or a Hash to
|
211
|
+
# supply both <tt>:name</tt> and <tt>:expression</tt> values.
|
212
|
+
#
|
213
|
+
# === Dropping CHECK Constraints
|
214
|
+
#
|
215
|
+
# Like all PostgreSQL constraints, you can use
|
216
|
+
# PostgreSQLAdapter#drop_constraint to remove a constraint from a
|
217
|
+
# table.
|
218
|
+
class PostgreSQLCheckConstraint < PostgreSQLConstraint
|
219
|
+
attr_accessor :expression
|
220
|
+
|
221
|
+
def initialize(base, expression, options = {}) #:nodoc:
|
222
|
+
@expression = expression
|
223
|
+
super(base, options)
|
224
|
+
end
|
225
|
+
|
226
|
+
def to_sql #:nodoc:
|
227
|
+
"#{constraint_name}CHECK (#{expression})"
|
228
|
+
end
|
229
|
+
alias :to_s :to_sql
|
230
|
+
end
|
231
|
+
|
232
|
+
# Creates UNIQUE constraints for PostgreSQL tables.
|
233
|
+
#
|
234
|
+
# This class is meant to be used by PostgreSQL column and table
|
235
|
+
# definition and manipulation methods. There are several ways to use
|
236
|
+
# this class:
|
237
|
+
#
|
238
|
+
# * on a column definition
|
239
|
+
# * on a table definition
|
240
|
+
# * when altering a table
|
241
|
+
#
|
242
|
+
# In PostgreSQL, a UNIQUE constraint is really just a unique index,
|
243
|
+
# so you can alternatively add a UNIQUE constraint using the standard
|
244
|
+
# ActiveRecord add_index method with the <tt>:unique</tt> option. You
|
245
|
+
# can also use our expanded PostgreSQLAdapter#create_index method,
|
246
|
+
# which adds additional PostgreSQL-specific options. See the
|
247
|
+
# PostgreSQLIndexDefinition class for details on these extra options.
|
248
|
+
#
|
249
|
+
# === Column Definition
|
250
|
+
#
|
251
|
+
# When creating a new table via PostgreSQLAdapter#create_table, you
|
252
|
+
# can specify UNIQUE constraints on individual columns during
|
253
|
+
# definition.
|
254
|
+
#
|
255
|
+
# ==== Example:
|
256
|
+
#
|
257
|
+
# ### ruby
|
258
|
+
# create_table(:foo) do |t|
|
259
|
+
# t.integer :fancy_id, :unique => true
|
260
|
+
# end
|
261
|
+
#
|
262
|
+
# # Produces:
|
263
|
+
# #
|
264
|
+
# # CREATE TABLE "foo" (
|
265
|
+
# # "id" serial primary key,
|
266
|
+
# # "fancy_id" integer DEFAULT NULL NULL,
|
267
|
+
# # UNIQUE ("fancy_id")
|
268
|
+
# # );
|
269
|
+
#
|
270
|
+
# You can provide additional options to the UNIQUE constraint by
|
271
|
+
# passing a Hash instead of true. See below for details on these
|
272
|
+
# additional options.
|
273
|
+
#
|
274
|
+
# === Table Definition
|
275
|
+
#
|
276
|
+
# UNIQUE constraints can also be applied to the table directly rather
|
277
|
+
# than on a column definition. This is useful when you want to add
|
278
|
+
# multiple columns to the constraint.
|
279
|
+
#
|
280
|
+
# ==== Example:
|
281
|
+
#
|
282
|
+
# ### ruby
|
283
|
+
# create_table(:foo) do |t|
|
284
|
+
# t.integer :fancy_id
|
285
|
+
# t.integer :another_fancy_id
|
286
|
+
# t.unique_constraint [ :fancy_id, :another_fancy_id ]
|
287
|
+
# end
|
288
|
+
#
|
289
|
+
# # Produces:
|
290
|
+
# #
|
291
|
+
# # CREATE TABLE "foo" (
|
292
|
+
# # "id" serial primary key,
|
293
|
+
# # "fancy_id" integer DEFAULT NULL NULL,
|
294
|
+
# # "another_fancy_id" integer DEFAULT NULL NULL,
|
295
|
+
# # UNIQUE ("fancy_id", "another_fancy_id")
|
296
|
+
# # );
|
297
|
+
#
|
298
|
+
# See below for additional options.
|
299
|
+
#
|
300
|
+
# === Table Manipulation
|
301
|
+
#
|
302
|
+
# You can also create new UNIQUE constraints outside of a table
|
303
|
+
# definition using the standard ActiveRecord add_index method.
|
304
|
+
# You can also use our custom add_unique_constraint which adds a couple
|
305
|
+
# of PostgreSQL-specific options.
|
306
|
+
#
|
307
|
+
# Additionally, since UNIQUE constraints in PostgreSQL are really just
|
308
|
+
# unique indexes, you can also use the the standard ActiveRecord
|
309
|
+
# add_index method with the :unique option or our custom
|
310
|
+
# PostgreSQLAdapter#create_index method similarly. The create_index
|
311
|
+
# method adds a couple of PostgreSQL-specific options if you need them.
|
312
|
+
#
|
313
|
+
# ==== Examples:
|
314
|
+
#
|
315
|
+
# ### ruby
|
316
|
+
# # using the constraint method:
|
317
|
+
# add_unique_constraint(:foo, [ :fancy_id, :another_fancy_id ])
|
318
|
+
# # => ALTER TABLE "foo" ADD UNIQUE ("fancy_id", "another_fancy_id");
|
319
|
+
#
|
320
|
+
# # using create_index:
|
321
|
+
# create_index('my_index_name', :foo, [ :fancy_id, :another_fancy_id ], :unique => true)
|
322
|
+
# # => CREATE UNIQUE INDEX "my_index_name" ON "foo"("fancy_id", "another_fancy_id");
|
323
|
+
#
|
324
|
+
# # using the standard ActiveRecord add_index:
|
325
|
+
# add_index(:foo, [ :fancy_id, :another_fancy_id ], :unique => true)
|
326
|
+
# # => CREATE UNIQUE INDEX "index_foo_on_fancy_id_and_another_fancy_id" ON "foo" ("fancy_id", "another_fancy_id");
|
327
|
+
#
|
328
|
+
# You'll notice that in create_index we must manually supply a name
|
329
|
+
# while add_index can generate one for us automatically. See the
|
330
|
+
# create_index documentation for details as to why this mysterious
|
331
|
+
# departure from the standard ActiveRecord method is necessary.
|
332
|
+
#
|
333
|
+
# === Options for UNIQUE Constraints
|
334
|
+
#
|
335
|
+
# When creating UNIQUE constraints using a column or table definition
|
336
|
+
# or when using add_unique_constraint, there are a hanful of
|
337
|
+
# PostgreSQL-specific options that you may find useful.
|
338
|
+
#
|
339
|
+
# * <tt>:name</tt> - specify a name for the index created by the
|
340
|
+
# UNIQUE constraint.
|
341
|
+
# * <tt>:storage_parameters</tt> - PostgreSQL allows you to add a
|
342
|
+
# couple of additional parameters to indexes to govern disk usage and
|
343
|
+
# such. This option is a simple String that lets you insert these
|
344
|
+
# options as necessary. See the PostgreSQL documentation on index
|
345
|
+
# storage parameters for details.
|
346
|
+
# * <tt>:tablespace</tt> - allows you to specify a tablespace for the
|
347
|
+
# unique index being created. See the PostgreSQL documentation on
|
348
|
+
# tablespaces for details.
|
349
|
+
#
|
350
|
+
# === Dropping UNIQUE Constraints
|
351
|
+
#
|
352
|
+
# Like all PostgreSQL constraints, you can use drop_constraint to
|
353
|
+
# remove a constraint from a table. Since a UNIQUE constraint is really
|
354
|
+
# just a unique index in PostgreSQL, you can also use the standard
|
355
|
+
# ActiveRecord remove_index method or our custom
|
356
|
+
# PostgreSQLAdapter#drop_index method.
|
357
|
+
#
|
358
|
+
# With drop_index, you can provide a couple of PostgreSQL-specific
|
359
|
+
# options, which may be useful in some situations. See the
|
360
|
+
# documentation for PostgreSQLAdapter#drop_index for details.
|
361
|
+
class PostgreSQLUniqueConstraint < PostgreSQLConstraint
|
362
|
+
attr_accessor :columns
|
363
|
+
|
364
|
+
def initialize(base, columns, options = {}) #:nodoc:
|
365
|
+
@columns = columns
|
366
|
+
super(base, options)
|
367
|
+
end
|
368
|
+
|
369
|
+
def to_sql #:nodoc:
|
370
|
+
sql = "#{constraint_name}UNIQUE ("
|
371
|
+
sql << Array(columns).collect { |c| base.quote_column_name(c) }.join(', ')
|
372
|
+
sql << ")"
|
373
|
+
sql << " WITH (#{options[:storage_parameters]})" if options[:storage_parameters]
|
374
|
+
sql << " USING INDEX TABLESPACE #{base.quote_tablespace(options[:tablespace])}" if options[:tablespace]
|
375
|
+
sql
|
376
|
+
end
|
377
|
+
alias :to_s :to_sql
|
378
|
+
end
|
379
|
+
|
380
|
+
# Creates FOREIGN KEY constraints for PostgreSQL tables and columns.
|
381
|
+
#
|
382
|
+
# This class is meant to be used by PostgreSQL column and table
|
383
|
+
# definition and manipulation methods. There are several ways to create
|
384
|
+
# a FOREIGN KEY constraint:
|
385
|
+
#
|
386
|
+
# * on a column definition
|
387
|
+
# * on a table definition
|
388
|
+
# * when altering a table
|
389
|
+
#
|
390
|
+
# === Column Definition
|
391
|
+
#
|
392
|
+
# When creating a new table via PostgreSQLAdapter#create_table, you
|
393
|
+
# can specify FOREIGN KEY constraints on individual columns during
|
394
|
+
# definition.
|
395
|
+
#
|
396
|
+
# ==== Example:
|
397
|
+
#
|
398
|
+
# ### ruby
|
399
|
+
# create_table(:foo) do |t|
|
400
|
+
# t.integer :bar_id, :references => { :table => :bar, :column => :id }
|
401
|
+
# end
|
402
|
+
#
|
403
|
+
# # Produces:
|
404
|
+
# #
|
405
|
+
# # CREATE TABLE "foo" (
|
406
|
+
# # "id" serial primary key,
|
407
|
+
# # "bar_id" integer DEFAULT NULL NULL,
|
408
|
+
# # FOREIGN KEY ("bar_id") REFERENCES "bar" ("id")
|
409
|
+
# # );
|
410
|
+
#
|
411
|
+
# You can leave out the :column option if you are following the Rails
|
412
|
+
# standards for foreign key referral, as PostgreSQL automatically
|
413
|
+
# assumes that it should be looking for a "column_name_id"-style
|
414
|
+
# column when creating references. Alternatively, you can simply
|
415
|
+
# specify <tt>:references => :bar</tt> if you don't need to add any
|
416
|
+
# additional options.
|
417
|
+
#
|
418
|
+
# See below for additional options for the <tt>:references</tt> Hash.
|
419
|
+
#
|
420
|
+
# === Table Definition
|
421
|
+
#
|
422
|
+
# FOREIGN KEY constraints can also be applied to the table directly
|
423
|
+
# rather than on a column definition.
|
424
|
+
#
|
425
|
+
# ==== Example:
|
426
|
+
#
|
427
|
+
# The following example produces the same result as above:
|
428
|
+
#
|
429
|
+
# ### ruby
|
430
|
+
# create_table(:foo) do |t|
|
431
|
+
# t.integer :bar_id
|
432
|
+
# t.foreign_key :bar_id, :bar, :id
|
433
|
+
# end
|
434
|
+
#
|
435
|
+
# # Produces:
|
436
|
+
# #
|
437
|
+
# # CREATE TABLE "foo" (
|
438
|
+
# # "id" serial primary key,
|
439
|
+
# # "bar_id" integer DEFAULT NULL NULL,
|
440
|
+
# # FOREIGN KEY ("bar_id") REFERENCES "bar" ("id")
|
441
|
+
# # );
|
442
|
+
#
|
443
|
+
# Defining a FOREIGN KEY constraint on the table-level allows you to
|
444
|
+
# create multicolumn foreign keys. You can define these super advanced
|
445
|
+
# foreign keys thusly:
|
446
|
+
#
|
447
|
+
# ### ruby
|
448
|
+
# create_table(:foo) {}
|
449
|
+
#
|
450
|
+
# create_table(:bar) do |t|
|
451
|
+
# t.integer :foo_id
|
452
|
+
# t.unique_constraint [ :id, :foo_id ]
|
453
|
+
# end
|
454
|
+
#
|
455
|
+
# create_table(:funk) do |t|
|
456
|
+
# t.integer :bar_id
|
457
|
+
# t.foreign_key [ :id, :bar_id ], :bar, [ :id, :foo_id ]
|
458
|
+
# end
|
459
|
+
#
|
460
|
+
# # Produces:
|
461
|
+
# #
|
462
|
+
# # CREATE TABLE "foo" (
|
463
|
+
# # "id" serial primary key
|
464
|
+
# # );
|
465
|
+
# #
|
466
|
+
# # CREATE TABLE "bar" (
|
467
|
+
# # "id" serial primary key,
|
468
|
+
# # "foo_id" integer DEFAULT NULL NULL,
|
469
|
+
# # UNIQUE ("id", "foo_id")
|
470
|
+
# # );
|
471
|
+
# #
|
472
|
+
# # CREATE TABLE "funk" (
|
473
|
+
# # "id" serial primary key,
|
474
|
+
# # "bar_id" integer DEFAULT NULL NULL,
|
475
|
+
# # FOREIGN KEY ("id", "bar_id") REFERENCES "bar" ("id", "foo_id")
|
476
|
+
# # );
|
477
|
+
#
|
478
|
+
# === Table Manipulation
|
479
|
+
#
|
480
|
+
# You can also create new FOREIGN KEY constraints outside of a table
|
481
|
+
# definition using PostgreSQLAdapter#add_foreign_key.
|
482
|
+
#
|
483
|
+
# ==== Examples:
|
484
|
+
#
|
485
|
+
# ### ruby
|
486
|
+
# add_foreign_key(:foo, :bar_id, :bar)
|
487
|
+
# # => ALTER TABLE "funk" ADD FOREIGN KEY ("bar_id") REFERENCES "bar";
|
488
|
+
#
|
489
|
+
# add_foreign_key(:foo, :bar_id, :bar, :id)
|
490
|
+
# # => ALTER TABLE "funk" ADD FOREIGN KEY ("bar_id") REFERENCES "bar"("id");
|
491
|
+
#
|
492
|
+
# add_foreign_key(:foo, [ :bar_id, :blort_id ], :bar, [ :id, :blort_id ],
|
493
|
+
# :name => 'my_fk', :match => :simple
|
494
|
+
# )
|
495
|
+
# # => ALTER TABLE "foo" ADD CONSTRAINT "my_fk" FOREIGN KEY ("id", "blort_id")
|
496
|
+
# # REFERENCES "bar" ("id", "blort_id") MATCH SIMPLE;
|
497
|
+
#
|
498
|
+
# === Options for FOREIGN KEY Constraints
|
499
|
+
#
|
500
|
+
# * <tt>:deferrable</tt> - sets whether or not the foreign key
|
501
|
+
# constraint check is deferrable during transactions. This value can
|
502
|
+
# be true for DEFERRABLE, false for NOT DEFERRABLE or a String/Symbol
|
503
|
+
# where you can set either <tt>:immediate</tt> or <tt>:deferred</tt>.
|
504
|
+
# * <tt>:name</tt> - sets the name of the constraint.
|
505
|
+
# * <tt>:match</tt> - sets how multicolumn foreign keys are matched
|
506
|
+
# against their referenced columns. This value can be <tt>:full</tt>
|
507
|
+
# or <tt>:simple</tt>, with PostgreSQL's default being
|
508
|
+
# <tt>:full</tt>.
|
509
|
+
# * <tt>:on_delete</tt> and <tt>:on_update</tt> - set the action to
|
510
|
+
# take when the referenced value is updated or deleted. Possible
|
511
|
+
# values are <tt>:no_action</tt>, <tt>:restrict</tt>,
|
512
|
+
# <tt>:cascade</tt>, <tt>:set_null</tt> and <tt>:set_default</tt>.
|
513
|
+
# PostgreSQL's default is <tt>:no_action</tt>.
|
514
|
+
#
|
515
|
+
# See the PostgreSQL documentation on foreign keys for details about
|
516
|
+
# the <tt>:deferrable</tt>, <tt>:match</tt>, <tt>:on_delete</tt>
|
517
|
+
# and <tt>:on_update</tt> options.
|
518
|
+
#
|
519
|
+
# === Dropping CHECK Constraints
|
520
|
+
#
|
521
|
+
# Like all PostgreSQL constraints, you can use
|
522
|
+
# PostgreSQLAdapter#drop_constraint to remove a constraint from a
|
523
|
+
# table.
|
524
|
+
class PostgreSQLForeignKeyConstraint < PostgreSQLConstraint
|
525
|
+
attr_accessor :columns, :ref_table, :ref_columns
|
526
|
+
|
527
|
+
def initialize(base, columns, ref_table, *args) #:nodoc:
|
528
|
+
options = args.extract_options!
|
529
|
+
ref_columns = args[0] unless args.empty?
|
530
|
+
|
531
|
+
assert_valid_match_type(options[:match]) if options[:match]
|
532
|
+
assert_valid_action(options[:on_delete]) if options[:on_delete]
|
533
|
+
assert_valid_action(options[:on_update]) if options[:on_update]
|
534
|
+
assert_valid_deferrable_option(options[:deferrable])
|
535
|
+
@columns, @ref_table, @ref_columns = columns, ref_table, ref_columns
|
536
|
+
@schema = base.current_schema
|
537
|
+
super(base, options)
|
538
|
+
end
|
539
|
+
|
540
|
+
def to_sql #:nodoc:
|
541
|
+
sql = String.new
|
542
|
+
base.with_schema(@schema) do
|
543
|
+
table = if ref_table.respond_to?(:join)
|
544
|
+
ref_table.join
|
545
|
+
else
|
546
|
+
ref_table
|
547
|
+
end
|
548
|
+
|
549
|
+
sql << "#{constraint_name}FOREIGN KEY ("
|
550
|
+
sql << Array(columns).collect { |c| base.quote_column_name(c) }.join(', ')
|
551
|
+
sql << ") REFERENCES #{base.quote_table_name(table)}"
|
552
|
+
sql << ' (%s)' % Array(ref_columns).collect { |c| base.quote_column_name(c) }.join(', ') if ref_columns
|
553
|
+
sql << " MATCH #{options[:match].to_s.upcase}" if options[:match]
|
554
|
+
sql << " ON DELETE #{options[:on_delete].to_s.gsub(/_/, ' ').upcase}" if options[:on_delete]
|
555
|
+
sql << " ON UPDATE #{options[:on_update].to_s.gsub(/_/, ' ').upcase}" if options[:on_update]
|
556
|
+
sql << deferrable
|
557
|
+
end
|
558
|
+
sql
|
559
|
+
end
|
560
|
+
alias :to_s :to_sql
|
561
|
+
|
562
|
+
private
|
563
|
+
MATCH_TYPES = %w{ full simple }.freeze
|
564
|
+
ACTION_TYPES = %w{ no_action restrict cascade set_null set_default }.freeze
|
565
|
+
|
566
|
+
def assert_valid_match_type(type) #:nodoc:
|
567
|
+
if !MATCH_TYPES.include? type.to_s
|
568
|
+
raise ActiveRecord::InvalidMatchType.new(type)
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
def assert_valid_action(type) #:nodoc:
|
573
|
+
if !ACTION_TYPES.include? type.to_s
|
574
|
+
raise ActiveRecord::InvalidForeignKeyAction.new(type)
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
end
|
579
|
+
end
|