arel 2.0.10 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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