arel 6.0.4 → 7.1.4

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.
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