postgres_ext 0.0.8 → 0.0.9
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.
- data/CHANGELOG.md +14 -0
- data/CONTRIBUTING.md +3 -1
- data/README.md +22 -3
- data/lib/postgres_ext/active_record/connection_adapters/postgres_adapter.rb +115 -14
- data/lib/postgres_ext/active_record/schema_dumper.rb +42 -3
- data/lib/postgres_ext/arel/visitors/to_sql.rb +2 -5
- data/lib/postgres_ext/version.rb +1 -1
- data/postgres_ext.gemspec +6 -3
- data/spec/arel/array_spec.rb +8 -1
- data/spec/columns/array_spec.rb +21 -1
- data/spec/dummy/config/environments/development.rb +1 -1
- data/spec/migrations/index_spec.rb +22 -0
- data/spec/models/array_spec.rb +137 -1
- data/spec/schema_dumper/index_spec.rb +21 -0
- data/spec/spec_helper.rb +1 -0
- metadata +10 -6
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## 0.0.9
|
2
|
+
|
3
|
+
* Fixes #<attribute_name>?, Adds (pending) test case for #update_column - Dan McClain (@danmcclain)
|
4
|
+
* Fix handing of pgsql arrays for the literal and argument-binding
|
5
|
+
cases - Michael Graff (@skandragon)
|
6
|
+
* Fixes UTF-8 strings in string arrays are not returned as UTF-8
|
7
|
+
encoded strings - Michael Graff (@skandragon)
|
8
|
+
* Documentation fixes - Michael Graff (@skandragon) and Dan McClain
|
9
|
+
(@danmcclain)
|
10
|
+
* Properly encode strings stored in an array. - Michael Graff
|
11
|
+
(@skandragon)
|
12
|
+
* Fixes integer array support - Keenan Brock (@kbrock)
|
13
|
+
* Adds more robust index types with add_index options :index_type and :where. - Keenan Brock (@kbrock)
|
14
|
+
|
1
15
|
## 0.0.8
|
2
16
|
|
3
17
|
Fixes add and change column
|
data/CONTRIBUTING.md
CHANGED
@@ -25,9 +25,11 @@ If you'd like to submit a pull request please adhere to the following:
|
|
25
25
|
5. General Rails/Ruby naming conventions for files and classes
|
26
26
|
6. *Do not* use Ruby 1.9 hash syntax
|
27
27
|
7. *Do not* use Ruby 1.9 stubby proc syntax
|
28
|
+
8. Follow Tim Pope's [model for git commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). This will make it
|
29
|
+
much easier to generate change logs and navigate through the logs
|
28
30
|
|
29
31
|
Plase note that you must adhere to each of the above mentioned rules.
|
30
32
|
Failure to do so will result in an immediate closing of the pull
|
31
33
|
request. If you update and rebase the pull request to follow the
|
32
34
|
guidelines your pull request will be re-opened and considered for
|
33
|
-
inclusion.
|
35
|
+
inclusion.
|
data/README.md
CHANGED
@@ -99,9 +99,9 @@ ones added by postgre\_ext), and respect length constraints
|
|
99
99
|
create_table :testing do |t|
|
100
100
|
t.integer :int_array, :array => true
|
101
101
|
# integer[]
|
102
|
-
t.integer :int_array, :array => true, :
|
102
|
+
t.integer :int_array, :array => true, :limit => 2
|
103
103
|
# smallint[]
|
104
|
-
t.string :macaddr_column_1, :array => true, :
|
104
|
+
t.string :macaddr_column_1, :array => true, :limit => 30
|
105
105
|
# char varying(30)[]
|
106
106
|
end
|
107
107
|
```
|
@@ -218,7 +218,7 @@ accompish this:
|
|
218
218
|
user_arel = User.arel_table
|
219
219
|
|
220
220
|
any_tags_function = Arel::Nodes::NamedFunction.new('ANY', [user_arel[:tags]])
|
221
|
-
predicate = Arel::Nodes::Equality('test', any_tags_function)
|
221
|
+
predicate = Arel::Nodes::Equality.new('test', any_tags_function)
|
222
222
|
|
223
223
|
User.where(predicate).to_sql
|
224
224
|
#=> SELECT \"users\".* FROM \"users\" WHERE 'test' = ANY(\"users\".\"tags\")
|
@@ -253,6 +253,25 @@ user_arel = User.arel_table
|
|
253
253
|
User.where(user_arel[:ip_address].contained_witin('127.0.0.1/24')).to_sql
|
254
254
|
# => SELECT \"users\".* FROM \"users\" WHERE \"users\".\"ip_address\" << '127.0.0.1/24'
|
255
255
|
```
|
256
|
+
|
257
|
+
## Indexes
|
258
|
+
|
259
|
+
### Index types
|
260
|
+
|
261
|
+
Postgres\_ext allows you to specify an index type at index creation.
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
add_index :table_name, :column, :index_type => :gin
|
265
|
+
```
|
266
|
+
|
267
|
+
### Where clauses
|
268
|
+
|
269
|
+
Postgres\_ext allows you to specify a where clause at index creation.
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
add_index :table_name, :column, :where => 'column < 50'
|
273
|
+
```
|
274
|
+
|
256
275
|
## Authors
|
257
276
|
|
258
277
|
Dan McClain [twitter](http://twitter.com/_danmcclain) [github](http://github.com/danmcclain)
|
@@ -4,6 +4,11 @@ require 'pg_array_parser'
|
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
module ConnectionAdapters
|
7
|
+
# class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :index_type)
|
8
|
+
class IndexDefinition
|
9
|
+
attr_accessor :index_type, :where
|
10
|
+
end
|
11
|
+
|
7
12
|
class PostgreSQLColumn
|
8
13
|
include PgArrayParser
|
9
14
|
attr_accessor :array
|
@@ -37,7 +42,7 @@ module ActiveRecord
|
|
37
42
|
else
|
38
43
|
case type
|
39
44
|
when :inet, :cidr then klass.string_to_cidr_address(value)
|
40
|
-
else
|
45
|
+
else
|
41
46
|
type_cast_without_extended_types(value)
|
42
47
|
end
|
43
48
|
end
|
@@ -49,7 +54,7 @@ module ActiveRecord
|
|
49
54
|
else
|
50
55
|
string_array = parse_pg_array value
|
51
56
|
if type == :string
|
52
|
-
string_array
|
57
|
+
force_character_encoding(string_array)
|
53
58
|
else
|
54
59
|
type_cast_array(string_array)
|
55
60
|
end
|
@@ -57,15 +62,13 @@ module ActiveRecord
|
|
57
62
|
end
|
58
63
|
|
59
64
|
def type_cast_array(array)
|
60
|
-
|
61
|
-
|
62
|
-
if Array === value
|
63
|
-
casted_array.push type_cast_array(value)
|
64
|
-
else
|
65
|
-
casted_array.push type_cast value
|
66
|
-
end
|
65
|
+
array.map do |value|
|
66
|
+
Array === value ? type_cast_array(value) : type_cast(value)
|
67
67
|
end
|
68
|
-
|
68
|
+
end
|
69
|
+
|
70
|
+
def number?
|
71
|
+
!self.array && super
|
69
72
|
end
|
70
73
|
|
71
74
|
def type_cast_code_with_extended_types(var_name)
|
@@ -89,8 +92,15 @@ module ActiveRecord
|
|
89
92
|
return IPAddr.new(string)
|
90
93
|
end
|
91
94
|
end
|
95
|
+
|
92
96
|
private
|
93
97
|
|
98
|
+
def force_character_encoding(string_array)
|
99
|
+
string_array.map do |item|
|
100
|
+
item.respond_to?(:force_encoding) ? item.force_encoding(ActiveRecord::Base.connection.encoding_for_ruby) : item
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
94
104
|
def simplified_type_with_extended_types(field_type)
|
95
105
|
case field_type
|
96
106
|
when 'uuid'
|
@@ -115,6 +125,17 @@ module ActiveRecord
|
|
115
125
|
attr_accessor :array
|
116
126
|
end
|
117
127
|
|
128
|
+
# Translate from the current database encoding to the encoding we
|
129
|
+
# will force string array components into on retrievial.
|
130
|
+
def encoding_for_ruby
|
131
|
+
@database_encoding ||= case ActiveRecord::Base.connection.encoding
|
132
|
+
when 'UTF8'
|
133
|
+
'UTF-8'
|
134
|
+
else
|
135
|
+
ActiveRecord::Base.connection.encoding
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
118
139
|
class TableDefinition
|
119
140
|
EXTENDED_TYPES.keys.map(&:to_s).each do |column_type|
|
120
141
|
class_eval <<-EOV, __FILE__, __LINE__ + 1
|
@@ -179,6 +200,15 @@ module ActiveRecord
|
|
179
200
|
super
|
180
201
|
end
|
181
202
|
|
203
|
+
def add_index(table_name, column_name, options = {})
|
204
|
+
index_name, unique, index_columns, _ = add_index_options(table_name, column_name, options)
|
205
|
+
if options.is_a? Hash
|
206
|
+
index_type = options[:index_type] ? " USING #{options[:index_type]} " : ""
|
207
|
+
index_options = options[:where] ? " WHERE #{options[:where]}" : ""
|
208
|
+
end
|
209
|
+
execute "CREATE #{unique} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}#{index_type}(#{index_columns})#{index_options}"
|
210
|
+
end
|
211
|
+
|
182
212
|
def change_table(table_name, options = {})
|
183
213
|
if supports_bulk_alter? && options[:bulk]
|
184
214
|
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
@@ -221,7 +251,7 @@ module ActiveRecord
|
|
221
251
|
end
|
222
252
|
when Array
|
223
253
|
if column.array
|
224
|
-
array_to_string(value, column)
|
254
|
+
array_to_string(value, column)
|
225
255
|
else
|
226
256
|
type_cast_without_extended_types(value, column)
|
227
257
|
end
|
@@ -234,22 +264,93 @@ module ActiveRecord
|
|
234
264
|
alias_method_chain :type_cast, :extended_types
|
235
265
|
|
236
266
|
def quote_with_extended_types(value, column = nil)
|
237
|
-
if
|
267
|
+
if value.is_a? IPAddr
|
238
268
|
"'#{type_cast(value, column)}'"
|
269
|
+
elsif value.is_a? Array
|
270
|
+
"'#{array_to_string(value, column, true)}'"
|
239
271
|
else
|
240
272
|
quote_without_extended_types(value, column)
|
241
273
|
end
|
242
274
|
end
|
243
275
|
alias_method_chain :quote, :extended_types
|
244
276
|
|
277
|
+
# this is based upon rails 4 changes to include different index methods
|
278
|
+
# Returns an array of indexes for the given table.
|
279
|
+
def indexes(table_name, name = nil)
|
280
|
+
result = select_rows(<<-SQL, name)
|
281
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
|
282
|
+
FROM pg_class t
|
283
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
284
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
285
|
+
WHERE i.relkind = 'i'
|
286
|
+
AND d.indisprimary = 'f'
|
287
|
+
AND t.relname = '#{table_name}'
|
288
|
+
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
|
289
|
+
ORDER BY i.relname
|
290
|
+
SQL
|
291
|
+
result.map do |row|
|
292
|
+
index_name = row[0]
|
293
|
+
unique = row[1] == 't'
|
294
|
+
indkey = row[2].split(" ")
|
295
|
+
inddef = row[3]
|
296
|
+
oid = row[4]
|
297
|
+
|
298
|
+
columns = Hash[select_rows(<<-SQL, "Columns for index #{row[0]} on #{table_name}")]
|
299
|
+
SELECT a.attnum::text, a.attname
|
300
|
+
FROM pg_attribute a
|
301
|
+
WHERE a.attrelid = #{oid}
|
302
|
+
AND a.attnum IN (#{indkey.join(",")})
|
303
|
+
SQL
|
304
|
+
column_names = columns.values_at(*indkey).compact
|
305
|
+
|
306
|
+
# add info on sort order for columns (only desc order is explicitly specified, asc is the default)
|
307
|
+
desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
|
308
|
+
orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
|
309
|
+
#changed from rails 3.2
|
310
|
+
where = inddef.scan(/WHERE (.+)$/).flatten[0]
|
311
|
+
index_type = inddef.scan(/USING (.+?) /).flatten[0].to_sym
|
312
|
+
if column_names.present?
|
313
|
+
index_def = IndexDefinition.new(table_name, index_name, unique, column_names, [], orders)
|
314
|
+
index_def.where = where
|
315
|
+
index_def.index_type = index_type if index_type && index_type != :btree
|
316
|
+
index_def
|
317
|
+
# else nil
|
318
|
+
end
|
319
|
+
#/changed
|
320
|
+
end.compact
|
321
|
+
end
|
322
|
+
|
245
323
|
private
|
246
324
|
|
247
325
|
def ipaddr_to_string(value)
|
248
326
|
"#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
|
249
327
|
end
|
250
328
|
|
251
|
-
def array_to_string(value, column)
|
252
|
-
"{#{value.map{|val|
|
329
|
+
def array_to_string(value, column, encode_single_quotes = false)
|
330
|
+
"{#{value.map { |val| item_to_string(val, column, encode_single_quotes) }.join(',')}}"
|
331
|
+
end
|
332
|
+
|
333
|
+
def item_to_string(value, column, encode_single_quotes = false)
|
334
|
+
if value.nil?
|
335
|
+
'NULL'
|
336
|
+
elsif value.is_a? String
|
337
|
+
value = type_cast(value, column, true).dup
|
338
|
+
# Encode backslashes. One backslash becomes 4 in the resulting SQL.
|
339
|
+
# (why 4, and not 2? Trial and error shows 4 works, 2 fails to parse.)
|
340
|
+
value.gsub!('\\', '\\\\\\\\')
|
341
|
+
# Encode a bare " in the string as \"
|
342
|
+
value.gsub!('"', '\\"')
|
343
|
+
# PostgreSQL parses the string values differently if they are quoted for
|
344
|
+
# use in a statement, or if it will be used as part of a bound argument.
|
345
|
+
# For directly-inserted values (UPDATE foo SET bar='{"array"}') we need to
|
346
|
+
# escape ' as ''. For bound arguments, do not escape them.
|
347
|
+
if encode_single_quotes
|
348
|
+
value.gsub!("'", "''")
|
349
|
+
end
|
350
|
+
"\"#{value}\""
|
351
|
+
else
|
352
|
+
type_cast(value, column, true)
|
353
|
+
end
|
253
354
|
end
|
254
355
|
end
|
255
356
|
end
|
@@ -2,6 +2,11 @@ require 'active_record/schema_dumper'
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
class SchemaDumper
|
5
|
+
VALID_COLUMN_SPEC_KEYS = [:name, :limit, :precision, :scale, :default, :null, :array]
|
6
|
+
def self.valid_column_spec_keys
|
7
|
+
VALID_COLUMN_SPEC_KEYS
|
8
|
+
end
|
9
|
+
|
5
10
|
private
|
6
11
|
def table(table, stream)
|
7
12
|
columns = @connection.columns(table)
|
@@ -30,11 +35,13 @@ module ActiveRecord
|
|
30
35
|
column_specs = columns.map do |column|
|
31
36
|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
|
32
37
|
next if column.name == pk
|
33
|
-
column_spec(column)
|
38
|
+
spec = column_spec(column)
|
39
|
+
(spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
|
40
|
+
spec
|
34
41
|
end.compact
|
35
42
|
|
36
43
|
# find all migration keys used in this table
|
37
|
-
keys =
|
44
|
+
keys = self.class.valid_column_spec_keys & column_specs.map{ |k| k.keys }.flatten
|
38
45
|
|
39
46
|
# figure out the lengths for each column based on above keys
|
40
47
|
lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
|
@@ -73,6 +80,37 @@ module ActiveRecord
|
|
73
80
|
stream
|
74
81
|
end
|
75
82
|
|
83
|
+
#mostly rails 3.2 code
|
84
|
+
def indexes(table, stream)
|
85
|
+
if (indexes = @connection.indexes(table)).any?
|
86
|
+
add_index_statements = indexes.map do |index|
|
87
|
+
statement_parts = [
|
88
|
+
('add_index ' + index.table.inspect),
|
89
|
+
index.columns.inspect,
|
90
|
+
(':name => ' + index.name.inspect),
|
91
|
+
]
|
92
|
+
statement_parts << ':unique => true' if index.unique
|
93
|
+
|
94
|
+
index_lengths = (index.lengths || []).compact
|
95
|
+
statement_parts << (':length => ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
|
96
|
+
|
97
|
+
index_orders = (index.orders || {})
|
98
|
+
statement_parts << (':order => ' + index.orders.inspect) unless index_orders.empty?
|
99
|
+
|
100
|
+
# changed from rails 2.3
|
101
|
+
statement_parts << (':where => ' + index.where.inspect) if index.where
|
102
|
+
statement_parts << (':index_type => ' + index.index_type.inspect) if index.index_type
|
103
|
+
# /changed
|
104
|
+
|
105
|
+
' ' + statement_parts.join(', ')
|
106
|
+
end
|
107
|
+
|
108
|
+
stream.puts add_index_statements.sort.join("\n")
|
109
|
+
stream.puts
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
#mostly rails 3.2 code (pulled out of table method)
|
76
114
|
def column_spec(column)
|
77
115
|
spec = {}
|
78
116
|
spec[:name] = column.name.inspect
|
@@ -89,8 +127,9 @@ module ActiveRecord
|
|
89
127
|
spec[:scale] = column.scale.inspect if column.scale
|
90
128
|
spec[:null] = 'false' unless column.null
|
91
129
|
spec[:default] = default_string(column.default) if column.has_default?
|
130
|
+
# changed from rails 3.2 code
|
92
131
|
spec[:array] = 'true' if column.respond_to?(:array) && column.array
|
93
|
-
|
132
|
+
# /changed
|
94
133
|
spec
|
95
134
|
end
|
96
135
|
end
|
@@ -21,11 +21,8 @@ module Arel
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def change_string value
|
24
|
-
|
25
|
-
|
26
|
-
else
|
27
|
-
value.gsub(/'/,'')
|
28
|
-
end
|
24
|
+
return value unless value.is_a?(String)
|
25
|
+
value.gsub(/^\'/, '"').gsub(/\'$/, '"')
|
29
26
|
end
|
30
27
|
end
|
31
28
|
end
|
data/lib/postgres_ext/version.rb
CHANGED
data/postgres_ext.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.version = PostgresExt::VERSION
|
17
17
|
|
18
18
|
gem.add_dependency 'activerecord', '~> 3.2.0'
|
19
|
-
gem.add_dependency 'pg_array_parser', '~> 0.0.
|
19
|
+
gem.add_dependency 'pg_array_parser', '~> 0.0.8'
|
20
20
|
|
21
21
|
gem.add_development_dependency 'rails', '~> 3.2.0'
|
22
22
|
gem.add_development_dependency 'rspec-rails', '~> 2.9.0'
|
@@ -27,8 +27,11 @@ Gem::Specification.new do |gem|
|
|
27
27
|
gem.add_development_dependency 'pg', '~> 0.13.2'
|
28
28
|
end
|
29
29
|
unless ENV['CI']
|
30
|
-
|
31
|
-
|
30
|
+
if RUBY_PLATFORM =~ /java/
|
31
|
+
gem.add_development_dependency 'ruby-debug'
|
32
|
+
elsif RUBY_VERSION == '1.9.3'
|
33
|
+
gem.add_development_dependency 'debugger', '~> 1.1.2'
|
34
|
+
end
|
32
35
|
end
|
33
36
|
gem.add_development_dependency 'fivemat'
|
34
37
|
end
|
data/spec/arel/array_spec.rb
CHANGED
@@ -6,6 +6,7 @@ describe 'Array Column Predicates' do
|
|
6
6
|
before do
|
7
7
|
adapter.create_table :arel_arrays, :force => true do |t|
|
8
8
|
t.string :tags, :array => true
|
9
|
+
t.integer :tag_ids, :array => true
|
9
10
|
end
|
10
11
|
|
11
12
|
class ArelArray < ActiveRecord::Base
|
@@ -22,7 +23,13 @@ describe 'Array Column Predicates' do
|
|
22
23
|
it 'converts Arel array_overlap statment' do
|
23
24
|
arel_table = ArelArray.arel_table
|
24
25
|
|
25
|
-
arel_table.where(arel_table[:tags].array_overlap(['tag','tag 2'])).to_sql.should match /&& '\{tag,tag 2\}'/
|
26
|
+
arel_table.where(arel_table[:tags].array_overlap(['tag','tag 2'])).to_sql.should match /&& '\{"tag","tag 2"\}'/
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'converts Arel array_overlap statment' do
|
30
|
+
arel_table = ArelArray.arel_table
|
31
|
+
|
32
|
+
arel_table.where(arel_table[:tag_ids].array_overlap([1,2])).to_sql.should match /&& '\{1,2\}'/
|
26
33
|
end
|
27
34
|
|
28
35
|
it 'returns matched records' do
|
data/spec/columns/array_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe 'Array column' do
|
@@ -13,14 +15,32 @@ describe 'Array column' do
|
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
18
|
+
context 'utf8' do
|
19
|
+
it "handles incoming UTF8 as UTF8" do
|
20
|
+
string_array_column.type_cast('{"Аркалио"}').should eq ['Аркалио']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
16
24
|
context 'corner cases, strings with commas and quotations' do
|
17
25
|
it 'converts the PostgreSQL value containing escaped " to an array' do
|
18
26
|
string_array_column.type_cast('{"has \" quote",another value}').should eq ['has " quote', 'another value']
|
19
27
|
end
|
20
28
|
|
21
|
-
it 'converts the PostgreSQL value
|
29
|
+
it 'converts the PostgreSQL value containing commas to an array' do
|
22
30
|
string_array_column.type_cast('{"has , comma",another value,"more, commas"}').should eq ['has , comma', 'another value', 'more, commas']
|
23
31
|
end
|
32
|
+
|
33
|
+
it 'converts strings containing , to the proper value' do
|
34
|
+
adapter.type_cast(['c,'], string_array_column).should eq '{"c,"}'
|
35
|
+
end
|
36
|
+
|
37
|
+
it "handles strings with double quotes" do
|
38
|
+
adapter.type_cast(['a"b'], string_array_column).should eq '{"a\\"b"}'
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'converts arrays of strings containing nil to the proper value' do
|
42
|
+
adapter.type_cast(['one', nil, 'two'], string_array_column).should eq '{"one",NULL,"two"}'
|
43
|
+
end
|
24
44
|
end
|
25
45
|
end
|
26
46
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
Dummy::Application.configure do
|
2
|
-
require 'debugger' if RUBY_VERSION == '1.9.3'
|
2
|
+
require 'debugger' if RUBY_VERSION == '1.9.3' && ! ENV['CI']
|
3
3
|
# Settings specified here will take precedence over those in config/application.rb
|
4
4
|
|
5
5
|
# In the development environment your application's code is reloaded on
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'index migrations' do
|
4
|
+
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
+
it 'creates special index' do
|
6
|
+
lambda do
|
7
|
+
connection.create_table :index_types do |t|
|
8
|
+
t.integer :col1, :array => true
|
9
|
+
t.integer :col2
|
10
|
+
end
|
11
|
+
connection.add_index(:index_types, :col1, :index_type => :gin)
|
12
|
+
connection.add_index(:index_types, :col2, :where => '(col2 > 50)')
|
13
|
+
end.should_not raise_exception
|
14
|
+
|
15
|
+
indexes = connection.indexes(:index_types)
|
16
|
+
index_1 = indexes.detect { |c| c.columns.map(&:to_s) == ['col1']}
|
17
|
+
index_2 = indexes.detect { |c| c.columns.map(&:to_s) == ['col2']}
|
18
|
+
|
19
|
+
index_1.index_type.to_s.should eq 'gin'
|
20
|
+
index_2.where.should match /col2 > 50/
|
21
|
+
end
|
22
|
+
end
|
data/spec/models/array_spec.rb
CHANGED
@@ -7,12 +7,13 @@ describe 'Models with array columns' do
|
|
7
7
|
before do
|
8
8
|
adapter.create_table :users, :force => true do |t|
|
9
9
|
t.string :nick_names, :array => true
|
10
|
+
t.integer :favorite_numbers, :array => true
|
10
11
|
|
11
12
|
t.timestamps
|
12
13
|
end
|
13
14
|
|
14
15
|
class User < ActiveRecord::Base
|
15
|
-
attr_accessible :nick_names
|
16
|
+
attr_accessible :nick_names, :favorite_numbers
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
@@ -73,6 +74,42 @@ describe 'Models with array columns' do
|
|
73
74
|
end
|
74
75
|
end
|
75
76
|
|
77
|
+
context '#<column>? do' do
|
78
|
+
describe 'checking a value via <attribute_name>? 'do
|
79
|
+
it 'returns false if it\'s an empty array' do
|
80
|
+
user = User.create(:nick_names => [], :favorite_numbers => [])
|
81
|
+
user.reload
|
82
|
+
|
83
|
+
user.nick_names?.should be_false
|
84
|
+
user.favorite_numbers?.should be_false
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'returns false if it\'s an empty array' do
|
88
|
+
user = User.create(:nick_names => ['bob'], :favorite_numbers => [0])
|
89
|
+
user.reload
|
90
|
+
|
91
|
+
user.nick_names?.should be_true
|
92
|
+
user.favorite_numbers?.should be_true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context '#update_column' do
|
98
|
+
describe 'setting a value via update_column' do
|
99
|
+
it 'returns the value set when the record is retrieved' do
|
100
|
+
pending #This fails, not sure where to fix this, takes different code path than #update_attribute
|
101
|
+
user = User.create(:nick_names => [])
|
102
|
+
user.reload
|
103
|
+
|
104
|
+
user.update_column(:nick_names, ['some', 'values'])
|
105
|
+
user.save
|
106
|
+
|
107
|
+
user.reload
|
108
|
+
user.nick_names.should eq ['some', 'values']
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
76
113
|
context '#update_attributes' do
|
77
114
|
describe 'setting a value via update_attributes' do
|
78
115
|
it 'returns the value set when the record is retrieved' do
|
@@ -87,6 +124,105 @@ describe 'Models with array columns' do
|
|
87
124
|
end
|
88
125
|
end
|
89
126
|
end
|
127
|
+
|
128
|
+
describe 'strings contain special characters' do
|
129
|
+
context '#save' do
|
130
|
+
it 'contains: \'' do
|
131
|
+
data = ['some\'thing']
|
132
|
+
u = User.create
|
133
|
+
u.nick_names = data
|
134
|
+
u.save!
|
135
|
+
u.reload
|
136
|
+
u.nick_names.should eq data
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'contains: {' do
|
140
|
+
data = ['some{thing']
|
141
|
+
u = User.create
|
142
|
+
u.nick_names = data
|
143
|
+
u.save!
|
144
|
+
u.reload
|
145
|
+
u.nick_names.should eq data
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'contains: }' do
|
149
|
+
data = ['some}thing']
|
150
|
+
u = User.create
|
151
|
+
u.nick_names = data
|
152
|
+
u.save!
|
153
|
+
u.reload
|
154
|
+
u.nick_names.should eq data
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'contains: backslash' do
|
158
|
+
data = ['some\\thing']
|
159
|
+
u = User.create
|
160
|
+
u.nick_names = data
|
161
|
+
u.save!
|
162
|
+
u.reload
|
163
|
+
u.nick_names.should eq data
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'contains: "' do
|
167
|
+
data = ['some"thing']
|
168
|
+
u = User.create
|
169
|
+
u.nick_names = data
|
170
|
+
u.save!
|
171
|
+
u.reload
|
172
|
+
u.nick_names.should eq data
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context '#create' do
|
177
|
+
it 'contains: \'' do
|
178
|
+
data = ['some\'thing']
|
179
|
+
u = User.create(:nick_names => data)
|
180
|
+
u.reload
|
181
|
+
u.nick_names.should eq data
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'contains: {' do
|
185
|
+
data = ['some{thing']
|
186
|
+
u = User.create(:nick_names => data)
|
187
|
+
u.reload
|
188
|
+
u.nick_names.should eq data
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'contains: }' do
|
192
|
+
data = ['some}thing']
|
193
|
+
u = User.create(:nick_names => data)
|
194
|
+
u.reload
|
195
|
+
u.nick_names.should eq data
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'contains: backslash' do
|
199
|
+
data = ['some\\thing']
|
200
|
+
u = User.create(:nick_names => data)
|
201
|
+
u.reload
|
202
|
+
u.nick_names.should eq data
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'contains: "' do
|
206
|
+
data = ['some"thing']
|
207
|
+
u = User.create(:nick_names => data)
|
208
|
+
u.reload
|
209
|
+
u.nick_names.should eq data
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe 'array_overlap' do
|
215
|
+
it "works" do
|
216
|
+
arel = User.arel_table
|
217
|
+
User.create(:nick_names => ['this'])
|
218
|
+
x = User.create
|
219
|
+
x.nick_names = ["s'o{m}e", 'thing']
|
220
|
+
x.save
|
221
|
+
u = User.where(arel[:nick_names].array_overlap(["s'o{m}e"]))
|
222
|
+
u.first.should_not be_nil
|
223
|
+
u.first.nick_names.should eq ["s'o{m}e", 'thing']
|
224
|
+
end
|
225
|
+
end
|
90
226
|
end
|
91
227
|
|
92
228
|
context 'default values' do
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'index schema dumper' do
|
4
|
+
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
+
it 'correctly generates index statements' do
|
6
|
+
connection.create_table :index_types do |t|
|
7
|
+
t.integer :col1, :array => true
|
8
|
+
t.integer :col2
|
9
|
+
end
|
10
|
+
connection.add_index(:index_types, :col1, :index_type => :gin)
|
11
|
+
connection.add_index(:index_types, :col2, :where => '(col2 > 50)')
|
12
|
+
|
13
|
+
stream = StringIO.new
|
14
|
+
ActiveRecord::SchemaDumper.dump(connection, stream)
|
15
|
+
output = stream.string
|
16
|
+
|
17
|
+
output.should match /:index_type => :gin/
|
18
|
+
output.should_not match /:index_type => :btree/
|
19
|
+
output.should match /:where => "\(col2 > 50\)"/
|
20
|
+
end
|
21
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: postgres_ext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -34,7 +34,7 @@ dependencies:
|
|
34
34
|
requirements:
|
35
35
|
- - ~>
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version: 0.0.
|
37
|
+
version: 0.0.8
|
38
38
|
type: :runtime
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -42,7 +42,7 @@ dependencies:
|
|
42
42
|
requirements:
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version: 0.0.
|
45
|
+
version: 0.0.8
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
47
|
name: rails
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -226,6 +226,7 @@ files:
|
|
226
226
|
- spec/migrations/active_record_migration_spec.rb
|
227
227
|
- spec/migrations/array_spec.rb
|
228
228
|
- spec/migrations/cidr_spec.rb
|
229
|
+
- spec/migrations/index_spec.rb
|
229
230
|
- spec/migrations/inet_spec.rb
|
230
231
|
- spec/migrations/macaddr_spec.rb
|
231
232
|
- spec/migrations/uuid_spec.rb
|
@@ -233,6 +234,7 @@ files:
|
|
233
234
|
- spec/models/inet_spec.rb
|
234
235
|
- spec/schema_dumper/array_spec.rb
|
235
236
|
- spec/schema_dumper/cidr_spec.rb
|
237
|
+
- spec/schema_dumper/index_spec.rb
|
236
238
|
- spec/schema_dumper/inet_spec.rb
|
237
239
|
- spec/schema_dumper/macaddr_spec.rb
|
238
240
|
- spec/schema_dumper/uuid_spec.rb
|
@@ -251,7 +253,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
251
253
|
version: '0'
|
252
254
|
segments:
|
253
255
|
- 0
|
254
|
-
hash: -
|
256
|
+
hash: -842218502147903775
|
255
257
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
256
258
|
none: false
|
257
259
|
requirements:
|
@@ -260,7 +262,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
260
262
|
version: '0'
|
261
263
|
segments:
|
262
264
|
- 0
|
263
|
-
hash: -
|
265
|
+
hash: -842218502147903775
|
264
266
|
requirements: []
|
265
267
|
rubyforge_project:
|
266
268
|
rubygems_version: 1.8.23
|
@@ -326,6 +328,7 @@ test_files:
|
|
326
328
|
- spec/migrations/active_record_migration_spec.rb
|
327
329
|
- spec/migrations/array_spec.rb
|
328
330
|
- spec/migrations/cidr_spec.rb
|
331
|
+
- spec/migrations/index_spec.rb
|
329
332
|
- spec/migrations/inet_spec.rb
|
330
333
|
- spec/migrations/macaddr_spec.rb
|
331
334
|
- spec/migrations/uuid_spec.rb
|
@@ -333,6 +336,7 @@ test_files:
|
|
333
336
|
- spec/models/inet_spec.rb
|
334
337
|
- spec/schema_dumper/array_spec.rb
|
335
338
|
- spec/schema_dumper/cidr_spec.rb
|
339
|
+
- spec/schema_dumper/index_spec.rb
|
336
340
|
- spec/schema_dumper/inet_spec.rb
|
337
341
|
- spec/schema_dumper/macaddr_spec.rb
|
338
342
|
- spec/schema_dumper/uuid_spec.rb
|