arel 2.0.10 → 2.1.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.
Files changed (102) hide show
  1. data/History.txt +51 -4
  2. data/Manifest.txt +15 -31
  3. data/README.markdown +27 -3
  4. data/Rakefile +0 -2
  5. data/arel.gemspec +20 -19
  6. data/lib/arel.rb +9 -1
  7. data/lib/arel/alias_predication.rb +7 -0
  8. data/lib/arel/attributes/attribute.rb +10 -1
  9. data/lib/arel/crud.rb +43 -8
  10. data/lib/arel/expression.rb +1 -0
  11. data/lib/arel/factory_methods.rb +35 -0
  12. data/lib/arel/insert_manager.rb +5 -1
  13. data/lib/arel/math.rb +19 -0
  14. data/lib/arel/nodes.rb +32 -42
  15. data/lib/arel/nodes/and.rb +18 -1
  16. data/lib/arel/nodes/binary.rb +28 -0
  17. data/lib/arel/nodes/count.rb +0 -3
  18. data/lib/arel/nodes/function.rb +13 -2
  19. data/lib/arel/nodes/infix_operation.rb +43 -0
  20. data/lib/arel/nodes/join_source.rb +14 -0
  21. data/lib/arel/nodes/named_function.rb +14 -0
  22. data/lib/arel/nodes/node.rb +5 -2
  23. data/lib/arel/nodes/select_core.rb +24 -10
  24. data/lib/arel/nodes/select_statement.rb +8 -6
  25. data/lib/arel/nodes/sql_literal.rb +2 -0
  26. data/lib/arel/nodes/string_join.rb +2 -4
  27. data/lib/arel/nodes/table_alias.rb +7 -3
  28. data/lib/arel/nodes/{as.rb → terminal.rb} +1 -1
  29. data/lib/arel/nodes/unary.rb +17 -0
  30. data/lib/arel/nodes/unqualified_column.rb +4 -0
  31. data/lib/arel/nodes/update_statement.rb +4 -2
  32. data/lib/arel/nodes/with.rb +10 -0
  33. data/lib/arel/order_predications.rb +13 -0
  34. data/lib/arel/predications.rb +6 -24
  35. data/lib/arel/select_manager.rb +95 -40
  36. data/lib/arel/table.rb +32 -19
  37. data/lib/arel/tree_manager.rb +1 -0
  38. data/lib/arel/update_manager.rb +4 -0
  39. data/lib/arel/visitors.rb +2 -0
  40. data/lib/arel/visitors/depth_first.rb +19 -9
  41. data/lib/arel/visitors/dot.rb +42 -25
  42. data/lib/arel/visitors/ibm_db.rb +12 -0
  43. data/lib/arel/visitors/join_sql.rb +2 -23
  44. data/lib/arel/visitors/mssql.rb +7 -0
  45. data/lib/arel/visitors/mysql.rb +4 -0
  46. data/lib/arel/visitors/oracle.rb +2 -2
  47. data/lib/arel/visitors/postgresql.rb +2 -38
  48. data/lib/arel/visitors/to_sql.rb +148 -67
  49. data/test/attributes/test_attribute.rb +3 -3
  50. data/test/helper.rb +1 -1
  51. data/test/nodes/test_as.rb +6 -0
  52. data/test/nodes/test_bin.rb +23 -0
  53. data/test/nodes/test_named_function.rb +30 -0
  54. data/test/nodes/test_node.rb +10 -0
  55. data/test/nodes/test_not.rb +4 -7
  56. data/test/nodes/test_select_core.rb +23 -14
  57. data/test/support/fake_record.rb +21 -4
  58. data/test/test_attributes.rb +8 -0
  59. data/test/test_crud.rb +6 -12
  60. data/test/test_factory_methods.rb +34 -0
  61. data/test/test_insert_manager.rb +19 -0
  62. data/test/test_select_manager.rb +343 -64
  63. data/test/test_table.rb +36 -45
  64. data/test/visitors/test_depth_first.rb +29 -11
  65. data/test/visitors/test_dot.rb +47 -0
  66. data/test/visitors/test_ibm_db.rb +27 -0
  67. data/test/visitors/test_join_sql.rb +14 -7
  68. data/test/visitors/test_mssql.rb +9 -0
  69. data/test/visitors/test_oracle.rb +11 -10
  70. data/test/visitors/test_postgres.rb +12 -0
  71. data/test/visitors/test_to_sql.rb +80 -17
  72. metadata +80 -101
  73. data/lib/arel/nodes/assignment.rb +0 -6
  74. data/lib/arel/nodes/avg.rb +0 -6
  75. data/lib/arel/nodes/between.rb +0 -6
  76. data/lib/arel/nodes/does_not_match.rb +0 -6
  77. data/lib/arel/nodes/except.rb +0 -7
  78. data/lib/arel/nodes/exists.rb +0 -7
  79. data/lib/arel/nodes/greater_than.rb +0 -6
  80. data/lib/arel/nodes/greater_than_or_equal.rb +0 -6
  81. data/lib/arel/nodes/group.rb +0 -6
  82. data/lib/arel/nodes/grouping.rb +0 -6
  83. data/lib/arel/nodes/having.rb +0 -6
  84. data/lib/arel/nodes/intersect.rb +0 -7
  85. data/lib/arel/nodes/join.rb +0 -13
  86. data/lib/arel/nodes/less_than.rb +0 -6
  87. data/lib/arel/nodes/less_than_or_equal.rb +0 -6
  88. data/lib/arel/nodes/limit.rb +0 -7
  89. data/lib/arel/nodes/lock.rb +0 -6
  90. data/lib/arel/nodes/matches.rb +0 -6
  91. data/lib/arel/nodes/max.rb +0 -6
  92. data/lib/arel/nodes/min.rb +0 -6
  93. data/lib/arel/nodes/not.rb +0 -6
  94. data/lib/arel/nodes/not_equal.rb +0 -6
  95. data/lib/arel/nodes/not_in.rb +0 -6
  96. data/lib/arel/nodes/offset.rb +0 -7
  97. data/lib/arel/nodes/on.rb +0 -6
  98. data/lib/arel/nodes/or.rb +0 -6
  99. data/lib/arel/nodes/sum.rb +0 -6
  100. data/lib/arel/nodes/top.rb +0 -6
  101. data/lib/arel/nodes/union.rb +0 -7
  102. data/lib/arel/nodes/union_all.rb +0 -7
@@ -2,6 +2,7 @@ module Arel
2
2
  class TreeManager
3
3
  # FIXME: Remove this.
4
4
  include Arel::Relation
5
+ include Arel::FactoryMethods
5
6
 
6
7
  attr_accessor :visitor
7
8
  attr_reader :ast, :engine
@@ -11,6 +11,10 @@ module Arel
11
11
  self
12
12
  end
13
13
 
14
+ def key= key
15
+ @ast.key = key
16
+ end
17
+
14
18
  def order *expr
15
19
  @ast.orders = expr
16
20
  self
@@ -10,6 +10,7 @@ require 'arel/visitors/join_sql'
10
10
  require 'arel/visitors/where_sql'
11
11
  require 'arel/visitors/order_clauses'
12
12
  require 'arel/visitors/dot'
13
+ require 'arel/visitors/ibm_db'
13
14
 
14
15
  module Arel
15
16
  module Visitors
@@ -22,6 +23,7 @@ module Arel
22
23
  'oracle_enhanced' => Arel::Visitors::Oracle,
23
24
  'sqlite' => Arel::Visitors::SQLite,
24
25
  'sqlite3' => Arel::Visitors::SQLite,
26
+ 'ibm_db' => Arel::Visitors::IBM_DB,
25
27
  }
26
28
 
27
29
  ENGINE_VISITORS = Hash.new do |hash, engine|
@@ -28,6 +28,7 @@ module Arel
28
28
  def function o
29
29
  visit o.expressions
30
30
  visit o.alias
31
+ visit o.distinct
31
32
  end
32
33
  alias :visit_Arel_Nodes_Avg :function
33
34
  alias :visit_Arel_Nodes_Exists :function
@@ -35,25 +36,28 @@ module Arel
35
36
  alias :visit_Arel_Nodes_Min :function
36
37
  alias :visit_Arel_Nodes_Sum :function
37
38
 
39
+ def visit_Arel_Nodes_NamedFunction o
40
+ visit o.name
41
+ visit o.expressions
42
+ visit o.distinct
43
+ visit o.alias
44
+ end
45
+
38
46
  def visit_Arel_Nodes_Count o
39
47
  visit o.expressions
40
48
  visit o.alias
41
49
  visit o.distinct
42
50
  end
43
51
 
44
- def join o
45
- visit o.left
46
- visit o.right
47
- visit o.constraint
52
+ def nary o
53
+ o.children.each { |child| visit child }
48
54
  end
49
- alias :visit_Arel_Nodes_InnerJoin :join
50
- alias :visit_Arel_Nodes_OuterJoin :join
55
+ alias :visit_Arel_Nodes_And :nary
51
56
 
52
57
  def binary o
53
58
  visit o.left
54
59
  visit o.right
55
60
  end
56
- alias :visit_Arel_Nodes_And :binary
57
61
  alias :visit_Arel_Nodes_As :binary
58
62
  alias :visit_Arel_Nodes_Assignment :binary
59
63
  alias :visit_Arel_Nodes_Between :binary
@@ -63,6 +67,8 @@ module Arel
63
67
  alias :visit_Arel_Nodes_GreaterThan :binary
64
68
  alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
65
69
  alias :visit_Arel_Nodes_In :binary
70
+ alias :visit_Arel_Nodes_JoinSource :binary
71
+ alias :visit_Arel_Nodes_InnerJoin :binary
66
72
  alias :visit_Arel_Nodes_LessThan :binary
67
73
  alias :visit_Arel_Nodes_LessThanOrEqual :binary
68
74
  alias :visit_Arel_Nodes_Matches :binary
@@ -70,10 +76,14 @@ module Arel
70
76
  alias :visit_Arel_Nodes_NotIn :binary
71
77
  alias :visit_Arel_Nodes_Or :binary
72
78
  alias :visit_Arel_Nodes_Ordering :binary
73
- alias :visit_Arel_Nodes_StringJoin :binary
79
+ alias :visit_Arel_Nodes_OuterJoin :binary
74
80
  alias :visit_Arel_Nodes_TableAlias :binary
75
81
  alias :visit_Arel_Nodes_Values :binary
76
82
 
83
+ def visit_Arel_Nodes_StringJoin o
84
+ visit o.left
85
+ end
86
+
77
87
  def visit_Arel_Attribute o
78
88
  visit o.relation
79
89
  visit o.name
@@ -120,7 +130,7 @@ module Arel
120
130
 
121
131
  def visit_Arel_Nodes_SelectCore o
122
132
  visit o.projections
123
- visit o.froms
133
+ visit o.source
124
134
  visit o.wheres
125
135
  visit o.groups
126
136
  visit o.having
@@ -36,15 +36,7 @@ module Arel
36
36
  def visit_Arel_Nodes_TableAlias o
37
37
  visit_edge o, "name"
38
38
  visit_edge o, "relation"
39
- visit_edge o, "columns"
40
- end
41
-
42
- def visit_Arel_Nodes_Sum o
43
- visit_edge o, "expressions"
44
- visit_edge o, "alias"
45
39
  end
46
- alias :visit_Arel_Nodes_Max :visit_Arel_Nodes_Sum
47
- alias :visit_Arel_Nodes_Avg :visit_Arel_Nodes_Sum
48
40
 
49
41
  def visit_Arel_Nodes_Count o
50
42
  visit_edge o, "expressions"
@@ -57,13 +49,11 @@ module Arel
57
49
 
58
50
  def visit_Arel_Nodes_StringJoin o
59
51
  visit_edge o, "left"
60
- visit_edge o, "right"
61
52
  end
62
53
 
63
54
  def visit_Arel_Nodes_InnerJoin o
64
55
  visit_edge o, "left"
65
56
  visit_edge o, "right"
66
- visit_edge o, "constraint"
67
57
  end
68
58
  alias :visit_Arel_Nodes_OuterJoin :visit_Arel_Nodes_InnerJoin
69
59
 
@@ -85,6 +75,24 @@ module Arel
85
75
  alias :visit_Arel_Nodes_Top :unary
86
76
  alias :visit_Arel_Nodes_UnqualifiedColumn :unary
87
77
 
78
+ def function o
79
+ visit_edge o, "expressions"
80
+ visit_edge o, "distinct"
81
+ visit_edge o, "alias"
82
+ end
83
+ alias :visit_Arel_Nodes_Exists :function
84
+ alias :visit_Arel_Nodes_Min :function
85
+ alias :visit_Arel_Nodes_Max :function
86
+ alias :visit_Arel_Nodes_Avg :function
87
+ alias :visit_Arel_Nodes_Sum :function
88
+
89
+ def visit_Arel_Nodes_NamedFunction o
90
+ visit_edge o, "name"
91
+ visit_edge o, "expressions"
92
+ visit_edge o, "distinct"
93
+ visit_edge o, "alias"
94
+ end
95
+
88
96
  def visit_Arel_Nodes_InsertStatement o
89
97
  visit_edge o, "relation"
90
98
  visit_edge o, "columns"
@@ -92,7 +100,7 @@ module Arel
92
100
  end
93
101
 
94
102
  def visit_Arel_Nodes_SelectCore o
95
- visit_edge o, "froms"
103
+ visit_edge o, "source"
96
104
  visit_edge o, "projections"
97
105
  visit_edge o, "wheres"
98
106
  end
@@ -125,23 +133,32 @@ module Arel
125
133
  alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
126
134
  alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
127
135
 
128
- def visit_Arel_Nodes_Equality o
136
+ def nary o
137
+ o.children.each_with_index do |x,i|
138
+ edge(i) { visit x }
139
+ end
140
+ end
141
+ alias :visit_Arel_Nodes_And :nary
142
+
143
+ def binary o
129
144
  visit_edge o, "left"
130
145
  visit_edge o, "right"
131
146
  end
132
- alias :visit_Arel_Nodes_And :visit_Arel_Nodes_Equality
133
- alias :visit_Arel_Nodes_Or :visit_Arel_Nodes_Equality
134
- alias :visit_Arel_Nodes_NotEqual :visit_Arel_Nodes_Equality
135
- alias :visit_Arel_Nodes_GreaterThan :visit_Arel_Nodes_Equality
136
- alias :visit_Arel_Nodes_GreaterThanOrEqual :visit_Arel_Nodes_Equality
137
- alias :visit_Arel_Nodes_Assignment :visit_Arel_Nodes_Equality
138
- alias :visit_Arel_Nodes_In :visit_Arel_Nodes_Equality
139
- alias :visit_Arel_Nodes_LessThan :visit_Arel_Nodes_Equality
140
- alias :visit_Arel_Nodes_LessThanOrEqual :visit_Arel_Nodes_Equality
141
- alias :visit_Arel_Nodes_Between :visit_Arel_Nodes_Equality
142
- alias :visit_Arel_Nodes_NotIn :visit_Arel_Nodes_Equality
143
- alias :visit_Arel_Nodes_DoesNotMatch :visit_Arel_Nodes_Equality
144
- alias :visit_Arel_Nodes_Matches :visit_Arel_Nodes_Equality
147
+ alias :visit_Arel_Nodes_As :binary
148
+ alias :visit_Arel_Nodes_Assignment :binary
149
+ alias :visit_Arel_Nodes_Between :binary
150
+ alias :visit_Arel_Nodes_DoesNotMatch :binary
151
+ alias :visit_Arel_Nodes_Equality :binary
152
+ alias :visit_Arel_Nodes_GreaterThan :binary
153
+ alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
154
+ alias :visit_Arel_Nodes_In :binary
155
+ alias :visit_Arel_Nodes_JoinSource :binary
156
+ alias :visit_Arel_Nodes_LessThan :binary
157
+ alias :visit_Arel_Nodes_LessThanOrEqual :binary
158
+ alias :visit_Arel_Nodes_Matches :binary
159
+ alias :visit_Arel_Nodes_NotEqual :binary
160
+ alias :visit_Arel_Nodes_NotIn :binary
161
+ alias :visit_Arel_Nodes_Or :binary
145
162
 
146
163
  def visit_String o
147
164
  @node_stack.last.fields << o
@@ -0,0 +1,12 @@
1
+ module Arel
2
+ module Visitors
3
+ class IBM_DB < Arel::Visitors::ToSql
4
+ private
5
+
6
+ def visit_Arel_Nodes_Limit o
7
+ "FETCH FIRST #{visit o.expr} ROWS ONLY"
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -8,32 +8,11 @@ module Arel
8
8
  #
9
9
  # This visitor is used in SelectManager#join_sql and is for backwards
10
10
  # compatibility with Arel V1.0
11
- class JoinSql < Arel::Visitors::ToSql
11
+ module JoinSql
12
12
  private
13
13
 
14
14
  def visit_Arel_Nodes_SelectCore o
15
- [o.froms].grep(Nodes::Join).map { |x| visit x }.join ', '
16
- end
17
-
18
- def visit_Arel_Nodes_StringJoin o
19
- [
20
- (visit o.left if Nodes::Join === o.left),
21
- visit(o.right)
22
- ].compact.join ' '
23
- end
24
-
25
- def visit_Arel_Nodes_OuterJoin o
26
- [
27
- (visit o.left if Nodes::Join === o.left),
28
- "LEFT OUTER JOIN #{visit o.right} #{visit o.constraint if o.constraint}"
29
- ].compact.join ' '
30
- end
31
-
32
- def visit_Arel_Nodes_InnerJoin o
33
- [
34
- (visit o.left if Nodes::Join === o.left),
35
- "INNER JOIN #{visit o.right} #{visit o.constraint if o.constraint}"
36
- ].compact.join ' '
15
+ o.source.right.map { |j| visit j }.join ' '
37
16
  end
38
17
  end
39
18
  end
@@ -3,6 +3,13 @@ module Arel
3
3
  class MSSQL < Arel::Visitors::ToSql
4
4
  private
5
5
 
6
+ def build_subselect key, o
7
+ stmt = super
8
+ core = stmt.cores.first
9
+ core.top = Nodes::Top.new(o.limit.expr) if o.limit
10
+ stmt
11
+ end
12
+
6
13
  def visit_Arel_Nodes_Limit o
7
14
  ""
8
15
  end
@@ -2,6 +2,10 @@ module Arel
2
2
  module Visitors
3
3
  class MySQL < Arel::Visitors::ToSql
4
4
  private
5
+ def visit_Arel_Nodes_Bin o
6
+ "BINARY #{visit o.expr}"
7
+ end
8
+
5
9
  def visit_Arel_Nodes_Lock o
6
10
  visit o.expr
7
11
  end
@@ -25,7 +25,7 @@ module Arel
25
25
  SELECT * FROM (
26
26
  SELECT raw_sql_.*, rownum raw_rnum_
27
27
  FROM (#{sql}) raw_sql_
28
- WHERE rownum <= #{offset.value.to_i + limit}
28
+ WHERE rownum <= #{offset.expr.to_i + limit}
29
29
  )
30
30
  WHERE #{visit offset}
31
31
  eosql
@@ -58,7 +58,7 @@ module Arel
58
58
  end
59
59
 
60
60
  def visit_Arel_Nodes_Offset o
61
- "raw_rnum_ > #{visit o.value}"
61
+ "raw_rnum_ > #{visit o.expr}"
62
62
  end
63
63
 
64
64
  def visit_Arel_Nodes_Except o
@@ -6,25 +6,6 @@ module Arel
6
6
  visit o.expr
7
7
  end
8
8
 
9
- def visit_Arel_Nodes_SelectStatement o
10
- if !o.orders.empty? && using_distinct_on?(o)
11
- subquery = o.dup
12
- subquery.orders = []
13
- subquery.limit = nil
14
- subquery.offset = nil
15
-
16
- sql = super(subquery)
17
- [
18
- "SELECT * FROM (#{sql}) AS id_list",
19
- "ORDER BY #{aliased_orders(o.orders).join(', ')}",
20
- (visit(o.limit) if o.limit),
21
- (visit(o.offset) if o.offset),
22
- ].compact.join ' '
23
- else
24
- super
25
- end
26
- end
27
-
28
9
  def visit_Arel_Nodes_Matches o
29
10
  "#{visit o.left} ILIKE #{visit o.right}"
30
11
  end
@@ -33,25 +14,8 @@ module Arel
33
14
  "#{visit o.left} NOT ILIKE #{visit o.right}"
34
15
  end
35
16
 
36
- def using_distinct_on?(o)
37
- o.cores.any? do |core|
38
- core.projections.any? do |projection|
39
- /DISTINCT ON/ === projection
40
- end
41
- end
42
- end
43
-
44
- def aliased_orders orders
45
- #orders = o.orders.map { |x| visit x }.join(', ').split(',')
46
- list = []
47
- orders.each_with_index do |o,i|
48
- list <<
49
- [
50
- "id_list.alias_#{i}",
51
- (o.index(/desc/i) && 'DESC')
52
- ].compact.join(' ')
53
- end
54
- list
17
+ def visit_Arel_Nodes_DistinctOn o
18
+ "DISTINCT ON ( #{visit o.expr} )"
55
19
  end
56
20
  end
57
21
  end
@@ -7,13 +7,16 @@ module Arel
7
7
  def initialize engine
8
8
  @engine = engine
9
9
  @connection = nil
10
+ @pool = nil
11
+ @last_column = nil
10
12
  @quoted_tables = {}
11
13
  @quoted_columns = {}
12
14
  end
13
15
 
14
16
  def accept object
15
- self.last_column = nil
16
- @engine.connection_pool.with_connection do |conn|
17
+ @last_column = nil
18
+ @pool = @engine.connection_pool
19
+ @pool.with_connection do |conn|
17
20
  @connection = conn
18
21
  super
19
22
  end
@@ -27,24 +30,39 @@ module Arel
27
30
  ].compact.join ' '
28
31
  end
29
32
 
33
+ # FIXME: we should probably have a 2-pass visitor for this
34
+ def build_subselect key, o
35
+ stmt = Nodes::SelectStatement.new
36
+ core = stmt.cores.first
37
+ core.froms = o.relation
38
+ core.wheres = o.wheres
39
+ core.projections = [key]
40
+ stmt.limit = o.limit
41
+ stmt.orders = o.orders
42
+ stmt
43
+ end
44
+
30
45
  def visit_Arel_Nodes_UpdateStatement o
31
46
  if o.orders.empty? && o.limit.nil?
32
47
  wheres = o.wheres
33
48
  else
34
- stmt = Nodes::SelectStatement.new
35
- core = stmt.cores.first
36
- core.froms = o.relation
37
- core.projections = [o.relation.primary_key]
38
- stmt.limit = o.limit
39
- stmt.orders = o.orders
40
-
41
- wheres = [Nodes::In.new(o.relation.primary_key, [stmt])]
49
+ key = o.key
50
+ unless key
51
+ warn(<<-eowarn) if $VERBOSE
52
+ (#{caller.first}) Using UpdateManager without setting UpdateManager#key is
53
+ deprecated and support will be removed in ARel 3.0.0. Please set the primary
54
+ key on UpdateManager using UpdateManager#key=
55
+ eowarn
56
+ key = o.relation.primary_key
57
+ end
58
+
59
+ wheres = [Nodes::In.new(key, [build_subselect(key, o)])]
42
60
  end
43
61
 
44
62
  [
45
63
  "UPDATE #{visit o.relation}",
46
64
  ("SET #{o.values.map { |value| visit value }.join ', '}" unless o.values.empty?),
47
- ("WHERE #{wheres.map { |x| visit x }.join ' AND '}" unless wheres.empty?)
65
+ ("WHERE #{wheres.map { |x| visit x }.join ' AND '}" unless wheres.empty?),
48
66
  ].compact.join ' '
49
67
  end
50
68
 
@@ -53,26 +71,48 @@ module Arel
53
71
  "INSERT INTO #{visit o.relation}",
54
72
 
55
73
  ("(#{o.columns.map { |x|
56
- quote_column_name x.name
57
- }.join ', '})" unless o.columns.empty?),
74
+ quote_column_name x.name
75
+ }.join ', '})" unless o.columns.empty?),
58
76
 
59
77
  (visit o.values if o.values),
60
78
  ].compact.join ' '
61
79
  end
62
80
 
63
81
  def visit_Arel_Nodes_Exists o
64
- "EXISTS (#{visit o.select_stmt})#{
82
+ "EXISTS (#{visit o.expressions})#{
65
83
  o.alias ? " AS #{visit o.alias}" : ''}"
66
84
  end
67
85
 
86
+ def table_exists? name
87
+ @pool.table_exists? name
88
+ end
89
+
90
+ def column_for attr
91
+ name = attr.name.to_s
92
+ table = attr.relation.table_name
93
+
94
+ return nil unless table_exists? table
95
+
96
+ column_cache[table][name]
97
+ end
98
+
99
+ def column_cache
100
+ @pool.columns_hash
101
+ end
102
+
68
103
  def visit_Arel_Nodes_Values o
69
- "VALUES (#{o.expressions.zip(o.columns).map { |value, column|
70
- quote(value, column && column.column)
104
+ "VALUES (#{o.expressions.zip(o.columns).map { |value, attr|
105
+ if Nodes::SqlLiteral === value
106
+ visit_Arel_Nodes_SqlLiteral value
107
+ else
108
+ quote(value, attr && column_for(attr))
109
+ end
71
110
  }.join ', '})"
72
111
  end
73
112
 
74
113
  def visit_Arel_Nodes_SelectStatement o
75
114
  [
115
+ (visit(o.with) if o.with),
76
116
  o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join,
77
117
  ("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
78
118
  (visit(o.limit) if o.limit),
@@ -85,14 +125,51 @@ module Arel
85
125
  [
86
126
  "SELECT",
87
127
  (visit(o.top) if o.top),
88
- "#{o.projections.map { |x| visit x }.join ', '}",
89
- ("FROM #{visit o.froms}" if o.froms),
128
+ (visit(o.set_quantifier) if o.set_quantifier),
129
+ ("#{o.projections.map { |x| visit x }.join ', '}" unless o.projections.empty?),
130
+ (visit(o.source) if o.source),
90
131
  ("WHERE #{o.wheres.map { |x| visit x }.join ' AND ' }" unless o.wheres.empty?),
91
132
  ("GROUP BY #{o.groups.map { |x| visit x }.join ', ' }" unless o.groups.empty?),
92
133
  (visit(o.having) if o.having),
93
134
  ].compact.join ' '
94
135
  end
95
136
 
137
+ def visit_Arel_Nodes_Bin o
138
+ visit o.expr
139
+ end
140
+
141
+ def visit_Arel_Nodes_Distinct o
142
+ 'DISTINCT'
143
+ end
144
+
145
+ def visit_Arel_Nodes_DistinctOn o
146
+ raise NotImplementedError, 'DISTINCT ON not implemented for this db'
147
+ end
148
+
149
+ def visit_Arel_Nodes_With o
150
+ "WITH #{o.children.map { |x| visit x }.join(', ')}"
151
+ end
152
+
153
+ def visit_Arel_Nodes_WithRecursive o
154
+ "WITH RECURSIVE #{o.children.map { |x| visit x }.join(', ')}"
155
+ end
156
+
157
+ def visit_Arel_Nodes_Union o
158
+ "( #{visit o.left} UNION #{visit o.right} )"
159
+ end
160
+
161
+ def visit_Arel_Nodes_UnionAll o
162
+ "( #{visit o.left} UNION ALL #{visit o.right} )"
163
+ end
164
+
165
+ def visit_Arel_Nodes_Intersect o
166
+ "( #{visit o.left} INTERSECT #{visit o.right} )"
167
+ end
168
+
169
+ def visit_Arel_Nodes_Except o
170
+ "( #{visit o.left} EXCEPT #{visit o.right} )"
171
+ end
172
+
96
173
  def visit_Arel_Nodes_Having o
97
174
  "HAVING #{visit o.expr}"
98
175
  end
@@ -127,6 +204,12 @@ module Arel
127
204
  visit o.expr
128
205
  end
129
206
 
207
+ def visit_Arel_Nodes_NamedFunction o
208
+ "#{o.name}(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
209
+ visit x
210
+ }.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
211
+ end
212
+
130
213
  def visit_Arel_Nodes_Count o
131
214
  "COUNT(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
132
215
  visit x
@@ -185,16 +268,26 @@ module Arel
185
268
  "#{visit o.left} NOT LIKE #{visit o.right}"
186
269
  end
187
270
 
271
+ def visit_Arel_Nodes_JoinSource o
272
+ return unless o.left || !o.right.empty?
273
+
274
+ [
275
+ "FROM",
276
+ (visit(o.left) if o.left),
277
+ o.right.map { |j| visit j }.join(' ')
278
+ ].compact.join ' '
279
+ end
280
+
188
281
  def visit_Arel_Nodes_StringJoin o
189
- "#{visit o.left} #{visit o.right}"
282
+ visit o.left
190
283
  end
191
284
 
192
285
  def visit_Arel_Nodes_OuterJoin o
193
- "#{visit o.left} LEFT OUTER JOIN #{visit o.right} #{visit o.constraint}"
286
+ "LEFT OUTER JOIN #{visit o.left} #{visit o.right}"
194
287
  end
195
288
 
196
289
  def visit_Arel_Nodes_InnerJoin o
197
- "#{visit o.left} INNER JOIN #{visit o.right} #{visit o.constraint if o.constraint}"
290
+ "INNER JOIN #{visit o.left} #{visit o.right if o.right}"
198
291
  end
199
292
 
200
293
  def visit_Arel_Nodes_On o
@@ -205,22 +298,6 @@ module Arel
205
298
  "NOT (#{visit o.expr})"
206
299
  end
207
300
 
208
- def visit_Arel_Nodes_Union o
209
- "( #{visit o.left} UNION #{visit o.right} )"
210
- end
211
-
212
- def visit_Arel_Nodes_UnionAll o
213
- "( #{visit o.left} UNION ALL #{visit o.right} )"
214
- end
215
-
216
- def visit_Arel_Nodes_Intersect o
217
- "( #{visit o.left} INTERSECT #{visit o.right} )"
218
- end
219
-
220
- def visit_Arel_Nodes_Except o
221
- "( #{visit o.left} EXCEPT #{visit o.right} )"
222
- end
223
-
224
301
  def visit_Arel_Table o
225
302
  if o.table_alias
226
303
  "#{quote_table_name o.name} #{quote_table_name o.table_alias}"
@@ -230,15 +307,15 @@ module Arel
230
307
  end
231
308
 
232
309
  def visit_Arel_Nodes_In o
233
- "#{visit o.left} IN (#{visit o.right})"
310
+ "#{visit o.left} IN (#{visit o.right})"
234
311
  end
235
312
 
236
313
  def visit_Arel_Nodes_NotIn o
237
- "#{visit o.left} NOT IN (#{visit o.right})"
314
+ "#{visit o.left} NOT IN (#{visit o.right})"
238
315
  end
239
316
 
240
317
  def visit_Arel_Nodes_And o
241
- "#{visit o.left} AND #{visit o.right}"
318
+ o.children.map { |x| visit x }.join ' AND '
242
319
  end
243
320
 
244
321
  def visit_Arel_Nodes_Or o
@@ -246,7 +323,7 @@ module Arel
246
323
  end
247
324
 
248
325
  def visit_Arel_Nodes_Assignment o
249
- right = quote(o.right, o.left.column)
326
+ right = quote(o.right, column_for(o.left))
250
327
  "#{visit o.left} = #{right}"
251
328
  end
252
329
 
@@ -279,7 +356,7 @@ module Arel
279
356
  end
280
357
 
281
358
  def visit_Arel_Attributes_Attribute o
282
- self.last_column = o.column
359
+ @last_column = column_for o
283
360
  join_name = o.relation.table_alias || o.relation.name
284
361
  "#{quote_table_name join_name}.#{quote_column_name o.name}"
285
362
  end
@@ -290,34 +367,38 @@ module Arel
290
367
  alias :visit_Arel_Attributes_Time :visit_Arel_Attributes_Attribute
291
368
  alias :visit_Arel_Attributes_Boolean :visit_Arel_Attributes_Attribute
292
369
 
293
- def visit_Fixnum o; o end
294
- alias :visit_Arel_Nodes_SqlLiteral :visit_Fixnum
295
- alias :visit_Arel_SqlLiteral :visit_Fixnum # This is deprecated
296
- alias :visit_Bignum :visit_Fixnum
370
+ def literal o; o end
297
371
 
298
- def visit_String o; quote(o, last_column) end
372
+ alias :visit_Arel_Nodes_SqlLiteral :literal
373
+ alias :visit_Arel_SqlLiteral :literal # This is deprecated
374
+ alias :visit_Bignum :literal
375
+ alias :visit_Fixnum :literal
299
376
 
300
- def last_column
301
- Thread.current[:arel_visitors_to_sql_last_column]
302
- end
377
+ def quoted o; quote(o, @last_column) end
378
+
379
+ alias :visit_ActiveSupport_Multibyte_Chars :quoted
380
+ alias :visit_ActiveSupport_StringInquirer :quoted
381
+ alias :visit_BigDecimal :quoted
382
+ alias :visit_Class :quoted
383
+ alias :visit_Date :quoted
384
+ alias :visit_DateTime :quoted
385
+ alias :visit_FalseClass :quoted
386
+ alias :visit_Float :quoted
387
+ alias :visit_Hash :quoted
388
+ alias :visit_NilClass :quoted
389
+ alias :visit_String :quoted
390
+ alias :visit_Symbol :quoted
391
+ alias :visit_Time :quoted
392
+ alias :visit_TrueClass :quoted
303
393
 
304
- def last_column= col
305
- Thread.current[:arel_visitors_to_sql_last_column] = col
394
+ def visit_Arel_Nodes_InfixOperation o
395
+ "#{visit o.left} #{o.operator} #{visit o.right}"
306
396
  end
307
397
 
308
- alias :visit_ActiveSupport_Multibyte_Chars :visit_String
309
- alias :visit_BigDecimal :visit_String
310
- alias :visit_Date :visit_String
311
- alias :visit_DateTime :visit_String
312
- alias :visit_FalseClass :visit_String
313
- alias :visit_Float :visit_String
314
- alias :visit_Hash :visit_String
315
- alias :visit_Symbol :visit_String
316
- alias :visit_Time :visit_String
317
- alias :visit_TrueClass :visit_String
318
- alias :visit_NilClass :visit_String
319
- alias :visit_ActiveSupport_StringInquirer :visit_String
320
- alias :visit_Class :visit_String
398
+ alias :visit_Arel_Nodes_Addition :visit_Arel_Nodes_InfixOperation
399
+ alias :visit_Arel_Nodes_Subtraction :visit_Arel_Nodes_InfixOperation
400
+ alias :visit_Arel_Nodes_Multiplication :visit_Arel_Nodes_InfixOperation
401
+ alias :visit_Arel_Nodes_Division :visit_Arel_Nodes_InfixOperation
321
402
 
322
403
  def visit_Array o
323
404
  o.empty? ? 'NULL' : o.map { |x| visit x }.join(', ')
@@ -328,11 +409,11 @@ module Arel
328
409
  end
329
410
 
330
411
  def quote_table_name name
331
- @quoted_tables[name] ||= @connection.quote_table_name(name)
412
+ @quoted_tables[name] ||= Arel::Nodes::SqlLiteral === name ? name : @connection.quote_table_name(name)
332
413
  end
333
414
 
334
415
  def quote_column_name name
335
- @quoted_columns[name] ||= @connection.quote_column_name(name)
416
+ @quoted_columns[name] ||= Arel::Nodes::SqlLiteral === name ? name : @connection.quote_column_name(name)
336
417
  end
337
418
  end
338
419
  end