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
@@ -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