postgres_ext 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|