arel 6.0.0 → 7.0.0

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: 607f322b2e6662d91cbef8d1433904774af2584c
4
- data.tar.gz: 7b45ade5c1572eb9b60ec5bc96b90cf307ddd470
3
+ metadata.gz: d7b8575f1877fe831a05c70632e7046c6ce384c9
4
+ data.tar.gz: ab4c6895e273c5ad2bd6456bdf2f0fd1b3f7e0a2
5
5
  SHA512:
6
- metadata.gz: 9d3471ace631b643c589efca9461e4810a5db0b5f4a4f0126b56cd033c802d75a3cfb5b44771738869582bf651103679e4bfa7b3628328c5493f9992490bf627
7
- data.tar.gz: 8e5a1949954982acc175e6bc1f6c3e15070245d5d2643c292ac47fdc56b9cd19a37ac7390ee97c3570dd4269d4b4ae886ee8b35fcdcfc29056b4840bb1ae225b
6
+ metadata.gz: 71feebe60212b48f1bce1fe0acc756728aff663f7e84836bc5888ed237bc6315b22d95993d9b9fce655dbccbf35c75652e9fd62c185d141c0c246084e3d70e68
7
+ data.tar.gz: 563e00ae3191b5d4be40b8ead9168ead202242593a9761a2a5e938454b13c3094542d83b2e3ca5706c572ca2d1551772a9bce80631e55be9aa3379435bfecb75
data/History.txt CHANGED
@@ -1,3 +1,11 @@
1
+ === 7.0.0 / 2015-12-17
2
+
3
+ * Enhancements
4
+
5
+ * Remove deprecated method `Table#primary_key`
6
+ * Remove engine from the constructor arguments `Arel::Table`
7
+ * Deprecate automatic type casting within Arel
8
+
1
9
  === 6.0.0 / 2014-11-25
2
10
 
3
11
  * Enhancements
data/MIT-LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007-2010 Nick Kallen, Bryan Helmkamp, Emilio Tagua, Aaron Patterson
1
+ Copyright (c) 2007-2015 Nick Kallen, Bryan Helmkamp, Emilio Tagua, Aaron Patterson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -18,3 +18,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
18
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
19
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.markdown CHANGED
@@ -1,4 +1,4 @@
1
- # Arel [![Build Status](https://secure.travis-ci.org/rails/arel.svg?branch=master)](http://travis-ci.org/rails/arel) [![Dependency Status](https://gemnasium.com/rails/arel.svg)](https://gemnasium.com/rails/arel)
1
+ # Arel
2
2
 
3
3
  * http://github.com/rails/arel
4
4
 
@@ -222,6 +222,12 @@ photos.project(photo_clicks.as("photo_clicks"))
222
222
  FROM "photos"
223
223
  ```
224
224
 
225
- ### License
225
+ ## Contributing to Arel
226
226
 
227
- Arel is released under the [MIT License](http://opensource.org/licenses/MIT).
227
+ Arel is work of many contributors. You're encouraged to submit pull requests, propose
228
+ features and discuss issues.
229
+
230
+ See [CONTRIBUTING](CONTRIBUTING.md).
231
+
232
+ ## License
233
+ Arel is released under the [MIT License](http://www.opensource.org/licenses/MIT).
@@ -12,6 +12,14 @@ module Arel
12
12
  def lower
13
13
  relation.lower self
14
14
  end
15
+
16
+ def type_cast_for_database(value)
17
+ relation.type_cast_for_database(name, value)
18
+ end
19
+
20
+ def able_to_type_cast?
21
+ relation.able_to_type_cast?
22
+ end
15
23
  end
16
24
 
17
25
  class String < Attribute; end
data/lib/arel/crud.rb CHANGED
@@ -3,7 +3,7 @@ module Arel
3
3
  # FIXME hopefully we can remove this
4
4
  module Crud
5
5
  def compile_update values, pk
6
- um = UpdateManager.new @engine
6
+ um = UpdateManager.new
7
7
 
8
8
  if Nodes::SqlLiteral === values
9
9
  relation = @ctx.from
@@ -26,11 +26,12 @@ module Arel
26
26
  end
27
27
 
28
28
  def create_insert
29
- InsertManager.new @engine
29
+ InsertManager.new
30
30
  end
31
31
 
32
32
  def compile_delete
33
- dm = DeleteManager.new @engine
33
+ dm = DeleteManager.new
34
+ dm.take @ast.limit.expr if @ast.limit
34
35
  dm.wheres = @ctx.wheres
35
36
  dm.from @ctx.froms
36
37
  dm
@@ -1,6 +1,6 @@
1
1
  module Arel
2
2
  class DeleteManager < Arel::TreeManager
3
- def initialize engine
3
+ def initialize
4
4
  super
5
5
  @ast = Nodes::DeleteStatement.new
6
6
  @ctx = @ast
@@ -11,6 +11,11 @@ module Arel
11
11
  self
12
12
  end
13
13
 
14
+ def take limit
15
+ @ast.limit = Nodes::Limit.new(Nodes.build_quoted(limit)) if limit
16
+ self
17
+ end
18
+
14
19
  def wheres= list
15
20
  @ast.wheres = list
16
21
  end
@@ -1,6 +1,6 @@
1
1
  module Arel
2
2
  class InsertManager < Arel::TreeManager
3
- def initialize engine
3
+ def initialize
4
4
  super
5
5
  @ast = Nodes::InsertStatement.new
6
6
  end
@@ -38,9 +38,7 @@ module Arel
38
38
  LessThanOrEqual
39
39
  NotEqual
40
40
  NotIn
41
- NotRegexp
42
41
  Or
43
- Regexp
44
42
  Union
45
43
  UnionAll
46
44
  Intersect
@@ -1,6 +1,9 @@
1
1
  module Arel
2
2
  module Nodes
3
3
  class BindParam < Node
4
+ def ==(other)
5
+ other.is_a?(BindParam)
6
+ end
4
7
  end
5
8
  end
6
9
  end
@@ -0,0 +1,40 @@
1
+ module Arel
2
+ module Nodes
3
+ class Casted < Arel::Nodes::Node # :nodoc:
4
+ attr_reader :val, :attribute
5
+ def initialize val, attribute
6
+ @val = val
7
+ @attribute = attribute
8
+ super()
9
+ end
10
+
11
+ def nil?; @val.nil?; end
12
+
13
+ def eql? other
14
+ self.class == other.class &&
15
+ self.val == other.val &&
16
+ self.attribute == other.attribute
17
+ end
18
+ alias :== :eql?
19
+ end
20
+
21
+ class Quoted < Arel::Nodes::Unary # :nodoc:
22
+ alias :val :value
23
+ def nil?; val.nil?; end
24
+ end
25
+
26
+ def self.build_quoted other, attribute = nil
27
+ case other
28
+ when Arel::Nodes::Node, Arel::Attributes::Attribute, Arel::Table, Arel::Nodes::BindParam, Arel::SelectManager, Arel::Nodes::Quoted
29
+ other
30
+ else
31
+ case attribute
32
+ when Arel::Attributes::Attribute
33
+ Casted.new other, attribute
34
+ else
35
+ Quoted.new other
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,6 +1,8 @@
1
1
  module Arel
2
2
  module Nodes
3
3
  class DeleteStatement < Arel::Nodes::Binary
4
+ attr_accessor :limit
5
+
4
6
  alias :relation :left
5
7
  alias :relation= :left=
6
8
  alias :wheres :right
@@ -3,6 +3,7 @@ module Arel
3
3
  class Function < Arel::Nodes::Node
4
4
  include Arel::Predications
5
5
  include Arel::WindowPredications
6
+ include Arel::OrderPredications
6
7
  attr_accessor :expressions, :alias, :distinct
7
8
 
8
9
  def initialize expr, aliaz = nil
@@ -2,10 +2,12 @@ module Arel
2
2
  module Nodes
3
3
  class Matches < Binary
4
4
  attr_reader :escape
5
+ attr_accessor :case_sensitive
5
6
 
6
- def initialize(left, right, escape = nil)
7
+ def initialize(left, right, escape = nil, case_sensitive = false)
7
8
  super(left, right)
8
9
  @escape = escape && Nodes.build_quoted(escape)
10
+ @case_sensitive = case_sensitive
9
11
  end
10
12
  end
11
13
 
@@ -0,0 +1,14 @@
1
+ module Arel
2
+ module Nodes
3
+ class Regexp < Binary
4
+ attr_accessor :case_sensitive
5
+
6
+ def initialize(left, right, case_sensitive = true)
7
+ super(left, right)
8
+ @case_sensitive = case_sensitive
9
+ end
10
+ end
11
+
12
+ class NotRegexp < Regexp; end
13
+ end
14
+ end
@@ -2,7 +2,7 @@ module Arel
2
2
  module Nodes
3
3
  class SelectCore < Arel::Nodes::Node
4
4
  attr_accessor :top, :projections, :wheres, :groups, :windows
5
- attr_accessor :having, :source, :set_quantifier
5
+ attr_accessor :havings, :source, :set_quantifier
6
6
 
7
7
  def initialize
8
8
  super()
@@ -14,7 +14,7 @@ module Arel
14
14
  @projections = []
15
15
  @wheres = []
16
16
  @groups = []
17
- @having = nil
17
+ @havings = []
18
18
  @windows = []
19
19
  end
20
20
 
@@ -35,14 +35,14 @@ module Arel
35
35
  @projections = @projections.clone
36
36
  @wheres = @wheres.clone
37
37
  @groups = @groups.clone
38
- @having = @having.clone if @having
38
+ @havings = @havings.clone
39
39
  @windows = @windows.clone
40
40
  end
41
41
 
42
42
  def hash
43
43
  [
44
44
  @source, @top, @set_quantifier, @projections,
45
- @wheres, @groups, @having, @windows
45
+ @wheres, @groups, @havings, @windows
46
46
  ].hash
47
47
  end
48
48
 
@@ -54,7 +54,7 @@ module Arel
54
54
  self.projections == other.projections &&
55
55
  self.wheres == other.wheres &&
56
56
  self.groups == other.groups &&
57
- self.having == other.having &&
57
+ self.havings == other.havings &&
58
58
  self.windows == other.windows
59
59
  end
60
60
  alias :== :eql?
@@ -13,8 +13,12 @@ module Arel
13
13
  relation.respond_to?(:name) ? relation.name : name
14
14
  end
15
15
 
16
- def engine
17
- relation.engine
16
+ def type_cast_for_database(*args)
17
+ relation.type_cast_for_database(*args)
18
+ end
19
+
20
+ def able_to_type_cast?
21
+ relation.respond_to?(:able_to_type_cast?) && relation.able_to_type_cast?
18
22
  end
19
23
  end
20
24
  end
@@ -23,7 +23,6 @@ module Arel
23
23
  %w{
24
24
  Bin
25
25
  Group
26
- Having
27
26
  Limit
28
27
  Not
29
28
  Offset
data/lib/arel/nodes.rb CHANGED
@@ -30,6 +30,7 @@ require 'arel/nodes/table_alias'
30
30
  require 'arel/nodes/infix_operation'
31
31
  require 'arel/nodes/over'
32
32
  require 'arel/nodes/matches'
33
+ require 'arel/nodes/regexp'
33
34
 
34
35
  # nary
35
36
  require 'arel/nodes/and'
@@ -55,41 +56,4 @@ require 'arel/nodes/string_join'
55
56
 
56
57
  require 'arel/nodes/sql_literal'
57
58
 
58
- module Arel
59
- module Nodes
60
- class Casted < Arel::Nodes::Node # :nodoc:
61
- attr_reader :val, :attribute
62
- def initialize val, attribute
63
- @val = val
64
- @attribute = attribute
65
- super()
66
- end
67
-
68
- def nil?; @val.nil?; end
69
-
70
- def eql? other
71
- self.class == other.class &&
72
- self.val == other.val &&
73
- self.attribute == other.attribute
74
- end
75
- alias :== :eql?
76
- end
77
-
78
- class Quoted < Arel::Nodes::Unary # :nodoc:
79
- end
80
-
81
- def self.build_quoted other, attribute = nil
82
- case other
83
- when Arel::Nodes::Node, Arel::Attributes::Attribute, Arel::Table, Arel::Nodes::BindParam, Arel::SelectManager
84
- other
85
- else
86
- case attribute
87
- when Arel::Attributes::Attribute
88
- Casted.new other, attribute
89
- else
90
- Quoted.new other
91
- end
92
- end
93
- end
94
- end
95
- end
59
+ require 'arel/nodes/casted'
@@ -25,15 +25,15 @@ module Arel
25
25
  end
26
26
 
27
27
  def between other
28
- if other.begin == -Float::INFINITY
29
- if other.end == Float::INFINITY
28
+ if equals_quoted?(other.begin, -Float::INFINITY)
29
+ if equals_quoted?(other.end, Float::INFINITY)
30
30
  not_in([])
31
31
  elsif other.exclude_end?
32
32
  lt(other.end)
33
33
  else
34
34
  lteq(other.end)
35
35
  end
36
- elsif other.end == Float::INFINITY
36
+ elsif equals_quoted?(other.end, Float::INFINITY)
37
37
  gteq(other.begin)
38
38
  elsif other.exclude_end?
39
39
  gteq(other.begin).and(lt(other.end))
@@ -71,15 +71,15 @@ Passing a range to `#in` is deprecated. Call `#between`, instead.
71
71
  end
72
72
 
73
73
  def not_between other
74
- if other.begin == -Float::INFINITY # The range begins with negative infinity
75
- if other.end == Float::INFINITY
74
+ if equals_quoted?(other.begin, -Float::INFINITY)
75
+ if equals_quoted?(other.end, Float::INFINITY)
76
76
  self.in([])
77
77
  elsif other.exclude_end?
78
78
  gteq(other.end)
79
79
  else
80
80
  gt(other.end)
81
81
  end
82
- elsif other.end == Float::INFINITY
82
+ elsif equals_quoted?(other.end, Float::INFINITY)
83
83
  lt(other.begin)
84
84
  else
85
85
  left = lt(other.begin)
@@ -118,20 +118,28 @@ Passing a range to `#not_in` is deprecated. Call `#not_between`, instead.
118
118
  grouping_all :not_in, others
119
119
  end
120
120
 
121
- def matches other, escape = nil
122
- Nodes::Matches.new self, quoted_node(other), escape
121
+ def matches other, escape = nil, case_sensitive = false
122
+ Nodes::Matches.new self, quoted_node(other), escape, case_sensitive
123
123
  end
124
124
 
125
- def matches_any others, escape = nil
126
- grouping_any :matches, others, escape
125
+ def matches_regexp other, case_sensitive = true
126
+ Nodes::Regexp.new self, quoted_node(other), case_sensitive
127
127
  end
128
128
 
129
- def matches_all others, escape = nil
130
- grouping_all :matches, others, escape
129
+ def matches_any others, escape = nil, case_sensitive = false
130
+ grouping_any :matches, others, escape, case_sensitive
131
131
  end
132
132
 
133
- def does_not_match other, escape = nil
134
- Nodes::DoesNotMatch.new self, quoted_node(other), escape
133
+ def matches_all others, escape = nil, case_sensitive = false
134
+ grouping_all :matches, others, escape, case_sensitive
135
+ end
136
+
137
+ def does_not_match other, escape = nil, case_sensitive = false
138
+ Nodes::DoesNotMatch.new self, quoted_node(other), escape, case_sensitive
139
+ end
140
+
141
+ def does_not_match_regexp other, case_sensitive = true
142
+ Nodes::NotRegexp.new self, quoted_node(other), case_sensitive
135
143
  end
136
144
 
137
145
  def does_not_match_any others, escape = nil
@@ -211,5 +219,13 @@ Passing a range to `#not_in` is deprecated. Call `#not_between`, instead.
211
219
  def quoted_array(others)
212
220
  others.map { |v| quoted_node(v) }
213
221
  end
222
+
223
+ def equals_quoted?(maybe_quoted, value)
224
+ if maybe_quoted.is_a?(Nodes::Quoted)
225
+ maybe_quoted.val == value
226
+ else
227
+ maybe_quoted == value
228
+ end
229
+ end
214
230
  end
215
231
  end
@@ -6,8 +6,8 @@ module Arel
6
6
 
7
7
  STRING_OR_SYMBOL_CLASS = [Symbol, String]
8
8
 
9
- def initialize engine, table = nil
10
- super(engine)
9
+ def initialize table = nil
10
+ super()
11
11
  @ast = Nodes::SelectStatement.new
12
12
  @ctx = @ast.cores.last
13
13
  from table
@@ -19,7 +19,7 @@ module Arel
19
19
  end
20
20
 
21
21
  def limit
22
- @ast.limit && @ast.limit.expr.expr
22
+ @ast.limit && @ast.limit.expr
23
23
  end
24
24
  alias :taken :limit
25
25
 
@@ -118,8 +118,8 @@ module Arel
118
118
  join(relation, Nodes::OuterJoin)
119
119
  end
120
120
 
121
- def having *exprs
122
- @ctx.having = Nodes::Having.new(collapse(exprs, @ctx.having))
121
+ def having expr
122
+ @ctx.havings << expr
123
123
  self
124
124
  end
125
125
 
@@ -176,10 +176,10 @@ module Arel
176
176
  @ast.orders
177
177
  end
178
178
 
179
- def where_sql
179
+ def where_sql engine = Table.engine
180
180
  return if @ctx.wheres.empty?
181
181
 
182
- viz = Visitors::WhereSql.new @engine.connection
182
+ viz = Visitors::WhereSql.new(engine.connection.visitor, engine.connection)
183
183
  Nodes::SqlLiteral.new viz.accept(@ctx, Collectors::SQLString.new).value
184
184
  end
185
185
 
@@ -216,8 +216,8 @@ module Arel
216
216
 
217
217
  def take limit
218
218
  if limit
219
- @ast.limit = Nodes::Limit.new(Nodes.build_quoted(limit))
220
- @ctx.top = Nodes::Top.new(Nodes.build_quoted(limit))
219
+ @ast.limit = Nodes::Limit.new(limit)
220
+ @ctx.top = Nodes::Top.new(limit)
221
221
  else
222
222
  @ast.limit = nil
223
223
  @ctx.top = nil
data/lib/arel/table.rb CHANGED
@@ -6,40 +6,24 @@ module Arel
6
6
  @engine = nil
7
7
  class << self; attr_accessor :engine; end
8
8
 
9
- attr_accessor :name, :engine, :aliases, :table_alias
9
+ attr_accessor :name, :aliases, :table_alias
10
10
 
11
11
  # TableAlias and Table both have a #table_name which is the name of the underlying table
12
12
  alias :table_name :name
13
13
 
14
- def initialize name, engine = Table.engine
14
+ def initialize(name, as: nil, type_caster: nil)
15
15
  @name = name.to_s
16
- @engine = engine
17
16
  @columns = nil
18
17
  @aliases = []
19
- @table_alias = nil
20
- @primary_key = nil
18
+ @type_caster = type_caster
21
19
 
22
- if Hash === engine
23
- @engine = engine[:engine] || Table.engine
24
-
25
- # Sometime AR sends an :as parameter to table, to let the table know
26
- # that it is an Alias. We may want to override new, and return a
27
- # TableAlias node?
28
- @table_alias = engine[:as] unless engine[:as].to_s == @name
29
- end
30
- end
31
-
32
- def primary_key
33
- if $VERBOSE
34
- warn <<-eowarn
35
- primary_key (#{caller.first}) is deprecated and will be removed in Arel 4.0.0
36
- eowarn
37
- end
38
- @primary_key ||= begin
39
- primary_key_name = @engine.connection.primary_key(name)
40
- # some tables might be without primary key
41
- primary_key_name && self[primary_key_name]
20
+ # Sometime AR sends an :as parameter to table, to let the table know
21
+ # that it is an Alias. We may want to override new, and return a
22
+ # TableAlias node?
23
+ if as.to_s == @name
24
+ as = nil
42
25
  end
26
+ @table_alias = as
43
27
  end
44
28
 
45
29
  def alias name = "#{self.name}_2"
@@ -48,12 +32,12 @@ primary_key (#{caller.first}) is deprecated and will be removed in Arel 4.0.0
48
32
  end
49
33
  end
50
34
 
51
- def from table
52
- SelectManager.new(@engine, table)
35
+ def from
36
+ SelectManager.new(self)
53
37
  end
54
38
 
55
39
  def join relation, klass = Nodes::InnerJoin
56
- return from(self) unless relation
40
+ return from unless relation
57
41
 
58
42
  case relation
59
43
  when String, Nodes::SqlLiteral
@@ -61,7 +45,7 @@ primary_key (#{caller.first}) is deprecated and will be removed in Arel 4.0.0
61
45
  klass = Nodes::StringJoin
62
46
  end
63
47
 
64
- from(self).join(relation, klass)
48
+ from.join(relation, klass)
65
49
  end
66
50
 
67
51
  def outer_join relation
@@ -69,55 +53,39 @@ primary_key (#{caller.first}) is deprecated and will be removed in Arel 4.0.0
69
53
  end
70
54
 
71
55
  def group *columns
72
- from(self).group(*columns)
56
+ from.group(*columns)
73
57
  end
74
58
 
75
59
  def order *expr
76
- from(self).order(*expr)
60
+ from.order(*expr)
77
61
  end
78
62
 
79
63
  def where condition
80
- from(self).where condition
64
+ from.where condition
81
65
  end
82
66
 
83
67
  def project *things
84
- from(self).project(*things)
68
+ from.project(*things)
85
69
  end
86
70
 
87
71
  def take amount
88
- from(self).take amount
72
+ from.take amount
89
73
  end
90
74
 
91
75
  def skip amount
92
- from(self).skip amount
76
+ from.skip amount
93
77
  end
94
78
 
95
79
  def having expr
96
- from(self).having expr
80
+ from.having expr
97
81
  end
98
82
 
99
83
  def [] name
100
84
  ::Arel::Attribute.new self, name
101
85
  end
102
86
 
103
- def select_manager
104
- SelectManager.new(@engine)
105
- end
106
-
107
- def insert_manager
108
- InsertManager.new(@engine)
109
- end
110
-
111
- def update_manager
112
- UpdateManager.new(@engine)
113
- end
114
-
115
- def delete_manager
116
- DeleteManager.new(@engine)
117
- end
118
-
119
87
  def hash
120
- # Perf note: aliases, table alias and engine is excluded from the hash
88
+ # Perf note: aliases and table alias is excluded from the hash
121
89
  # aliases can have a loop back to this table breaking hashes in parent
122
90
  # relations, for the vast majority of cases @name is unique to a query
123
91
  @name.hash
@@ -126,12 +94,23 @@ primary_key (#{caller.first}) is deprecated and will be removed in Arel 4.0.0
126
94
  def eql? other
127
95
  self.class == other.class &&
128
96
  self.name == other.name &&
129
- self.engine == other.engine &&
130
97
  self.aliases == other.aliases &&
131
98
  self.table_alias == other.table_alias
132
99
  end
133
100
  alias :== :eql?
134
101
 
102
+ def type_cast_for_database(attribute_name, value)
103
+ type_caster.type_cast_for_database(attribute_name, value)
104
+ end
105
+
106
+ def able_to_type_cast?
107
+ !type_caster.nil?
108
+ end
109
+
110
+ protected
111
+
112
+ attr_reader :type_caster
113
+
135
114
  private
136
115
 
137
116
  def attributes_for columns
@@ -8,8 +8,7 @@ module Arel
8
8
 
9
9
  attr_accessor :bind_values
10
10
 
11
- def initialize engine
12
- @engine = engine
11
+ def initialize
13
12
  @ctx = nil
14
13
  @bind_values = []
15
14
  end
@@ -20,13 +19,9 @@ module Arel
20
19
  collector.value
21
20
  end
22
21
 
23
- def visitor
24
- engine.connection.visitor
25
- end
26
-
27
- def to_sql
22
+ def to_sql engine = Table.engine
28
23
  collector = Arel::Collectors::SQLString.new
29
- collector = visitor.accept @ast, collector
24
+ collector = engine.connection.visitor.accept @ast, collector
30
25
  collector.value
31
26
  end
32
27
 
@@ -1,6 +1,6 @@
1
1
  module Arel
2
2
  class UpdateManager < Arel::TreeManager
3
- def initialize engine
3
+ def initialize
4
4
  super
5
5
  @ast = Nodes::UpdateStatement.new
6
6
  @ctx = @ast
@@ -146,7 +146,7 @@ module Arel
146
146
  visit o.wheres
147
147
  visit o.groups
148
148
  visit o.windows
149
- visit o.having
149
+ visit o.havings
150
150
  end
151
151
 
152
152
  def visit_Arel_Nodes_SelectStatement o
@@ -34,8 +34,13 @@ module Arel
34
34
  collector = inject_join o.groups, collector, ", "
35
35
  end
36
36
 
37
- maybe_visit o.having, collector
37
+ if o.havings.any?
38
+ collector << " HAVING "
39
+ collector = inject_join o.havings, collector, " AND "
40
+ end
41
+ collector
38
42
  end
43
+
39
44
  def visit_Arel_Nodes_Offset o, collector
40
45
  collector << "SKIP "
41
46
  visit o.expr, collector
@@ -3,6 +3,11 @@ module Arel
3
3
  class MSSQL < Arel::Visitors::ToSql
4
4
  RowNumber = Struct.new :children
5
5
 
6
+ def initialize(*)
7
+ @primary_keys = {}
8
+ super
9
+ end
10
+
6
11
  private
7
12
 
8
13
  # `top` wouldn't really work here. I.e. User.select("distinct first_name").limit(10) would generate
@@ -61,6 +66,23 @@ module Arel
61
66
  end
62
67
  end
63
68
 
69
+ def visit_Arel_Nodes_DeleteStatement o, collector
70
+ collector << 'DELETE '
71
+ if o.limit
72
+ collector << 'TOP ('
73
+ visit o.limit.expr, collector
74
+ collector << ') '
75
+ end
76
+ collector << 'FROM '
77
+ collector = visit o.relation, collector
78
+ if o.wheres.any?
79
+ collector << ' WHERE '
80
+ inject_join o.wheres, collector, AND
81
+ else
82
+ collector
83
+ end
84
+ end
85
+
64
86
  def determine_order_by orders, x
65
87
  if orders.any?
66
88
  orders
@@ -81,10 +103,20 @@ module Arel
81
103
  end
82
104
 
83
105
  # FIXME raise exception of there is no pk?
84
- # FIXME!! Table.primary_key will be deprecated. What is the replacement??
85
106
  def find_left_table_pk o
86
- return o.primary_key if o.instance_of? Arel::Table
87
- find_left_table_pk o.left if o.kind_of? Arel::Nodes::Join
107
+ if o.kind_of?(Arel::Nodes::Join)
108
+ find_left_table_pk(o.left)
109
+ elsif o.instance_of?(Arel::Table)
110
+ find_primary_key(o)
111
+ end
112
+ end
113
+
114
+ def find_primary_key(o)
115
+ @primary_keys[o.name] ||= begin
116
+ primary_key_name = @connection.primary_key(o.name)
117
+ # some tables might be without primary key
118
+ primary_key_name && o[primary_key_name]
119
+ end
88
120
  end
89
121
  end
90
122
  end
@@ -40,7 +40,7 @@ module Arel
40
40
  # http://dev.mysql.com/doc/refman/5.0/en/select.html#id3482214
41
41
  def visit_Arel_Nodes_SelectStatement o, collector
42
42
  if o.offset && !o.limit
43
- o.limit = Arel::Nodes::Limit.new(Nodes.build_quoted(18446744073709551615))
43
+ o.limit = Arel::Nodes::Limit.new(18446744073709551615)
44
44
  end
45
45
  super
46
46
  end
@@ -17,7 +17,7 @@ module Arel
17
17
 
18
18
  if o.limit && o.offset
19
19
  o = o.dup
20
- limit = o.limit.expr.expr
20
+ limit = o.limit.expr
21
21
  offset = o.offset
22
22
  o.offset = nil
23
23
  collector << "
@@ -0,0 +1,53 @@
1
+ module Arel
2
+ module Visitors
3
+ class Oracle12 < Arel::Visitors::ToSql
4
+ private
5
+
6
+ def visit_Arel_Nodes_SelectStatement o, collector
7
+ # Oracle does not allow LIMIT clause with select for update
8
+ if o.limit && o.lock
9
+ o = o.dup
10
+ o.limit = []
11
+ end
12
+
13
+ super
14
+ end
15
+
16
+ def visit_Arel_Nodes_SelectOptions o, collector
17
+ collector = maybe_visit o.offset, collector
18
+ collector = maybe_visit o.limit, collector
19
+ collector = maybe_visit o.lock, collector
20
+ end
21
+
22
+ def visit_Arel_Nodes_Limit o, collector
23
+ collector << "FETCH FIRST "
24
+ collector = visit o.expr, collector
25
+ collector << " ROWS ONLY"
26
+ end
27
+
28
+ def visit_Arel_Nodes_Offset o, collector
29
+ collector << "OFFSET "
30
+ visit o.expr, collector
31
+ collector << " ROWS"
32
+ end
33
+
34
+ def visit_Arel_Nodes_Except o, collector
35
+ collector << "( "
36
+ collector = infix_value o, collector, " MINUS "
37
+ collector << " )"
38
+ end
39
+
40
+ def visit_Arel_Nodes_UpdateStatement o, collector
41
+ # Oracle does not allow ORDER BY/LIMIT in UPDATEs.
42
+ if o.orders.any? && o.limit.nil?
43
+ # However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided,
44
+ # otherwise let the user deal with the error
45
+ o = o.dup
46
+ o.orders = []
47
+ end
48
+
49
+ super
50
+ end
51
+ end
52
+ end
53
+ end
@@ -4,19 +4,35 @@ module Arel
4
4
  private
5
5
 
6
6
  def visit_Arel_Nodes_Matches o, collector
7
- infix_value o, collector, ' ILIKE '
7
+ op = o.case_sensitive ? ' LIKE ' : ' ILIKE '
8
+ collector = infix_value o, collector, op
9
+ if o.escape
10
+ collector << ' ESCAPE '
11
+ visit o.escape, collector
12
+ else
13
+ collector
14
+ end
8
15
  end
9
16
 
10
17
  def visit_Arel_Nodes_DoesNotMatch o, collector
11
- infix_value o, collector, ' NOT ILIKE '
18
+ op = o.case_sensitive ? ' NOT LIKE ' : ' NOT ILIKE '
19
+ collector = infix_value o, collector, op
20
+ if o.escape
21
+ collector << ' ESCAPE '
22
+ visit o.escape, collector
23
+ else
24
+ collector
25
+ end
12
26
  end
13
27
 
14
28
  def visit_Arel_Nodes_Regexp o, collector
15
- infix_value o, collector, ' ~ '
29
+ op = o.case_sensitive ? ' ~ ' : ' ~* '
30
+ infix_value o, collector, op
16
31
  end
17
32
 
18
33
  def visit_Arel_Nodes_NotRegexp o, collector
19
- infix_value o, collector, ' !~ '
34
+ op = o.case_sensitive ? ' !~ ' : ' !~* '
35
+ infix_value o, collector, op
20
36
  end
21
37
 
22
38
  def visit_Arel_Nodes_DistinctOn o, collector
@@ -4,6 +4,12 @@ require 'arel/visitors/reduce'
4
4
 
5
5
  module Arel
6
6
  module Visitors
7
+ class UnsupportedVisitError < StandardError
8
+ def initialize(object)
9
+ super "Unsupported argument type: #{object.class.name}. Construct an Arel node instead."
10
+ end
11
+ end
12
+
7
13
  class ToSql < Arel::Visitors::Reduce
8
14
  ##
9
15
  # This is some roflscale crazy stuff. I'm roflscaling this because
@@ -74,14 +80,14 @@ module Arel
74
80
  end
75
81
 
76
82
  def visit_Arel_Nodes_DeleteStatement o, collector
77
- collector << "DELETE FROM "
83
+ collector << 'DELETE FROM '
78
84
  collector = visit o.relation, collector
79
85
  if o.wheres.any?
80
- collector << " WHERE "
81
- inject_join o.wheres, collector, AND
82
- else
83
- collector
86
+ collector << ' WHERE '
87
+ collector = inject_join o.wheres, collector, AND
84
88
  end
89
+
90
+ maybe_visit o.limit, collector
85
91
  end
86
92
 
87
93
  # FIXME: we should probably have a 2-pass visitor for this
@@ -211,7 +217,6 @@ module Arel
211
217
  }
212
218
 
213
219
  unless o.orders.empty?
214
- collector << SPACE
215
220
  collector << ORDER_BY
216
221
  len = o.orders.length - 1
217
222
  o.orders.each_with_index { |x, i|
@@ -220,11 +225,15 @@ module Arel
220
225
  }
221
226
  end
222
227
 
228
+ visit_Arel_Nodes_SelectOptions(o, collector)
229
+
230
+ collector
231
+ end
232
+
233
+ def visit_Arel_Nodes_SelectOptions o, collector
223
234
  collector = maybe_visit o.limit, collector
224
235
  collector = maybe_visit o.offset, collector
225
236
  collector = maybe_visit o.lock, collector
226
-
227
- collector
228
237
  end
229
238
 
230
239
  def visit_Arel_Nodes_SelectCore o, collector
@@ -266,7 +275,10 @@ module Arel
266
275
  end
267
276
  end
268
277
 
269
- collector = maybe_visit o.having, collector
278
+ unless o.havings.empty?
279
+ collector << " HAVING "
280
+ inject_join o.havings, collector, AND
281
+ end
270
282
 
271
283
  unless o.windows.empty?
272
284
  collector << WINDOW
@@ -405,11 +417,6 @@ module Arel
405
417
  end
406
418
  end
407
419
 
408
- def visit_Arel_Nodes_Having o, collector
409
- collector << "HAVING "
410
- visit o.expr, collector
411
- end
412
-
413
420
  def visit_Arel_Nodes_Offset o, collector
414
421
  collector << "OFFSET "
415
422
  visit o.expr, collector
@@ -575,8 +582,11 @@ module Arel
575
582
  visit o.left, collector
576
583
  end
577
584
 
578
- def visit_Arel_Nodes_FullOuterJoin o
579
- "FULL OUTER JOIN #{visit o.left} #{visit o.right}"
585
+ def visit_Arel_Nodes_FullOuterJoin o, collector
586
+ collector << "FULL OUTER JOIN "
587
+ collector = visit o.left, collector
588
+ collector << SPACE
589
+ visit o.right, collector
580
590
  end
581
591
 
582
592
  def visit_Arel_Nodes_OuterJoin o, collector
@@ -586,8 +596,11 @@ module Arel
586
596
  visit o.right, collector
587
597
  end
588
598
 
589
- def visit_Arel_Nodes_RightOuterJoin o
590
- "RIGHT OUTER JOIN #{visit o.left} #{visit o.right}"
599
+ def visit_Arel_Nodes_RightOuterJoin o, collector
600
+ collector << "RIGHT OUTER JOIN "
601
+ collector = visit o.left, collector
602
+ collector << SPACE
603
+ visit o.right, collector
591
604
  end
592
605
 
593
606
  def visit_Arel_Nodes_InnerJoin o, collector
@@ -722,11 +735,15 @@ module Arel
722
735
  alias :visit_Fixnum :literal
723
736
 
724
737
  def quoted o, a
725
- quote(o, column_for(a))
738
+ if a && a.able_to_type_cast?
739
+ quote(a.type_cast_for_database(o))
740
+ else
741
+ quote(o, column_for(a))
742
+ end
726
743
  end
727
744
 
728
745
  def unsupported o, collector
729
- raise "unsupported: #{o.class.name}"
746
+ raise UnsupportedVisitError.new(o)
730
747
  end
731
748
 
732
749
  alias :visit_ActiveSupport_Multibyte_Chars :unsupported
@@ -762,6 +779,9 @@ module Arel
762
779
 
763
780
  def quote value, column = nil
764
781
  return value if Arel::Nodes::SqlLiteral === value
782
+ if column
783
+ print_type_cast_deprecation
784
+ end
765
785
  @connection.quote value, column
766
786
  end
767
787
 
@@ -811,6 +831,20 @@ module Arel
811
831
  collector
812
832
  end
813
833
  end
834
+
835
+ def print_type_cast_deprecation
836
+ unless defined?($arel_silence_type_casting_deprecation) && $arel_silence_type_casting_deprecation
837
+ warn <<-eowarn
838
+ Arel performing automatic type casting is deprecated, and will be removed in Arel 8.0. If you are seeing this, it is because you are manually passing a value to an Arel predicate, and the `Arel::Table` object was constructed manually. The easiest way to remove this warning is to use an `Arel::Table` object returned from calling `arel_table` on an ActiveRecord::Base subclass.
839
+
840
+ If you're certain the value is already of the right type, change `attribute.eq(value)` to `attribute.eq(Arel::Nodes::Quoted.new(value))` (you will be able to remove that in Arel 8.0, it is only required to silence this deprecation warning).
841
+
842
+ You can also silence this warning globally by setting `$arel_silence_type_casting_deprecation` to `true`. (Do NOT do this if you are a library author)
843
+
844
+ If you are passing user input to a predicate, you must either give an appropriate type caster object to the `Arel::Table`, or manually cast the value before passing it to Arel.
845
+ eowarn
846
+ end
847
+ end
814
848
  end
815
849
  end
816
850
  end
@@ -1,11 +1,20 @@
1
1
  module Arel
2
2
  module Visitors
3
3
  class WhereSql < Arel::Visitors::ToSql
4
+ def initialize(inner_visitor, *args, &block)
5
+ @inner_visitor = inner_visitor
6
+ super(*args, &block)
7
+ end
8
+
4
9
  private
5
10
 
6
11
  def visit_Arel_Nodes_SelectCore o, collector
7
12
  collector << "WHERE "
8
- inject_join o.wheres, collector, ' AND '
13
+ wheres = o.wheres.map do |where|
14
+ Nodes::SqlLiteral.new(@inner_visitor.accept(where, collector.class.new).value)
15
+ end
16
+
17
+ inject_join wheres, collector, ' AND '
9
18
  end
10
19
  end
11
20
  end
data/lib/arel/visitors.rb CHANGED
@@ -6,6 +6,7 @@ require 'arel/visitors/postgresql'
6
6
  require 'arel/visitors/mysql'
7
7
  require 'arel/visitors/mssql'
8
8
  require 'arel/visitors/oracle'
9
+ require 'arel/visitors/oracle12'
9
10
  require 'arel/visitors/where_sql'
10
11
  require 'arel/visitors/dot'
11
12
  require 'arel/visitors/ibm_db'
data/lib/arel.rb CHANGED
@@ -21,7 +21,7 @@ require 'arel/delete_manager'
21
21
  require 'arel/nodes'
22
22
 
23
23
  module Arel
24
- VERSION = '6.0.0'
24
+ VERSION = '7.0.0'
25
25
 
26
26
  def self.sql raw_sql
27
27
  Arel::Nodes::SqlLiteral.new raw_sql
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arel
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0
4
+ version: 7.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Patterson
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2014-11-25 00:00:00.000000000 Z
14
+ date: 2015-12-17 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: minitest
@@ -99,6 +99,7 @@ files:
99
99
  - lib/arel/nodes/ascending.rb
100
100
  - lib/arel/nodes/binary.rb
101
101
  - lib/arel/nodes/bind_param.rb
102
+ - lib/arel/nodes/casted.rb
102
103
  - lib/arel/nodes/count.rb
103
104
  - lib/arel/nodes/delete_statement.rb
104
105
  - lib/arel/nodes/descending.rb
@@ -118,6 +119,7 @@ files:
118
119
  - lib/arel/nodes/node.rb
119
120
  - lib/arel/nodes/outer_join.rb
120
121
  - lib/arel/nodes/over.rb
122
+ - lib/arel/nodes/regexp.rb
121
123
  - lib/arel/nodes/right_outer_join.rb
122
124
  - lib/arel/nodes/select_core.rb
123
125
  - lib/arel/nodes/select_statement.rb
@@ -148,6 +150,7 @@ files:
148
150
  - lib/arel/visitors/mssql.rb
149
151
  - lib/arel/visitors/mysql.rb
150
152
  - lib/arel/visitors/oracle.rb
153
+ - lib/arel/visitors/oracle12.rb
151
154
  - lib/arel/visitors/postgresql.rb
152
155
  - lib/arel/visitors/reduce.rb
153
156
  - lib/arel/visitors/sqlite.rb
@@ -177,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
177
180
  version: '0'
178
181
  requirements: []
179
182
  rubyforge_project:
180
- rubygems_version: 2.2.2
183
+ rubygems_version: 2.4.5.1
181
184
  signing_key:
182
185
  specification_version: 4
183
186
  summary: Arel Really Exasperates Logicians Arel is a SQL AST manager for Ruby