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.
- checksums.yaml +15 -0
- data/.gitignore +4 -0
- data/Gemfile +1 -0
- data/Guardfile +3 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +10 -3
- data/lib/active_record/postgresql_extensions/adapter_extensions.rb +100 -60
- data/lib/active_record/postgresql_extensions/constraints.rb +13 -17
- data/lib/active_record/postgresql_extensions/event_triggers.rb +129 -0
- data/lib/active_record/postgresql_extensions/extensions.rb +14 -15
- data/lib/active_record/postgresql_extensions/features.rb +80 -41
- data/lib/active_record/postgresql_extensions/functions.rb +1 -1
- data/lib/active_record/postgresql_extensions/geometry.rb +6 -8
- data/lib/active_record/postgresql_extensions/indexes.rb +19 -11
- data/lib/active_record/postgresql_extensions/languages.rb +1 -1
- data/lib/active_record/postgresql_extensions/materialized_views.rb +272 -0
- data/lib/active_record/postgresql_extensions/permissions.rb +60 -22
- data/lib/active_record/postgresql_extensions/roles.rb +18 -7
- data/lib/active_record/postgresql_extensions/rules.rb +5 -0
- data/lib/active_record/postgresql_extensions/schemas.rb +39 -3
- data/lib/active_record/postgresql_extensions/sequences.rb +6 -3
- data/lib/active_record/postgresql_extensions/tables.rb +47 -19
- data/lib/active_record/postgresql_extensions/tablespaces.rb +1 -1
- data/lib/active_record/postgresql_extensions/text_search.rb +3 -3
- data/lib/active_record/postgresql_extensions/triggers.rb +3 -3
- data/lib/active_record/postgresql_extensions/types.rb +104 -1
- data/lib/active_record/postgresql_extensions/utils.rb +35 -13
- data/lib/active_record/postgresql_extensions/vacuum.rb +1 -1
- data/lib/active_record/postgresql_extensions/version.rb +1 -1
- data/lib/active_record/postgresql_extensions/views.rb +137 -6
- data/lib/activerecord-postgresql-extensions.rb +13 -11
- data/test/{adapter_tests.rb → adapter_extensions_tests.rb} +96 -3
- data/test/constraints_tests.rb +216 -104
- data/test/event_triggers_tests.rb +109 -0
- data/test/extensions_tests.rb +47 -39
- data/test/functions_tests.rb +47 -38
- data/test/geometry_tests.rb +268 -135
- data/test/{index_tests.rb → indexes_tests.rb} +16 -16
- data/test/languages_tests.rb +26 -9
- data/test/materialized_views_tests.rb +174 -0
- data/test/permissions_tests.rb +159 -45
- data/test/roles_tests.rb +17 -7
- data/test/rules_tests.rb +14 -6
- data/test/schemas_tests.rb +35 -9
- data/test/sequences_tests.rb +9 -11
- data/test/tables_tests.rb +132 -42
- data/test/tablespace_tests.rb +21 -15
- data/test/test_helper.rb +56 -10
- data/test/text_search_tests.rb +42 -44
- data/test/trigger_tests.rb +1 -3
- data/test/types_tests.rb +95 -0
- data/test/vacuum_tests.rb +1 -3
- data/test/views_tests.rb +203 -0
- 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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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
|
33
|
-
#
|
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(' ')};"
|