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