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.
Files changed (34) hide show
  1. data/MIT-LICENSE +23 -0
  2. data/README.rdoc +32 -0
  3. data/Rakefile +42 -0
  4. data/VERSION +1 -0
  5. data/lib/activerecord-postgresql-extensions.rb +30 -0
  6. data/lib/postgresql_extensions/foreign_key_associations.rb +367 -0
  7. data/lib/postgresql_extensions/postgresql_adapter_extensions.rb +646 -0
  8. data/lib/postgresql_extensions/postgresql_constraints.rb +579 -0
  9. data/lib/postgresql_extensions/postgresql_functions.rb +345 -0
  10. data/lib/postgresql_extensions/postgresql_geometry.rb +212 -0
  11. data/lib/postgresql_extensions/postgresql_indexes.rb +219 -0
  12. data/lib/postgresql_extensions/postgresql_languages.rb +80 -0
  13. data/lib/postgresql_extensions/postgresql_permissions.rb +322 -0
  14. data/lib/postgresql_extensions/postgresql_rules.rb +112 -0
  15. data/lib/postgresql_extensions/postgresql_schemas.rb +49 -0
  16. data/lib/postgresql_extensions/postgresql_sequences.rb +222 -0
  17. data/lib/postgresql_extensions/postgresql_tables.rb +308 -0
  18. data/lib/postgresql_extensions/postgresql_triggers.rb +131 -0
  19. data/lib/postgresql_extensions/postgresql_types.rb +17 -0
  20. data/lib/postgresql_extensions/postgresql_views.rb +103 -0
  21. data/postgresql-extensions.gemspec +50 -0
  22. data/test/adapter_test.rb +45 -0
  23. data/test/constraints_test.rb +98 -0
  24. data/test/functions_test.rb +112 -0
  25. data/test/geometry_test.rb +43 -0
  26. data/test/index_test.rb +68 -0
  27. data/test/languages_test.rb +48 -0
  28. data/test/permissions_test.rb +163 -0
  29. data/test/rules_test.rb +32 -0
  30. data/test/schemas_test.rb +43 -0
  31. data/test/sequences_test.rb +90 -0
  32. data/test/tables_test.rb +49 -0
  33. data/test/test_helper.rb +64 -0
  34. 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