activerecord-postgresql-extensions 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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