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
@@ -66,7 +66,7 @@ module ActiveRecord
66
66
 
67
67
  # Returns an Array of available languages.
68
68
  def languages(name = nil)
69
- query(<<-SQL, name).map { |row| row[0] }
69
+ query(PostgreSQLExtensions::Utils.strip_heredoc(<<-SQL), name).map { |row| row[0] }
70
70
  SELECT lanname
71
71
  FROM pg_language;
72
72
  SQL
@@ -0,0 +1,272 @@
1
+
2
+ require 'active_record/connection_adapters/postgresql_adapter'
3
+
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ class PostgreSQLAdapter
7
+ # Creates a new PostgreSQL materialized view.
8
+ #
9
+ # +name+ is the name of the view. View quoting works the same as
10
+ # table quoting, so you can use PostgreSQLAdapter#with_schema and
11
+ # friends. See PostgreSQLAdapter#with_schema and
12
+ # PostgreSQLAdapter#quote_table_name for details.
13
+ #
14
+ # +query+ is the SELECT query to use for the view. This is just
15
+ # a straight-up String, so quoting rules will not apply.
16
+ #
17
+ # Note that you can grant privileges on views using the
18
+ # grant_view_privileges method and revoke them using
19
+ # revoke_view_privileges.
20
+ #
21
+ # ==== Options
22
+ #
23
+ # * <tt>:columns</tt> - you can rename the output columns as
24
+ # necessary. Note that this can be an Array and that it must be
25
+ # the same length as the number of output columns created by
26
+ # +query+.
27
+ # * <tt>:tablespace</tt> - allows you to set the tablespace of a
28
+ # materialized view.
29
+ # * <tt>:with_data</tt> - whether to populate the materialized view
30
+ # upon creation. The default is true.
31
+ #
32
+ # ==== Examples
33
+ #
34
+ # create_materialized_view(:foo_view, 'SELECT * FROM bar')
35
+ # # => CREATE MATERIALIZED VIEW "foo_view" AS SELECT * FROM bar;
36
+ #
37
+ # create_view(
38
+ # { :geospatial => :foo_view },
39
+ # 'SELECT * FROM bar',
40
+ # :columns => [ :id, :name, :the_geom ],
41
+ # :with_data => false
42
+ # )
43
+ # # => CREATE MATERIALIZED VIEW "geospatial"."foo_view" ("id", "name", "the_geom") AS SELECT * FROM bar WITH NO DATA;
44
+ def create_materialized_view(name, query, options = {})
45
+ ActiveRecord::PostgreSQLExtensions::Features.check_feature(:materialized_views)
46
+
47
+ execute PostgreSQLMaterializedViewDefinition.new(self, name, query, options).to_s
48
+ end
49
+
50
+ # Drops a materialized view.
51
+ #
52
+ # ==== Options
53
+ #
54
+ # * <tt>:if_exists</tt> - adds IF EXISTS.
55
+ # * <tt>:cascade</tt> - adds CASCADE.
56
+ def drop_materialized_view(*args)
57
+ options = args.extract_options!
58
+ args.flatten!
59
+
60
+ sql = 'DROP MATERIALIZED VIEW '
61
+ sql << 'IF EXISTS ' if options[:if_exists]
62
+ sql << Array.wrap(args).collect { |v| quote_view_name(v) }.join(', ')
63
+ sql << ' CASCADE' if options[:cascade]
64
+ execute("#{sql};")
65
+ end
66
+
67
+ # Renames a materialized view.
68
+ def rename_materialized_view(name, new_name, options = {})
69
+ execute PostgreSQLMaterializedViewAlterer.new(self, name, {
70
+ :rename_to => new_name
71
+ }, options).to_sql
72
+ end
73
+
74
+ # Change the default of a materialized view column. The default value can
75
+ # be either a straight-up value or a Hash containing an expression
76
+ # in the form <tt>:expression => value</tt> which will be passed
77
+ # through unescaped. This allows you to set expressions and use
78
+ # functions and the like.
79
+ def alter_materialized_view_set_column_default(name, column, default, options = {})
80
+ execute PostgreSQLMaterializedViewAlterer.new(self, name, {
81
+ :column => column,
82
+ :set_default => default
83
+ }, options).to_sql
84
+ end
85
+
86
+ # Drop the default value on a materialized view column
87
+ def alter_materialized_view_drop_column_default(name, column, options = {})
88
+ execute PostgreSQLMaterializedViewAlterer.new(self, name, {
89
+ :drop_default => column
90
+ }, options).to_sql
91
+ end
92
+
93
+ # Change the ownership of a materialized view.
94
+ def alter_materialized_view_owner(name, role, options = {})
95
+ execute PostgreSQLMaterializedViewAlterer.new(self, name, {
96
+ :owner_to => role
97
+ }, options).to_sql
98
+ end
99
+
100
+ # Alter a materialized view's schema.
101
+ def alter_materialized_view_schema(name, schema, options = {})
102
+ execute PostgreSQLMaterializedViewAlterer.new(self, name, {
103
+ :set_schema => schema
104
+ }, options).to_sql
105
+ end
106
+
107
+ # Sets a materialized view's options using a Hash.
108
+ def alter_materialized_view_set_options(name, set_options, options = {})
109
+ execute PostgreSQLMaterializedViewAlterer.new(self, name, {
110
+ :set_options => set_options
111
+ }, options).to_sql
112
+ end
113
+
114
+ # Resets a materialized view's options.
115
+ def alter_materialized_view_reset_options(name, *args)
116
+ options = args.extract_options!
117
+
118
+ execute PostgreSQLMaterializedViewAlterer.new(self, name, {
119
+ :reset_options => args
120
+ }, options).to_sql
121
+ end
122
+
123
+ # Cluster a materialized view on an index.
124
+ def cluster_materialized_view(name, index_name)
125
+ execute PostgreSQLMaterializedViewAlterer.new(self, name, {
126
+ :cluster_on => index_name
127
+ }).to_sql
128
+ end
129
+
130
+ # Remove a cluster from materialized view.
131
+ def remove_cluster_from_materialized_view(name)
132
+ execute PostgreSQLMaterializedViewAlterer.new(self, name, {
133
+ :remove_cluster => true
134
+ }).to_sql
135
+ end
136
+
137
+ # Refreshes the data in a materialized view.
138
+ #
139
+ # ==== Options
140
+ #
141
+ # * <tt>:with_data</tt> - whether to populate the materialized view with
142
+ # data. The default is true.
143
+ def refresh_materialized_view(name, options = {})
144
+ options = {
145
+ :with_data => true
146
+ }.merge(options)
147
+
148
+ sql = "REFRESH MATERIALIZED VIEW #{quote_view_name(name)}"
149
+ sql << " WITH NO DATA" unless options[:with_data]
150
+
151
+ execute "#{sql};"
152
+ end
153
+ end
154
+
155
+ # Creates a PostgreSQL materialized view definition. This class isn't
156
+ # really meant to be used directly. Instead, see
157
+ # PostgreSQLAdapter#create_materialized_view for usage.
158
+ class PostgreSQLMaterializedViewDefinition
159
+ include ActiveRecord::PostgreSQLExtensions::Utils
160
+
161
+ attr_accessor :base, :name, :query, :options
162
+
163
+ def initialize(base, name, query, options = {}) #:nodoc:
164
+ @base, @name, @query, @options = base, name, query, options
165
+ end
166
+
167
+ def to_sql #:nodoc:
168
+ sql = "CREATE MATERIALIZED VIEW #{base.quote_view_name(name)} "
169
+
170
+ if options[:columns]
171
+ sql << '(' << Array.wrap(options[:columns]).collect do |c|
172
+ base.quote_column_name(c)
173
+ end.join(', ') << ') '
174
+ end
175
+
176
+ sql << "WITH (#{options_from_hash_or_string(options[:with_options])}) " if options[:with_options].present?
177
+
178
+ sql << "TABLESPACE #{base.quote_tablespace(options[:tablespace])} " if options[:tablespace]
179
+ sql << "AS #{query}"
180
+ sql << " WITH NO DATA" if options.key?(:with_data) && !options[:with_data]
181
+ "#{sql};"
182
+ end
183
+ alias :to_s :to_sql
184
+ end
185
+
186
+ # Alters a PostgreSQL materialized view definition. This class isn't
187
+ # really meant to be used directly. Instead, see the various
188
+ # PostgreSQLAdapter materialied views methods for usage.
189
+ class PostgreSQLMaterializedViewAlterer
190
+ include ActiveRecord::PostgreSQLExtensions::Utils
191
+
192
+ attr_accessor :base, :name, :actions, :options
193
+
194
+ VALID_OPTIONS = %w{
195
+ set_default
196
+ drop_default
197
+ owner_to
198
+ rename_to
199
+ set_schema
200
+ set_options
201
+ reset_options
202
+ cluster_on
203
+ remove_cluster
204
+ }.freeze
205
+
206
+ def initialize(base, name, actions, options = {}) #:nodoc:
207
+ @base, @name, @actions, @options = base, name, actions, options
208
+ end
209
+
210
+ def to_sql #:nodoc:
211
+ all_sql = []
212
+
213
+ VALID_OPTIONS.each do |key|
214
+ key = key.to_sym
215
+
216
+ if actions.key?(key)
217
+ sql = "ALTER MATERIALIZED VIEW "
218
+ sql << "IF EXISTS " if options[:if_exists]
219
+ sql << "#{base.quote_view_name(name)} "
220
+
221
+ sql << case key
222
+ when :set_default
223
+ expression = if actions[:set_default].is_a?(Hash) && actions[:set_default].key?(:expression)
224
+ actions[:set_default][:expression]
225
+ else
226
+ base.quote(actions[:set_default])
227
+ end
228
+
229
+ "ALTER COLUMN #{base.quote_column_name(actions[:column])} SET DEFAULT #{expression}"
230
+
231
+ when :drop_default
232
+ "ALTER COLUMN #{base.quote_column_name(actions[:drop_default])} DROP DEFAULT"
233
+
234
+ when :owner_to
235
+ "OWNER TO #{base.quote_role(actions[:owner_to])}"
236
+
237
+ when :rename_to
238
+ "RENAME TO #{base.quote_generic_ignore_scoped_schema(actions[:rename_to])}"
239
+
240
+ when :set_schema
241
+ "SET SCHEMA #{base.quote_schema(actions[:set_schema])}"
242
+
243
+ when :set_options
244
+ ActiveRecord::PostgreSQLExtensions::Features.check_feature(:view_set_options)
245
+
246
+ "SET (#{options_from_hash_or_string(actions[:set_options])})" if actions[:set_options].present?
247
+
248
+ when :reset_options
249
+ ActiveRecord::PostgreSQLExtensions::Features.check_feature(:view_set_options)
250
+
251
+ 'RESET (' << Array.wrap(actions[:reset_options]).collect { |value|
252
+ base.quote_generic(value)
253
+ }.join(", ") << ')'
254
+
255
+ when :cluster_on
256
+ "CLUSTER ON #{base.quote_generic(actions[:cluster_on])}"
257
+
258
+ when :remove_cluster
259
+ next unless actions[:remove_cluster]
260
+
261
+ "SET WITHOUT CLUSTER"
262
+ end
263
+
264
+ all_sql << "#{sql};"
265
+ end
266
+ end
267
+
268
+ all_sql.join("\n")
269
+ end
270
+ end
271
+ end
272
+ end
@@ -66,6 +66,24 @@ module ActiveRecord
66
66
  execute PostgreSQLGrantPrivilege.new(self, :tablespace, tablespaces, privileges, roles, options).to_sql
67
67
  end
68
68
 
69
+ # Grants privileges on views. You can specify multiple views,
70
+ # roles and privileges all at once using Arrays for each of the
71
+ # desired parameters. See PostgreSQLGrantPrivilege for
72
+ # usage.
73
+ def grant_view_privileges(views, privileges, roles, options = {})
74
+ execute PostgreSQLGrantPrivilege.new(self, :view, views, privileges, roles, options, :named_object_type => false).to_sql
75
+ end
76
+
77
+ # Grants privileges on views. You can specify multiple
78
+ # materialized views, roles and privileges all at once using Arrays for
79
+ # each of the desired parameters. See PostgreSQLGrantPrivilege for
80
+ # usage.
81
+ def grant_materialized_view_privileges(materialized_views, privileges, roles, options = {})
82
+ ActiveRecord::PostgreSQLExtensions::Features.check_feature(:materialized_views)
83
+
84
+ execute PostgreSQLGrantPrivilege.new(self, :materialized_view, materialized_views, privileges, roles, options, :named_object_type => false).to_sql
85
+ end
86
+
69
87
  # Grants role membership to another role. You can specify multiple
70
88
  # roles for both the roles and the role_names parameters using
71
89
  # Arrays.
@@ -76,9 +94,9 @@ module ActiveRecord
76
94
  # clause to the command.
77
95
  def grant_role_membership(roles, role_names, options = {})
78
96
  sql = "GRANT "
79
- sql << Array(roles).collect { |r| quote_role(r) }.join(', ')
97
+ sql << Array.wrap(roles).collect { |r| quote_role(r) }.join(', ')
80
98
  sql << ' TO '
81
- sql << Array(role_names).collect { |r| quote_role(r) }.join(', ')
99
+ sql << Array.wrap(role_names).collect { |r| quote_role(r) }.join(', ')
82
100
  sql << ' WITH ADMIN OPTION' if options[:with_admin_option]
83
101
  execute("#{sql};")
84
102
  end
@@ -139,6 +157,24 @@ module ActiveRecord
139
157
  execute PostgreSQLRevokePrivilege.new(self, :tablespace, tablespaces, privileges, roles, options).to_sql
140
158
  end
141
159
 
160
+ # Revokes materialized view privileges. You can specify multiple
161
+ # materialized views, roles and privileges all at once using Arrays for
162
+ # each of the desired parameters. See PostgreSQLRevokePrivilege for
163
+ # usage.
164
+ def revoke_materialized_view_privileges(materialized_views, privileges, roles, options = {})
165
+ ActiveRecord::PostgreSQLExtensions::Features.check_feature(:materialized_views)
166
+
167
+ execute PostgreSQLRevokePrivilege.new(self, :materialized_view, materialized_views, privileges, roles, options, :named_object_type => false).to_sql
168
+ end
169
+
170
+ # Revokes view privileges. You can specify multiple views,
171
+ # roles and privileges all at once using Arrays for each of the
172
+ # desired parameters. See PostgreSQLRevokePrivilege for
173
+ # usage.
174
+ def revoke_view_privileges(views, privileges, roles, options = {})
175
+ execute PostgreSQLRevokePrivilege.new(self, :view, views, privileges, roles, options, :named_object_type => false).to_sql
176
+ end
177
+
142
178
  # Revokes role membership. You can specify multiple
143
179
  # roles for both the roles and the role_names parameters using
144
180
  # Arrays.
@@ -151,9 +187,9 @@ module ActiveRecord
151
187
  def revoke_role_membership(roles, role_names, options = {})
152
188
  sql = 'REVOKE '
153
189
  sql << 'ADMIN_OPTION_FOR ' if options[:admin_option_for]
154
- sql << Array(roles).collect { |r| quote_role(r) }.join(', ')
190
+ sql << Array.wrap(roles).collect { |r| quote_role(r) }.join(', ')
155
191
  sql << ' FROM '
156
- sql << Array(role_names).collect { |r| quote_role(r) }.join(', ')
192
+ sql << Array.wrap(role_names).collect { |r| quote_role(r) }.join(', ')
157
193
  sql << ' CASCADE' if options[:cascade]
158
194
  execute("#{sql};")
159
195
  end
@@ -178,11 +214,13 @@ module ActiveRecord
178
214
  :function => [ 'execute', 'all' ],
179
215
  :language => [ 'usage', 'all' ],
180
216
  :schema => [ 'create', 'usage', 'all' ],
181
- :tablespace => [ 'create', 'all' ]
217
+ :tablespace => [ 'create', 'all' ],
218
+ :view => [ 'select', 'insert', 'update', 'delete', 'references', 'trigger', 'all' ],
219
+ :materialized_view => [ 'select', 'insert', 'update', 'delete', 'references', 'trigger' ]
182
220
  }.freeze
183
221
 
184
222
  def assert_valid_privileges type, privileges
185
- check_privileges = Array(privileges).collect(&:to_s) - PRIVILEGE_TYPES[type]
223
+ check_privileges = Array.wrap(privileges).collect(&:to_s) - PRIVILEGE_TYPES[type]
186
224
  if !check_privileges.empty?
187
225
  raise ActiveRecord::InvalidPrivilegeTypes.new(type, check_privileges)
188
226
  end
@@ -221,20 +259,20 @@ module ActiveRecord
221
259
  class PostgreSQLGrantPrivilege < PostgreSQLPrivilege
222
260
  def to_sql #:nodoc:
223
261
  my_query_options = {
224
- :quote_objects => true
262
+ :quote_objects => true,
263
+ :named_object_type => true
225
264
  }.merge query_options
226
265
 
227
- sql = "GRANT #{Array(privileges).collect(&:to_s).collect(&:upcase).join(', ')} ON "
266
+ sql = "GRANT #{Array.wrap(privileges).collect(&:to_s).collect(&:upcase).join(', ')} ON "
228
267
 
229
268
  if options[:all]
230
- if !ActiveRecord::PostgreSQLExtensions::Features.modify_mass_privileges?
231
- raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('modify mass privileges')
232
- end
269
+ ActiveRecord::PostgreSQLExtensions::Features.check_feature(:modify_mass_privileges)
233
270
 
234
271
  sql << "ALL #{type.to_s.upcase}S IN SCHEMA #{base.quote_schema(objects)}"
235
272
  else
236
- sql << "#{type.to_s.upcase} "
237
- sql << Array(objects).collect do |t|
273
+ sql << "#{type.to_s.gsub('_', ' ').upcase} " if my_query_options[:named_object_type]
274
+
275
+ sql << Array.wrap(objects).collect do |t|
238
276
  if my_query_options[:quote_objects]
239
277
  if my_query_options[:ignore_schema]
240
278
  base.quote_generic_ignore_scoped_schema(t)
@@ -247,7 +285,7 @@ module ActiveRecord
247
285
  end.join(', ')
248
286
  end
249
287
 
250
- sql << ' TO ' << Array(roles).collect do |r|
288
+ sql << ' TO ' << Array.wrap(roles).collect do |r|
251
289
  r = r.to_s
252
290
  if r.upcase == 'PUBLIC'
253
291
  'PUBLIC'
@@ -299,22 +337,22 @@ module ActiveRecord
299
337
  class PostgreSQLRevokePrivilege < PostgreSQLPrivilege
300
338
  def to_sql #:nodoc:
301
339
  my_query_options = {
302
- :quote_objects => true
340
+ :quote_objects => true,
341
+ :named_object_type => true
303
342
  }.merge query_options
304
343
 
305
344
  sql = 'REVOKE '
306
345
  sql << 'GRANT OPTION FOR ' if options[:grant_option_for]
307
- sql << "#{Array(privileges).collect(&:to_s).collect(&:upcase).join(', ')} ON "
346
+ sql << "#{Array.wrap(privileges).collect(&:to_s).collect(&:upcase).join(', ')} ON "
308
347
 
309
348
  if options[:all]
310
- if !ActiveRecord::PostgreSQLExtensions::Features.modify_mass_privileges?
311
- raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('modify mass privileges')
312
- end
349
+ ActiveRecord::PostgreSQLExtensions::Features.check_feature(:modify_mass_privileges)
313
350
 
314
351
  sql << "ALL #{type.to_s.upcase}S IN SCHEMA #{base.quote_schema(objects)}"
315
352
  else
316
- sql << "#{type.to_s.upcase} "
317
- sql << Array(objects).collect do |t|
353
+ sql << "#{type.to_s.upcase} " if my_query_options[:named_object_type]
354
+
355
+ sql << Array.wrap(objects).collect do |t|
318
356
  if my_query_options[:quote_objects]
319
357
  if my_query_options[:ignore_schema]
320
358
  base.quote_generic_ignore_scoped_schema(t)
@@ -327,7 +365,7 @@ module ActiveRecord
327
365
  end.join(', ')
328
366
  end
329
367
 
330
- sql << ' FROM ' << Array(roles).collect do |r|
368
+ sql << ' FROM ' << Array.wrap(roles).collect do |r|
331
369
  r = r.to_s
332
370
  if r.upcase == 'PUBLIC'
333
371
  'PUBLIC'
@@ -10,27 +10,38 @@ module ActiveRecord
10
10
 
11
11
  module ConnectionAdapters
12
12
  class PostgreSQLAdapter
13
+ # Creates a PostgreSQL ROLE. See PostgreSQLRole for details on options.
13
14
  def create_role(name, options = {})
14
15
  execute PostgreSQLRole.new(self, :create, name, options).to_sql
15
16
  end
16
17
  alias :create_user :create_role
17
18
 
19
+ # Alters a PostgreSQL ROLE. See PostgreSQLRole for details on options.
18
20
  def alter_role(name, options = {})
19
21
  execute PostgreSQLRole.new(self, :alter, name, options).to_sql
20
22
  end
21
23
  alias :alter_user :alter_role
22
24
 
23
- def drop_role(name, options = {})
25
+ # Drop PostgreSQL ROLEs.
26
+ #
27
+ # ==== Options
28
+ #
29
+ # * <tt>:if_exists</tt> - don't raise an error if the ROLE doesn't
30
+ # exist. The default is false.
31
+ def drop_role(*args)
32
+ options = args.extract_options!
33
+ args.flatten!
34
+
24
35
  sql = 'DROP ROLE '
25
36
  sql << 'IF EXISTS ' if options[:if_exists]
26
- sql << Array(name).collect { |r| quote_role(r) }.join(', ')
37
+ sql << Array.wrap(args).collect { |r| quote_role(r) }.join(', ')
27
38
  execute("#{sql};")
28
39
  end
29
40
  alias :drop_user :drop_role
30
41
  end
31
42
 
32
- # This is a base class for PostgreSQLGrantPrivilege and
33
- # PostgreSQLRevokePrivilege and is not meant to be used directly.
43
+ # This is a base class for creating and altering ROLEs and is not meant to
44
+ # be used directly.
34
45
  class PostgreSQLRole
35
46
  attr_accessor :base, :action, :name, :options
36
47
 
@@ -99,17 +110,17 @@ module ActiveRecord
99
110
 
100
111
  if options[:in_role].present?
101
112
  sql << 'IN ROLE'
102
- sql << Array(options[:in_role]).collect { |r| base.quote_role(r) }.join(', ')
113
+ sql << Array.wrap(options[:in_role]).collect { |r| base.quote_role(r) }.join(', ')
103
114
  end
104
115
 
105
116
  if options[:role].present?
106
117
  sql << 'ROLE'
107
- sql << Array(options[:role]).collect { |r| base.quote_role(r) }.join(', ')
118
+ sql << Array.wrap(options[:role]).collect { |r| base.quote_role(r) }.join(', ')
108
119
  end
109
120
 
110
121
  if options[:admin].present?
111
122
  sql << 'ADMIN'
112
- sql << Array(options[:admin]).collect { |r| base.quote_role(r) }.join(', ')
123
+ sql << Array.wrap(options[:admin]).collect { |r| base.quote_role(r) }.join(', ')
113
124
  end
114
125
 
115
126
  "#{sql.join(' ')};"