activerecord-postgresql-extensions 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc CHANGED
@@ -29,9 +29,20 @@ PostgreSQL adapter including:
29
29
  geos-extensions gem at https://github.com/zoocasa/geos-extensions or
30
30
  the RGeo project at https://github.com/dazuma/rgeo .
31
31
 
32
- == First Public Release Notice!
32
+ == Building db/schema.rb
33
33
 
34
- This is the first public release of this gem, so while it has been kind to us
35
- and our software, we can't guarantee that for everyone. That said, we do hope
36
- that someone out there finds it useful and enjoys the new-found powers of
37
- PostgreSQL!
34
+ While this extension has support for creating a bunch of PostgreSQL-specific
35
+ DDL statements, it can't actually reverse the process yet to create the Ruby
36
+ equivalent code in your schema.rb file. To create a suitable schema file
37
+ that you can use for creating test databases or just to view the schema
38
+ itself, you can try switching the ActiveRecord configuration option for
39
+ schema_format from :ruby to :sql like so:
40
+
41
+ config.active_record.schema_format = :sql
42
+
43
+ This will tell ActiveRecord to use the pg_dump utility to create a file
44
+ containing the database schema in db/development_structure.sql.
45
+
46
+ Details on this configuration option can be found here:
47
+
48
+ http://guides.rubyonrails.org/configuring.html
@@ -28,5 +28,7 @@ Gem::Specification.new do |s|
28
28
  end
29
29
  s.add_dependency("rdoc")
30
30
  s.add_dependency("rake", ["~> 0.9"])
31
+ s.add_dependency("minitest")
32
+ s.add_dependency("turn")
31
33
  end
32
34
 
@@ -39,7 +39,7 @@ module ActiveRecord
39
39
  # can have multiple with_schemas wrapped around each other, and
40
40
  # hopefully they won't collide with one another.
41
41
  #
42
- # Examples:
42
+ # ==== Examples
43
43
  #
44
44
  # # should produce '"geospatial"."my_tables"'
45
45
  # with_schema :geospatial do
@@ -62,7 +62,7 @@ module ActiveRecord
62
62
  # When using with_schema, you can temporarily ignore the scoped
63
63
  # schemas with ignore_block.
64
64
  #
65
- # Example:
65
+ # ==== Example
66
66
  #
67
67
  # with_schema :geospatial do
68
68
  # create_table(:test) do |t|
@@ -676,6 +676,18 @@ module ActiveRecord
676
676
  end
677
677
  end
678
678
  alias_method_chain :change_column_default, :expression
679
+
680
+ def change_column_null_with_expression(table_name, column_name, null, default = nil) #:nodoc:
681
+ if default.is_a?(Hash) && default.has_key?(:expression)
682
+ unless null
683
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)} = #{default[:expression]} WHERE #{quote_column_name(column_name)} IS NULL")
684
+ end
685
+ execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
686
+ else
687
+ change_column_null_without(table_name, column_name, null, default = nil)
688
+ end
689
+ end
690
+ alias_method_chain :change_column_null, :expression
679
691
  end
680
692
 
681
693
  class PostgreSQLColumn
@@ -47,11 +47,12 @@ module ActiveRecord
47
47
 
48
48
  # Adds a FOREIGN KEY constraint to the table. See
49
49
  # PostgreSQLForeignKeyConstraint for details.
50
- def add_foreign_key(table, columns, ref_table, *args)
50
+ def add_foreign_key_constraint(table, columns, ref_table, *args)
51
51
  sql = "ALTER TABLE #{quote_table_name(table)} ADD "
52
52
  sql << PostgreSQLForeignKeyConstraint.new(self, columns, ref_table, *args).to_s
53
53
  execute("#{sql};")
54
54
  end
55
+ alias :add_foreign_key :add_foreign_key_constraint
55
56
 
56
57
  # Adds an EXCLUDE constraint to the table. See
57
58
  # PostgreSQLExcludeConstraint for details.
@@ -61,6 +62,15 @@ module ActiveRecord
61
62
  execute("#{sql};")
62
63
  end
63
64
 
65
+ # Adds a PRIMARY KEY constraint to the table. See
66
+ # PostgreSQLPrimaryKeyConstraint for details.
67
+ def add_primary_key_constraint(table, columns, options = {})
68
+ sql = "ALTER TABLE #{quote_table_name(table)} ADD "
69
+ sql << PostgreSQLPrimaryKeyConstraint.new(self, columns, options).to_s
70
+ execute("#{sql};")
71
+ end
72
+ alias :add_primary_key :add_primary_key_constraint
73
+
64
74
  # Drops a constraint from the table. Use this to drop CHECK,
65
75
  # UNIQUE, EXCLUDE and FOREIGN KEY constraints from a table.
66
76
  #
@@ -73,6 +83,12 @@ module ActiveRecord
73
83
  sql << ' CASCADE' if options[:cascade]
74
84
  execute("#{sql};")
75
85
  end
86
+
87
+ # Validates a constraint and removes the NOT VALID clause from its
88
+ # definition.
89
+ def validate_constraint(table, name)
90
+ execute("ALTER TABLE #{quote_table_name(table)} VALIDATE CONSTRAINT #{quote_generic(name)};")
91
+ end
76
92
  end
77
93
 
78
94
  # This is a base class for other PostgreSQL constraint classes. It
@@ -110,6 +126,39 @@ module ActiveRecord
110
126
  "CONSTRAINT #{base.quote_generic(options[:name])} "
111
127
  end
112
128
  end
129
+
130
+ def storage_parameters
131
+ if options[:index_parameters] || options[:storage_parameters]
132
+ " WITH (#{options[:index_parameters] || options[:storage_parameters]})"
133
+ else
134
+ ''
135
+ end
136
+ end
137
+ alias :index_parameters :storage_parameters
138
+
139
+ def using_tablespace
140
+ if options[:tablespace]
141
+ " USING INDEX TABLESPACE #{base.quote_tablespace(options[:tablespace])}"
142
+ else
143
+ ''
144
+ end
145
+ end
146
+
147
+ def not_valid
148
+ if options[:not_valid]
149
+ " NOT VALID"
150
+ else
151
+ ''
152
+ end
153
+ end
154
+
155
+ def no_inherit
156
+ if options[:no_inherit]
157
+ " NO INHERIT"
158
+ else
159
+ ''
160
+ end
161
+ end
113
162
  end
114
163
 
115
164
  # Creates CHECK constraints for PostgreSQL tables.
@@ -216,6 +265,9 @@ module ActiveRecord
216
265
  # * <tt>:expression</tt> - when creating a column definition, you can
217
266
  # supply either a String containing the expression or a Hash to
218
267
  # supply both <tt>:name</tt> and <tt>:expression</tt> values.
268
+ # * <tt>:not_valid</tt> - adds the NOT VALID clause. Only useful when
269
+ # altering an existing table.
270
+ # * <tt>:no_inherit</tt> - adds the NO INHERIT clause.
219
271
  #
220
272
  # === Dropping CHECK Constraints
221
273
  #
@@ -231,7 +283,7 @@ module ActiveRecord
231
283
  end
232
284
 
233
285
  def to_sql #:nodoc:
234
- "#{constraint_name}CHECK (#{expression})"
286
+ "#{constraint_name}CHECK (#{expression})#{not_valid}#{no_inherit}"
235
287
  end
236
288
  alias :to_s :to_sql
237
289
  end
@@ -259,7 +311,7 @@ module ActiveRecord
259
311
  # can specify UNIQUE constraints on individual columns during
260
312
  # definition.
261
313
  #
262
- # ==== Example:
314
+ # ==== Example
263
315
  #
264
316
  # create_table(:foo) do |t|
265
317
  # t.integer :fancy_id, :unique => true
@@ -283,7 +335,7 @@ module ActiveRecord
283
335
  # than on a column definition. This is useful when you want to add
284
336
  # multiple columns to the constraint.
285
337
  #
286
- # ==== Example:
338
+ # ==== Example
287
339
  #
288
340
  # create_table(:foo) do |t|
289
341
  # t.integer :fancy_id
@@ -340,8 +392,7 @@ module ActiveRecord
340
392
  # or when using add_unique_constraint, there are a hanful of
341
393
  # PostgreSQL-specific options that you may find useful.
342
394
  #
343
- # * <tt>:name</tt> - specify a name for the index created by the
344
- # UNIQUE constraint.
395
+ # * <tt>:name</tt> - specifies a name for the constraint.
345
396
  # * <tt>:storage_parameters</tt> - PostgreSQL allows you to add a
346
397
  # couple of additional parameters to indexes to govern disk usage and
347
398
  # such. This option is a simple String that lets you insert these
@@ -375,8 +426,8 @@ module ActiveRecord
375
426
  sql = "#{constraint_name}UNIQUE ("
376
427
  sql << Array(columns).collect { |c| base.quote_column_name(c) }.join(', ')
377
428
  sql << ")"
378
- sql << " WITH (#{options[:storage_parameters] || options[:index_parameters]})" if options[:storage_parameters] || options[:index_parameters]
379
- sql << " USING INDEX TABLESPACE #{base.quote_tablespace(options[:tablespace])}" if options[:tablespace]
429
+ sql << storage_parameters
430
+ sql << using_tablespace
380
431
  sql
381
432
  end
382
433
  alias :to_s :to_sql
@@ -398,7 +449,7 @@ module ActiveRecord
398
449
  # can specify FOREIGN KEY constraints on individual columns during
399
450
  # definition.
400
451
  #
401
- # ==== Example:
452
+ # ==== Example
402
453
  #
403
454
  # create_table(:foo) do |t|
404
455
  # t.integer :bar_id, :references => { :table => :bar, :column => :id }
@@ -426,7 +477,7 @@ module ActiveRecord
426
477
  # FOREIGN KEY constraints can also be applied to the table directly
427
478
  # rather than on a column definition.
428
479
  #
429
- # ==== Example:
480
+ # ==== Example
430
481
  #
431
482
  # The following example produces the same result as above:
432
483
  #
@@ -512,6 +563,8 @@ module ActiveRecord
512
563
  # values are <tt>:no_action</tt>, <tt>:restrict</tt>,
513
564
  # <tt>:cascade</tt>, <tt>:set_null</tt> and <tt>:set_default</tt>.
514
565
  # PostgreSQL's default is <tt>:no_action</tt>.
566
+ # * <tt>:not_valid</tt> - adds the NOT VALID clause. Only useful when
567
+ # altering an existing table.
515
568
  #
516
569
  # See the PostgreSQL documentation on foreign keys for details about
517
570
  # the <tt>:deferrable</tt>, <tt>:match</tt>, <tt>:on_delete</tt>
@@ -554,6 +607,7 @@ module ActiveRecord
554
607
  sql << " MATCH #{options[:match].to_s.upcase}" if options[:match]
555
608
  sql << " ON DELETE #{options[:on_delete].to_s.gsub(/_/, ' ').upcase}" if options[:on_delete]
556
609
  sql << " ON UPDATE #{options[:on_update].to_s.gsub(/_/, ' ').upcase}" if options[:on_update]
610
+ sql << not_valid
557
611
  sql << deferrable
558
612
  end
559
613
  sql
@@ -597,7 +651,7 @@ module ActiveRecord
597
651
  # EXCLUDE constraints can also be applied to the table directly
598
652
  # rather than on a column definition.
599
653
  #
600
- # ==== Example:
654
+ # ==== Example
601
655
  #
602
656
  # The following example produces the same result as above:
603
657
  #
@@ -631,6 +685,7 @@ module ActiveRecord
631
685
  #
632
686
  # === Options for EXCLUDE Constraints
633
687
  #
688
+ # * <tt>:name</tt> - specifies a name for the constraint.
634
689
  # * <tt>:using</tt> - sets the index type to be used. Usually this will
635
690
  # <tt>:gist</tt>, but the default is left blank to allow for the PostgreSQL
636
691
  # default which is <tt>:btree</tt>. See the PostgreSQL docs for details.
@@ -668,12 +723,147 @@ module ActiveRecord
668
723
  "#{e[:element]} WITH #{e[:with] || e[:operator]}"
669
724
  }.join(', ')
670
725
  sql << ")"
671
- sql << " WITH (#{options[:index_parameters] || options[:storage_parameters]})" if options[:index_parameters] || options[:storage_parameters]
672
- sql << " USING INDEX TABLESPACE #{base.quote_tablespace(options[:tablespace])}" if options[:tablespace]
726
+ sql << storage_parameters
727
+ sql << using_tablespace
673
728
  sql << " WHERE (#{options[:conditions] || options[:where]})" if options[:conditions] || options[:where]
674
729
  sql
675
730
  end
676
731
  alias :to_s :to_sql
677
732
  end
733
+
734
+ # Creates PRIMARY KEY constraints for PostgreSQL tables and columns.
735
+ #
736
+ # This class is meant to be used by PostgreSQL column and table
737
+ # definition and manipulation methods. There are several ways to create
738
+ # a PRIMARY KEY constraint:
739
+ #
740
+ # * on a table definition
741
+ # * on a column definition
742
+ # * when altering a table
743
+ #
744
+ # ActiveRecord itself already provides some methods of creating PRIMARY
745
+ # KEYS, but we've added some PostgreSQL-specific extensions here. To
746
+ # override ActiveRecord's built-in PRIMARY KEY generation, add an
747
+ # option for <tt>:id => false</tt> when creating the table via
748
+ # <tt>create_table</tt>.
749
+ #
750
+ # When creating PRIMARY KEYs, you can use an options Hash to add various
751
+ # PostgreSQL-specific options as necessary or simply use a true statement
752
+ # to create a PRIMARY KEY on the column. Composite PRIMARY KEYs can also
753
+ # be created across multiple columns using a table definition or the
754
+ # PostgreSQLAdapter#add_primary_key method.
755
+ #
756
+ # === Column Definition
757
+ #
758
+ # When creating a new table via PostgreSQLAdapter#create_table, you
759
+ # can specify PRIMARY KEY constraints on individual columns during
760
+ # definition.
761
+ #
762
+ # ==== Examples
763
+ #
764
+ # create_table(:foo, :id => false) do |t|
765
+ # t.integer :foo_id, :primary_key => true
766
+ # end
767
+ #
768
+ # # Produces:
769
+ # # CREATE TABLE "foo" (
770
+ # # "foo_id" integer,
771
+ # # PRIMARY KEY ("foo_id")
772
+ # # );
773
+ #
774
+ # create_table('foo', :id => false) do |t|
775
+ # t.integer :foo_id, :primary_key => {
776
+ # :tablespace => 'fubar',
777
+ # :index_parameters => 'FILLFACTOR=10'
778
+ # }
779
+ # end
780
+ #
781
+ # # Produces:
782
+ # # CREATE TABLE "foo" (
783
+ # # "foo_id" integer,
784
+ # # PRIMARY KEY ("foo_id") WITH (FILLFACTOR=10) USING INDEX TABLESPACE "fubar"
785
+ # # );
786
+ #
787
+ # === Table Definition
788
+ #
789
+ # PRIMARY KEY constraints can also be applied to the table directly
790
+ # rather than on a column definition.
791
+ #
792
+ # ==== Examples
793
+ #
794
+ # The following examples produces the same results as above:
795
+ #
796
+ # create_table('foo', :id => false) do |t|
797
+ # t.integer :foo_id
798
+ # t.primary_key_constraint :foo_id
799
+ # end
800
+ #
801
+ # create_table('foo', :id => false) do |t|
802
+ # t.integer :foo_id
803
+ # t.primary_key_constraint :foo_id, {
804
+ # :tablespace => 'fubar',
805
+ # :index_parameters => 'FILLFACTOR=10'
806
+ # }
807
+ # end
808
+ #
809
+ # === Table Manipulation
810
+ #
811
+ # You can also create new PRIMARY KEY constraints outside of a table
812
+ # definition using PostgreSQLAdapter#add_primary_key or
813
+ # PostgreSQLAdapter#add_primary_key_constraint.
814
+ #
815
+ # ==== Examples
816
+ #
817
+ # add_primary_key(:foo, :bar_id)
818
+ # add_primary_key(:foo, [ :bar_id, :baz_id ])
819
+ # add_primary_key(:foo, :bar_id, :name => 'foo_pk')
820
+ # add_primary_key(:foo, :bar_id, :tablespace => 'fubar', :index_parameters => 'FILLFACTOR=10')
821
+ #
822
+ # # Produces:
823
+ # # ALTER TABLE "foo" ADD PRIMARY KEY ("bar_id");
824
+ # # ALTER TABLE "foo" ADD PRIMARY KEY ("bar_id", "baz_id");
825
+ # # ALTER TABLE "foo" ADD CONSTRAINT "foo_pk" PRIMARY KEY ("bar_id");
826
+ # # ALTER TABLE "foo" ADD PRIMARY KEY ("bar_id") WITH (FILLFACTOR=10) USING INDEX TABLESPACE "fubar";
827
+ #
828
+ # === Options for PRIMARY KEY Constraints
829
+ #
830
+ # * <tt>:name</tt> - specifies a name for the constraint.
831
+ # * <tt>:storage_parameters</tt> - PostgreSQL allows you to add a
832
+ # 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
835
+ # storage parameters for details. <tt>:index_parameters</tt> can also
836
+ # be used.
837
+ # * <tt>:tablespace</tt> - allows you to specify a tablespace for the
838
+ # index being created. See the PostgreSQL documentation on
839
+ # tablespaces for details.
840
+ #
841
+ # === Dropping PRIMARY KEY Constraints
842
+ #
843
+ # Like all PostgreSQL constraints, you can use
844
+ # PostgreSQLAdapter#drop_constraint to remove a constraint from a
845
+ # table.
846
+ class PostgreSQLPrimaryKeyConstraint < PostgreSQLConstraint
847
+ attr_accessor :columns
848
+
849
+ def initialize(base, columns, options = {}) #:nodoc:
850
+ @columns = columns
851
+
852
+ super(base, options)
853
+ end
854
+
855
+ def to_sql #:nodoc:
856
+ sql = String.new
857
+ sql << "#{constraint_name}PRIMARY KEY "
858
+ sql << "(" << Array(columns).collect { |column|
859
+ base.quote_column_name(column)
860
+ }.join(', ')
861
+ sql << ")"
862
+ sql << storage_parameters
863
+ sql << using_tablespace
864
+ sql
865
+ end
866
+ alias :to_s :to_sql
867
+ end
678
868
  end
679
869
  end
@@ -8,6 +8,9 @@ module ActiveRecord
8
8
  # either a parser_name or a source_config option as per the PostgreSQL
9
9
  # text search docs.
10
10
  def create_extension(name, options = {})
11
+ raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('extensions') if
12
+ !ActiveRecord::PostgreSQLExtensions::Features.extensions?
13
+
11
14
  sql = "CREATE EXTENSION "
12
15
  sql << "IF NOT EXISTS " if options[:if_not_exists]
13
16
  sql << quote_generic(name)
@@ -23,6 +26,9 @@ module ActiveRecord
23
26
  # * <tt>if_exists</tt> - adds IF EXISTS.
24
27
  # * <tt>cascade</tt> - adds CASCADE.
25
28
  def drop_extension(*args)
29
+ raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('extensions') if
30
+ !ActiveRecord::PostgreSQLExtensions::Features.extensions?
31
+
26
32
  options = args.extract_options!
27
33
 
28
34
  sql = 'DROP EXTENSION '
@@ -34,12 +40,18 @@ module ActiveRecord
34
40
  end
35
41
 
36
42
  def update_extension(name, new_version = nil)
43
+ raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('extensions') if
44
+ !ActiveRecord::PostgreSQLExtensions::Features.extensions?
45
+
37
46
  sql = "ALTER EXTENSION #{quote_generic(name)} UPDATE"
38
47
  sql << " TO #{quote_generic(new_version)}" if new_version;
39
48
  execute("#{sql};")
40
49
  end
41
50
 
42
51
  def alter_extension_schema(name, schema)
52
+ raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('extensions') if
53
+ !ActiveRecord::PostgreSQLExtensions::Features.extensions?
54
+
43
55
  execute "ALTER EXTENSION #{quote_generic(name)} SET SCHEMA #{quote_schema(schema)};"
44
56
  end
45
57
 
@@ -112,6 +124,9 @@ module ActiveRecord
112
124
  # * <tt>:operator_class</tt> and <tt>:operator_family</tt> - <tt>:name</tt>
113
125
  # and <tt>:indexing_method</tt>.
114
126
  def alter_extension(name, options = {})
127
+ raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('extensions') if
128
+ !ActiveRecord::PostgreSQLExtensions::Features.extensions?
129
+
115
130
  alterer = PostgreSQLExtensionAlterer.new(self, name, options)
116
131
 
117
132
  if block_given?
@@ -124,6 +139,9 @@ module ActiveRecord
124
139
 
125
140
  class PostgreSQLExtensionAlterer
126
141
  def initialize(base, name, options = {}) #:nodoc:
142
+ raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('extensions') if
143
+ !ActiveRecord::PostgreSQLExtensions::Features.extensions?
144
+
127
145
  @base, @name, @options = base, name, options
128
146
  @sql = options.collect { |k, v| build_statement(k, v) }
129
147
  end