postgres_ext 2.3.0 → 2.4.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6f8878d742f2c1d692cca006bbd3fe55dfb69e1d
4
- data.tar.gz: 64ae985d2dae1389ee05b0c843b2ad8d578d1ffc
3
+ metadata.gz: 45761b40db17573204e88b48c6eb91c135968fde
4
+ data.tar.gz: d774a33f235ac5f450df26f6f517575ba1360bed
5
5
  SHA512:
6
- metadata.gz: 1a8cd94d52fce6e7dbf5472899b1a1f45b9328fbf31e42a912c4ec4cf10880d2e398f0f954bf9ab49fea5e503c11a660f6ee89fbf690162e2bf3c96d64a338bc
7
- data.tar.gz: 66a9c9c002c548b0920657f243c6c23ea04a4468666b3799f5bc52ea76b25d0f47f2e84ec8071e2f818638f6af12e804223994e515456540b1eab9f38cbadcab
6
+ metadata.gz: c2b883ca3787352f2d6394d45116485565b4c0631b3f84e17405949cbd63049201c087860e470d88b3cad19e849f841b32d3537d5dfef29162d3e153bfb40538
7
+ data.tar.gz: 7f0224e503f2d0883aae7974b94de5366d612cba43ad4e243e1bf7d16bbffc5d3e2562eae66ad60e55af76915c5c7b1d45907d3652e607471f9e803d77994128
data/.travis.yml CHANGED
@@ -5,6 +5,7 @@ rvm:
5
5
  gemfile:
6
6
  - gemfiles/Gemfile.activerecord-4.0.x
7
7
  - gemfiles/Gemfile.activerecord-4.1.x
8
+ - gemfiles/Gemfile.activerecord-4.2.x
8
9
 
9
10
  env: DATABASE_URL=postgres://postgres@localhost/postgres_ext_test
10
11
 
data/Rakefile CHANGED
@@ -38,7 +38,7 @@ namespace :test do
38
38
  desc 'Test against all supported ActiveRecord versions'
39
39
  task :all do
40
40
  # Currently only supports Active Record v4.0
41
- %w(4.0.x 4.1.x).each do |version|
41
+ %w(4.0.x 4.1.x 4.2.x).each do |version|
42
42
  sh "BUNDLE_GEMFILE='gemfiles/Gemfile.activerecord-#{version}' bundle install --quiet"
43
43
  sh "BUNDLE_GEMFILE='gemfiles/Gemfile.activerecord-#{version}' bundle exec rake test"
44
44
  end
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec :path => '..'
4
+
5
+ gem "activerecord", "~> 4.2.0.beta2"
6
+
7
+ unless ENV['CI'] || RUBY_PLATFORM =~ /java/
8
+ gem 'byebug'
9
+ end
@@ -22,34 +22,9 @@ class CTEProxy
22
22
  name
23
23
  end
24
24
 
25
- def column_names
26
- @model.column_names
27
- end
28
-
29
- def columns_hash
30
- @model.columns_hash
31
- end
32
-
33
- def model_name
34
- @model.model_name
35
- end
36
-
37
- def primary_key
38
- @model.primary_key
39
- end
40
-
41
- def attribute_alias?(*args)
42
- @model.attribute_alias?(*args)
43
- end
44
-
45
- def aggregate_reflections(*args)
46
- @model.aggregate_reflections(*args)
47
- end
48
-
49
- def instantiate(record, column_types = {})
50
- @model.instantiate(record, column_types)
51
- end
52
-
25
+ delegate :column_names, :columns_hash, :model_name, :primary_key, :attribute_alias?,
26
+ :aggregate_reflections, :instantiate, :type_for_attribute, to: :@model
27
+
53
28
  private
54
29
 
55
30
  def reflections
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
  case value
9
9
  when Array
10
10
  engine = attribute.relation.engine
11
- column = engine.connection.columns(attribute.relation.name).detect{ |col| col.name.to_s == attribute.name.to_s }
11
+ column = engine.connection.schema_cache.columns(attribute.relation.name).detect{ |col| col.name.to_s == attribute.name.to_s }
12
12
  if column && column.array
13
13
  attribute.eq(value)
14
14
  else
@@ -147,7 +147,6 @@ module ActiveRecord
147
147
  end
148
148
 
149
149
  def build_with(arel)
150
- visitor = arel.engine.connection.visitor
151
150
  with_statements = with_values.flat_map do |with_value|
152
151
  case with_value
153
152
  when String
@@ -156,13 +155,11 @@ module ActiveRecord
156
155
  with_value.map do |name, expression|
157
156
  case expression
158
157
  when String
159
- select = Arel::SqlLiteral.new "(#{expression})"
160
- when ActiveRecord::Relation
161
- select = Arel::SqlLiteral.new "(#{expression.to_sql})"
162
- when Arel::SelectManager
163
- select = Arel::SqlLiteral.new visitor.accept(expression)
158
+ select = Arel::Nodes::SqlLiteral.new "(#{expression})"
159
+ when ActiveRecord::Relation, Arel::SelectManager
160
+ select = Arel::Nodes::SqlLiteral.new "(#{expression.to_sql})"
164
161
  end
165
- as = Arel::Nodes::As.new Arel::SqlLiteral.new("\"#{name.to_s}\""), select
162
+ Arel::Nodes::As.new Arel::Nodes::SqlLiteral.new("\"#{name.to_s}\""), select
166
163
  end
167
164
  end
168
165
  end
@@ -1,9 +1,19 @@
1
- require 'arel/visitors/visitor'
1
+ require 'arel/visitors/postgresql'
2
+
2
3
  module Arel
3
4
  module Visitors
4
- class Visitor
5
- # We are adding our visitors to the main visitor for the time being until the right spot is found to monkey patch
5
+ class PostgreSQL
6
6
  private
7
+
8
+ def visit_Array o, a
9
+ column = a.relation.engine.connection.schema_cache.columns(a.relation.name).find { |col| col.name == a.name.to_s } if a
10
+ if column && column.respond_to?(:array) && column.array
11
+ quoted o, a
12
+ else
13
+ o.empty? ? 'NULL' : o.map { |x| visit x }.join(', ')
14
+ end
15
+ end
16
+
7
17
  def visit_Arel_Nodes_ContainedWithin o, a = nil
8
18
  "#{visit o.left, a} << #{visit o.right, o.left}"
9
19
  end
@@ -0,0 +1 @@
1
+ require 'postgres_ext/arel/4.1/visitors/postgresql'
@@ -0,0 +1,35 @@
1
+ require 'arel/predications'
2
+
3
+ module Arel
4
+ module Predications
5
+ def contained_within(other)
6
+ Nodes::ContainedWithin.new self, Nodes.build_quoted(other, self)
7
+ end
8
+
9
+ def contained_within_or_equals(other)
10
+ Nodes::ContainedWithinEquals.new self, Nodes.build_quoted(other, self)
11
+ end
12
+
13
+ def contains(other)
14
+ Nodes::Contains.new self, Nodes.build_quoted(other, self)
15
+ end
16
+
17
+ def contains_or_equals(other)
18
+ Nodes::ContainsEquals.new self, Nodes.build_quoted(other, self)
19
+ end
20
+
21
+ def overlap(other)
22
+ Nodes::Overlap.new self, Nodes.build_quoted(other, self)
23
+ end
24
+
25
+ def any(other)
26
+ any_tags_function = Arel::Nodes::NamedFunction.new('ANY', [self])
27
+ Arel::Nodes::Equality.new(Nodes.build_quoted(other, self), any_tags_function)
28
+ end
29
+
30
+ def all(other)
31
+ any_tags_function = Arel::Nodes::NamedFunction.new('ALL', [self])
32
+ Arel::Nodes::Equality.new(Nodes.build_quoted(other, self), any_tags_function)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,39 @@
1
+ require 'arel/visitors/postgresql'
2
+
3
+ module Arel
4
+ module Visitors
5
+ class PostgreSQL
6
+ private
7
+
8
+ def visit_Arel_Nodes_ContainedWithin o, collector
9
+ infix_value o, collector, " << "
10
+ end
11
+
12
+ def visit_Arel_Nodes_ContainedWithinEquals o, collector
13
+ infix_value o, collector, " <<= "
14
+ end
15
+
16
+ def visit_Arel_Nodes_Contains o, collector
17
+ left_column = o.left.relation.engine.columns.find { |col| col.name == o.left.name.to_s }
18
+
19
+ if left_column && (left_column.type == :hstore || (left_column.respond_to?(:array) && left_column.array))
20
+ infix_value o, collector, " @> "
21
+ else
22
+ infix_value o, collector, " >> "
23
+ end
24
+ end
25
+
26
+ def visit_Arel_Nodes_ContainsEquals o, collector
27
+ infix_value o, collector, " >>= "
28
+ end
29
+
30
+ def visit_Arel_Nodes_Overlap o, collector
31
+ infix_value o, collector, " && "
32
+ end
33
+
34
+ def visit_IPAddr value, collector
35
+ collector << quote("#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1 @@
1
+ require 'postgres_ext/arel/4.2/visitors/postgresql'
@@ -1,3 +1,14 @@
1
+ ## TODO: Change to ~> 4.2.0 on gem release
2
+
3
+ gdep = Gem::Dependency.new('activerecord', '~> 4.2.0.beta2')
4
+ ar_version_cutoff = gdep.matching_specs.sort_by(&:version).last
5
+
1
6
  require 'postgres_ext/arel/nodes'
2
- require 'postgres_ext/arel/predications'
3
- require 'postgres_ext/arel/visitors'
7
+ if ar_version_cutoff
8
+ require 'postgres_ext/arel/4.2/predications'
9
+ require 'postgres_ext/arel/4.2/visitors'
10
+ else
11
+ require 'postgres_ext/arel/4.1/predications'
12
+ require 'postgres_ext/arel/4.1/visitors'
13
+ end
14
+
@@ -1,3 +1,3 @@
1
1
  module PostgresExt
2
- VERSION = '2.3.0'
2
+ VERSION = '2.4.0.beta.1'
3
3
  end
@@ -32,7 +32,7 @@ describe 'Array Column Predicates' do
32
32
 
33
33
  describe 'Array Overlap' do
34
34
  it 'converts Arel overlap statement' do
35
- arel_table.where(arel_table[:tags].overlap(['tag','tag 2'])).to_sql.must_match /&& '\{"tag","tag 2"\}'/
35
+ arel_table.where(arel_table[:tags].overlap(['tag','tag 2'])).to_sql.must_match /&& '\{"?tag"?,"tag 2"\}'/
36
36
  end
37
37
 
38
38
  it 'converts Arel overlap statement' do
@@ -61,7 +61,7 @@ describe 'Array Column Predicates' do
61
61
 
62
62
  describe 'Array Contains' do
63
63
  it 'converts Arel contains statement and escapes strings' do
64
- arel_table.where(arel_table[:tags].contains(['tag','tag 2'])).to_sql.must_match /@> '\{"tag","tag 2"\}'/
64
+ arel_table.where(arel_table[:tags].contains(['tag','tag 2'])).to_sql.must_match /@> '\{"?tag"?,"tag 2"\}'/
65
65
  end
66
66
 
67
67
  it 'converts Arel contains statement with numbers' do
@@ -1,7 +1,7 @@
1
1
  require 'test_helper'
2
2
 
3
3
  describe 'Array queries' do
4
- let(:equality_regex) { %r{\"people\"\.\"tags\" = '\{\"working\"\}'} }
4
+ let(:equality_regex) { %r{\"people\"\.\"tags\" = '\{"?working"?\}'} }
5
5
  let(:overlap_regex) { %r{\"people\"\.\"tag_ids\" && '\{1,2\}'} }
6
6
  let(:any_regex) { %r{2 = ANY\(\"people\"\.\"tag_ids\"\)} }
7
7
  let(:all_regex) { %r{2 = ALL\(\"people\"\.\"tag_ids\"\)} }
@@ -4,22 +4,22 @@ describe 'Common Table Expression queries' do
4
4
  describe '.with(common_table_expression_hash)' do
5
5
  it 'generates an expression with the CTE' do
6
6
  query = Person.with(lucky_number_seven: Person.where(lucky_number: 7)).joins('JOIN lucky_number_seven ON lucky_number_seven.id = people.id')
7
- query.to_sql.must_equal 'WITH "lucky_number_seven" AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 7) SELECT "people".* FROM "people" JOIN lucky_number_seven ON lucky_number_seven.id = people.id'
7
+ query.to_sql.must_match(/WITH "lucky_number_seven" AS \(SELECT "people".* FROM "people"(\s+)WHERE "people"."lucky_number" = 7\) SELECT "people".* FROM "people" JOIN lucky_number_seven ON lucky_number_seven.id = people.id/)
8
8
  end
9
9
 
10
10
  it 'generates an expression with multiple CTEs' do
11
11
  query = Person.with(lucky_number_seven: Person.where(lucky_number: 7), lucky_number_three: Person.where(lucky_number: 3)).joins('JOIN lucky_number_seven ON lucky_number_seven.id = people.id').joins('JOIN lucky_number_three ON lucky_number_three.id = people.id')
12
- query.to_sql.must_equal 'WITH "lucky_number_seven" AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 7), "lucky_number_three" AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 3) SELECT "people".* FROM "people" JOIN lucky_number_seven ON lucky_number_seven.id = people.id JOIN lucky_number_three ON lucky_number_three.id = people.id'
12
+ query.to_sql.must_match(/WITH "lucky_number_seven" AS \(SELECT "people".* FROM "people"(\s+)WHERE "people"."lucky_number" = 7\), "lucky_number_three" AS \(SELECT "people".* FROM "people"(\s+)WHERE "people"."lucky_number" = 3\) SELECT "people".* FROM "people" JOIN lucky_number_seven ON lucky_number_seven.id = people.id JOIN lucky_number_three ON lucky_number_three.id = people.id/)
13
13
  end
14
14
 
15
15
  it 'generates an expression with multiple with calls' do
16
16
  query = Person.with(lucky_number_seven: Person.where(lucky_number: 7)).with(lucky_number_three: Person.where(lucky_number: 3)).joins('JOIN lucky_number_seven ON lucky_number_seven.id = people.id').joins('JOIN lucky_number_three ON lucky_number_three.id = people.id')
17
- query.to_sql.must_equal 'WITH "lucky_number_seven" AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 7), "lucky_number_three" AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 3) SELECT "people".* FROM "people" JOIN lucky_number_seven ON lucky_number_seven.id = people.id JOIN lucky_number_three ON lucky_number_three.id = people.id'
17
+ query.to_sql.must_match(/WITH "lucky_number_seven" AS \(SELECT "people".* FROM "people"(\s+)WHERE "people"."lucky_number" = 7\), "lucky_number_three" AS \(SELECT "people".* FROM "people"(\s+)WHERE "people"."lucky_number" = 3\) SELECT "people".* FROM "people" JOIN lucky_number_seven ON lucky_number_seven.id = people.id JOIN lucky_number_three ON lucky_number_three.id = people.id/)
18
18
  end
19
19
 
20
20
  it 'generates an expression with recursive' do
21
21
  query = Person.with.recursive(lucky_number_seven: Person.where(lucky_number: 7)).joins('JOIN lucky_number_seven ON lucky_number_seven.id = people.id')
22
- query.to_sql.must_equal 'WITH RECURSIVE "lucky_number_seven" AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 7) SELECT "people".* FROM "people" JOIN lucky_number_seven ON lucky_number_seven.id = people.id'
22
+ query.to_sql.must_match(/WITH RECURSIVE "lucky_number_seven" AS \(SELECT "people".* FROM "people"(\s+)WHERE "people"."lucky_number" = 7\) SELECT "people".* FROM "people" JOIN lucky_number_seven ON lucky_number_seven.id = people.id/)
23
23
  end
24
24
 
25
25
  it 'accepts Arel::SelectMangers' do
@@ -34,7 +34,7 @@ describe 'Common Table Expression queries' do
34
34
  describe '.from_cte(common_table_expression_hash)' do
35
35
  it 'generates an expression with the CTE as the main table' do
36
36
  query = Person.from_cte('lucky_number_seven', Person.where(lucky_number: 7)).where(id: 5)
37
- query.to_sql.must_equal 'WITH "lucky_number_seven" AS (SELECT "people".* FROM "people" WHERE "people"."lucky_number" = 7) SELECT "lucky_number_seven".* FROM "lucky_number_seven" WHERE "lucky_number_seven"."id" = 5'
37
+ query.to_sql.must_match(/WITH "lucky_number_seven" AS \(SELECT "people".* FROM "people"(\s+)WHERE "people"."lucky_number" = 7\) SELECT "lucky_number_seven".* FROM "lucky_number_seven"(\s+)WHERE "lucky_number_seven"."id" = 5/)
38
38
  end
39
39
 
40
40
  it 'returns instances of the model' do
@@ -7,7 +7,7 @@ describe 'Contains queries' do
7
7
  let(:contains_array_regex) { %r{\"people\"\.\"tag_ids\" @> '\{1,2\}'} }
8
8
  let(:contains_hstore_regex) { %r{\"people\"\.\"data\" @> '\"nickname\"=>"Dan"'} }
9
9
  let(:contains_equals_regex) { %r{\"people\"\.\"ip\" >>= '127.0.0.1'} }
10
- let(:equality_regex) { %r{\"people\"\.\"tags\" = '\{\"working\"\}'} }
10
+ let(:equality_regex) { %r{\"people\"\.\"tags\" = '\{"?working"?\}'} }
11
11
 
12
12
  describe '.where.contained_within(:column, value)' do
13
13
  it 'generates the appropriate where clause' do
@@ -17,8 +17,8 @@ describe 'Ensure that we don\'t stomp on Active Record\'s queries' do
17
17
 
18
18
  describe '.where(joins: { column: [] })' do
19
19
  it 'generates IN clause for non array columns' do
20
- query = Person.joins(:hm_tags).where(tags: { id: ['working'] }).to_sql
21
- query.must_match /WHERE "tags"\."id" IN \('working'\)/
20
+ query = Person.joins(:hm_tags).where(tags: { id: [1,2,3] }).to_sql
21
+ query.must_match /WHERE "tags"\."id" IN \(1, 2, 3\)/
22
22
  end
23
23
  end
24
24
 
@@ -4,12 +4,12 @@ describe 'Window functions' do
4
4
  describe 'ranked' do
5
5
  it 'uses the order when no order is passed to ranked' do
6
6
  query = Person.order(lucky_number: :desc).ranked
7
- query.to_sql.must_equal 'SELECT "people".*, rank() OVER (ORDER BY "people"."lucky_number" DESC) FROM "people" ORDER BY "people"."lucky_number" DESC'
7
+ query.to_sql.must_match(/SELECT "people".*, rank\(\) OVER \(ORDER BY "people"."lucky_number" DESC\) FROM "people"(\s+)ORDER BY "people"."lucky_number" DESC/)
8
8
  end
9
9
 
10
10
  it 'uses the order when no order is passed to ranked, swapped calls' do
11
11
  query = Person.ranked.order(lucky_number: :desc)
12
- query.to_sql.must_equal 'SELECT "people".*, rank() OVER (ORDER BY "people"."lucky_number" DESC) FROM "people" ORDER BY "people"."lucky_number" DESC'
12
+ query.to_sql.must_match(/SELECT "people".*, rank\(\) OVER \(ORDER BY "people"."lucky_number" DESC\) FROM "people"(\s+)ORDER BY "people"."lucky_number" DESC/)
13
13
  end
14
14
 
15
15
  it 'uses the rank value when there is an order passed to it' do
@@ -29,7 +29,7 @@ describe 'Window functions' do
29
29
 
30
30
  it 'combines the order and rank' do
31
31
  query = Person.ranked(lucky_number: :desc).order(id: :asc)
32
- query.to_sql.must_equal 'SELECT "people".*, rank() OVER (ORDER BY "people"."lucky_number" DESC) FROM "people" ORDER BY "people"."id" ASC'
32
+ query.to_sql.must_match(/SELECT "people".*, rank\(\) OVER \(ORDER BY "people"."lucky_number" DESC\) FROM "people"(\s+)ORDER BY "people"."id" ASC/)
33
33
  end
34
34
 
35
35
  it 'executes the query with the rank' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postgres_ext
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0.beta.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan McClain
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-18 00:00:00.000000000 Z
11
+ date: 2014-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -168,6 +168,7 @@ files:
168
168
  - docs/querying.md
169
169
  - gemfiles/Gemfile.activerecord-4.0.x
170
170
  - gemfiles/Gemfile.activerecord-4.1.x
171
+ - gemfiles/Gemfile.activerecord-4.2.x
171
172
  - lib/postgres_ext.rb
172
173
  - lib/postgres_ext/active_record.rb
173
174
  - lib/postgres_ext/active_record/cte_proxy.rb
@@ -176,13 +177,15 @@ files:
176
177
  - lib/postgres_ext/active_record/relation/predicate_builder.rb
177
178
  - lib/postgres_ext/active_record/relation/query_methods.rb
178
179
  - lib/postgres_ext/arel.rb
180
+ - lib/postgres_ext/arel/4.1/predications.rb
181
+ - lib/postgres_ext/arel/4.1/visitors.rb
182
+ - lib/postgres_ext/arel/4.1/visitors/postgresql.rb
183
+ - lib/postgres_ext/arel/4.2/predications.rb
184
+ - lib/postgres_ext/arel/4.2/visitors.rb
185
+ - lib/postgres_ext/arel/4.2/visitors/postgresql.rb
179
186
  - lib/postgres_ext/arel/nodes.rb
180
187
  - lib/postgres_ext/arel/nodes/array_nodes.rb
181
188
  - lib/postgres_ext/arel/nodes/contained_within.rb
182
- - lib/postgres_ext/arel/predications.rb
183
- - lib/postgres_ext/arel/visitors.rb
184
- - lib/postgres_ext/arel/visitors/to_sql.rb
185
- - lib/postgres_ext/arel/visitors/visitor.rb
186
189
  - lib/postgres_ext/version.rb
187
190
  - postgres_ext.gemspec
188
191
  - test/arel/array_test.rb
@@ -208,9 +211,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
208
211
  version: '0'
209
212
  required_rubygems_version: !ruby/object:Gem::Requirement
210
213
  requirements:
211
- - - ">="
214
+ - - ">"
212
215
  - !ruby/object:Gem::Version
213
- version: '0'
216
+ version: 1.3.1
214
217
  requirements: []
215
218
  rubyforge_project:
216
219
  rubygems_version: 2.2.0
@@ -1,16 +0,0 @@
1
- require 'arel/visitors/to_sql'
2
-
3
- module Arel
4
- module Visitors
5
- class ToSql
6
- def visit_Array o, a
7
- column = a.relation.engine.connection.columns(a.relation.name).find { |col| col.name == a.name.to_s } if a
8
- if column && column.respond_to?(:array) && column.array
9
- quoted o, a
10
- else
11
- o.empty? ? 'NULL' : o.map { |x| visit x }.join(', ')
12
- end
13
- end
14
- end
15
- end
16
- end
@@ -1,2 +0,0 @@
1
- require 'postgres_ext/arel/visitors/visitor'
2
- require 'postgres_ext/arel/visitors/to_sql'