activerecord-postgresql-extensions 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -53,6 +53,11 @@ module ActiveRecord
53
53
  def drop_rule(name, table)
54
54
  execute "DROP RULE #{quote_rule(name)} ON #{quote_table_name(table)};"
55
55
  end
56
+
57
+ # Renames a rule. Available in PostgreSQL 9.3+.
58
+ def rename_rule(old_name, table, new_name)
59
+ execute "ALTER RULE #{quote_rule(old_name)} ON #{quote_table_name(table)} RENAME TO #{quote_rule(new_name)};"
60
+ end
56
61
  end
57
62
 
58
63
  # Creates a PostgreSQL rule.
@@ -15,8 +15,18 @@ module ActiveRecord
15
15
  # * <tt>:authorization</tt> - adds an AUTHORIZATION clause. This is
16
16
  # used to set the owner of the schema. This can be changed with
17
17
  # alter_schema_owner as necessary.
18
+ # * <tt>:if_not_exists</tt> - adds an IF NOT EXISTS clause. Available
19
+ # in PostgreSQL 9.3+.
18
20
  def create_schema(schema, options = {})
19
- sql = "CREATE SCHEMA #{quote_schema(schema)}"
21
+ sql = 'CREATE SCHEMA '
22
+
23
+ if options.key?(:if_not_exists)
24
+ ActiveRecord::PostgreSQLExtensions::Features.check_feature(:create_schema_if_not_exists)
25
+
26
+ sql << 'IF NOT EXISTS ' if options[:if_not_exists]
27
+ end
28
+
29
+ sql << quote_schema(schema)
20
30
  sql << " AUTHORIZATION #{quote_role(options[:authorization])}" if options[:authorization]
21
31
  execute("#{sql};")
22
32
  end
@@ -27,10 +37,13 @@ module ActiveRecord
27
37
  #
28
38
  # * <tt>:if_exists</tt> - adds IF EXISTS.
29
39
  # * <tt>:cascade</tt> - adds CASCADE.
30
- def drop_schema(schemas, options = {})
40
+ def drop_schema(*args)
41
+ options = args.extract_options!
42
+ args.flatten!
43
+
31
44
  sql = 'DROP SCHEMA '
32
45
  sql << 'IF EXISTS ' if options[:if_exists]
33
- sql << Array(schemas).collect { |s| quote_schema(s) }.join(', ')
46
+ sql << Array.wrap(args).collect { |s| quote_schema(s) }.join(', ')
34
47
  sql << ' CASCADE' if options[:cascade]
35
48
  execute("#{sql};")
36
49
  end
@@ -44,6 +57,29 @@ module ActiveRecord
44
57
  def alter_schema_owner(schema, role)
45
58
  execute("ALTER SCHEMA #{quote_schema(schema)} OWNER TO #{quote_role(role)};")
46
59
  end
60
+
61
+ # Creates a schema based on a role name. This is a shortcut to using
62
+ # <tt>create_schema</tt> with the <tt>:authorization</tt> option and
63
+ # ensures that the schema created has the same name as the role
64
+ # specified.
65
+ #
66
+ # ==== Options
67
+ #
68
+ # * <tt>:if_not_exists</tt> - adds IF NOT EXISTS. Available in PostgreSQL
69
+ # 9.3+.
70
+ def create_schema_authorization(name, options = {})
71
+ sql = 'CREATE SCHEMA'
72
+
73
+ if options.key?(:if_not_exists)
74
+ ActiveRecord::PostgreSQLExtensions::Features.check_feature(:create_schema_if_not_exists)
75
+
76
+ sql << ' IF NOT EXISTS' if options[:if_not_exists]
77
+ end
78
+
79
+ sql << " AUTHORIZATION #{quote_role(name)}"
80
+
81
+ execute("#{sql};")
82
+ end
47
83
  end
48
84
  end
49
85
  end
@@ -63,10 +63,13 @@ module ActiveRecord
63
63
  # * <tt>:if_exists</tt> - adds IF EXISTS.
64
64
  # * <tt>:cascade</tt> - cascades the operation down to objects
65
65
  # referring to the sequence.
66
- def drop_sequence(name, options = {})
66
+ def drop_sequence(*args)
67
+ options = args.extract_options!
68
+ args.flatten!
69
+
67
70
  sql = 'DROP SEQUENCE '
68
71
  sql << 'IF EXISTS ' if options[:if_exists]
69
- sql << Array(name).collect { |s| quote_sequence(s) }.join(', ')
72
+ sql << Array.wrap(args).collect { |s| quote_sequence(s) }.join(', ')
70
73
  sql << ' CASCADE' if options[:cascade]
71
74
  execute("#{sql};")
72
75
  end
@@ -116,7 +119,7 @@ module ActiveRecord
116
119
 
117
120
  # Returns an Array of available sequences.
118
121
  def sequences(name = nil)
119
- query(<<-SQL, name).map { |row| row[0] }
122
+ query(PostgreSQLExtensions::Utils.strip_heredoc(<<-SQL), name).map { |row| row[0] }
120
123
  SELECT c.relname AS sequencename
121
124
  FROM pg_class c
122
125
  WHERE c.relkind = 'S'::"char";
@@ -51,6 +51,9 @@ module ActiveRecord
51
51
  # * <tt>:of_type</tt> - for "OF type_name" clauses.
52
52
  # * <tt>:if_not_exists</tt> - adds the "IF NOT EXISTS" clause.
53
53
  # * <tt>:unlogged</tt> - creates an UNLOGGED table.
54
+ # * <tt>:storage_parameters</tt> - a simple String or Hash used to
55
+ # assign table storage parameters. See the PostgreSQL docs for
56
+ # details on the various storage parameters available.
54
57
  #
55
58
  # We're expanding the doors of table definition perception with
56
59
  # this exciting new addition to the world of ActiveRecord
@@ -112,7 +115,7 @@ module ActiveRecord
112
115
  execute table_definition.to_s
113
116
  unless table_definition.post_processing.blank?
114
117
  table_definition.post_processing.each do |pp|
115
- execute pp
118
+ execute pp.to_s
116
119
  end
117
120
  end
118
121
  end
@@ -130,10 +133,13 @@ module ActiveRecord
130
133
  # dropped as well. See the PostgreSQL documentation for details.
131
134
  #
132
135
  # You can still access the original method via original_drop_table.
133
- def drop_table(tables, options = {})
136
+ def drop_table(*args)
137
+ options = args.extract_options!
138
+ args.flatten!
139
+
134
140
  sql = 'DROP TABLE '
135
141
  sql << 'IF EXISTS ' if options[:if_exists]
136
- sql << Array(tables).collect { |t| quote_table_name(t) }.join(', ')
142
+ sql << Array.wrap(args).collect { |t| quote_table_name(t) }.join(', ')
137
143
  sql << ' CASCADE' if options[:cascade]
138
144
  execute("#{sql};")
139
145
  end
@@ -172,10 +178,9 @@ module ActiveRecord
172
178
  # been created. See the source code for PostgreSQLAdapter#create_table
173
179
  # and PostgreSQLTableDefinition#geometry for an example of its use.
174
180
  class PostgreSQLTableDefinition < TableDefinition
175
- attr_accessor :base, :table_name, :options, :post_processing
181
+ attr_accessor :base, :table_name, :options
176
182
 
177
183
  def initialize(base, table_name, options = {}) #:nodoc:
178
- @table_constraints = Array.new
179
184
  @table_name, @options = table_name, options
180
185
  super(base)
181
186
  end
@@ -193,6 +198,12 @@ module ActiveRecord
193
198
  end
194
199
  end
195
200
 
201
+ if options.key?(:if_not_exists)
202
+ ActiveRecord::PostgreSQLExtensions::Features.check_feature(:create_table_if_not_exists)
203
+ elsif options.key?(:unlogged)
204
+ ActiveRecord::PostgreSQLExtensions::Features.check_feature(:create_table_unlogged)
205
+ end
206
+
196
207
  unless options[:id] == false
197
208
  self.primary_key(options[:primary_key] || Base.get_primary_key(table_name))
198
209
 
@@ -213,7 +224,7 @@ module ActiveRecord
213
224
  ary << @columns.collect(&:to_sql)
214
225
  ary << @like if defined?(@like) && @like
215
226
  end
216
- ary << @table_constraints unless @table_constraints.empty?
227
+ ary << table_constraints unless table_constraints.empty?
217
228
 
218
229
  unless ary.empty?
219
230
  sql << " (\n "
@@ -221,7 +232,8 @@ module ActiveRecord
221
232
  sql << "\n)"
222
233
  end
223
234
 
224
- sql << "\nINHERITS (" << Array(options[:inherits]).collect { |i| base.quote_table_name(i) }.join(', ') << ')' if options[:inherits]
235
+ sql << "\nINHERITS (" << Array.wrap(options[:inherits]).collect { |i| base.quote_table_name(i) }.join(', ') << ')' if options[:inherits]
236
+ sql << "\nWITH (#{ActiveRecord::PostgreSQLExtensions::Utils.options_from_hash_or_string(options[:storage_parameters], base)})" if options[:storage_parameters].present?
225
237
  sql << "\nON COMMIT #{options[:on_commit].to_s.upcase.gsub(/_/, ' ')}" if options[:on_commit]
226
238
  sql << "\n#{options[:options]}" if options[:options]
227
239
  sql << "\nTABLESPACE #{base.quote_tablespace(options[:tablespace])}" if options[:tablespace]
@@ -253,11 +265,11 @@ module ActiveRecord
253
265
  @like = "LIKE #{@base.quote_table_name(parent_table)}"
254
266
 
255
267
  if options[:including]
256
- @like << Array(options[:including]).collect { |l| " INCLUDING #{l.to_s.upcase}" }.join
268
+ @like << Array.wrap(options[:including]).collect { |l| " INCLUDING #{l.to_s.upcase}" }.join
257
269
  end
258
270
 
259
271
  if options[:excluding]
260
- @like << Array(options[:excluding]).collect { |l| " EXCLUDING #{l.to_s.upcase}" }.join
272
+ @like << Array.wrap(options[:excluding]).collect { |l| " EXCLUDING #{l.to_s.upcase}" }.join
261
273
  end
262
274
  @like
263
275
  end
@@ -265,29 +277,29 @@ module ActiveRecord
265
277
  # Add a CHECK constraint to the table. See
266
278
  # PostgreSQLCheckConstraint for more details.
267
279
  def check_constraint(expression, options = {})
268
- @table_constraints << PostgreSQLCheckConstraint.new(@base, expression, options)
280
+ table_constraints << PostgreSQLCheckConstraint.new(@base, expression, options)
269
281
  end
270
282
 
271
283
  # Add a UNIQUE constraint to the table. See
272
284
  # PostgreSQLUniqueConstraint for more details.
273
285
  def unique_constraint(columns, options = {})
274
- @table_constraints << PostgreSQLUniqueConstraint.new(@base, columns, options)
286
+ table_constraints << PostgreSQLUniqueConstraint.new(@base, columns, options)
275
287
  end
276
288
 
277
289
  # Add a FOREIGN KEY constraint to the table. See
278
290
  # PostgreSQLForeignKeyConstraint for more details.
279
291
  def foreign_key(columns, ref_table, *args)
280
- @table_constraints << PostgreSQLForeignKeyConstraint.new(@base, columns, ref_table, *args)
292
+ table_constraints << PostgreSQLForeignKeyConstraint.new(@base, columns, ref_table, *args)
281
293
  end
282
294
 
283
295
  # Add an EXCLUDE constraint to the table. See PostgreSQLExcludeConstraint
284
296
  # for more details.
285
297
  def exclude(excludes, options = {})
286
- @table_constraints << PostgreSQLExcludeConstraint.new(@base, table_name, excludes, options)
298
+ table_constraints << PostgreSQLExcludeConstraint.new(@base, table_name, excludes, options)
287
299
  end
288
300
 
289
301
  def primary_key_constraint(columns, options = {})
290
- @table_constraints << PostgreSQLPrimaryKeyConstraint.new(@base, columns, options)
302
+ table_constraints << PostgreSQLPrimaryKeyConstraint.new(@base, columns, options)
291
303
  end
292
304
 
293
305
  def column_with_constraints(name, type, *args) #:nodoc:
@@ -305,7 +317,7 @@ module ActiveRecord
305
317
  check
306
318
  end
307
319
 
308
- @table_constraints << check.collect do |c|
320
+ table_constraints << check.collect do |c|
309
321
  if c.is_a?(Hash)
310
322
  PostgreSQLCheckConstraint.new(@base, c.delete(:expression), c)
311
323
  else
@@ -321,7 +333,7 @@ module ActiveRecord
321
333
  [ references, {} ]
322
334
  end
323
335
 
324
- @table_constraints << PostgreSQLForeignKeyConstraint.new(
336
+ table_constraints << PostgreSQLForeignKeyConstraint.new(
325
337
  @base,
326
338
  name,
327
339
  ref_table,
@@ -333,26 +345,42 @@ module ActiveRecord
333
345
  unless unique.is_a?(Hash)
334
346
  unique = {}
335
347
  end
336
- @table_constraints << PostgreSQLUniqueConstraint.new(@base, name, unique)
348
+ table_constraints << PostgreSQLUniqueConstraint.new(@base, name, unique)
337
349
  end
338
350
 
339
351
  if primary_key
340
352
  unless primary_key.is_a?(Hash)
341
353
  primary_key = {}
342
354
  end
343
- @table_constraints << PostgreSQLPrimaryKeyConstraint.new(@base, name, primary_key)
355
+ table_constraints << PostgreSQLPrimaryKeyConstraint.new(@base, name, primary_key)
344
356
  end
345
357
 
346
358
  self
347
359
  end
348
360
  alias_method_chain :column, :constraints
349
361
 
362
+ # Add an INDEX to the table. This INDEX will be added during post
363
+ # processing after the table has been created. See
364
+ # PostgreSQLIndexDefinition for more details.
365
+ def index(name, columns, options = {})
366
+ post_processing << PostgreSQLIndexDefinition.new(@base, name, self.table_name, columns, options)
367
+ end
368
+
369
+ # Add statements to execute to after a table has been created.
370
+ def post_processing
371
+ @post_processing ||= []
372
+ end
373
+
350
374
  private
351
375
  LIKE_TYPES = %w{ defaults constraints indexes }.freeze
352
376
 
377
+ def table_constraints
378
+ @table_constraints ||= []
379
+ end
380
+
353
381
  def assert_valid_like_types(likes) #:nodoc:
354
382
  unless likes.blank?
355
- check_likes = Array(likes).collect(&:to_s) - LIKE_TYPES
383
+ check_likes = Array.wrap(likes).collect(&:to_s) - LIKE_TYPES
356
384
  if !check_likes.empty?
357
385
  raise ActiveRecord::InvalidLikeTypes.new(check_likes)
358
386
  end
@@ -76,7 +76,7 @@ module ActiveRecord
76
76
 
77
77
  def assert_valid_tablespace_parameter(parameter)
78
78
  if !TABLESPACE_PARAMETERS.include? parameter.to_s.downcase
79
- raise ActiveRecord::InvalidTablespaceParameter.new(option)
79
+ raise ActiveRecord::InvalidTablespaceParameter.new(parameter)
80
80
  end
81
81
  end
82
82
  end
@@ -54,13 +54,13 @@ module ActiveRecord
54
54
  add_or_alter = options[:action].to_s.upcase
55
55
 
56
56
  sql = "ALTER TEXT SEARCH CONFIGURATION #{quote_generic_with_schema(name)} #{add_or_alter} MAPPING FOR "
57
- sql << Array(tokens).collect { |token|
57
+ sql << Array.wrap(tokens).collect { |token|
58
58
  quote_generic(token)
59
59
  }.join(', ')
60
60
 
61
61
  sql << ' WITH '
62
62
 
63
- sql << Array(dictionaries).collect { |dictionary|
63
+ sql << Array.wrap(dictionaries).collect { |dictionary|
64
64
  quote_generic(dictionary)
65
65
  }.join(', ')
66
66
 
@@ -80,7 +80,7 @@ module ActiveRecord
80
80
  end
81
81
 
82
82
  sql = "ALTER TEXT SEARCH CONFIGURATION #{quote_generic_with_schema(name)} ALTER MAPPING FOR "
83
- sql << Array(mappings).collect { |token_type|
83
+ sql << Array.wrap(mappings).collect { |token_type|
84
84
  quote_generic(token_type)
85
85
  }.join(', ')
86
86
  sql << " REPLACE #{quote_generic(old_dictionary)} WITH #{quote_generic(new_dictionary)}"
@@ -117,8 +117,8 @@ module ActiveRecord
117
117
 
118
118
  def to_sql #:nodoc:
119
119
  sql = "CREATE TRIGGER #{base.quote_generic(name)} #{called.to_s.upcase} "
120
- sql << Array(events).collect { |e| e.to_s.upcase }.join(' OR ')
121
- sql << " OF " << Array(options[:of]).collect { |o| base.quote_generic(o) }.join(', ') if options[:of].present?
120
+ sql << Array.wrap(events).collect { |e| e.to_s.upcase }.join(' OR ')
121
+ sql << " OF " << Array.wrap(options[:of]).collect { |o| base.quote_generic(o) }.join(', ') if options[:of].present?
122
122
  sql << " ON #{base.quote_table_name(table)}"
123
123
  sql << " FOR EACH #{options[:for_each].to_s.upcase}" if options[:for_each]
124
124
  sql << " EXECUTE PROCEDURE #{base.quote_function(function)}(#{options[:args]})"
@@ -138,7 +138,7 @@ module ActiveRecord
138
138
  end
139
139
 
140
140
  def assert_valid_events(events) #:nodoc:
141
- check_events = Array(events).collect(&:to_s) - EVENT_TYPES
141
+ check_events = Array.wrap(events).collect(&:to_s) - EVENT_TYPES
142
142
  if !check_events.empty?
143
143
  raise ActiveRecord::InvalidTriggerEvent.new(check_events)
144
144
  end
@@ -2,16 +2,119 @@
2
2
  require 'active_record/connection_adapters/postgresql_adapter'
3
3
 
4
4
  module ActiveRecord
5
+ class InvalidAddEnumValueOptions < ActiveRecordError #:nodoc:
6
+ end
7
+
5
8
  module ConnectionAdapters
6
9
  class PostgreSQLAdapter
7
10
  # Returns an Array of available languages.
8
11
  def types(name = nil)
9
- query(%{SELECT typname FROM pg_type;}, name).map { |row| row[0] }
12
+ query(PostgreSQLExtensions::Utils.strip_heredoc(<<-SQL), name).map(&:first)
13
+ SELECT t.typname as type
14
+ FROM pg_type t
15
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
16
+ WHERE (t.typrelid = 0 OR (
17
+ SELECT c.relkind = 'c'
18
+ FROM pg_catalog.pg_class c
19
+ WHERE c.oid = t.typrelid
20
+ )) AND
21
+ NOT EXISTS(
22
+ SELECT 1
23
+ FROM pg_catalog.pg_type el
24
+ WHERE el.oid = t.typelem
25
+ AND el.typarray = t.oid
26
+ ) AND
27
+ n.nspname NOT IN ('information_schema');
28
+ SQL
10
29
  end
11
30
 
12
31
  def type_exists?(name)
13
32
  types.include?(name.to_s)
14
33
  end
34
+
35
+ # Creates an ENUM TYPE. An ENUM can contain zero or more values. ENUMs
36
+ # can be dropped with #drop_type.
37
+ def create_enum(name, *values)
38
+ execute PostgreSQLEnumDefinition.new(self, name, *values).to_s
39
+ end
40
+
41
+ # Adds a new value to an ENUM.
42
+ #
43
+ # ==== Options
44
+ #
45
+ # * <tt>:before</tt> - add the new value before this value.
46
+ # * <tt>:after</tt> - add the new value after this value.
47
+ # * <tt>:if_not_exists</tt> - adds the value if it doesn't already
48
+ # exist. Available in PostgreSQL 9.3+.
49
+ def add_enum_value(enum, value, options = {})
50
+ assert_valid_add_enum_value_options(options)
51
+
52
+ sql = "ALTER TYPE #{quote_generic(enum)} ADD VALUE"
53
+
54
+ if options.key?(:if_not_exists)
55
+ ActiveRecord::PostgreSQLExtensions::Features.check_feature(:type_if_not_exists)
56
+
57
+ sql << " IF NOT EXISTS" if options[:if_not_exists]
58
+ end
59
+
60
+ sql << " #{quote(value)}"
61
+
62
+ if options[:before]
63
+ sql << " BEFORE #{quote(options[:before])}"
64
+ elsif options[:after]
65
+ sql << " AFTER #{quote(options[:after])}"
66
+ end
67
+
68
+ execute("#{sql};")
69
+ end
70
+
71
+ # Drop TYPEs.
72
+ #
73
+ # ==== Options
74
+ #
75
+ # * <tt>:if_exists</tt> - adds IF EXISTS.
76
+ # * <tt>:cascade</tt> - adds CASCADE.
77
+ def drop_type(*args)
78
+ options = args.extract_options!
79
+ args.flatten!
80
+
81
+ sql = 'DROP TYPE '
82
+ sql << 'IF EXISTS ' if options[:if_exists]
83
+ sql << Array.wrap(args).collect { |i| quote_generic(i) }.join(', ')
84
+ sql << ' CASCADE' if options[:cascade]
85
+ execute("#{sql};")
86
+ end
87
+
88
+ # Returns an Array of possible
89
+ def enum_values(name)
90
+ query(%{SELECT unnest(enum_range(NULL::#{quote_generic(name)}))}, 'Enum values').map(&:first)
91
+ end
92
+
93
+ private
94
+ def assert_valid_add_enum_value_options(options)
95
+ if options[:before] && options[:after]
96
+ raise InvalidAddEnumValueOptions.new("Can't use both :before and :after options together")
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ # Creates a PostgreSQL enum type definition. This class isn't really meant
103
+ # to be used directly. Instead, see PostgreSQLAdapter#create_enum for
104
+ # usage.
105
+ class PostgreSQLEnumDefinition
106
+ attr_accessor :base, :name, :values
107
+
108
+ def initialize(base, name, *values)
109
+ @base, @name, @values = base, name, values.flatten
110
+ end
111
+
112
+ def to_sql #:nodoc:
113
+ sql = "CREATE TYPE #{base.quote_generic(name)} AS ENUM ("
114
+ sql << values.collect { |t| base.quote(t.to_s) }.join(', ')
115
+ sql << ")"
116
+ "#{sql};"
15
117
  end
118
+ alias :to_s :to_sql
16
119
  end
17
120
  end