arel 6.0.4 → 7.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/History.txt +31 -12
  3. data/MIT-LICENSE.txt +2 -1
  4. data/{README.markdown → README.md} +88 -31
  5. data/lib/arel.rb +1 -1
  6. data/lib/arel/attributes/attribute.rb +8 -0
  7. data/lib/arel/crud.rb +4 -3
  8. data/lib/arel/delete_manager.rb +6 -1
  9. data/lib/arel/insert_manager.rb +1 -1
  10. data/lib/arel/math.rb +24 -0
  11. data/lib/arel/nodes.rb +6 -38
  12. data/lib/arel/nodes/binary.rb +0 -2
  13. data/lib/arel/nodes/bind_param.rb +3 -0
  14. data/lib/arel/nodes/case.rb +57 -0
  15. data/lib/arel/nodes/casted.rb +44 -0
  16. data/lib/arel/nodes/delete_statement.rb +2 -0
  17. data/lib/arel/nodes/infix_operation.rb +36 -1
  18. data/lib/arel/nodes/matches.rb +3 -1
  19. data/lib/arel/nodes/regexp.rb +14 -0
  20. data/lib/arel/nodes/select_core.rb +5 -5
  21. data/lib/arel/nodes/table_alias.rb +6 -2
  22. data/lib/arel/nodes/unary.rb +6 -3
  23. data/lib/arel/nodes/unary_operation.rb +25 -0
  24. data/lib/arel/predications.rb +38 -14
  25. data/lib/arel/select_manager.rb +6 -6
  26. data/lib/arel/table.rb +34 -59
  27. data/lib/arel/tree_manager.rb +3 -8
  28. data/lib/arel/update_manager.rb +1 -1
  29. data/lib/arel/visitors.rb +1 -23
  30. data/lib/arel/visitors/depth_first.rb +14 -2
  31. data/lib/arel/visitors/dot.rb +12 -1
  32. data/lib/arel/visitors/informix.rb +6 -1
  33. data/lib/arel/visitors/mssql.rb +35 -3
  34. data/lib/arel/visitors/mysql.rb +8 -0
  35. data/lib/arel/visitors/oracle.rb +13 -2
  36. data/lib/arel/visitors/oracle12.rb +59 -0
  37. data/lib/arel/visitors/postgresql.rb +56 -4
  38. data/lib/arel/visitors/sqlite.rb +9 -0
  39. data/lib/arel/visitors/to_sql.rb +94 -52
  40. data/lib/arel/visitors/where_sql.rb +10 -1
  41. metadata +11 -6
data/lib/arel/table.rb CHANGED
@@ -6,54 +6,35 @@ 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, :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
- @aliases = []
19
- @table_alias = nil
20
- @primary_key = nil
17
+ @type_caster = type_caster
21
18
 
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]
19
+ # Sometime AR sends an :as parameter to table, to let the table know
20
+ # that it is an Alias. We may want to override new, and return a
21
+ # TableAlias node?
22
+ if as.to_s == @name
23
+ as = nil
42
24
  end
25
+ @table_alias = as
43
26
  end
44
27
 
45
28
  def alias name = "#{self.name}_2"
46
- Nodes::TableAlias.new(self, name).tap do |node|
47
- @aliases << node
48
- end
29
+ Nodes::TableAlias.new(self, name)
49
30
  end
50
31
 
51
- def from table
52
- SelectManager.new(@engine, table)
32
+ def from
33
+ SelectManager.new(self)
53
34
  end
54
35
 
55
36
  def join relation, klass = Nodes::InnerJoin
56
- return from(self) unless relation
37
+ return from unless relation
57
38
 
58
39
  case relation
59
40
  when String, Nodes::SqlLiteral
@@ -61,7 +42,7 @@ primary_key (#{caller.first}) is deprecated and will be removed in Arel 4.0.0
61
42
  klass = Nodes::StringJoin
62
43
  end
63
44
 
64
- from(self).join(relation, klass)
45
+ from.join(relation, klass)
65
46
  end
66
47
 
67
48
  def outer_join relation
@@ -69,55 +50,39 @@ primary_key (#{caller.first}) is deprecated and will be removed in Arel 4.0.0
69
50
  end
70
51
 
71
52
  def group *columns
72
- from(self).group(*columns)
53
+ from.group(*columns)
73
54
  end
74
55
 
75
56
  def order *expr
76
- from(self).order(*expr)
57
+ from.order(*expr)
77
58
  end
78
59
 
79
60
  def where condition
80
- from(self).where condition
61
+ from.where condition
81
62
  end
82
63
 
83
64
  def project *things
84
- from(self).project(*things)
65
+ from.project(*things)
85
66
  end
86
67
 
87
68
  def take amount
88
- from(self).take amount
69
+ from.take amount
89
70
  end
90
71
 
91
72
  def skip amount
92
- from(self).skip amount
73
+ from.skip amount
93
74
  end
94
75
 
95
76
  def having expr
96
- from(self).having expr
77
+ from.having expr
97
78
  end
98
79
 
99
80
  def [] name
100
81
  ::Arel::Attribute.new self, name
101
82
  end
102
83
 
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
84
  def hash
120
- # Perf note: aliases, table alias and engine is excluded from the hash
85
+ # Perf note: aliases and table alias is excluded from the hash
121
86
  # aliases can have a loop back to this table breaking hashes in parent
122
87
  # relations, for the vast majority of cases @name is unique to a query
123
88
  @name.hash
@@ -126,12 +91,22 @@ primary_key (#{caller.first}) is deprecated and will be removed in Arel 4.0.0
126
91
  def eql? other
127
92
  self.class == other.class &&
128
93
  self.name == other.name &&
129
- self.engine == other.engine &&
130
- self.aliases == other.aliases &&
131
94
  self.table_alias == other.table_alias
132
95
  end
133
96
  alias :== :eql?
134
97
 
98
+ def type_cast_for_database(attribute_name, value)
99
+ type_caster.type_cast_for_database(attribute_name, value)
100
+ end
101
+
102
+ def able_to_type_cast?
103
+ !type_caster.nil?
104
+ end
105
+
106
+ protected
107
+
108
+ attr_reader :type_caster
109
+
135
110
  private
136
111
 
137
112
  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
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'
@@ -13,28 +14,5 @@ require 'arel/visitors/informix'
13
14
 
14
15
  module Arel
15
16
  module Visitors
16
- VISITORS = {
17
- 'postgresql' => Arel::Visitors::PostgreSQL,
18
- 'mysql' => Arel::Visitors::MySQL,
19
- 'mysql2' => Arel::Visitors::MySQL,
20
- 'mssql' => Arel::Visitors::MSSQL,
21
- 'sqlserver' => Arel::Visitors::MSSQL,
22
- 'oracle_enhanced' => Arel::Visitors::Oracle,
23
- 'sqlite' => Arel::Visitors::SQLite,
24
- 'sqlite3' => Arel::Visitors::SQLite,
25
- 'ibm_db' => Arel::Visitors::IBM_DB,
26
- 'informix' => Arel::Visitors::Informix,
27
- }
28
-
29
- ENGINE_VISITORS = Hash.new do |hash, engine|
30
- pool = engine.connection_pool
31
- adapter = pool.spec.config[:adapter]
32
- hash[engine] = (VISITORS[adapter] || Visitors::ToSql).new(engine)
33
- end
34
-
35
- def self.visitor_for engine
36
- ENGINE_VISITORS[engine]
37
- end
38
- class << self; alias :for :visitor_for; end
39
17
  end
40
18
  end
@@ -16,7 +16,12 @@ module Arel
16
16
  def unary o
17
17
  visit o.expr
18
18
  end
19
+ alias :visit_Arel_Nodes_Else :unary
19
20
  alias :visit_Arel_Nodes_Group :unary
21
+ alias :visit_Arel_Nodes_Cube :unary
22
+ alias :visit_Arel_Nodes_RollUp :unary
23
+ alias :visit_Arel_Nodes_GroupingSet :unary
24
+ alias :visit_Arel_Nodes_GroupingElement :unary
20
25
  alias :visit_Arel_Nodes_Grouping :unary
21
26
  alias :visit_Arel_Nodes_Having :unary
22
27
  alias :visit_Arel_Nodes_Limit :unary
@@ -53,6 +58,12 @@ module Arel
53
58
  visit o.distinct
54
59
  end
55
60
 
61
+ def visit_Arel_Nodes_Case o
62
+ visit o.case
63
+ visit o.conditions
64
+ visit o.default
65
+ end
66
+
56
67
  def nary o
57
68
  o.children.each { |child| visit child}
58
69
  end
@@ -65,6 +76,7 @@ module Arel
65
76
  alias :visit_Arel_Nodes_As :binary
66
77
  alias :visit_Arel_Nodes_Assignment :binary
67
78
  alias :visit_Arel_Nodes_Between :binary
79
+ alias :visit_Arel_Nodes_Concat :binary
68
80
  alias :visit_Arel_Nodes_DeleteStatement :binary
69
81
  alias :visit_Arel_Nodes_DoesNotMatch :binary
70
82
  alias :visit_Arel_Nodes_Equality :binary
@@ -87,7 +99,7 @@ module Arel
87
99
  alias :visit_Arel_Nodes_RightOuterJoin :binary
88
100
  alias :visit_Arel_Nodes_TableAlias :binary
89
101
  alias :visit_Arel_Nodes_Values :binary
90
- alias :visit_Arel_Nodes_Union :binary
102
+ alias :visit_Arel_Nodes_When :binary
91
103
 
92
104
  def visit_Arel_Nodes_StringJoin o
93
105
  visit o.left
@@ -147,7 +159,7 @@ module Arel
147
159
  visit o.wheres
148
160
  visit o.groups
149
161
  visit o.windows
150
- visit o.having
162
+ visit o.havings
151
163
  end
152
164
 
153
165
  def visit_Arel_Nodes_SelectStatement o
@@ -69,6 +69,10 @@ module Arel
69
69
  visit_edge o, "expr"
70
70
  end
71
71
  alias :visit_Arel_Nodes_Group :unary
72
+ alias :visit_Arel_Nodes_Cube :unary
73
+ alias :visit_Arel_Nodes_RollUp :unary
74
+ alias :visit_Arel_Nodes_GroupingSet :unary
75
+ alias :visit_Arel_Nodes_GroupingElement :unary
72
76
  alias :visit_Arel_Nodes_Grouping :unary
73
77
  alias :visit_Arel_Nodes_Having :unary
74
78
  alias :visit_Arel_Nodes_Limit :unary
@@ -151,6 +155,11 @@ module Arel
151
155
  visit_edge o, "name"
152
156
  end
153
157
 
158
+ def visit_Arel_Nodes_Casted o
159
+ visit_edge o, 'val'
160
+ visit_edge o, 'attribute'
161
+ end
162
+
154
163
  def visit_Arel_Attribute o
155
164
  visit_edge o, "relation"
156
165
  visit_edge o, "name"
@@ -176,6 +185,7 @@ module Arel
176
185
  alias :visit_Arel_Nodes_As :binary
177
186
  alias :visit_Arel_Nodes_Assignment :binary
178
187
  alias :visit_Arel_Nodes_Between :binary
188
+ alias :visit_Arel_Nodes_Concat :binary
179
189
  alias :visit_Arel_Nodes_DoesNotMatch :binary
180
190
  alias :visit_Arel_Nodes_Equality :binary
181
191
  alias :visit_Arel_Nodes_GreaterThan :binary
@@ -199,7 +209,6 @@ module Arel
199
209
  alias :visit_NilClass :visit_String
200
210
  alias :visit_TrueClass :visit_String
201
211
  alias :visit_FalseClass :visit_String
202
- alias :visit_Arel_Nodes_BindParam :visit_String
203
212
  alias :visit_Integer :visit_String
204
213
  alias :visit_Fixnum :visit_String
205
214
  alias :visit_BigDecimal :visit_String
@@ -207,6 +216,8 @@ module Arel
207
216
  alias :visit_Symbol :visit_String
208
217
  alias :visit_Arel_Nodes_SqlLiteral :visit_String
209
218
 
219
+ def visit_Arel_Nodes_BindParam o; end
220
+
210
221
  def visit_Hash o
211
222
  o.each_with_index do |pair, i|
212
223
  edge("pair_#{i}") { visit pair }
@@ -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
@@ -72,6 +72,14 @@ module Arel
72
72
  maybe_visit o.limit, collector
73
73
  end
74
74
 
75
+ def visit_Arel_Nodes_Concat o, collector
76
+ collector << " CONCAT("
77
+ visit o.left, collector
78
+ collector << ", "
79
+ visit o.right, collector
80
+ collector << ") "
81
+ collector
82
+ end
75
83
  end
76
84
  end
77
85
  end
@@ -26,11 +26,22 @@ module Arel
26
26
  FROM ("
27
27
 
28
28
  collector = super(o, collector)
29
- collector << ") raw_sql_
29
+
30
+ if offset.expr.is_a? Nodes::BindParam
31
+ offset_bind = nil
32
+ collector << ') raw_sql_ WHERE rownum <= ('
33
+ collector.add_bind(offset.expr) { |i| offset_bind = ":a#{i}" }
34
+ collector << ' + '
35
+ collector.add_bind(limit) { |i| ":a#{i}" }
36
+ collector << ") ) WHERE raw_rnum_ > #{offset_bind}"
37
+ return collector
38
+ else
39
+ collector << ") raw_sql_
30
40
  WHERE rownum <= #{offset.expr.to_i + limit}
31
41
  )
32
42
  WHERE "
33
- return visit(offset, collector)
43
+ return visit(offset, collector)
44
+ end
34
45
  end
35
46
 
36
47
  if o.limit