activerecord-postgresql-extensions 0.1.0 → 0.2.0

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