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
@@ -0,0 +1,129 @@
|
|
1
|
+
|
2
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
class InvalidEventTriggerEventType < ActiveRecordError #:nodoc:
|
6
|
+
def initialize(events)
|
7
|
+
super("Invalid trigger event(s) - #{events.inspect}")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ConnectionAdapters
|
12
|
+
class PostgreSQLAdapter
|
13
|
+
# Creates a PostgreSQL event trigger. Available in PostgreSQL 9.3+.
|
14
|
+
#
|
15
|
+
# +event+ is one of the valid event trigger event names. See the
|
16
|
+
# PostgreSQL documentation for details.
|
17
|
+
#
|
18
|
+
# ==== Options
|
19
|
+
#
|
20
|
+
# ==== Example
|
21
|
+
#
|
22
|
+
def create_event_trigger(name, event, function, options = {})
|
23
|
+
ActiveRecord::PostgreSQLExtensions::Features.check_feature(:event_triggers)
|
24
|
+
|
25
|
+
execute PostgreSQLEventTriggerDefinition.new(self, name, event, function, options).to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
# Drops an event trigger.
|
29
|
+
#
|
30
|
+
# ==== Options
|
31
|
+
#
|
32
|
+
# * <tt>:if_exists</tt> - adds IF EXISTS.
|
33
|
+
# * <tt>:cascade</tt> - cascades changes down to objects referring
|
34
|
+
# to the trigger.
|
35
|
+
def drop_event_trigger(name, options = {})
|
36
|
+
sql = 'DROP EVENT TRIGGER '
|
37
|
+
sql << 'IF EXISTS ' if options[:if_exists]
|
38
|
+
sql << quote_generic(name)
|
39
|
+
sql << ' CASCADE' if options[:cascade]
|
40
|
+
execute("#{sql};")
|
41
|
+
end
|
42
|
+
|
43
|
+
# Renames an event trigger.
|
44
|
+
def rename_event_trigger(name, new_name)
|
45
|
+
execute "ALTER EVENT TRIGGER #{quote_generic(name)} RENAME TO #{quote_generic(new_name)};"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Reassigns ownership of an event trigger.
|
49
|
+
def alter_event_trigger_owner(name, role)
|
50
|
+
execute "ALTER EVENT TRIGGER #{quote_generic(name)} OWNER TO #{quote_generic(role)};"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Enables an event trigger.
|
54
|
+
#
|
55
|
+
# ==== Options
|
56
|
+
#
|
57
|
+
# * <tt>:replica
|
58
|
+
def enable_event_trigger(name, options = {})
|
59
|
+
if options[:always] && options[:replica]
|
60
|
+
raise ArgumentError.new("Cannot use :replica and :always together when enabling an event trigger.")
|
61
|
+
end
|
62
|
+
|
63
|
+
sql = "ALTER EVENT TRIGGER #{quote_generic(name)} ENABLE"
|
64
|
+
|
65
|
+
if options[:always]
|
66
|
+
sql << ' ALWAYS'
|
67
|
+
elsif options[:replica]
|
68
|
+
sql << ' REPLICA'
|
69
|
+
end
|
70
|
+
|
71
|
+
execute "#{sql};"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Disables an event trigger.
|
75
|
+
def disable_event_trigger(name)
|
76
|
+
execute "ALTER EVENT TRIGGER #{quote_generic(name)} DISABLE;"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Creates a PostgreSQL event trigger definition. This class isn't really
|
81
|
+
# meant to be used directly. You'd be better off sticking to
|
82
|
+
# PostgreSQLAdapter#create_event_trigger. Honestly.
|
83
|
+
class PostgreSQLEventTriggerDefinition
|
84
|
+
attr_accessor :base, :name, :event, :function, :options
|
85
|
+
|
86
|
+
def initialize(base, name, event, function, options = {}) #:nodoc:
|
87
|
+
assert_valid_event_name(event)
|
88
|
+
|
89
|
+
@base, @name, @event, @function, @options =
|
90
|
+
base, name, event, function, options
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_sql #:nodoc:
|
94
|
+
sql = "CREATE EVENT TRIGGER #{base.quote_generic(name)} ON #{base.quote_generic(event)}"
|
95
|
+
|
96
|
+
if options[:when].present?
|
97
|
+
sql << "\n WHEN "
|
98
|
+
|
99
|
+
sql << options[:when].inject([]) { |memo, (k, v)|
|
100
|
+
memo.tap {
|
101
|
+
values = Array.wrap(v).collect { |value|
|
102
|
+
base.quote(value)
|
103
|
+
}.join(', ')
|
104
|
+
|
105
|
+
memo << "#{base.quote_generic(k)} IN (#{values})"
|
106
|
+
}
|
107
|
+
}.join("\n AND ")
|
108
|
+
|
109
|
+
sql << "\n "
|
110
|
+
end
|
111
|
+
|
112
|
+
sql << " EXECUTE PROCEDURE #{base.quote_function(function)}()"
|
113
|
+
|
114
|
+
"#{sql};"
|
115
|
+
end
|
116
|
+
alias :to_s :to_sql
|
117
|
+
|
118
|
+
|
119
|
+
private
|
120
|
+
EVENT_NAMES = %w{ ddl_command_start ddl_command_end sql_drop }.freeze
|
121
|
+
|
122
|
+
def assert_valid_event_name(event) #:nodoc:
|
123
|
+
if !EVENT_NAMES.include?(event.to_s.downcase)
|
124
|
+
raise ActiveRecord::InvalidEventTriggerEventType.new(event)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -8,8 +8,7 @@ module ActiveRecord
|
|
8
8
|
# either a parser_name or a source_config option as per the PostgreSQL
|
9
9
|
# text search docs.
|
10
10
|
def create_extension(name, options = {})
|
11
|
-
|
12
|
-
!ActiveRecord::PostgreSQLExtensions::Features.extensions?
|
11
|
+
ActiveRecord::PostgreSQLExtensions::Features.check_feature(:extensions)
|
13
12
|
|
14
13
|
sql = "CREATE EXTENSION "
|
15
14
|
sql << "IF NOT EXISTS " if options[:if_not_exists]
|
@@ -26,22 +25,20 @@ module ActiveRecord
|
|
26
25
|
# * <tt>if_exists</tt> - adds IF EXISTS.
|
27
26
|
# * <tt>cascade</tt> - adds CASCADE.
|
28
27
|
def drop_extension(*args)
|
29
|
-
|
30
|
-
!ActiveRecord::PostgreSQLExtensions::Features.extensions?
|
28
|
+
ActiveRecord::PostgreSQLExtensions::Features.check_feature(:extensions)
|
31
29
|
|
32
30
|
options = args.extract_options!
|
33
31
|
|
34
32
|
sql = 'DROP EXTENSION '
|
35
33
|
sql << 'IF EXISTS ' if options[:if_exists]
|
36
|
-
sql << Array(args).collect { |name| quote_generic(name) }.join(', ')
|
34
|
+
sql << Array.wrap(args).collect { |name| quote_generic(name) }.join(', ')
|
37
35
|
sql << ' CASCADE' if options[:cascade]
|
38
36
|
|
39
37
|
execute("#{sql};")
|
40
38
|
end
|
41
39
|
|
42
40
|
def update_extension(name, new_version = nil)
|
43
|
-
|
44
|
-
!ActiveRecord::PostgreSQLExtensions::Features.extensions?
|
41
|
+
ActiveRecord::PostgreSQLExtensions::Features.check_feature(:extensions)
|
45
42
|
|
46
43
|
sql = "ALTER EXTENSION #{quote_generic(name)} UPDATE"
|
47
44
|
sql << " TO #{quote_generic(new_version)}" if new_version;
|
@@ -49,8 +46,7 @@ module ActiveRecord
|
|
49
46
|
end
|
50
47
|
|
51
48
|
def alter_extension_schema(name, schema)
|
52
|
-
|
53
|
-
!ActiveRecord::PostgreSQLExtensions::Features.extensions?
|
49
|
+
ActiveRecord::PostgreSQLExtensions::Features.check_feature(:extensions)
|
54
50
|
|
55
51
|
execute "ALTER EXTENSION #{quote_generic(name)} SET SCHEMA #{quote_schema(schema)};"
|
56
52
|
end
|
@@ -124,8 +120,7 @@ module ActiveRecord
|
|
124
120
|
# * <tt>:operator_class</tt> and <tt>:operator_family</tt> - <tt>:name</tt>
|
125
121
|
# and <tt>:indexing_method</tt>.
|
126
122
|
def alter_extension(name, options = {})
|
127
|
-
|
128
|
-
!ActiveRecord::PostgreSQLExtensions::Features.extensions?
|
123
|
+
ActiveRecord::PostgreSQLExtensions::Features.check_feature(:extensions)
|
129
124
|
|
130
125
|
alterer = PostgreSQLExtensionAlterer.new(self, name, options)
|
131
126
|
|
@@ -139,8 +134,7 @@ module ActiveRecord
|
|
139
134
|
|
140
135
|
class PostgreSQLExtensionAlterer
|
141
136
|
def initialize(base, name, options = {}) #:nodoc:
|
142
|
-
|
143
|
-
!ActiveRecord::PostgreSQLExtensions::Features.extensions?
|
137
|
+
ActiveRecord::PostgreSQLExtensions::Features.check_feature(:extensions)
|
144
138
|
|
145
139
|
@base, @name, @options = base, name, options
|
146
140
|
@sql = options.collect { |k, v| build_statement(k, v) }
|
@@ -187,6 +181,8 @@ module ActiveRecord
|
|
187
181
|
action = :add
|
188
182
|
end
|
189
183
|
|
184
|
+
assert_valid_action(action)
|
185
|
+
|
190
186
|
sql = "ALTER EXTENSION #{@base.quote_generic(@name || name)} #{action.to_s.upcase} "
|
191
187
|
sql << case option
|
192
188
|
when 'aggregate'
|
@@ -199,7 +195,7 @@ module ActiveRecord
|
|
199
195
|
|
200
196
|
"AGGREGATE %s (%s)" % [
|
201
197
|
@base.quote_generic(name),
|
202
|
-
Array(types).collect { |t|
|
198
|
+
Array.wrap(types).collect { |t|
|
203
199
|
@base.quote_generic(t)
|
204
200
|
}.join(', ')
|
205
201
|
]
|
@@ -223,7 +219,7 @@ module ActiveRecord
|
|
223
219
|
[ args.shift, *args ]
|
224
220
|
end
|
225
221
|
|
226
|
-
"FUNCTION #{@base.quote_function(name)}(#{Array(arguments).join(', ')})"
|
222
|
+
"FUNCTION #{@base.quote_function(name)}(#{Array.wrap(arguments).join(', ')})"
|
227
223
|
when 'operator'
|
228
224
|
name, left_type, right_type =
|
229
225
|
extract_hash_or_array_options(args, :name, :left_type, :right_type)
|
@@ -234,6 +230,9 @@ module ActiveRecord
|
|
234
230
|
extract_hash_or_array_options(args, :name, :indexing_method)
|
235
231
|
|
236
232
|
"#{option.upcase.gsub('_', ' ')} #{@base.quote_generic(object_name)} USING #{@base.quote_generic(indexing_method)})"
|
233
|
+
|
234
|
+
else
|
235
|
+
raise ArgumentError.new("Unknown operation #{option}")
|
237
236
|
end
|
238
237
|
sql
|
239
238
|
end
|
@@ -1,46 +1,85 @@
|
|
1
1
|
|
2
2
|
module ActiveRecord
|
3
3
|
module PostgreSQLExtensions
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
4
|
+
class FeatureNotSupportedError < Exception
|
5
|
+
def initialize(feature)
|
6
|
+
super(%{The feature "#{feature}" is not supported by server. (Server version #{ActiveRecord::PostgreSQLExtensions.SERVER_VERSION}.)"})
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Features
|
11
|
+
class << self
|
12
|
+
%w{
|
13
|
+
copy_from_encoding
|
14
|
+
copy_from_freeze
|
15
|
+
copy_from_program
|
16
|
+
create_schema_if_not_exists
|
17
|
+
create_table_if_not_exists
|
18
|
+
create_table_unlogged
|
19
|
+
event_triggers
|
20
|
+
extensions
|
21
|
+
foreign_tables
|
22
|
+
materialized_views
|
23
|
+
modify_mass_privileges
|
24
|
+
postgis
|
25
|
+
rename_rule
|
26
|
+
type_if_not_exists
|
27
|
+
view_if_exists
|
28
|
+
view_recursive
|
29
|
+
view_set_options
|
30
|
+
}.each do |feature|
|
31
|
+
self.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
32
|
+
def #{feature}?
|
33
|
+
sniff_features unless sniffed?
|
34
|
+
!!@has_#{feature}
|
35
|
+
end
|
36
|
+
RUBY
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_feature(feature)
|
40
|
+
if !self.send("#{feature}?")
|
41
|
+
raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new(feature)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def sniffed?
|
47
|
+
@sniffed
|
48
|
+
end
|
49
|
+
|
50
|
+
def sniff_features
|
51
|
+
@sniffed = true
|
52
|
+
|
53
|
+
if ActiveRecord::PostgreSQLExtensions.SERVER_VERSION >= '9.3'
|
54
|
+
@has_copy_from_freeze = true
|
55
|
+
@has_copy_from_program = true
|
56
|
+
@has_create_schema_if_not_exists = true
|
57
|
+
@has_event_triggers = true
|
58
|
+
@has_materialized_views = true
|
59
|
+
@has_rename_rule = true
|
60
|
+
@has_type_if_not_exists = true
|
61
|
+
@has_view_recursive = true
|
62
|
+
end
|
63
|
+
|
64
|
+
if ActiveRecord::PostgreSQLExtensions.SERVER_VERSION >= '9.1'
|
65
|
+
@has_copy_from_encoding = true
|
66
|
+
@has_create_table_if_not_exists = true
|
67
|
+
@has_create_table_unlogged = true
|
68
|
+
@has_extensions = true
|
69
|
+
@has_foreign_tables = true
|
70
|
+
@has_view_if_exists = true
|
71
|
+
@has_view_set_options = true
|
72
|
+
end
|
73
|
+
|
74
|
+
if ActiveRecord::PostgreSQLExtensions.SERVER_VERSION >= '9.0'
|
75
|
+
@has_modify_mass_privileges = true
|
76
|
+
end
|
77
|
+
|
78
|
+
if !!ActiveRecord::PostgreSQLExtensions::PostGIS.VERSION
|
79
|
+
@has_postgis = true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
45
84
|
end
|
46
85
|
end
|
@@ -134,20 +134,20 @@ module ActiveRecord
|
|
134
134
|
ActiveRecord::PostgreSQLExtensions::PostGIS.VERSION[:lib] < '2.0' ||
|
135
135
|
opts[:force_constraints]
|
136
136
|
)
|
137
|
-
|
137
|
+
table_constraints << PostgreSQLCheckConstraint.new(
|
138
138
|
base,
|
139
139
|
"ST_srid(#{base.quote_column_name(column_name)}) = (#{opts[:srid].to_i})",
|
140
140
|
:name => "enforce_srid_#{column_name}"
|
141
141
|
)
|
142
142
|
|
143
|
-
|
143
|
+
table_constraints << PostgreSQLCheckConstraint.new(
|
144
144
|
base,
|
145
145
|
"ST_ndims(#{base.quote_column_name(column_name)}) = #{opts[:ndims].to_i}",
|
146
146
|
:name => "enforce_dims_#{column_name}"
|
147
147
|
)
|
148
148
|
|
149
149
|
if opts[:geometry_type].to_s.upcase != 'GEOMETRY'
|
150
|
-
|
150
|
+
table_constraints << PostgreSQLCheckConstraint.new(
|
151
151
|
base,
|
152
152
|
"geometrytype(#{base.quote_column_name(column_name)}) = '#{opts[:geometry_type].to_s.upcase}'::text OR #{base.quote_column_name(column_name)} IS NULL",
|
153
153
|
:name => "enforce_geotype_#{column_name}"
|
@@ -167,13 +167,11 @@ module ActiveRecord
|
|
167
167
|
[ schema || 'public', table_name ]
|
168
168
|
end
|
169
169
|
|
170
|
-
@post_processing ||= Array.new
|
171
|
-
|
172
170
|
if opts[:add_geometry_columns_entry] &&
|
173
171
|
opts[:spatial_column_type].to_s != 'geography' &&
|
174
172
|
ActiveRecord::PostgreSQLExtensions::PostGIS.VERSION[:lib] < '2.0'
|
175
173
|
|
176
|
-
|
174
|
+
self.post_processing << sprintf(
|
177
175
|
"DELETE FROM \"geometry_columns\" WHERE f_table_catalog = '' AND " +
|
178
176
|
"f_table_schema = %s AND " +
|
179
177
|
"f_table_name = %s AND " +
|
@@ -183,7 +181,7 @@ module ActiveRecord
|
|
183
181
|
base.quote(column_name.to_s)
|
184
182
|
)
|
185
183
|
|
186
|
-
|
184
|
+
self.post_processing << sprintf(
|
187
185
|
"INSERT INTO \"geometry_columns\" VALUES ('', %s, %s, %s, %d, %d, %s);",
|
188
186
|
base.quote(current_scoped_schema.to_s),
|
189
187
|
base.quote(current_table_name.to_s),
|
@@ -201,7 +199,7 @@ module ActiveRecord
|
|
201
199
|
"#{current_table_name}_#{column_name}_gist_index"
|
202
200
|
end
|
203
201
|
|
204
|
-
|
202
|
+
self.post_processing << PostgreSQLIndexDefinition.new(
|
205
203
|
base,
|
206
204
|
index_name,
|
207
205
|
{ current_scoped_schema => current_table_name },
|
@@ -18,7 +18,8 @@ module ActiveRecord
|
|
18
18
|
class PostgreSQLAdapter
|
19
19
|
# Creates an index. This method is an alternative to the standard
|
20
20
|
# ActiveRecord add_index method and includes PostgreSQL-specific
|
21
|
-
# options.
|
21
|
+
# options. Indexes can be created on tables as well as materialized views
|
22
|
+
# starting with PostgreSQL 9.3.
|
22
23
|
#
|
23
24
|
# === Differences to add_index
|
24
25
|
#
|
@@ -62,6 +63,9 @@ module ActiveRecord
|
|
62
63
|
# * <tt>:conditions</tt> - adds an optional WHERE clause to the
|
63
64
|
# index. (You can alternatively use the option <tt>:where</tt>
|
64
65
|
# instead.)
|
66
|
+
# * <tt>:index_parameters</tt> - a simple String or Hash used to
|
67
|
+
# assign index storage parameters. See the PostgreSQL docs for
|
68
|
+
# details on the various storage parameters available.
|
65
69
|
#
|
66
70
|
# ==== Column Options
|
67
71
|
#
|
@@ -106,8 +110,8 @@ module ActiveRecord
|
|
106
110
|
# # additional options
|
107
111
|
# create_index('search_idx', :foo, :tsvector, :using => :gin)
|
108
112
|
# # => CREATE INDEX "search_idx" ON "foo" USING "gin"("tsvector");
|
109
|
-
def create_index(name,
|
110
|
-
execute PostgreSQLIndexDefinition.new(self, name,
|
113
|
+
def create_index(name, object, columns, options = {})
|
114
|
+
execute PostgreSQLIndexDefinition.new(self, name, object, columns, options).to_s
|
111
115
|
end
|
112
116
|
|
113
117
|
# PostgreSQL-specific version of the standard ActiveRecord
|
@@ -130,17 +134,20 @@ module ActiveRecord
|
|
130
134
|
# the INDEX. When using the :concurrently option, only one INDEX can
|
131
135
|
# specified and the :cascade option cannot be used. See the PostgreSQL
|
132
136
|
# documentation for details.
|
133
|
-
def drop_index(
|
137
|
+
def drop_index(*args)
|
138
|
+
options = args.extract_options!
|
139
|
+
args.flatten!
|
140
|
+
|
134
141
|
if options[:concurrently] && options[:cascade]
|
135
142
|
raise ArgumentError.new("The :concurrently and :cascade options cannot be used together.")
|
136
|
-
elsif options[:concurrently] &&
|
143
|
+
elsif options[:concurrently] && args.length > 1
|
137
144
|
raise ArgumentError.new("The :concurrently option can only be used on a single INDEX.")
|
138
145
|
end
|
139
146
|
|
140
147
|
sql = 'DROP INDEX '
|
141
148
|
sql << 'CONCURRENTLY ' if options[:concurrently]
|
142
149
|
sql << 'IF EXISTS ' if options[:if_exists]
|
143
|
-
sql << Array(
|
150
|
+
sql << Array.wrap(args).collect { |i| quote_generic(i) }.join(', ')
|
144
151
|
sql << ' CASCADE' if options[:cascade]
|
145
152
|
execute("#{sql};")
|
146
153
|
end
|
@@ -160,13 +167,13 @@ module ActiveRecord
|
|
160
167
|
# to be used directly. Instead, see PostgreSQLAdapter#create_index
|
161
168
|
# for usage.
|
162
169
|
class PostgreSQLIndexDefinition
|
163
|
-
attr_accessor :base, :name, :
|
170
|
+
attr_accessor :base, :name, :object, :columns, :options
|
164
171
|
|
165
|
-
def initialize(base, name,
|
172
|
+
def initialize(base, name, object, columns, options = {}) #:nodoc:
|
166
173
|
assert_valid_columns(columns)
|
167
174
|
assert_valid_fill_factor(options[:fill_factor])
|
168
175
|
|
169
|
-
@base, @name, @
|
176
|
+
@base, @name, @object, @columns, @options = base, name, object, columns, options
|
170
177
|
end
|
171
178
|
|
172
179
|
def to_sql #:nodoc:
|
@@ -174,7 +181,7 @@ module ActiveRecord
|
|
174
181
|
sql << 'UNIQUE ' if options[:unique]
|
175
182
|
sql << 'INDEX '
|
176
183
|
sql << 'CONCURRENTLY ' if options[:concurrently]
|
177
|
-
sql << "#{base.quote_generic(name)} ON #{base.quote_table_name(
|
184
|
+
sql << "#{base.quote_generic(name)} ON #{base.quote_table_name(object)}"
|
178
185
|
sql << " USING #{base.quote_generic(options[:using])}" if options[:using]
|
179
186
|
sql << '('
|
180
187
|
sql << [ columns ].flatten.collect do |column|
|
@@ -196,6 +203,7 @@ module ActiveRecord
|
|
196
203
|
end.join(', ')
|
197
204
|
sql << ')'
|
198
205
|
sql << " WITH (FILLFACTOR = #{options[:fill_factor].to_i})" if options[:fill_factor]
|
206
|
+
sql << " WITH (#{ActiveRecord::PostgreSQLExtensions::Utils.options_from_hash_or_string(options[:index_parameters], base)})" if options[:index_parameters].present?
|
199
207
|
sql << " TABLESPACE #{base.quote_tablespace(options[:tablespace])}" if options[:tablespace]
|
200
208
|
sql << " WHERE (#{options[:conditions] || options[:where]})" if options[:conditions] || options[:where]
|
201
209
|
"#{sql};"
|
@@ -204,7 +212,7 @@ module ActiveRecord
|
|
204
212
|
|
205
213
|
private
|
206
214
|
def assert_valid_columns(columns) #:nodoc:
|
207
|
-
Array(columns).each do |column|
|
215
|
+
Array.wrap(columns).each do |column|
|
208
216
|
if column.is_a?(Hash)
|
209
217
|
if column.has_key?(:column) && column.has_key?(:expression)
|
210
218
|
raise ActiveRecord::InvalidIndexColumnDefinition.new("You can't specify both :column and :expression in a column definition", column)
|