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.
- 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(' ')};"
|