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
@@ -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
- raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('extensions') if
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
- raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('extensions') if
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
- raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('extensions') if
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
- raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('extensions') if
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
- raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('extensions') if
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
- raise ActiveRecord::PostgreSQLExtensions::FeatureNotSupportedError.new('extensions') if
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
- 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
- def extensions?
13
- if defined?(@has_extensions)
14
- @has_extensions
15
- else
16
- @has_extensions = ActiveRecord::PostgreSQLExtensions.SERVER_VERSION >= '9.1'
17
- end
18
- end
19
-
20
- def foreign_tables?
21
- if defined?(@has_foreign_tables)
22
- @has_foreign_tables
23
- else
24
- @has_foreign_tables = ActiveRecord::PostgreSQLExtensions.SERVER_VERSION >= '9.1'
25
- end
26
- end
27
-
28
- def modify_mass_privileges?
29
- if defined?(@has_modify_mass_privileges)
30
- @has_modify_mass_privileges
31
- else
32
- @has_modify_mass_privileges = ActiveRecord::PostgreSQLExtensions.SERVER_VERSION >= '9.0'
33
- end
34
- end
35
-
36
- def postgis?
37
- if defined?(@has_postgis)
38
- @has_postgis
39
- else
40
- @has_postgis = !!ActiveRecord::PostgreSQLExtensions::PostGIS.VERSION
41
- end
42
- end
43
- end
44
- end
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
@@ -228,7 +228,7 @@ module ActiveRecord
228
228
  sql << set_me
229
229
  end
230
230
  else
231
- sql << Array(opts).collect do |s|
231
+ sql << Array.wrap(opts).collect do |s|
232
232
  "SET #{s.to_s}"
233
233
  end
234
234
  end
@@ -134,20 +134,20 @@ module ActiveRecord
134
134
  ActiveRecord::PostgreSQLExtensions::PostGIS.VERSION[:lib] < '2.0' ||
135
135
  opts[:force_constraints]
136
136
  )
137
- @table_constraints << PostgreSQLCheckConstraint.new(
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
- @table_constraints << PostgreSQLCheckConstraint.new(
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
- @table_constraints << PostgreSQLCheckConstraint.new(
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
- @post_processing << sprintf(
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
- @post_processing << sprintf(
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
- @post_processing << PostgreSQLIndexDefinition.new(
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, table, columns, options = {})
110
- execute PostgreSQLIndexDefinition.new(self, name, table, columns, options).to_s
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(name, options = {})
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] && name.is_a?(Array) && name.length > 1
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(name).collect { |i| quote_generic(i) }.join(', ')
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, :table, :columns, :options
170
+ attr_accessor :base, :name, :object, :columns, :options
164
171
 
165
- def initialize(base, name, table, columns, options = {}) #:nodoc:
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, @table, @columns, @options = base, name, table, columns, options
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(table)}"
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)