activerecord-postgresql-extensions 0.2.2 → 0.3.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.
Files changed (54) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +4 -0
  3. data/Gemfile +1 -0
  4. data/Guardfile +3 -3
  5. data/MIT-LICENSE +1 -1
  6. data/README.rdoc +10 -3
  7. data/lib/active_record/postgresql_extensions/adapter_extensions.rb +100 -60
  8. data/lib/active_record/postgresql_extensions/constraints.rb +13 -17
  9. data/lib/active_record/postgresql_extensions/event_triggers.rb +129 -0
  10. data/lib/active_record/postgresql_extensions/extensions.rb +14 -15
  11. data/lib/active_record/postgresql_extensions/features.rb +80 -41
  12. data/lib/active_record/postgresql_extensions/functions.rb +1 -1
  13. data/lib/active_record/postgresql_extensions/geometry.rb +6 -8
  14. data/lib/active_record/postgresql_extensions/indexes.rb +19 -11
  15. data/lib/active_record/postgresql_extensions/languages.rb +1 -1
  16. data/lib/active_record/postgresql_extensions/materialized_views.rb +272 -0
  17. data/lib/active_record/postgresql_extensions/permissions.rb +60 -22
  18. data/lib/active_record/postgresql_extensions/roles.rb +18 -7
  19. data/lib/active_record/postgresql_extensions/rules.rb +5 -0
  20. data/lib/active_record/postgresql_extensions/schemas.rb +39 -3
  21. data/lib/active_record/postgresql_extensions/sequences.rb +6 -3
  22. data/lib/active_record/postgresql_extensions/tables.rb +47 -19
  23. data/lib/active_record/postgresql_extensions/tablespaces.rb +1 -1
  24. data/lib/active_record/postgresql_extensions/text_search.rb +3 -3
  25. data/lib/active_record/postgresql_extensions/triggers.rb +3 -3
  26. data/lib/active_record/postgresql_extensions/types.rb +104 -1
  27. data/lib/active_record/postgresql_extensions/utils.rb +35 -13
  28. data/lib/active_record/postgresql_extensions/vacuum.rb +1 -1
  29. data/lib/active_record/postgresql_extensions/version.rb +1 -1
  30. data/lib/active_record/postgresql_extensions/views.rb +137 -6
  31. data/lib/activerecord-postgresql-extensions.rb +13 -11
  32. data/test/{adapter_tests.rb → adapter_extensions_tests.rb} +96 -3
  33. data/test/constraints_tests.rb +216 -104
  34. data/test/event_triggers_tests.rb +109 -0
  35. data/test/extensions_tests.rb +47 -39
  36. data/test/functions_tests.rb +47 -38
  37. data/test/geometry_tests.rb +268 -135
  38. data/test/{index_tests.rb → indexes_tests.rb} +16 -16
  39. data/test/languages_tests.rb +26 -9
  40. data/test/materialized_views_tests.rb +174 -0
  41. data/test/permissions_tests.rb +159 -45
  42. data/test/roles_tests.rb +17 -7
  43. data/test/rules_tests.rb +14 -6
  44. data/test/schemas_tests.rb +35 -9
  45. data/test/sequences_tests.rb +9 -11
  46. data/test/tables_tests.rb +132 -42
  47. data/test/tablespace_tests.rb +21 -15
  48. data/test/test_helper.rb +56 -10
  49. data/test/text_search_tests.rb +42 -44
  50. data/test/trigger_tests.rb +1 -3
  51. data/test/types_tests.rb +95 -0
  52. data/test/vacuum_tests.rb +1 -3
  53. data/test/views_tests.rb +203 -0
  54. metadata +22 -16
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZDVjMzkzNjNhYzc2MzQxNDkyZTA3MWQzM2FlZGZlZmQzYzkxMDkxNA==
5
+ data.tar.gz: !binary |-
6
+ NjMzOTZjOTFkOGE3NTQyMmU1ODg2NzJhNmQ3MzQ3NjgwNTRkNzZhNQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MzEzN2MzMjg4YjYzZWI5NjIyNWJmNmNjNjc1NmZiMjNkYjZmODM5MjUyN2Iw
10
+ NTJjYzBkYzMxZTZjYTk3OTE2ODZhZDZmZjc5YmUxNzY2YmIxMDdjZjRiMzk1
11
+ YjRjZjk0NTkxMDdjN2Q5MTRkYmM3YmMxMWU4MDhkZGFhMWMxMDA=
12
+ data.tar.gz: !binary |-
13
+ ZTUyOGIwZTc5MjAwMDRlZGE4ZTI0MzI5OTk1YTM1NjM3MTVkZGJlYjA2MTA0
14
+ MzczY2U4NTMwNDdhZTU2MmJhNTJmMjBlYjFmZDU1NzExMDI2ZjRhZmVhNzAz
15
+ YWNjYmE1NWU1MzkxMDk3ZDMwYzBiMzNkZGNhZjUyMTg5OTAzN2I=
data/.gitignore CHANGED
@@ -1,3 +1,6 @@
1
+ .*.swp
2
+ .*.swo
3
+ *.tmp
1
4
  *.kpf
2
5
  *.komodoproject
3
6
  *~
@@ -5,6 +8,7 @@
5
8
  *tmp*
6
9
  pkg/
7
10
  doc/
11
+ coverage/
8
12
  *.orig
9
13
  *.patch
10
14
  .vimrc_local
data/Gemfile CHANGED
@@ -13,6 +13,7 @@ gem "rake", "~> 10.0"
13
13
  gem "minitest"
14
14
  gem "minitest-reporters"
15
15
  gem "guard-minitest"
16
+ gem "simplecov"
16
17
 
17
18
  if RbConfig::CONFIG['host_os'] =~ /^darwin/
18
19
  gem "rb-fsevent"
data/Guardfile CHANGED
@@ -1,9 +1,9 @@
1
1
 
2
- guard 'minitest', :test_folders => 'test', :test_file_patterns => '*_tests.rb' do
2
+ guard 'minitest', :test_folders => 'test', :test_file_patterns => '*_tests.rb', :all_on_start => false do
3
3
  watch(%r|^test/(.+)_tests\.rb|)
4
4
 
5
- watch(%r|^lib/(.*)([^/]+)\.rb|) do |m|
6
- "test/#{m[1]}#{m[2]}_tests.rb"
5
+ watch(%r|^lib/(.*/)?(.+)\.rb|) do |m|
6
+ "test/#{m[2]}_tests.rb"
7
7
  end
8
8
 
9
9
  watch(%r|^test/test_helper\.rb|) do
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 2167961 Ontario Inc., Zoocasa <code@zoocasa.com>
1
+ Copyright (c) 2013 2167961 Ontario Inc., Zoocasa <code@zoocasa.com>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person
4
4
  obtaining a copy of this software and associated documentation
data/README.rdoc CHANGED
@@ -9,7 +9,8 @@ PostgreSQL adapter including:
9
9
  and per table.
10
10
 
11
11
  * some methods for manipulating all sorts of stuff like procedural languages,
12
- functions, sequences, views, schemas and triggers.
12
+ functions, sequences, views, schemas, triggers, types, extensions,
13
+ materialized views and event triggers.
13
14
 
14
15
  * better support for creating indexes with expressions so you can use functions
15
16
  and GiST and GIN indexes.
@@ -26,8 +27,8 @@ PostgreSQL adapter including:
26
27
 
27
28
  * support for some PostGIS functionality including some support for using
28
29
  PostGIS columns in migrations. For manipulating PostGIS data, see our
29
- geos-extensions gem at https://github.com/zoocasa/geos-extensions or
30
- the RGeo project at https://github.com/dazuma/rgeo .
30
+ activerecord-spatial gem at https://github.com/zoocasa/activerecord-spatial
31
+ or the RGeo project at https://github.com/dazuma/rgeo .
31
32
 
32
33
  == Building db/schema.rb
33
34
 
@@ -46,3 +47,9 @@ containing the database schema in db/development_structure.sql.
46
47
  Details on this configuration option can be found here:
47
48
 
48
49
  http://guides.rubyonrails.org/configuring.html
50
+
51
+ == License
52
+
53
+ This gem is licensed under an MIT-style license. See the +MIT-LICENSE+ file for
54
+ details.
55
+
@@ -1,39 +1,10 @@
1
1
 
2
2
  module ActiveRecord
3
+ class InvalidCopyFromOptions < ActiveRecordError #:nodoc:
4
+ end
5
+
3
6
  module ConnectionAdapters
4
7
  class PostgreSQLAdapter
5
- if defined?(Rails)
6
- LOGGER_REGEXP = /^#{Rails.root}(?!\/vendor\/rails)/
7
-
8
- def query_with_extra_logging(*args) #:nodoc:
9
- if ActiveRecord::Base.enable_extended_logging && Rails.logger && Rails.logger.level == Logger::DEBUG
10
- sql = args.first
11
- unless (sql =~ /(pg_get_constraintdef|pg_attribute|pg_class)/)
12
- Rails.logger.debug
13
- Rails.logger.debug(caller.select { |x|
14
- ActiveRecord::Base.enable_really_extended_logging || x.match(LOGGER_REGEXP)
15
- }.join("\n"))
16
- end
17
- end
18
- query_without_extra_logging(*args)
19
- end
20
- alias_method_chain :query, :extra_logging
21
-
22
- def execute_with_extra_logging(*args) #:nodoc:
23
- if ActiveRecord::Base.enable_extended_logging && Rails.logger && Rails.logger.level == Logger::DEBUG
24
- sql = args.first
25
- unless (sql =~ /(pg_get_constraintdef|pg_attribute|pg_class)/)
26
- Rails.logger.debug
27
- Rails.logger.debug(caller.select { |x|
28
- ActiveRecord::Base.enable_really_extended_logging || x.match(LOGGER_REGEXP)
29
- }.join("\n"))
30
- end
31
- end
32
- execute_without_extra_logging(*args)
33
- end
34
- alias_method_chain :execute, :extra_logging
35
- end
36
-
37
8
  # with_schema is kind of like with_scope. It wraps various
38
9
  # object names in SQL statements into a PostgreSQL schema. You
39
10
  # can have multiple with_schemas wrapped around each other, and
@@ -301,16 +272,27 @@ module ActiveRecord
301
272
  # * <tt>:not_null</tt> - allows you to specify one or more columns
302
273
  # to be inserted with a default value rather than NULL for any
303
274
  # missing values.
275
+ # * <tt>:freeze</tt> - a performance enhancement added in PostgreSQL 9.3.
276
+ # See the PostgreSQL documentation for details.
277
+ # * <tt>:encoding</tt> - set the encoding of the input. Available in
278
+ # PostgreSQL 9.1+.
304
279
  #
305
- # ==== Local Server Files vs. Local Client Files
280
+ # ==== Local Server Files vs. Local Client Files vs. PROGRAM
306
281
  #
307
- # The copy_from_file method allows you to import rows from a file
282
+ # The copy_from method allows you to import rows from a file
308
283
  # that exists on either your client's file system or on the
309
284
  # database server's file system using the <tt>:local</tt> option.
310
285
  #
286
+ # PostgreSQL 9.3 additionally introduced the PROGRAM option to COPY
287
+ # FROM that allows you to pipe the output of a shell command to
288
+ # STDIN. This option requires that the COPY FROM command be run from on
289
+ # the server and as such may be limited by server restrictions such as
290
+ # access controls and permissions.
291
+ #
311
292
  # To process a file on the remote database server's file system:
312
293
  #
313
- # * the file must be given as an absolute path;
294
+ # * the file must be given as an absolute path or as a valid shell
295
+ # command if using the PROGRAM option;
314
296
  # * must be readable by the user that the actual PostgreSQL
315
297
  # database server runs under; and
316
298
  # * the COPY FROM command itself can only be performed by database
@@ -336,27 +318,33 @@ module ActiveRecord
336
318
  # exists on the file system accessible to the database server,
337
319
  # something that you may not even have access to in the first
338
320
  # place.
339
- def copy_from_file(table_name, file, options = {})
321
+ def copy_from(table_name, file, options = {})
340
322
  options = {
341
323
  :local => true
342
324
  }.merge(options)
343
325
 
344
- sql = "COPY #{quote_table_name(table_name)} "
326
+ assert_valid_copy_from_options(options)
327
+
328
+ sql = "COPY #{quote_table_name(table_name)}"
345
329
 
346
330
  unless options[:columns].blank?
347
- sql << '(' << Array(options[:columns]).collect { |c| quote_column_name(c) }.join(', ') << ')'
331
+ sql << ' (' << Array.wrap(options[:columns]).collect { |c| quote_column_name(c) }.join(', ') << ')'
348
332
  end
349
333
 
350
- if options[:local]
334
+ if options[:program]
335
+ sql << " FROM PROGRAM #{quote(file)}"
336
+ elsif options[:local]
351
337
  sql << " FROM STDIN"
352
338
  else
353
339
  sql << " FROM #{quote(file)}"
354
340
  end
355
341
 
342
+ sql << ' FREEZE' if options[:freeze]
356
343
  sql << ' BINARY' if options[:binary]
357
344
  sql << ' OIDS' if options[:oids]
358
345
  sql << " DELIMITER AS #{quote(options[:delimiter])}" if options[:delimiter]
359
346
  sql << " NULL AS #{quote(options[:null_as])}" if options[:null]
347
+ sql << " ENCODING #{quote(options[:encoding])}" if options[:encoding]
360
348
 
361
349
  if options[:csv]
362
350
  sql << ' CSV'
@@ -364,25 +352,39 @@ module ActiveRecord
364
352
  sql << ' HEADER' if options[:csv][:header]
365
353
  sql << " QUOTE AS #{quote(options[:csv][:quote])}" if options[:csv][:quote]
366
354
  sql << " ESCAPE AS #{quote(options[:csv][:escape])}" if options[:csv][:escape]
367
- sql << ' FORCE NOT NULL ' << Array(options[:csv][:not_null]).collect do |c|
355
+ sql << ' FORCE NOT NULL ' << Array.wrap(options[:csv][:not_null]).collect do |c|
368
356
  quote_column_name(c)
369
357
  end.join(', ') if options[:csv][:not_null]
370
358
  end
371
359
  end
372
360
 
373
- execute sql
361
+ sql << ';'
362
+
363
+ if options[:program] || !options[:local]
364
+ execute sql
365
+ else
366
+ fp = File.open(file, 'r')
374
367
 
375
- if options[:local]
376
- File.open(file, 'r').each do |l|
377
- self.raw_connection.put_copy_data(l)
368
+ if self.raw_connection.respond_to?(:copy_data)
369
+ self.raw_connection.copy_data(sql) do
370
+ fp.each do |l|
371
+ self.raw_connection.put_copy_data(l)
372
+ end
373
+ end
374
+ else
375
+ execute sql
376
+ fp.each do |l|
377
+ self.raw_connection.put_copy_data(l)
378
+ end
379
+ self.raw_connection.put_copy_end
378
380
  end
379
- self.raw_connection.put_copy_end
380
381
  end
381
382
  end
383
+ alias :copy_from_file :copy_from
382
384
 
383
385
  # Returns an Array of database views.
384
386
  def views(name = nil)
385
- query(<<-SQL, name).map { |row| row[0] }
387
+ query(PostgreSQLExtensions::Utils.strip_heredoc(<<-SQL), name).map { |row| row[0] }
386
388
  SELECT viewname
387
389
  FROM pg_views
388
390
  WHERE schemaname = ANY (current_schemas(false))
@@ -403,7 +405,7 @@ module ActiveRecord
403
405
  schema = nil
404
406
  end
405
407
 
406
- query(<<-SQL).first[0].to_i > 0
408
+ query(PostgreSQLExtensions::Utils.strip_heredoc(<<-SQL)).first[0].to_i > 0
407
409
  SELECT COUNT(*)
408
410
  FROM pg_views
409
411
  WHERE viewname = '#{view.gsub(/(^"|"$)/,'')}'
@@ -412,7 +414,7 @@ module ActiveRecord
412
414
  end
413
415
 
414
416
  def roles(name = nil)
415
- query(<<-SQL, name).map { |row| row[0] }
417
+ query(PostgreSQLExtensions::Utils.strip_heredoc(<<-SQL), name).map { |row| row[0] }
416
418
  SELECT rolname
417
419
  FROM pg_roles
418
420
  SQL
@@ -450,7 +452,7 @@ module ActiveRecord
450
452
 
451
453
  # Returns an Array of tables to ignore.
452
454
  def ignored_tables(name = nil)
453
- query(<<-SQL, name).map { |row| row[0] }
455
+ query(PostgreSQLExtensions::Utils.strip_heredoc(<<-SQL), name).map { |row| row[0] }
454
456
  SELECT tablename
455
457
  FROM pg_tables
456
458
  WHERE schemaname IN ('pg_catalog');
@@ -506,7 +508,7 @@ module ActiveRecord
506
508
  'ALL'
507
509
  end
508
510
 
509
- Array(triggers).each do |trigger|
511
+ Array.wrap(triggers).each do |trigger|
510
512
  execute("ALTER TABLE #{quoted_table_name} ENABLE TRIGGER #{trigger};")
511
513
  end
512
514
  end
@@ -523,7 +525,7 @@ module ActiveRecord
523
525
  'ALL'
524
526
  end
525
527
 
526
- Array(triggers).each do |trigger|
528
+ Array.wrap(triggers).each do |trigger|
527
529
  execute("ALTER TABLE #{quoted_table_name} DISABLE TRIGGER #{trigger};")
528
530
  end
529
531
  end
@@ -542,7 +544,7 @@ module ActiveRecord
542
544
  # contains the table being referenced, the foreign key and the
543
545
  # name of the column in the referenced table.
544
546
  def foreign_keys(table_name, name = nil)
545
- sql = <<-SQL
547
+ sql = PostgreSQLExtensions::Utils.strip_heredoc(<<-SQL)
546
548
  SELECT
547
549
  confrelid::regclass AS referenced_table_name,
548
550
  a.attname AS foreign_key,
@@ -600,7 +602,7 @@ module ActiveRecord
600
602
  # particular Array contains the referencing table, the foreign key
601
603
  # and the name of the column in the referenced table.
602
604
  def referenced_foreign_keys(table_name, name = nil)
603
- sql = <<-SQL
605
+ sql = PostgreSQLExtensions::Utils.strip_heredoc(<<-SQL)
604
606
  SELECT
605
607
  c2.relname AS table_name,
606
608
  a.attname AS foreign_key,
@@ -656,6 +658,36 @@ module ActiveRecord
656
658
  end
657
659
  end
658
660
 
661
+ # Run the CLUSTER command on all previously clustered tables available
662
+ # to be clustered by the current user.
663
+ #
664
+ # ==== Options
665
+ #
666
+ # * <tt>:verbose</tt> - Adds the VERBOSE clause.
667
+ def cluster_all(options = {})
668
+ sql = 'CLUSTER'
669
+ sql << ' VERBOSE' if options[:verbose]
670
+
671
+ execute "#{sql};"
672
+ end
673
+
674
+ # Cluster a table or materialized view on an index.
675
+ #
676
+ # ==== Options
677
+ #
678
+ # * <tt>:using</tt> - adds a USING clause to cluster on. If no
679
+ # <tt>:using</tt> option is provided, the object itself will be
680
+ # re-clustered.
681
+ # * <tt>:verbose</tt> - Adds the VERBOSE clause.
682
+ def cluster(name, options = {})
683
+ sql = 'CLUSTER '
684
+ sql << 'VERBOSE ' if options[:verbose]
685
+ sql << quote_table_name(name)
686
+ sql << " USING #{quote_generic(options[:using])}" if options[:using]
687
+
688
+ execute "#{sql};"
689
+ end
690
+
659
691
  def add_column_options_with_expression!(sql, options) #:nodoc:
660
692
  if options_include_default?(options) &&
661
693
  options[:default].is_a?(Hash) &&
@@ -688,6 +720,21 @@ module ActiveRecord
688
720
  end
689
721
  end
690
722
  alias_method_chain :change_column_null, :expression
723
+
724
+ private
725
+ def assert_valid_copy_from_options(options)
726
+ if options[:program] && !ActiveRecord::PostgreSQLExtensions::Features.copy_from_program?
727
+ raise InvalidCopyFromOptions.new("The :program option is only available in PostgreSQL 9.3+.")
728
+ end
729
+
730
+ if options[:freeze] && !ActiveRecord::PostgreSQLExtensions::Features.copy_from_freeze?
731
+ raise InvalidCopyFromOptions.new("The :freeze option is only available in PostgreSQL 9.3+.")
732
+ end
733
+
734
+ if options[:encoding] && !ActiveRecord::PostgreSQLExtensions::Features.copy_from_encoding?
735
+ raise InvalidCopyFromOptions.new("The :encoding option is only available in PostgreSQL 9.1+.")
736
+ end
737
+ end
691
738
  end
692
739
 
693
740
  class PostgreSQLColumn
@@ -742,10 +789,3 @@ module ActiveRecord
742
789
  end
743
790
  end
744
791
 
745
- ActiveRecord::Base.class_eval do
746
- # Enable extended query logging
747
- cattr_accessor :enable_extended_logging
748
-
749
- # Enable REALLY extended query logging
750
- cattr_accessor :enable_really_extended_logging
751
- end
@@ -21,12 +21,6 @@ module ActiveRecord
21
21
  end
22
22
  end
23
23
 
24
- class InvalidConstraintDependencyAction < ActiveRecordError #:nodoc:
25
- def initialize(option)
26
- super("Invalid constraint dependency action - #{option}")
27
- end
28
- end
29
-
30
24
  module ConnectionAdapters
31
25
  class PostgreSQLAdapter
32
26
  # Adds a CHECK constraint to the table. See
@@ -94,6 +88,8 @@ module ActiveRecord
94
88
  # This is a base class for other PostgreSQL constraint classes. It
95
89
  # isn't really meant to be used directly.
96
90
  class PostgreSQLConstraint
91
+ include ActiveRecord::PostgreSQLExtensions::Utils
92
+
97
93
  attr_accessor :base, :options
98
94
 
99
95
  def initialize(base, options) #:nodoc:
@@ -129,7 +125,7 @@ module ActiveRecord
129
125
 
130
126
  def storage_parameters
131
127
  if options[:index_parameters] || options[:storage_parameters]
132
- " WITH (#{options[:index_parameters] || options[:storage_parameters]})"
128
+ " WITH (#{options_from_hash_or_string(options[:index_parameters] || options[:storage_parameters])})"
133
129
  else
134
130
  ''
135
131
  end
@@ -395,8 +391,8 @@ module ActiveRecord
395
391
  # * <tt>:name</tt> - specifies a name for the constraint.
396
392
  # * <tt>:storage_parameters</tt> - PostgreSQL allows you to add a
397
393
  # couple of additional parameters to indexes to govern disk usage and
398
- # such. This option is a simple String that lets you insert these
399
- # options as necessary. See the PostgreSQL documentation on index
394
+ # such. This option is a simple String or a Hash that lets you insert
395
+ # these options as necessary. See the PostgreSQL documentation on index
400
396
  # storage parameters for details. <tt>:index_parameters</tt> can also
401
397
  # be used.
402
398
  # * <tt>:tablespace</tt> - allows you to specify a tablespace for the
@@ -424,7 +420,7 @@ module ActiveRecord
424
420
 
425
421
  def to_sql #:nodoc:
426
422
  sql = "#{constraint_name}UNIQUE ("
427
- sql << Array(columns).collect { |c| base.quote_column_name(c) }.join(', ')
423
+ sql << Array.wrap(columns).collect { |c| base.quote_column_name(c) }.join(', ')
428
424
  sql << ")"
429
425
  sql << storage_parameters
430
426
  sql << using_tablespace
@@ -601,9 +597,9 @@ module ActiveRecord
601
597
  end
602
598
 
603
599
  sql << "#{constraint_name}FOREIGN KEY ("
604
- sql << Array(columns).collect { |c| base.quote_column_name(c) }.join(', ')
600
+ sql << Array.wrap(columns).collect { |c| base.quote_column_name(c) }.join(', ')
605
601
  sql << ") REFERENCES #{base.quote_table_name(table)}"
606
- sql << ' (%s)' % Array(ref_columns).collect { |c| base.quote_column_name(c) }.join(', ') if ref_columns
602
+ sql << ' (%s)' % Array.wrap(ref_columns).collect { |c| base.quote_column_name(c) }.join(', ') if ref_columns
607
603
  sql << " MATCH #{options[:match].to_s.upcase}" if options[:match]
608
604
  sql << " ON DELETE #{options[:on_delete].to_s.gsub(/_/, ' ').upcase}" if options[:on_delete]
609
605
  sql << " ON UPDATE #{options[:on_update].to_s.gsub(/_/, ' ').upcase}" if options[:on_update]
@@ -691,8 +687,8 @@ module ActiveRecord
691
687
  # default which is <tt>:btree</tt>. See the PostgreSQL docs for details.
692
688
  # * <tt>:storage_parameters</tt> - PostgreSQL allows you to add a
693
689
  # couple of additional parameters to indexes to govern disk usage and
694
- # such. This option is a simple String that lets you insert these
695
- # options as necessary. See the PostgreSQL documentation on index
690
+ # such. This option is a simple String or a Hash that lets you insert
691
+ # these options as necessary. See the PostgreSQL documentation on index
696
692
  # storage parameters for details. <tt>:index_parameters</tt> can also
697
693
  # be used.
698
694
  # * <tt>:tablespace</tt> - allows you to specify a tablespace for the
@@ -830,8 +826,8 @@ module ActiveRecord
830
826
  # * <tt>:name</tt> - specifies a name for the constraint.
831
827
  # * <tt>:storage_parameters</tt> - PostgreSQL allows you to add a
832
828
  # couple of additional parameters to indexes to govern disk usage and
833
- # such. This option is a simple String that lets you insert these
834
- # options as necessary. See the PostgreSQL documentation on index
829
+ # such. This option is a simple String or a Hash that lets you insert
830
+ # these options as necessary. See the PostgreSQL documentation on index
835
831
  # storage parameters for details. <tt>:index_parameters</tt> can also
836
832
  # be used.
837
833
  # * <tt>:tablespace</tt> - allows you to specify a tablespace for the
@@ -855,7 +851,7 @@ module ActiveRecord
855
851
  def to_sql #:nodoc:
856
852
  sql = String.new
857
853
  sql << "#{constraint_name}PRIMARY KEY "
858
- sql << "(" << Array(columns).collect { |column|
854
+ sql << "(" << Array.wrap(columns).collect { |column|
859
855
  base.quote_column_name(column)
860
856
  }.join(', ')
861
857
  sql << ")"