postgres_ext 1.0.0 → 2.0.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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/README.md +6 -47
  4. data/lib/postgres_ext/active_record/relation/predicate_builder.rb +36 -55
  5. data/lib/postgres_ext/active_record/relation/query_methods.rb +0 -14
  6. data/lib/postgres_ext/active_record.rb +0 -3
  7. data/lib/postgres_ext/version.rb +1 -1
  8. data/postgres_ext.gemspec +6 -5
  9. data/spec/arel/arel_spec.rb +0 -1
  10. data/spec/dummy/app/models/person.rb +0 -1
  11. data/spec/dummy/config/application.rb +0 -3
  12. data/spec/dummy/config/environments/development.rb +2 -7
  13. data/spec/dummy/config/environments/production.rb +1 -0
  14. data/spec/dummy/config/environments/test.rb +1 -5
  15. data/spec/dummy/db/migrate/20120501163758_create_people.rb +3 -3
  16. data/spec/dummy/db/schema.rb +15 -12
  17. data/spec/queries/sanity_spec.rb +1 -1
  18. data/spec/spec_helper.rb +1 -2
  19. metadata +21 -105
  20. data/docs/indexes.md +0 -28
  21. data/docs/migrations.md +0 -92
  22. data/docs/type_casting.md +0 -70
  23. data/lib/postgres_ext/active_record/connection_adapters/postgres_adapter.rb +0 -537
  24. data/lib/postgres_ext/active_record/connection_adapters.rb +0 -1
  25. data/lib/postgres_ext/active_record/sanitization.rb +0 -30
  26. data/lib/postgres_ext/active_record/schema_dumper.rb +0 -157
  27. data/spec/columns/array_spec.rb +0 -119
  28. data/spec/columns/inet_spec.rb +0 -25
  29. data/spec/columns/ranges/daterange_spec.rb +0 -37
  30. data/spec/columns/ranges/int4range_spec.rb +0 -38
  31. data/spec/columns/ranges/int8range_spec.rb +0 -38
  32. data/spec/columns/ranges/numrange_spec.rb +0 -37
  33. data/spec/columns/ranges/tsrange_spec.rb +0 -37
  34. data/spec/migrations/active_record_migration_spec.rb +0 -29
  35. data/spec/migrations/array_spec.rb +0 -214
  36. data/spec/migrations/cidr_spec.rb +0 -26
  37. data/spec/migrations/citext_spec.rb +0 -32
  38. data/spec/migrations/ean13_spec.rb +0 -27
  39. data/spec/migrations/index_spec.rb +0 -67
  40. data/spec/migrations/inet_spec.rb +0 -26
  41. data/spec/migrations/macaddr_spec.rb +0 -26
  42. data/spec/migrations/ranges/daterange_spec.rb +0 -27
  43. data/spec/migrations/ranges/int4range_spec.rb +0 -27
  44. data/spec/migrations/ranges/int8range_spec.rb +0 -27
  45. data/spec/migrations/ranges/numrange_spec.rb +0 -27
  46. data/spec/migrations/ranges/tsrange_spec.rb +0 -27
  47. data/spec/migrations/ranges/tstzrange_spec.rb +0 -27
  48. data/spec/migrations/uuid_spec.rb +0 -26
  49. data/spec/models/array_spec.rb +0 -285
  50. data/spec/models/ean13_spec.rb +0 -57
  51. data/spec/models/inet_spec.rb +0 -88
  52. data/spec/models/ranges/daterange_spec.rb +0 -88
  53. data/spec/models/ranges/int4range_spec.rb +0 -85
  54. data/spec/models/ranges/int8range_spec.rb +0 -85
  55. data/spec/models/ranges/numrange_spec.rb +0 -85
  56. data/spec/models/ranges/tsrange_spec.rb +0 -89
  57. data/spec/models/ranges/tstzrange_spec.rb +0 -89
  58. data/spec/schema_dumper/array_spec.rb +0 -17
  59. data/spec/schema_dumper/cidr_spec.rb +0 -17
  60. data/spec/schema_dumper/citext_spec.rb +0 -17
  61. data/spec/schema_dumper/ean13_spec.rb +0 -17
  62. data/spec/schema_dumper/extension_spec.rb +0 -14
  63. data/spec/schema_dumper/index_spec.rb +0 -46
  64. data/spec/schema_dumper/inet_spec.rb +0 -17
  65. data/spec/schema_dumper/macaddr_spec.rb +0 -17
  66. data/spec/schema_dumper/ranges/daterange_spec.rb +0 -18
  67. data/spec/schema_dumper/ranges/int4range_spec.rb +0 -18
  68. data/spec/schema_dumper/ranges/int8range_spec.rb +0 -18
  69. data/spec/schema_dumper/ranges/numrange_spec.rb +0 -18
  70. data/spec/schema_dumper/ranges/tsrange_spec.rb +0 -18
  71. data/spec/schema_dumper/ranges/tstzrange_spec.rb +0 -17
  72. data/spec/schema_dumper/uuid_spec.rb +0 -17
data/docs/migrations.md DELETED
@@ -1,92 +0,0 @@
1
- # Migration/Schema.rb support
2
-
3
- ## INET
4
-
5
- ```ruby
6
- create_table :testing do |t|
7
- t.inet :inet_column
8
- # or
9
- t.inet :inet_column_1, :inet_column_2
10
- # or
11
- t.column :inet_column, :inet
12
- end
13
- ```
14
-
15
- ## CIDR
16
-
17
- ```ruby
18
- create_table :testing do |t|
19
- t.cidr :cidr_column
20
- # or
21
- t.cidr :cidr_column_1, :cidr_column_2
22
- # or
23
- t.column :cidr_column, :cidr
24
- end
25
- ```
26
-
27
- ## MACADDR
28
-
29
- ```ruby
30
- create_table :testing do |t|
31
- t.macaddr :macaddr_column
32
- # or
33
- t.macaddr :macaddr_column_1, :macaddr_column_2
34
- # or
35
- t.column :macaddr_column, :macaddr
36
- end
37
- ```
38
-
39
- ## UUID
40
-
41
- ```ruby
42
- create_table :testing do |t|
43
- t.uuid :uuid_column
44
- # or
45
- t.uuid :uuid_column_1, :uuid_column_2
46
- # or
47
- t.column :uuid_column, :uuid
48
- end
49
- ```
50
-
51
- ## CITEXT
52
-
53
- ```ruby
54
- create_table :testing do |t|
55
- t.citext :citext_column
56
- # or
57
- t.citext :citext_column_1, :citext_column_2
58
- # or
59
- t.column :citext_column, :citext
60
- end
61
- ```
62
-
63
- ## Arrays
64
- Arrays are created from any ActiveRecord supported datatype (including
65
- ones added by postgres\_ext), and respect length constraints
66
-
67
- ```ruby
68
- create_table :testing do |t|
69
- t.integer :int_array, :array => true
70
- # integer[]
71
- t.integer :int_array, :array => true, :limit => 2
72
- # smallint[]
73
- t.string :macaddr_column_1, :array => true, :limit => 30
74
- # char varying(30)[]
75
- end
76
- ```
77
-
78
- ### Converting to Arrays
79
-
80
- If you have an existing column with a string-delimited array (e.g. 'val1 val2 val3') convert that data using SQL in your migration.
81
-
82
- ```ruby
83
- class AddLinkedArticleIdsToLinkSet < ActiveRecord::Migration
84
- def change
85
- add_column :link_sets, :linked_article_ids, :integer, :array => true, :default => []
86
- execute <<-eos
87
- UPDATE link_sets
88
- SET linked_article_ids = cast (string_to_array(linked_articles_string, ' ') as integer[])
89
- eos
90
- end
91
- end
92
- ````
data/docs/type_casting.md DELETED
@@ -1,70 +0,0 @@
1
- # Type Casting support
2
-
3
- ## INET and CIDR
4
- INET and CIDR values are converted to
5
- [IPAddr](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/ipaddr/rdoc/IPAddr.html)
6
- objects when retrieved from the database, or set as a string.
7
-
8
- ```ruby
9
- create_table :inet_examples do |t|
10
- t.inet :ip_address
11
- end
12
-
13
- class InetExample < ActiveRecord::Base
14
- end
15
-
16
- inetExample = InetExample.new
17
- inetExample.ip_address = '127.0.0.0/24'
18
- inetExample.ip_address
19
- # => #<IPAddr: IPv4:127.0.0.0/255.255.255.0>
20
- inetExample.save
21
-
22
- inet_2 = InetExample.first
23
- inet_2.ip_address
24
- # => #<IPAddr: IPv4:127.0.0.0/255.255.255.0>
25
- ```
26
-
27
- ## Arrays
28
- Array values can be set with Array objects. Any array stored in the
29
- database will be converted to a properly casted array of values on the
30
- way out.
31
-
32
- ```ruby
33
- create_table :people do |t|
34
- t.integer :favorite_numbers, :array => true
35
- end
36
-
37
- class Person < ActiveRecord::Base
38
- end
39
-
40
- person = Person.new
41
- person.favorite_numbers = [1,2,3]
42
- person.favorite_numbers
43
- # => [1,2,3]
44
- person.save
45
-
46
- person_2 = Person.first
47
- person_2.favorite_numbers
48
- # => [1,2,3]
49
- person_2.favorite_numbers.first.class
50
- # => Fixnum
51
- ```
52
-
53
- ## Ranges
54
- Like array objects, postgres\_ext supports range types as well.
55
- Numrange, in4range, int8range, daterange, tsrange, and tstzrange are all
56
- supported, but there are some notable caveats.
57
-
58
- ### Int and Date ranges
59
- As integers and days are discrete measurements, PostgreSQL will
60
- normalize these ranges as they are store in the database. PostgreSQL
61
- will convert end-inclusive ranges to end-exclusive, meaning that `0..4`
62
- becomes `0...5`. Developers should be aware of this when using integer
63
- and date ranges, since ruby will treat these ranges differently from
64
- PostgreSQL.
65
-
66
- ### Timestamp with and without timezone
67
- Ruby/Rails 3.2.x does not support datetime ranges that begin or end with
68
- infinity. Rails 4 has patched datetime and time so that infinity
69
- terminated ranges work, but currently postgres\_ext has not patched the
70
- required methods.
@@ -1,537 +0,0 @@
1
- require 'active_record/connection_adapters/postgresql_adapter'
2
- require 'ipaddr'
3
- require 'pg_array_parser'
4
-
5
- module ActiveRecord
6
- module ConnectionAdapters
7
- class IndexDefinition
8
- attr_accessor :using, :where, :index_opclass
9
- end
10
-
11
- class PostgreSQLColumn
12
- include PgArrayParser
13
- attr_accessor :array
14
-
15
- def initialize_with_extended_types(name, default, sql_type = nil, null = true)
16
- if sql_type =~ /\[\]$/
17
- @array = true
18
- initialize_without_extended_types(name, default, sql_type[0..sql_type.length - 3], null)
19
- @sql_type = sql_type
20
- else
21
- initialize_without_extended_types(name,default, sql_type, null)
22
- end
23
- end
24
- alias_method_chain :initialize, :extended_types
25
-
26
- def klass_with_extended_types
27
- case type
28
- when :inet, :cidr then IPAddr
29
- else
30
- klass_without_extended_types
31
- end
32
- end
33
- alias_method_chain :klass, :extended_types
34
-
35
- def type_cast_with_extended_types(value)
36
- return nil if value.nil?
37
- return coder.load(value) if encoded?
38
-
39
- klass = self.class
40
- if self.array && String === value && value.start_with?('{') && value.end_with?('}')
41
- string_to_array value
42
- elsif self.array && Array === value
43
- value
44
- else
45
- case type
46
- when :inet, :cidr then klass.string_to_cidr_address(value)
47
- when :numrange,:int4range,:int8range then klass.string_to_numeric_range(value,type)
48
- when :daterange then klass.string_to_date_range(value)
49
- when :tsrange,:tstzrange then klass.string_to_datetime_range(value)
50
- else
51
- type_cast_without_extended_types(value)
52
- end
53
- end
54
- end
55
- alias_method_chain :type_cast, :extended_types
56
-
57
- def string_to_array(value)
58
- if Array === value
59
- value
60
- else
61
- string_array = parse_pg_array value
62
- type_cast_array(string_array)
63
- end
64
- end
65
-
66
- def type_cast_array(array)
67
- array.map do |value|
68
- Array === value ? type_cast_array(value) : type_cast(value)
69
- end
70
- end
71
-
72
- def number?
73
- !self.array && super
74
- end
75
-
76
- def type_cast_code_with_extended_types(var_name)
77
- klass = self.class.name
78
-
79
- if self.array
80
- "#{klass}.new('#{self.name}', #{self.default.nil? ? 'nil' : "'#{self.default}'"}, '#{self.sql_type}').string_to_array(#{var_name})"
81
- else
82
- case type
83
- when :inet, :cidr then "#{klass}.string_to_cidr_address(#{var_name})"
84
- when :numrange,:int4range,:int8range then "#{klass}.string_to_numeric_range(#{var_name},#{type.inspect})"
85
- when :daterange then "#{klass}.string_to_date_range(#{var_name})"
86
- when :tsrange,:tstzrange then "#{klass}.string_to_datetime_range(#{var_name})"
87
- else
88
- type_cast_code_without_extended_types(var_name)
89
- end
90
- end
91
- end
92
- alias_method_chain :type_cast_code, :extended_types
93
-
94
- if RUBY_PLATFORM =~ /java/
95
- def default_value_with_extended_types(default)
96
- case default
97
- when /\A'(.*)'::(?:(num|int[48]|date|ts(tz)?)range)\z/
98
- $1
99
- else
100
- default_value_without_extended_types(default)
101
- end
102
-
103
- end
104
- alias_method_chain :default_value, :extended_types
105
- end
106
-
107
- class << self
108
- unless RUBY_PLATFORM =~ /java/
109
- def extract_value_from_default_with_extended_types(default)
110
- case default
111
- when /\A'(.*)'::(?:(num|int[48]|date|ts(tz)?)range)\z/
112
- $1
113
- else
114
- extract_value_from_default_without_extended_types(default)
115
- end
116
-
117
- end
118
- alias_method_chain :extract_value_from_default, :extended_types
119
- end
120
-
121
- def string_to_cidr_address(string)
122
- return string unless String === string
123
-
124
- if string.present?
125
- IPAddr.new(string)
126
- end
127
- end
128
-
129
- def string_to_numeric_range(value, type)
130
- if type == :numrange
131
- extract_range(value) do |end_value|
132
- end_value.to_f
133
- end
134
- else
135
- extract_range(value) do |end_value|
136
- end_value.to_i
137
- end
138
- end
139
- end
140
-
141
- def string_to_date_range(value)
142
- extract_range(value) do |end_value|
143
- Date.parse(end_value)
144
- end
145
- end
146
-
147
- def string_to_datetime_range(value)
148
- extract_range(value) do |end_value|
149
- Time.parse(end_value)
150
- end
151
- end
152
-
153
- private
154
-
155
- def extract_range(value, &conversion)
156
- if Range === value
157
- value
158
- else
159
- # Until 1.8.7 support is dropped, must use group numbers instead of named groups
160
- #range_regex = /\A(?<open>\[|\()(?<start>.*?),(?<end>.*?)(?<close>\]|\))\z/
161
- range_regex = /\A(\[|\()(.*?),(.*?)(\]|\))\z/
162
- if match = value.match(range_regex)
163
- if match = value.match(range_regex)
164
- start_value = match[2].empty? ? -(1.0/0.0) : conversion.call(match[2])
165
- end_value = match[3].empty? ? (1.0/0.0) : conversion.call(match[3])
166
-
167
- end_exclusive = end_value != (1.0/0.0) && match[4] == ')'
168
- Range.new start_value, end_value, end_exclusive
169
- end
170
- end
171
- end
172
- end
173
- end
174
-
175
- private
176
-
177
- def simplified_type_with_extended_types(field_type)
178
- case field_type
179
- when 'uuid'
180
- :uuid
181
- when 'citext'
182
- :citext
183
- when 'inet'
184
- :inet
185
- when 'cidr'
186
- :cidr
187
- when 'macaddr'
188
- :macaddr
189
- when 'ean13'
190
- :ean13
191
- when 'int4range'
192
- :int4range
193
- when 'int8range'
194
- :int8range
195
- when 'numrange'
196
- :numrange
197
- when 'daterange'
198
- :daterange
199
- when 'tsrange'
200
- :tsrange
201
- when 'tstzrange'
202
- :tstzrange
203
- else
204
- simplified_type_without_extended_types field_type
205
- end
206
- end
207
-
208
- alias_method_chain :simplified_type, :extended_types
209
- end
210
-
211
- class PostgreSQLAdapter
212
- class UnsupportedFeature < Exception; end
213
-
214
- EXTENDED_TYPES = { :inet => {:name => 'inet'}, :cidr => {:name => 'cidr'}, :macaddr => {:name => 'macaddr'},
215
- :uuid => {:name => 'uuid'}, :citext => {:name => 'citext'}, :ean13 => {:name => 'ean13'}, :numrange => {:name => 'numrange'},
216
- :daterange => {:name => 'daterange'}, :int4range => {:name => 'int4range'}, :int8range => {:name => 'int8range'}, :tsrange => {:name => 'tsrange'}, :tstzrange => {:name => 'tstzrange'} }
217
-
218
- class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
219
- attr_accessor :array
220
- end
221
-
222
- class TableDefinition
223
- EXTENDED_TYPES.keys.map(&:to_s).each do |column_type|
224
- class_eval <<-EOV, __FILE__, __LINE__ + 1
225
- def #{column_type}(*args) # def string(*args)
226
- options = args.extract_options! # options = args.extract_options!
227
- column_names = args # column_names = args
228
- type = :'#{column_type}' # type = :string
229
- column_names.each { |name| column(name, type, options) } # column_names.each { |name| column(name, type, options) }
230
- end # end
231
- EOV
232
- end
233
-
234
- def column(name, type=nil, options = {})
235
- super
236
-
237
- column = self[name]
238
- column.array = options[:array]
239
-
240
- self
241
- end
242
-
243
- private
244
-
245
- def new_column_definition(base, name, type)
246
- definition = ColumnDefinition.new base, name, type
247
- @columns << definition
248
- @columns_hash[name] = definition
249
- definition
250
- end
251
- end
252
-
253
- class Table < ActiveRecord::ConnectionAdapters::Table
254
- EXTENDED_TYPES.keys.map(&:to_s).each do |column_type|
255
- class_eval <<-EOV, __FILE__, __LINE__ + 1
256
- def #{column_type}(*args) # def string(*args)
257
- options = args.extract_options! # options = args.extract_options!
258
- column_names = args # column_names = args
259
- type = :'#{column_type}' # type = :string
260
- column_names.each do |name| # column_names.each do |name|
261
- column = ColumnDefinition.new(@base, name.to_s, type) # column = ColumnDefinition.new(@base, name, type)
262
- if options[:limit] # if options[:limit]
263
- column.limit = options[:limit] # column.limit = options[:limit]
264
- elsif native[type].is_a?(Hash) # elsif native[type].is_a?(Hash)
265
- column.limit = native[type][:limit] # column.limit = native[type][:limit]
266
- end # end
267
- column.precision = options[:precision] # column.precision = options[:precision]
268
- column.scale = options[:scale] # column.scale = options[:scale]
269
- column.default = options[:default] # column.default = options[:default]
270
- column.null = options[:null] # column.null = options[:null]
271
- @base.add_column(@table_name, name, column.sql_type, options) # @base.add_column(@table_name, name, column.sql_type, options)
272
- end # end
273
- end # end
274
- EOV
275
- end
276
- end
277
-
278
- NATIVE_DATABASE_TYPES.merge!(EXTENDED_TYPES)
279
-
280
- def supports_extensions?
281
- postgresql_version > 90100
282
- end
283
-
284
- def add_column_options!(sql, options)
285
- if options[:array] || options[:column].try(:array)
286
- sql << '[]'
287
- end
288
- super
289
- end
290
-
291
- def add_index(table_name, column_name, options = {})
292
- index_name, unique, index_columns, _ = add_index_options(table_name, column_name, options)
293
- if options.is_a? Hash
294
- index_type = options[:using] ? " USING #{options[:using]} " : ""
295
- index_options = options[:where] ? " WHERE #{options[:where]}" : ""
296
- index_opclass = options[:index_opclass]
297
- index_algorithm = options[:algorithm] == :concurrently ? ' CONCURRENTLY' : ''
298
-
299
- if options[:algorithm].present? && options[:algorithm] != :concurrently
300
- raise ArgumentError.new 'Algorithm must be one of the following: :concurrently'
301
- end
302
- end
303
- execute "CREATE #{unique} INDEX#{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}#{index_type}(#{index_columns} #{index_opclass})#{index_options}"
304
- end
305
-
306
- def add_extension(extension_name, options={})
307
- raise UnsupportedFeature.new('Extensions are not support by this version of PostgreSQL') unless supports_extensions?
308
- execute "CREATE extension IF NOT EXISTS \"#{extension_name}\""
309
- end
310
-
311
- def change_table(table_name, options = {})
312
- if supports_bulk_alter? && options[:bulk]
313
- recorder = ActiveRecord::Migration::CommandRecorder.new(self)
314
- yield Table.new(table_name, recorder)
315
- bulk_change_table(table_name, recorder.commands)
316
- else
317
- yield Table.new(table_name, self)
318
- end
319
- end
320
-
321
- if RUBY_PLATFORM =~ /java/
322
- # The activerecord-jbdc-adapter implements PostgreSQLAdapter#add_column differently from the active-record version
323
- # so we have to patch that version in JRuby, but not in MRI/YARV
324
- def add_column(table_name, column_name, type, options = {})
325
- default = options[:default]
326
- notnull = options[:null] == false
327
- sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
328
-
329
- if options[:array]
330
- sql_type << '[]'
331
- end
332
-
333
- # Add the column.
334
- execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{sql_type}")
335
-
336
- change_column_default(table_name, column_name, default) if options_include_default?(options)
337
- change_column_null(table_name, column_name, false, default) if notnull
338
- end
339
- end
340
-
341
- def type_cast_extended(value, column, part_array = false)
342
- case value
343
- when NilClass
344
- if column.array && part_array
345
- 'NULL'
346
- elsif column.array && !part_array
347
- value
348
- else
349
- type_cast_without_extended_types(value, column)
350
- end
351
- when Float
352
- if [:numrange,:int4range,:int8range,:daterange].include?(column.type)&& value.abs == (1.0/0.0)
353
- ''
354
- else
355
- type_cast_without_extended_types(value, column)
356
- end
357
- when Date, DateTime, Time
358
- if column.type == :tstzrange
359
- quoted_date(value)
360
- elsif column.type == :tsrange
361
- value.to_s(:db)
362
- else
363
- type_cast_without_extended_types(value, column)
364
- end
365
- when Range
366
- range_to_string(value, column)
367
- when Array
368
- if column.array
369
- array_to_string(value, column)
370
- else
371
- type_cast_without_extended_types(value, column)
372
- end
373
- when IPAddr
374
- ipaddr_to_string(value)
375
- else
376
- type_cast_without_extended_types(value, column)
377
- end
378
- end
379
-
380
- def type_cast_with_extended_types(value, column)
381
- type_cast_extended(value, column)
382
- end
383
- alias_method_chain :type_cast, :extended_types
384
-
385
- def quote_with_extended_types(value, column = nil)
386
- if value.is_a? IPAddr
387
- "'#{type_cast(value, column)}'"
388
- elsif value.is_a? Array
389
- "'#{array_to_string(value, column, true)}'"
390
- elsif column.respond_to?(:array) && column.array && value =~ /^\{.*\}$/
391
- "'#{value}'"
392
- elsif value.is_a? Range
393
- "'#{type_cast(value, column)}'"
394
- else
395
- quote_without_extended_types(value, column)
396
- end
397
- end
398
- alias_method_chain :quote, :extended_types
399
-
400
- def opclasses
401
- @opclasses ||= select_rows('SELECT opcname FROM pg_opclass').flatten.uniq
402
- end
403
-
404
- # this is based upon rails 4 changes to include different index methods
405
- # Returns an array of indexes for the given table.
406
- def indexes(table_name, name = nil)
407
- opclasses
408
- result = select_rows(<<-SQL, name)
409
- SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
410
- FROM pg_class t
411
- INNER JOIN pg_index d ON t.oid = d.indrelid
412
- INNER JOIN pg_class i ON d.indexrelid = i.oid
413
- WHERE i.relkind = 'i'
414
- AND d.indisprimary = 'f'
415
- AND t.relname = '#{table_name}'
416
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
417
- ORDER BY i.relname
418
- SQL
419
- result.map do |row|
420
- index_name = row[0]
421
- unique = row[1] == 't'
422
- indkey = row[2].split(" ")
423
- inddef = row[3]
424
- oid = row[4]
425
-
426
- columns = Hash[select_rows(<<-SQL, "Columns for index #{row[0]} on #{table_name}")]
427
- SELECT a.attnum::text, a.attname
428
- FROM pg_attribute a
429
- WHERE a.attrelid = #{oid}
430
- AND a.attnum IN (#{indkey.join(",")})
431
- SQL
432
- column_names = columns.values_at(*indkey).compact
433
-
434
- # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
435
- desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
436
- orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
437
- #changed from rails 3.2
438
- where = inddef.scan(/WHERE (.+)$/).flatten[0]
439
- using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
440
- if using
441
- index_op = inddef.scan(/USING .+? \(.+? (#{opclasses.join('|')})\)/).flatten
442
- index_op = index_op[0].to_sym if index_op.present?
443
- end
444
- if column_names.present?
445
- index_def = IndexDefinition.new(table_name, index_name, unique, column_names, [], orders)
446
- index_def.where = where
447
- index_def.using = using if using && using != :btree
448
- index_def.index_opclass = index_op if using && using != :btree && index_op
449
- index_def
450
- # else nil
451
- end
452
- #/changed
453
- end.compact
454
- end
455
-
456
- def extensions
457
- select_rows('select extname from pg_extension', 'extensions').map { |row| row[0] }.delete_if {|name| name == 'plpgsql'}
458
- end
459
-
460
- def change_column_with_extended_types(table_name, column_name, type, options = {})
461
- if options[:array]
462
- clear_cache!
463
- quoted_table_name = quote_table_name(table_name)
464
-
465
- if type.to_s =~ /string|text/
466
- execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}[] USING string_to_array(#{quote_column_name(column_name)}, ',')"
467
- else
468
- execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}[]"
469
- end
470
-
471
- change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
472
- change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
473
- else
474
- change_column_without_extended_types(table_name, column_name, type, options)
475
- end
476
- end
477
-
478
- alias_method_chain :change_column, :extended_types
479
-
480
- private
481
-
482
- def ipaddr_to_string(value)
483
- "#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
484
- end
485
-
486
- def array_to_string(value, column, encode_single_quotes = false)
487
- "{#{value.map { |val| item_to_string(val, column, encode_single_quotes) }.join(',')}}"
488
- end
489
-
490
- def range_to_string(value, column)
491
- "#{range_lower_bound_character value}#{type_cast value.begin, column},#{type_cast value.end, column}#{range_upper_bound_character value}"
492
- end
493
-
494
- def range_lower_bound_character(value)
495
- if value.begin == -(1.0/0.0)
496
- '('
497
- else
498
- '['
499
- end
500
- end
501
-
502
- def range_upper_bound_character(value)
503
- if value.end == (1.0/0.0) || value.exclude_end?
504
- ')'
505
- else
506
- ']'
507
- end
508
- end
509
-
510
- def item_to_string(value, column, encode_single_quotes = false)
511
- return 'NULL' if value.nil?
512
-
513
- casted_value = type_cast_extended(value, column, true)
514
-
515
- if casted_value.is_a?(String) && value.is_a?(String)
516
- casted_value = casted_value.dup
517
- # Encode backslashes. One backslash becomes 4 in the resulting SQL.
518
- # (why 4, and not 2? Trial and error shows 4 works, 2 fails to parse.)
519
- casted_value.gsub!('\\', '\\\\\\\\')
520
- # Encode a bare " in the string as \"
521
- casted_value.gsub!('"', '\\"')
522
- # PostgreSQL parses the string values differently if they are quoted for
523
- # use in a statement, or if it will be used as part of a bound argument.
524
- # For directly-inserted values (UPDATE foo SET bar='{"array"}') we need to
525
- # escape ' as ''. For bound arguments, do not escape them.
526
- if encode_single_quotes
527
- casted_value.gsub!("'", "''")
528
- end
529
-
530
- "\"#{casted_value}\""
531
- else
532
- casted_value
533
- end
534
- end
535
- end
536
- end
537
- end
@@ -1 +0,0 @@
1
- require 'postgres_ext/active_record/connection_adapters/postgres_adapter'