postgres_ext 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +2 -2
- data/docs/indexes.md +11 -2
- data/docs/migrations.md +16 -0
- data/docs/querying.md +32 -0
- data/lib/postgres_ext/active_record/connection_adapters/postgres_adapter.rb +16 -8
- data/lib/postgres_ext/active_record/relation/query_methods.rb +1 -1
- data/lib/postgres_ext/active_record/schema_dumper.rb +3 -2
- data/lib/postgres_ext/arel/nodes.rb +1 -0
- data/lib/postgres_ext/arel/nodes/array_nodes.rb +9 -0
- data/lib/postgres_ext/arel/nodes/contained_within.rb +2 -6
- data/lib/postgres_ext/arel/predications.rb +2 -2
- data/lib/postgres_ext/arel/visitors/visitor.rb +9 -7
- data/lib/postgres_ext/version.rb +1 -1
- data/spec/arel/array_spec.rb +50 -30
- data/spec/arel/inet_spec.rb +9 -25
- data/spec/dummy/app/models/person.rb +1 -1
- data/spec/dummy/db/migrate/20120501163758_create_people.rb +6 -4
- data/spec/dummy/db/schema.rb +6 -13
- data/spec/migrations/index_spec.rb +38 -20
- data/spec/models/array_spec.rb +2 -2
- data/spec/models/inet_spec.rb +9 -0
- data/spec/queries/array_queries_spec.rb +15 -0
- data/spec/schema_dumper/index_spec.rb +13 -14
- data/spec/spec_helper.rb +1 -1
- metadata +8 -27
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 78f29ab859ad31017d1173ec07e12a0b5603b823
|
4
|
+
data.tar.gz: 3ae3cbc58fc1cdf7018721d01078855cc4fcc089
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 995ffd20fefb645371140742c7742cee1ebb9bc1cdbc9bd9bd7f9b32fa8057ac83fc661daed70bffb311700cb9c5e1b87ad64102008509ddf93ec6a7e2f11d14
|
7
|
+
data.tar.gz: ae873dde5e07a41dee3b5ccccbbdd6f06443f7a0ccb0e65ff3f2678a7a8fcb91d5323afb6bbcbf17be03a30dd9278ff6fee71b96b5e0bc7e863ef9274615d992
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## 0.3.0
|
2
|
+
|
3
|
+
* Adds support to create indexes concurrently - Dan McClain
|
4
|
+
* Changes using syntax, updates specs - Dan McClain
|
5
|
+
* Empty strings are converted to nil by string_to_cidr_address - Dan McClain
|
6
|
+
* Replaced .symbolize with .to_sym in arel nodes. - OMCnet Development Team
|
7
|
+
* Removes array_contains in favor of a column aware contains - Dan McClain
|
8
|
+
* Renames Arel array_overlap to overlap - Dan McClain
|
9
|
+
* Merge pull request #67 from jagregory/array_contains Array contains operator support - Dan McClain
|
10
|
+
* Update querying doc to include array_contains - James Gregory
|
11
|
+
* Array contains operator ( @> ) support - James Gregory
|
12
|
+
* how to use SQL to convert string-delimited arrays in docs - Turadg Aleahmad
|
13
|
+
* Check if connection responds to #support_extensions? before invoking it - Dirk von Grünigen
|
14
|
+
|
1
15
|
## 0.2.2
|
2
16
|
|
3
17
|
* Fixes issue with visit_Array monkey patch - Dan McClain (@danmcclain)
|
data/Gemfile
CHANGED
data/docs/indexes.md
CHANGED
@@ -6,8 +6,8 @@ Postgres\_ext allows you to specify index type and index operator
|
|
6
6
|
class at index creation.
|
7
7
|
|
8
8
|
```ruby
|
9
|
-
add_index :table_name, :column, :
|
10
|
-
add_index :table_name, :column, :
|
9
|
+
add_index :table_name, :column, :using => :gin
|
10
|
+
add_index :table_name, :column, :using => :gin, :index_opclass => :gin_trgm_ops
|
11
11
|
```
|
12
12
|
|
13
13
|
## Where clauses
|
@@ -17,3 +17,12 @@ Postgres\_ext allows you to specify a where clause at index creation.
|
|
17
17
|
```ruby
|
18
18
|
add_index :table_name, :column, :where => 'column < 50'
|
19
19
|
```
|
20
|
+
|
21
|
+
## Concurrent Indexes
|
22
|
+
|
23
|
+
Postgres\_ext allows you to create indexes concurrently using the
|
24
|
+
`:algorithm` option
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
add_index :table_name, :column, :algorithm => :concurrently
|
28
|
+
```
|
data/docs/migrations.md
CHANGED
@@ -74,3 +74,19 @@ create_table :testing do |t|
|
|
74
74
|
# char varying(30)[]
|
75
75
|
end
|
76
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/querying.md
CHANGED
@@ -42,6 +42,38 @@ User.where(user_arel[:tags].array_overlap(['one','two']))
|
|
42
42
|
# => SELECT \"users\".* FROM \"users\" WHERE \"users\".\"tags\" && '{one,two}'
|
43
43
|
```
|
44
44
|
|
45
|
+
### @> - Array Contains operator
|
46
|
+
|
47
|
+
PostgreSQL has a contains (`@>`) operator for querying whether all the
|
48
|
+
elements of an array are within another.
|
49
|
+
|
50
|
+
```sql
|
51
|
+
ARRAY[1,2,3] @> ARRAY[3,4]
|
52
|
+
-- f
|
53
|
+
|
54
|
+
ARRAY[1,2,3] @> ARRAY[2,3]
|
55
|
+
-- t
|
56
|
+
```
|
57
|
+
|
58
|
+
Postgres\_ext extends the `ActiveRecord::Relation.where` method by
|
59
|
+
adding a `contains` method. To make a contains query, you can do:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
User.where.contains(:nick_names => ['Bob', 'Fred'])
|
63
|
+
```
|
64
|
+
|
65
|
+
Postgres\_ext defines `array_contains`, an [Arel](https://github.com/rails/arel)
|
66
|
+
predicate for the `@>` operator. This is utilized by the
|
67
|
+
`where.array_contains` call above.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
user_arel = User.arel_table
|
71
|
+
|
72
|
+
# Execute the query
|
73
|
+
User.where(user_arel[:tags].array_contains(['one','two']))
|
74
|
+
# => SELECT "users".* FROM "users" WHERE "users"."tags" @> '{"one","two"}'
|
75
|
+
```
|
76
|
+
|
45
77
|
### ANY or ALL functions
|
46
78
|
|
47
79
|
When querying array columns, you have the ability to see if a predicate
|
@@ -5,7 +5,7 @@ require 'pg_array_parser'
|
|
5
5
|
module ActiveRecord
|
6
6
|
module ConnectionAdapters
|
7
7
|
class IndexDefinition
|
8
|
-
attr_accessor :
|
8
|
+
attr_accessor :using, :where, :index_opclass
|
9
9
|
end
|
10
10
|
|
11
11
|
class PostgreSQLColumn
|
@@ -92,7 +92,10 @@ module ActiveRecord
|
|
92
92
|
class << self
|
93
93
|
def string_to_cidr_address(string)
|
94
94
|
return string unless String === string
|
95
|
-
|
95
|
+
|
96
|
+
if string.present?
|
97
|
+
IPAddr.new(string)
|
98
|
+
end
|
96
99
|
end
|
97
100
|
end
|
98
101
|
|
@@ -217,11 +220,16 @@ module ActiveRecord
|
|
217
220
|
def add_index(table_name, column_name, options = {})
|
218
221
|
index_name, unique, index_columns, _ = add_index_options(table_name, column_name, options)
|
219
222
|
if options.is_a? Hash
|
220
|
-
index_type = options[:
|
223
|
+
index_type = options[:using] ? " USING #{options[:using]} " : ""
|
221
224
|
index_options = options[:where] ? " WHERE #{options[:where]}" : ""
|
222
225
|
index_opclass = options[:index_opclass]
|
226
|
+
index_algorithm = options[:algorithm] == :concurrently ? ' CONCURRENTLY' : ''
|
227
|
+
|
228
|
+
if options[:algorithm].present? && options[:algorithm] != :concurrently
|
229
|
+
raise ArgumentError.new 'Algorithm must be one of the following: :concurrently'
|
230
|
+
end
|
223
231
|
end
|
224
|
-
execute "CREATE #{unique} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}#{index_type}(#{index_columns} #{index_opclass})#{index_options}"
|
232
|
+
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}"
|
225
233
|
end
|
226
234
|
|
227
235
|
def add_extension(extension_name, options={})
|
@@ -339,16 +347,16 @@ module ActiveRecord
|
|
339
347
|
orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
|
340
348
|
#changed from rails 3.2
|
341
349
|
where = inddef.scan(/WHERE (.+)$/).flatten[0]
|
342
|
-
|
343
|
-
if
|
350
|
+
using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
|
351
|
+
if using
|
344
352
|
index_op = inddef.scan(/USING .+? \(.+? (#{opclasses.join('|')})\)/).flatten
|
345
353
|
index_op = index_op[0].to_sym if index_op.present?
|
346
354
|
end
|
347
355
|
if column_names.present?
|
348
356
|
index_def = IndexDefinition.new(table_name, index_name, unique, column_names, [], orders)
|
349
357
|
index_def.where = where
|
350
|
-
index_def.
|
351
|
-
index_def.index_opclass = index_op if
|
358
|
+
index_def.using = using if using && using != :btree
|
359
|
+
index_def.index_opclass = index_op if using && using != :btree && index_op
|
352
360
|
index_def
|
353
361
|
# else nil
|
354
362
|
end
|
@@ -10,7 +10,8 @@ module ActiveRecord
|
|
10
10
|
def dump(stream)
|
11
11
|
header(stream)
|
12
12
|
# added
|
13
|
-
extensions(stream) if @connection.supports_extensions?
|
13
|
+
extensions(stream) if @connection.respond_to?(:supports_extensions?) &&
|
14
|
+
@connection.supports_extensions?
|
14
15
|
# /added
|
15
16
|
tables(stream)
|
16
17
|
trailer(stream)
|
@@ -118,7 +119,7 @@ module ActiveRecord
|
|
118
119
|
|
119
120
|
# changed from rails 2.3
|
120
121
|
statement_parts << (':where => ' + index.where.inspect) if index.where
|
121
|
-
statement_parts << (':
|
122
|
+
statement_parts << (':using => ' + index.using.inspect) if index.using
|
122
123
|
statement_parts << (':index_opclass => ' + index.index_opclass.inspect) if index.index_opclass.present?
|
123
124
|
# /changed
|
124
125
|
|
@@ -6,7 +6,7 @@ module Arel
|
|
6
6
|
end
|
7
7
|
|
8
8
|
class ContainedWithinEquals < Arel::Nodes::Binary
|
9
|
-
def operator; '<<='.
|
9
|
+
def operator; '<<='.to_sym end
|
10
10
|
end
|
11
11
|
|
12
12
|
class Contains < Arel::Nodes::Binary
|
@@ -14,11 +14,7 @@ module Arel
|
|
14
14
|
end
|
15
15
|
|
16
16
|
class ContainsEquals < Arel::Nodes::Binary
|
17
|
-
def operator; '>>='.
|
18
|
-
end
|
19
|
-
|
20
|
-
class ArrayOverlap < Arel::Nodes::Binary
|
21
|
-
def operator; '&&' end
|
17
|
+
def operator; '>>='.to_sym end
|
22
18
|
end
|
23
19
|
end
|
24
20
|
end
|
@@ -13,19 +13,21 @@ module Arel
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def visit_Arel_Nodes_Contains o
|
16
|
-
|
16
|
+
left_column = o.left.relation.engine.columns.find { |col| col.name == o.left.name.to_s }
|
17
|
+
|
18
|
+
if left_column && left_column.respond_to?(:array) && left_column.array
|
19
|
+
"#{visit o.left} @> #{visit o.right}"
|
20
|
+
else
|
21
|
+
"#{visit o.left} >> #{visit o.right}"
|
22
|
+
end
|
17
23
|
end
|
18
24
|
|
19
25
|
def visit_Arel_Nodes_ContainsEquals o
|
20
26
|
"#{visit o.left} >>= #{visit o.right}"
|
21
27
|
end
|
22
28
|
|
23
|
-
def
|
24
|
-
|
25
|
-
"#{visit o.left} && #{visit o.right}"
|
26
|
-
else
|
27
|
-
"#{visit o.left} && #{visit o.right}"
|
28
|
-
end
|
29
|
+
def visit_Arel_Nodes_Overlap o
|
30
|
+
"#{visit o.left} && #{visit o.right}"
|
29
31
|
end
|
30
32
|
|
31
33
|
def visit_IPAddr value
|
data/lib/postgres_ext/version.rb
CHANGED
data/spec/arel/array_spec.rb
CHANGED
@@ -1,57 +1,77 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'Array Column Predicates' do
|
4
|
-
|
4
|
+
describe 'Array Overlap' do
|
5
|
+
it 'converts Arel overlap statment' do
|
6
|
+
arel_table = Person.arel_table
|
5
7
|
|
6
|
-
|
7
|
-
adapter.create_table :arel_arrays, :force => true do |t|
|
8
|
-
t.string :tags, :array => true
|
9
|
-
t.integer :tag_ids, :array => true
|
8
|
+
arel_table.where(arel_table[:tags].overlap(['tag','tag 2'])).to_sql.should match /&& '\{"tag","tag 2"\}'/
|
10
9
|
end
|
11
10
|
|
12
|
-
|
13
|
-
|
11
|
+
it 'converts Arel overlap statment' do
|
12
|
+
arel_table = Person.arel_table
|
13
|
+
|
14
|
+
arel_table.where(arel_table[:tag_ids].overlap([1,2])).to_sql.should match /&& '\{1,2\}'/
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'works with count (and other predicates)' do
|
18
|
+
arel_table = Person.arel_table
|
19
|
+
|
20
|
+
Person.where(arel_table[:tag_ids].overlap([1,2])).count.should eq 0
|
14
21
|
end
|
15
|
-
end
|
16
22
|
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
it 'returns matched records' do
|
24
|
+
one = Person.create!(:tags => ['one'])
|
25
|
+
two = Person.create!(:tags => ['two'])
|
26
|
+
arel_table = Person.arel_table
|
27
|
+
|
28
|
+
query = arel_table.where(arel_table[:tags].overlap(['one'])).project(Arel.sql('*'))
|
29
|
+
Person.find_by_sql(query.to_sql).should include(one)
|
30
|
+
|
31
|
+
query = arel_table.where(arel_table[:tags].overlap(['two'])).project(Arel.sql('*'))
|
32
|
+
Person.find_by_sql(query.to_sql).should include(two)
|
33
|
+
|
34
|
+
query = arel_table.where(arel_table[:tags].overlap(['two','one'])).project(Arel.sql('*'))
|
35
|
+
Person.find_by_sql(query.to_sql).should include(two)
|
36
|
+
Person.find_by_sql(query.to_sql).should include(one)
|
37
|
+
end
|
20
38
|
end
|
21
39
|
|
22
|
-
describe 'Array
|
23
|
-
it 'converts Arel
|
24
|
-
arel_table =
|
40
|
+
describe 'Array Contains' do
|
41
|
+
it 'converts Arel contains statement and escapes strings' do
|
42
|
+
arel_table = Person.arel_table
|
25
43
|
|
26
|
-
arel_table.where(arel_table[:tags].
|
44
|
+
arel_table.where(arel_table[:tags].contains(['tag','tag 2'])).to_sql.should match /@> '\{"tag","tag 2"\}'/
|
27
45
|
end
|
28
46
|
|
29
|
-
it 'converts Arel
|
30
|
-
arel_table =
|
47
|
+
it 'converts Arel contains statement with numbers' do
|
48
|
+
arel_table = Person.arel_table
|
31
49
|
|
32
|
-
arel_table.where(arel_table[:tag_ids].
|
50
|
+
arel_table.where(arel_table[:tag_ids].contains([1,2])).to_sql.should match /@> '\{1,2\}'/
|
33
51
|
end
|
34
52
|
|
35
53
|
it 'works with count (and other predicates)' do
|
36
|
-
arel_table =
|
54
|
+
arel_table = Person.arel_table
|
37
55
|
|
38
|
-
|
56
|
+
Person.where(arel_table[:tag_ids].contains([1,2])).count.should eq 0
|
39
57
|
end
|
40
58
|
|
41
59
|
it 'returns matched records' do
|
42
|
-
one =
|
43
|
-
two =
|
44
|
-
arel_table =
|
60
|
+
one = Person.create!(:tags => ['one', 'two', 'three'])
|
61
|
+
two = Person.create!(:tags => ['one', 'three'])
|
62
|
+
arel_table = Person.arel_table
|
45
63
|
|
46
|
-
query = arel_table.where(arel_table[:tags].
|
47
|
-
|
64
|
+
query = arel_table.where(arel_table[:tags].contains(['one', 'two'])).project(Arel.sql('*'))
|
65
|
+
Person.find_by_sql(query.to_sql).should include one
|
66
|
+
Person.find_by_sql(query.to_sql).should_not include two
|
48
67
|
|
49
|
-
query = arel_table.where(arel_table[:tags].
|
50
|
-
|
68
|
+
query = arel_table.where(arel_table[:tags].contains(['one', 'three'])).project(Arel.sql('*'))
|
69
|
+
Person.find_by_sql(query.to_sql).should include one
|
70
|
+
Person.find_by_sql(query.to_sql).should include two
|
51
71
|
|
52
|
-
query = arel_table.where(arel_table[:tags].
|
53
|
-
|
54
|
-
|
72
|
+
query = arel_table.where(arel_table[:tags].contains(['two'])).project(Arel.sql('*'))
|
73
|
+
Person.find_by_sql(query.to_sql).should include one
|
74
|
+
Person.find_by_sql(query.to_sql).should_not include two
|
55
75
|
end
|
56
76
|
end
|
57
77
|
end
|
data/spec/arel/inet_spec.rb
CHANGED
@@ -1,56 +1,40 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'INET related AREL functions' do
|
4
|
-
let!(:adapter) { ActiveRecord::Base.connection }
|
5
|
-
before do
|
6
|
-
adapter.create_table :ip_addresses, :force => true do |t|
|
7
|
-
t.inet :address
|
8
|
-
end
|
9
|
-
|
10
|
-
class IpAddress < ActiveRecord::Base
|
11
|
-
attr_accessible :address
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
after do
|
16
|
-
adapter.drop_table :ip_addresses
|
17
|
-
Object.send(:remove_const, :IpAddress)
|
18
|
-
end
|
19
|
-
|
20
4
|
describe 'quoting IPAddr in sql statement' do
|
21
5
|
it 'properly converts IPAddr to quoted strings when passed as an argument to a where clause' do
|
22
|
-
|
6
|
+
Person.where(:ip => IPAddr.new('127.0.0.1')).to_sql.should include("'127.0.0.1/32'")
|
23
7
|
end
|
24
8
|
end
|
25
9
|
|
26
10
|
describe 'contained with (<<) operator' do
|
27
11
|
it 'converts Arel contained_within statements to <<' do
|
28
|
-
arel_table =
|
12
|
+
arel_table = Person.arel_table
|
29
13
|
|
30
|
-
arel_table.where(arel_table[:
|
14
|
+
arel_table.where(arel_table[:ip].contained_within(IPAddr.new('127.0.0.1/24'))).to_sql.should match /<< '127.0.0.0\/24'/
|
31
15
|
end
|
32
16
|
end
|
33
17
|
|
34
18
|
describe 'contained within or equals (<<=) operator' do
|
35
19
|
it 'converts Arel contained_within_or_equals statements to <<=' do
|
36
|
-
arel_table =
|
20
|
+
arel_table = Person.arel_table
|
37
21
|
|
38
|
-
arel_table.where(arel_table[:
|
22
|
+
arel_table.where(arel_table[:ip].contained_within_or_equals(IPAddr.new('127.0.0.1/24'))).to_sql.should match /<<= '127.0.0.0\/24'/
|
39
23
|
end
|
40
24
|
end
|
41
25
|
describe 'contains (>>) operator' do
|
42
26
|
it 'converts Arel contains statements to >>' do
|
43
|
-
arel_table =
|
27
|
+
arel_table = Person.arel_table
|
44
28
|
|
45
|
-
arel_table.where(arel_table[:
|
29
|
+
arel_table.where(arel_table[:ip].contains(IPAddr.new('127.0.0.1/24'))).to_sql.should match />> '127.0.0.0\/24'/
|
46
30
|
end
|
47
31
|
end
|
48
32
|
|
49
33
|
describe 'contains or equals (>>=) operator' do
|
50
34
|
it 'converts Arel contains_or_equals statements to >>=' do
|
51
|
-
arel_table =
|
35
|
+
arel_table = Person.arel_table
|
52
36
|
|
53
|
-
arel_table.where(arel_table[:
|
37
|
+
arel_table.where(arel_table[:ip].contains_or_equals(IPAddr.new('127.0.0.1/24'))).to_sql.should match />>= '127.0.0.0\/24'/
|
54
38
|
end
|
55
39
|
end
|
56
40
|
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
class CreatePeople < ActiveRecord::Migration
|
2
2
|
def change
|
3
3
|
create_table :people do |t|
|
4
|
-
t.inet
|
5
|
-
t.cidr
|
6
|
-
t.
|
7
|
-
t.string
|
4
|
+
t.inet :ip
|
5
|
+
t.cidr :subnet
|
6
|
+
t.integer :tag_ids, :array => true
|
7
|
+
t.string :tags, :array => true
|
8
|
+
t.text :biography
|
9
|
+
t.integer :lucky_number
|
8
10
|
|
9
11
|
t.timestamps
|
10
12
|
end
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -13,22 +13,15 @@
|
|
13
13
|
|
14
14
|
ActiveRecord::Schema.define(:version => 20120501163758) do
|
15
15
|
|
16
|
-
add_extension "hstore"
|
17
|
-
add_extension "pg_trgm"
|
18
|
-
add_extension "citext"
|
19
|
-
|
20
16
|
create_table "people", :force => true do |t|
|
21
17
|
t.inet "ip"
|
22
18
|
t.cidr "subnet"
|
23
|
-
t.
|
24
|
-
t.string "tags",
|
25
|
-
t.
|
26
|
-
t.
|
27
|
-
|
28
|
-
|
29
|
-
create_table "sanity_tests", :force => true do |t|
|
30
|
-
t.string "tags", :array => true
|
31
|
-
t.integer "tag_ids", :array => true
|
19
|
+
t.integer "tag_ids", :array => true
|
20
|
+
t.string "tags", :array => true
|
21
|
+
t.text "biography"
|
22
|
+
t.integer "lucky_number"
|
23
|
+
t.datetime "created_at", :null => false
|
24
|
+
t.datetime "updated_at", :null => false
|
32
25
|
end
|
33
26
|
|
34
27
|
end
|
@@ -2,48 +2,66 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'Index migrations' do
|
4
4
|
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
-
|
5
|
+
let!(:stream) { StringIO.new }
|
6
6
|
before do
|
7
|
-
|
8
|
-
t.integer :col1, :array => true
|
9
|
-
t.integer :col2
|
10
|
-
t.text :col3
|
11
|
-
end
|
7
|
+
ActiveRecord::Base.logger = ActiveSupport::TaggedLogging.new(Logger.new stream)
|
12
8
|
end
|
13
9
|
|
14
|
-
after
|
10
|
+
after do
|
11
|
+
[:tag_ids, :lucky_number, :biography].each do |column|
|
12
|
+
begin
|
13
|
+
connection.remove_index :people, column
|
14
|
+
rescue ArgumentError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
15
18
|
|
16
19
|
it 'creates special index' do
|
17
20
|
lambda do
|
18
|
-
connection.add_index(:
|
21
|
+
connection.add_index(:people, :tag_ids, :using => :gin)
|
19
22
|
end.should_not raise_exception
|
20
23
|
|
21
|
-
indexes = connection.indexes(:
|
22
|
-
index_1 = indexes.detect { |c| c.columns.map(&:to_s) == ['
|
24
|
+
indexes = connection.indexes(:people)
|
25
|
+
index_1 = indexes.detect { |c| c.columns.map(&:to_s) == ['tag_ids']}
|
23
26
|
|
24
|
-
index_1.
|
27
|
+
index_1.using.should eq :gin
|
25
28
|
end
|
26
29
|
|
27
30
|
it 'creates indexes with where clauses' do
|
28
31
|
lambda do
|
29
|
-
connection.add_index(:
|
32
|
+
connection.add_index(:people, :lucky_number, :where => '(lucky_number > 50)')
|
33
|
+
end.should_not raise_exception
|
34
|
+
|
35
|
+
indexes = connection.indexes(:people)
|
36
|
+
index_2 = indexes.detect { |c| c.columns.map(&:to_s) == ['lucky_number']}
|
37
|
+
|
38
|
+
index_2.where.should match /lucky_number > 50/
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'creates index concurrently' do
|
42
|
+
lambda do
|
43
|
+
connection.add_index(:people, :lucky_number, :algorithm => :concurrently)
|
30
44
|
end.should_not raise_exception
|
31
45
|
|
32
|
-
|
33
|
-
|
46
|
+
output = stream.string
|
47
|
+
output.should match /CREATE INDEX CONCURRENTLY "index_people_on_lucky_number" ON "people"\(\"lucky_number\" \)/
|
48
|
+
end
|
34
49
|
|
35
|
-
|
50
|
+
it 'rejects bad algorithm arguments' do
|
51
|
+
lambda do
|
52
|
+
connection.add_index(:people, :lucky_number, :algorithm => :conurrently)
|
53
|
+
end.should raise_exception
|
36
54
|
end
|
37
55
|
|
38
56
|
it 'creates indexes with operator classes', :if => ActiveRecord::Base.connection.supports_extensions? do
|
39
57
|
lambda do
|
40
|
-
connection.add_index(:
|
58
|
+
connection.add_index(:people, :biography, :using => :gin, :index_opclass => :gin_trgm_ops)
|
41
59
|
end.should_not raise_exception
|
42
60
|
|
43
|
-
indexes = connection.indexes(:
|
44
|
-
index_3 = indexes.detect { |c| c.columns.map(&:to_s) == ['
|
61
|
+
indexes = connection.indexes(:people)
|
62
|
+
index_3 = indexes.detect { |c| c.columns.map(&:to_s) == ['biography']}
|
45
63
|
|
46
|
-
index_3.
|
47
|
-
index_3.index_opclass.should
|
64
|
+
index_3.using.should eq :gin
|
65
|
+
index_3.index_opclass.should eq :gin_trgm_ops
|
48
66
|
end
|
49
67
|
end
|
data/spec/models/array_spec.rb
CHANGED
@@ -222,14 +222,14 @@ describe 'Models with array columns' do
|
|
222
222
|
end
|
223
223
|
end
|
224
224
|
|
225
|
-
describe '
|
225
|
+
describe 'overlap' do
|
226
226
|
it "works" do
|
227
227
|
arel = User.arel_table
|
228
228
|
User.create(:nick_names => ['this'])
|
229
229
|
x = User.create
|
230
230
|
x.nick_names = ["s'o{m}e", 'thing']
|
231
231
|
x.save
|
232
|
-
u = User.where(arel[:nick_names].
|
232
|
+
u = User.where(arel[:nick_names].overlap(["s'o{m}e"]))
|
233
233
|
u.first.should_not be_nil
|
234
234
|
u.first.nick_names.should eq ["s'o{m}e", 'thing']
|
235
235
|
end
|
data/spec/models/inet_spec.rb
CHANGED
@@ -50,6 +50,15 @@ describe 'Models with inet columns' do
|
|
50
50
|
address.ip_address.should eq IPAddr.new('192.168.1.2')
|
51
51
|
end
|
52
52
|
|
53
|
+
it 'converts empty strings to nil' do
|
54
|
+
address = Address.create
|
55
|
+
address.ip_address = ''
|
56
|
+
address.save
|
57
|
+
|
58
|
+
address.reload
|
59
|
+
address.ip_address.should eq nil
|
60
|
+
end
|
61
|
+
|
53
62
|
it 'updates an address with an IPAddr' do
|
54
63
|
ip_addr_1 = IPAddr.new('192.168.0.1')
|
55
64
|
ip_addr_2 = IPAddr.new('192.168.1.2')
|
@@ -3,6 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe 'Array queries' do
|
4
4
|
let(:equality_regex) { %r{\"people\"\.\"tags\" = '\{\"working\"\}'} }
|
5
5
|
let(:overlap_regex) { %r{\"people\"\.\"tag_ids\" && '\{1,2\}'} }
|
6
|
+
let(:contains_regex) { %r{\"people\"\.\"tag_ids\" @> '\{1,2\}'} }
|
6
7
|
let(:any_regex) { %r{2 = ANY\(\"people\"\.\"tag_ids\"\)} }
|
7
8
|
let(:all_regex) { %r{2 = ALL\(\"people\"\.\"tag_ids\"\)} }
|
8
9
|
|
@@ -27,6 +28,20 @@ describe 'Array queries' do
|
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
31
|
+
describe '.where.contains(:column => value)' do
|
32
|
+
it 'generates the appropriate where clause' do
|
33
|
+
query = Person.where.contains(:tag_ids => [1,2])
|
34
|
+
query.to_sql.should match contains_regex
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'allows chaining' do
|
38
|
+
query = Person.where.contains(:tag_ids => [1,2]).where(:tags => ['working']).to_sql
|
39
|
+
|
40
|
+
query.should match contains_regex
|
41
|
+
query.should match equality_regex
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
30
45
|
describe '.where.any(:column => value)' do
|
31
46
|
it 'generates the appropriate where clause' do
|
32
47
|
query = Person.where.any(:tag_ids => 2)
|
@@ -3,45 +3,44 @@ require 'spec_helper'
|
|
3
3
|
describe 'Index schema dumper' do
|
4
4
|
let!(:connection) { ActiveRecord::Base.connection }
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
after do
|
7
|
+
[:tag_ids, :lucky_number, :biography].each do |column|
|
8
|
+
begin
|
9
|
+
connection.remove_index :people, column
|
10
|
+
rescue ArgumentError
|
11
|
+
end
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
14
|
-
after { connection.drop_table :index_types }
|
15
|
-
|
16
15
|
it 'handles index type parameters' do
|
17
|
-
connection.add_index(:
|
16
|
+
connection.add_index(:people, :tag_ids, :using => :gin)
|
18
17
|
|
19
18
|
stream = StringIO.new
|
20
19
|
ActiveRecord::SchemaDumper.dump(connection, stream)
|
21
20
|
output = stream.string
|
22
21
|
|
23
|
-
output.should match /:
|
24
|
-
output.should_not match /:
|
22
|
+
output.should match /:using => :gin/
|
23
|
+
output.should_not match /:using => :btree/
|
25
24
|
output.should_not match /:index_opclass =>/
|
26
25
|
end
|
27
26
|
|
28
27
|
it 'handles index where clauses' do
|
29
|
-
connection.add_index(:
|
28
|
+
connection.add_index(:people, :lucky_number, :where => '(lucky_number > 50)')
|
30
29
|
|
31
30
|
stream = StringIO.new
|
32
31
|
ActiveRecord::SchemaDumper.dump(connection, stream)
|
33
32
|
output = stream.string
|
34
33
|
|
35
|
-
output.should match /:where => "\(
|
34
|
+
output.should match /:where => "\(lucky_number > 50\)"/
|
36
35
|
end
|
37
36
|
|
38
37
|
it 'dumps index operator classes', :if => ActiveRecord::Base.connection.supports_extensions? do
|
39
|
-
connection.add_index(:
|
38
|
+
connection.add_index(:people, :biography, :using => :gin, :index_opclass => :gin_trgm_ops)
|
40
39
|
|
41
40
|
stream = StringIO.new
|
42
41
|
ActiveRecord::SchemaDumper.dump(connection, stream)
|
43
42
|
output = stream.string
|
44
43
|
|
45
|
-
output.should match /:
|
44
|
+
output.should match /:using => :gin,\s+:index_opclass => :gin_trgm_ops/
|
46
45
|
end
|
47
46
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,7 +5,7 @@ require File.expand_path('../dummy/config/environment.rb', __FILE__)
|
|
5
5
|
require 'rspec/rails'
|
6
6
|
require 'rspec/autorun'
|
7
7
|
require 'bourne'
|
8
|
-
require 'debugger' unless ENV['CI'] || (RUBY_PLATFORM =~ /java/)
|
8
|
+
#require 'debugger' unless ENV['CI'] || (RUBY_PLATFORM =~ /java/)
|
9
9
|
|
10
10
|
ENGINE_RAILS_ROOT=File.join(File.dirname(__FILE__), '../')
|
11
11
|
|
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: postgres_ext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.3.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Dan McClain
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-03-26 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: activerecord
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -30,7 +27,6 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: pg_array_parser
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
31
|
- - ~>
|
36
32
|
- !ruby/object:Gem::Version
|
@@ -38,7 +34,6 @@ dependencies:
|
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
38
|
- - ~>
|
44
39
|
- !ruby/object:Gem::Version
|
@@ -46,7 +41,6 @@ dependencies:
|
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: rails
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
45
|
- - ~>
|
52
46
|
- !ruby/object:Gem::Version
|
@@ -54,7 +48,6 @@ dependencies:
|
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
52
|
- - ~>
|
60
53
|
- !ruby/object:Gem::Version
|
@@ -62,7 +55,6 @@ dependencies:
|
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: rspec-rails
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
59
|
- - ~>
|
68
60
|
- !ruby/object:Gem::Version
|
@@ -70,7 +62,6 @@ dependencies:
|
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
66
|
- - ~>
|
76
67
|
- !ruby/object:Gem::Version
|
@@ -78,7 +69,6 @@ dependencies:
|
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: bourne
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
73
|
- - ~>
|
84
74
|
- !ruby/object:Gem::Version
|
@@ -86,7 +76,6 @@ dependencies:
|
|
86
76
|
type: :development
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
80
|
- - ~>
|
92
81
|
- !ruby/object:Gem::Version
|
@@ -94,7 +83,6 @@ dependencies:
|
|
94
83
|
- !ruby/object:Gem::Dependency
|
95
84
|
name: pg
|
96
85
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
86
|
requirements:
|
99
87
|
- - ~>
|
100
88
|
- !ruby/object:Gem::Version
|
@@ -102,7 +90,6 @@ dependencies:
|
|
102
90
|
type: :development
|
103
91
|
prerelease: false
|
104
92
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
93
|
requirements:
|
107
94
|
- - ~>
|
108
95
|
- !ruby/object:Gem::Version
|
@@ -138,6 +125,7 @@ files:
|
|
138
125
|
- lib/postgres_ext/active_record/schema_dumper.rb
|
139
126
|
- lib/postgres_ext/arel.rb
|
140
127
|
- lib/postgres_ext/arel/nodes.rb
|
128
|
+
- lib/postgres_ext/arel/nodes/array_nodes.rb
|
141
129
|
- lib/postgres_ext/arel/nodes/contained_within.rb
|
142
130
|
- lib/postgres_ext/arel/predications.rb
|
143
131
|
- lib/postgres_ext/arel/visitors.rb
|
@@ -225,33 +213,26 @@ files:
|
|
225
213
|
- spec/spec_helper.rb
|
226
214
|
homepage: ''
|
227
215
|
licenses: []
|
216
|
+
metadata: {}
|
228
217
|
post_install_message:
|
229
218
|
rdoc_options: []
|
230
219
|
require_paths:
|
231
220
|
- lib
|
232
221
|
required_ruby_version: !ruby/object:Gem::Requirement
|
233
|
-
none: false
|
234
222
|
requirements:
|
235
|
-
- -
|
223
|
+
- - '>='
|
236
224
|
- !ruby/object:Gem::Version
|
237
225
|
version: '0'
|
238
|
-
segments:
|
239
|
-
- 0
|
240
|
-
hash: 3137999377489191858
|
241
226
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
242
|
-
none: false
|
243
227
|
requirements:
|
244
|
-
- -
|
228
|
+
- - '>='
|
245
229
|
- !ruby/object:Gem::Version
|
246
230
|
version: '0'
|
247
|
-
segments:
|
248
|
-
- 0
|
249
|
-
hash: 3137999377489191858
|
250
231
|
requirements: []
|
251
232
|
rubyforge_project:
|
252
|
-
rubygems_version:
|
233
|
+
rubygems_version: 2.0.3
|
253
234
|
signing_key:
|
254
|
-
specification_version:
|
235
|
+
specification_version: 4
|
255
236
|
summary: Extends ActiveRecord to handle native PostgreSQL data types
|
256
237
|
test_files:
|
257
238
|
- spec/arel/arel_spec.rb
|