arel 0.1.0 → 0.2.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 (116) hide show
  1. data/.gitmodules +3 -0
  2. data/History.txt +11 -0
  3. data/README.markdown +26 -26
  4. data/Rakefile +34 -46
  5. data/Thorfile +124 -0
  6. data/arel.gemspec +216 -201
  7. data/lib/arel/algebra/{primitives/attribute.rb → attribute.rb} +7 -7
  8. data/lib/arel/algebra/{extensions → core_extensions}/class.rb +2 -2
  9. data/lib/arel/algebra/{extensions → core_extensions}/hash.rb +1 -1
  10. data/lib/arel/algebra/{extensions → core_extensions}/object.rb +1 -1
  11. data/lib/arel/algebra/{extensions → core_extensions}/symbol.rb +1 -1
  12. data/lib/arel/algebra/core_extensions.rb +4 -0
  13. data/lib/arel/algebra/{primitives/ordering.rb → ordering.rb} +1 -1
  14. data/lib/arel/algebra/predicates.rb +32 -30
  15. data/lib/arel/algebra/relations/operations/from.rb +6 -0
  16. data/lib/arel/algebra/relations/operations/having.rb +13 -0
  17. data/lib/arel/algebra/relations/operations/join.rb +4 -0
  18. data/lib/arel/algebra/relations/operations/lock.rb +12 -0
  19. data/lib/arel/algebra/relations/operations/project.rb +1 -1
  20. data/lib/arel/algebra/relations/relation.rb +9 -2
  21. data/lib/arel/algebra/relations/utilities/compound.rb +2 -2
  22. data/lib/arel/algebra/relations.rb +3 -0
  23. data/lib/arel/algebra.rb +7 -2
  24. data/lib/arel/engines/memory/predicates.rb +26 -24
  25. data/lib/arel/engines/memory/relations/operations.rb +6 -0
  26. data/lib/arel/engines/sql/christener.rb +2 -1
  27. data/lib/arel/engines/sql/{extensions → core_extensions}/array.rb +6 -2
  28. data/lib/arel/engines/sql/{extensions → core_extensions}/nil_class.rb +1 -1
  29. data/lib/arel/engines/sql/{extensions → core_extensions}/object.rb +1 -1
  30. data/lib/arel/engines/sql/{extensions → core_extensions}/range.rb +1 -1
  31. data/lib/arel/engines/sql/core_extensions.rb +4 -0
  32. data/lib/arel/engines/sql/engine.rb +4 -0
  33. data/lib/arel/engines/sql/formatters.rb +12 -3
  34. data/lib/arel/engines/sql/predicates.rb +37 -35
  35. data/lib/arel/engines/sql/primitives.rb +10 -2
  36. data/lib/arel/engines/sql/relations/operations/join.rb +2 -2
  37. data/lib/arel/engines/sql/relations/relation.rb +46 -10
  38. data/lib/arel/engines/sql/relations/table.rb +18 -5
  39. data/lib/arel/engines/sql/relations/utilities/compound.rb +1 -1
  40. data/lib/arel/engines/sql/relations/writes.rb +58 -11
  41. data/lib/arel/engines/sql.rb +1 -1
  42. data/lib/arel/session.rb +9 -6
  43. data/lib/arel.rb +9 -8
  44. data/spec/arel/algebra/unit/predicates/binary_spec.rb +23 -21
  45. data/spec/arel/algebra/unit/predicates/equality_spec.rb +20 -18
  46. data/spec/arel/algebra/unit/predicates/in_spec.rb +7 -5
  47. data/spec/arel/algebra/unit/primitives/attribute_spec.rb +10 -12
  48. data/spec/arel/algebra/unit/primitives/expression_spec.rb +1 -1
  49. data/spec/arel/algebra/unit/primitives/value_spec.rb +1 -1
  50. data/spec/arel/algebra/unit/relations/alias_spec.rb +2 -2
  51. data/spec/arel/algebra/unit/relations/delete_spec.rb +1 -1
  52. data/spec/arel/algebra/unit/relations/group_spec.rb +1 -1
  53. data/spec/arel/algebra/unit/relations/insert_spec.rb +1 -1
  54. data/spec/arel/algebra/unit/relations/join_spec.rb +1 -1
  55. data/spec/arel/algebra/unit/relations/order_spec.rb +2 -2
  56. data/spec/arel/algebra/unit/relations/project_spec.rb +1 -1
  57. data/spec/arel/algebra/unit/relations/relation_spec.rb +9 -9
  58. data/spec/arel/algebra/unit/relations/skip_spec.rb +1 -1
  59. data/spec/arel/algebra/unit/relations/table_spec.rb +2 -2
  60. data/spec/arel/algebra/unit/relations/take_spec.rb +1 -1
  61. data/spec/arel/algebra/unit/relations/update_spec.rb +1 -1
  62. data/spec/arel/algebra/unit/relations/where_spec.rb +1 -1
  63. data/spec/arel/algebra/unit/session/session_spec.rb +6 -6
  64. data/spec/arel/engines/memory/integration/joins/cross_engine_spec.rb +3 -4
  65. data/spec/arel/engines/memory/unit/relations/array_spec.rb +1 -1
  66. data/spec/arel/engines/memory/unit/relations/insert_spec.rb +8 -8
  67. data/spec/arel/engines/memory/unit/relations/join_spec.rb +1 -1
  68. data/spec/arel/engines/memory/unit/relations/order_spec.rb +1 -1
  69. data/spec/arel/engines/memory/unit/relations/project_spec.rb +1 -1
  70. data/spec/arel/engines/memory/unit/relations/skip_spec.rb +1 -1
  71. data/spec/arel/engines/memory/unit/relations/take_spec.rb +1 -1
  72. data/spec/arel/engines/memory/unit/relations/where_spec.rb +1 -1
  73. data/spec/arel/engines/sql/integration/joins/with_adjacency_spec.rb +1 -1
  74. data/spec/arel/engines/sql/integration/joins/with_aggregations_spec.rb +1 -1
  75. data/spec/arel/engines/sql/integration/joins/with_compounds_spec.rb +1 -1
  76. data/spec/arel/engines/sql/unit/engine_spec.rb +1 -1
  77. data/spec/arel/engines/sql/unit/predicates/binary_spec.rb +73 -70
  78. data/spec/arel/engines/sql/unit/predicates/equality_spec.rb +45 -28
  79. data/spec/arel/engines/sql/unit/predicates/in_spec.rb +75 -54
  80. data/spec/arel/engines/sql/unit/predicates/predicates_spec.rb +48 -46
  81. data/spec/arel/engines/sql/unit/primitives/attribute_spec.rb +1 -1
  82. data/spec/arel/engines/sql/unit/primitives/expression_spec.rb +1 -1
  83. data/spec/arel/engines/sql/unit/primitives/literal_spec.rb +13 -1
  84. data/spec/arel/engines/sql/unit/primitives/value_spec.rb +3 -3
  85. data/spec/arel/engines/sql/unit/relations/alias_spec.rb +1 -1
  86. data/spec/arel/engines/sql/unit/relations/delete_spec.rb +1 -1
  87. data/spec/arel/engines/sql/unit/relations/from_spec.rb +50 -0
  88. data/spec/arel/engines/sql/unit/relations/group_spec.rb +1 -1
  89. data/spec/arel/engines/sql/unit/relations/having_spec.rb +36 -0
  90. data/spec/arel/engines/sql/unit/relations/insert_spec.rb +1 -1
  91. data/spec/arel/engines/sql/unit/relations/join_spec.rb +85 -4
  92. data/spec/arel/engines/sql/unit/relations/lock_spec.rb +61 -0
  93. data/spec/arel/engines/sql/unit/relations/order_spec.rb +1 -1
  94. data/spec/arel/engines/sql/unit/relations/project_spec.rb +1 -1
  95. data/spec/arel/engines/sql/unit/relations/skip_spec.rb +1 -1
  96. data/spec/arel/engines/sql/unit/relations/table_spec.rb +42 -3
  97. data/spec/arel/engines/sql/unit/relations/take_spec.rb +1 -1
  98. data/spec/arel/engines/sql/unit/relations/update_spec.rb +13 -7
  99. data/spec/arel/engines/sql/unit/relations/where_spec.rb +1 -1
  100. data/spec/connections/mysql_connection.rb +3 -3
  101. data/spec/connections/postgresql_connection.rb +2 -2
  102. data/spec/connections/sqlite3_connection.rb +3 -2
  103. data/spec/doubles/hash.rb +4 -0
  104. data/spec/schemas/mysql_schema.rb +7 -0
  105. data/spec/schemas/postgresql_schema.rb +7 -0
  106. data/spec/schemas/sqlite3_schema.rb +7 -0
  107. data/spec/spec.opts +3 -0
  108. data/spec/spec_helper.rb +9 -1
  109. metadata +50 -25
  110. data/VERSION +0 -1
  111. data/lib/arel/.DS_Store +0 -0
  112. data/lib/arel/algebra/extensions.rb +0 -4
  113. data/lib/arel/algebra/primitives.rb +0 -5
  114. data/lib/arel/engines/sql/extensions.rb +0 -4
  115. /data/lib/arel/algebra/{primitives/expression.rb → expression.rb} +0 -0
  116. /data/lib/arel/algebra/{primitives/value.rb → value.rb} +0 -0
@@ -1,41 +1,43 @@
1
1
  module Arel
2
- class Predicate
3
- def or(other_predicate)
4
- Or.new(self, other_predicate)
5
- end
2
+ module Predicates
3
+ class Predicate
4
+ def or(other_predicate)
5
+ Or.new(self, other_predicate)
6
+ end
6
7
 
7
- def and(other_predicate)
8
- And.new(self, other_predicate)
8
+ def and(other_predicate)
9
+ And.new(self, other_predicate)
10
+ end
9
11
  end
10
- end
11
12
 
12
- class Binary < Predicate
13
- attributes :operand1, :operand2
14
- deriving :initialize
13
+ class Binary < Predicate
14
+ attributes :operand1, :operand2
15
+ deriving :initialize
15
16
 
16
- def ==(other)
17
- self.class === other and
18
- @operand1 == other.operand1 and
19
- @operand2 == other.operand2
20
- end
17
+ def ==(other)
18
+ self.class === other and
19
+ @operand1 == other.operand1 and
20
+ @operand2 == other.operand2
21
+ end
21
22
 
22
- def bind(relation)
23
- self.class.new(operand1.find_correlate_in(relation), operand2.find_correlate_in(relation))
23
+ def bind(relation)
24
+ self.class.new(operand1.find_correlate_in(relation), operand2.find_correlate_in(relation))
25
+ end
24
26
  end
25
- end
26
27
 
27
- class Equality < Binary
28
- def ==(other)
29
- Equality === other and
30
- ((operand1 == other.operand1 and operand2 == other.operand2) or
31
- (operand1 == other.operand2 and operand2 == other.operand1))
28
+ class Equality < Binary
29
+ def ==(other)
30
+ Equality === other and
31
+ ((operand1 == other.operand1 and operand2 == other.operand2) or
32
+ (operand1 == other.operand2 and operand2 == other.operand1))
33
+ end
32
34
  end
33
- end
34
35
 
35
- class GreaterThanOrEqualTo < Binary; end
36
- class GreaterThan < Binary; end
37
- class LessThanOrEqualTo < Binary; end
38
- class LessThan < Binary; end
39
- class Match < Binary; end
40
- class In < Binary; end
36
+ class GreaterThanOrEqualTo < Binary; end
37
+ class GreaterThan < Binary; end
38
+ class LessThanOrEqualTo < Binary; end
39
+ class LessThan < Binary; end
40
+ class Match < Binary; end
41
+ class In < Binary; end
42
+ end
41
43
  end
@@ -0,0 +1,6 @@
1
+ module Arel
2
+ class From < Compound
3
+ attributes :relation, :sources
4
+ deriving :initialize, :==
5
+ end
6
+ end
@@ -0,0 +1,13 @@
1
+ module Arel
2
+ class Having < Compound
3
+ attributes :relation, :havings
4
+ deriving :==
5
+
6
+ def initialize(relation, *havings, &block)
7
+ @relation = relation
8
+ @havings = (havings + arguments_from_block(relation, &block)) \
9
+ .collect { |g| g.bind(relation) }
10
+ end
11
+ end
12
+ end
13
+
@@ -47,6 +47,10 @@ module Arel
47
47
  class InnerJoin < Join; end
48
48
  class OuterJoin < Join; end
49
49
  class StringJoin < Join
50
+ def externalizable?
51
+ relation1.externalizable?
52
+ end
53
+
50
54
  def attributes
51
55
  relation1.externalize.attributes
52
56
  end
@@ -0,0 +1,12 @@
1
+ module Arel
2
+ class Lock < Compound
3
+ attributes :relation, :locked
4
+ deriving :initialize, :==
5
+
6
+ def initialize(relation, locked, &block)
7
+ @relation = relation
8
+ @locked = locked.blank? ? " FOR UPDATE" : locked
9
+ end
10
+ end
11
+ end
12
+
@@ -14,7 +14,7 @@ module Arel
14
14
  end
15
15
 
16
16
  def externalizable?
17
- attributes.any?(&:aggregation?) or relation.externalizable?
17
+ attributes.any? { |a| a.respond_to?(:aggregation?) && a.aggregation? } || relation.externalizable?
18
18
  end
19
19
  end
20
20
  end
@@ -43,7 +43,7 @@ module Arel
43
43
  join(other_relation, OuterJoin)
44
44
  end
45
45
 
46
- [:where, :project, :order, :take, :skip, :group].each do |operation_name|
46
+ [:where, :project, :order, :take, :skip, :group, :from, :having].each do |operation_name|
47
47
  class_eval <<-OPERATION, __FILE__, __LINE__
48
48
  def #{operation_name}(*arguments, &block)
49
49
  arguments.all?(&:blank?) && !block_given?? self : #{operation_name.to_s.classify}.new(self, *arguments, &block)
@@ -51,6 +51,10 @@ module Arel
51
51
  OPERATION
52
52
  end
53
53
 
54
+ def lock(locking = nil)
55
+ Lock.new(self, locking)
56
+ end
57
+
54
58
  def alias
55
59
  Alias.new(self)
56
60
  end
@@ -110,7 +114,7 @@ module Arel
110
114
  private
111
115
  def matching_attributes(attribute)
112
116
  (@matching_attributes ||= attributes.inject({}) do |hash, a|
113
- (hash[a.root] ||= []) << a
117
+ (hash[a.is_a?(Value) ? a.value : a.root] ||= []) << a
114
118
  hash
115
119
  end)[attribute.root] || []
116
120
  end
@@ -127,9 +131,12 @@ module Arel
127
131
  def orders; [] end
128
132
  def inserts; [] end
129
133
  def groupings; [] end
134
+ def havings; [] end
130
135
  def joins(formatter = nil); nil end # FIXME
131
136
  def taken; nil end
132
137
  def skipped; nil end
138
+ def sources; [] end
139
+ def locked; [] end
133
140
  end
134
141
  include DefaultOperations
135
142
  end
@@ -2,10 +2,10 @@ module Arel
2
2
  class Compound < Relation
3
3
  attr_reader :relation
4
4
  delegate :joins, :join?, :inserts, :taken, :skipped, :name, :externalizable?,
5
- :column_for, :engine,
5
+ :column_for, :engine, :sources, :locked, :table_alias,
6
6
  :to => :relation
7
7
 
8
- [:attributes, :wheres, :groupings, :orders].each do |operation_name|
8
+ [:attributes, :wheres, :groupings, :orders, :havings].each do |operation_name|
9
9
  class_eval <<-OPERATION, __FILE__, __LINE__
10
10
  def #{operation_name}
11
11
  @#{operation_name} ||= relation.#{operation_name}.collect { |o| o.bind(self) }
@@ -5,10 +5,13 @@ require 'arel/algebra/relations/utilities/externalization'
5
5
  require 'arel/algebra/relations/row'
6
6
  require 'arel/algebra/relations/writes'
7
7
  require 'arel/algebra/relations/operations/alias'
8
+ require 'arel/algebra/relations/operations/from'
8
9
  require 'arel/algebra/relations/operations/group'
10
+ require 'arel/algebra/relations/operations/having'
9
11
  require 'arel/algebra/relations/operations/join'
10
12
  require 'arel/algebra/relations/operations/order'
11
13
  require 'arel/algebra/relations/operations/project'
12
14
  require 'arel/algebra/relations/operations/where'
13
15
  require 'arel/algebra/relations/operations/skip'
14
16
  require 'arel/algebra/relations/operations/take'
17
+ require 'arel/algebra/relations/operations/lock'
data/lib/arel/algebra.rb CHANGED
@@ -1,4 +1,9 @@
1
- require 'arel/algebra/extensions'
1
+ require 'arel/algebra/core_extensions'
2
+
3
+ require 'arel/algebra/attribute'
4
+ require 'arel/algebra/expression'
5
+ require 'arel/algebra/ordering'
2
6
  require 'arel/algebra/predicates'
3
7
  require 'arel/algebra/relations'
4
- require 'arel/algebra/primitives'
8
+ require 'arel/algebra/value'
9
+
@@ -1,35 +1,37 @@
1
1
  module Arel
2
- class Binary < Predicate
3
- def eval(row)
4
- operand1.eval(row).send(operator, operand2.eval(row))
2
+ module Predicates
3
+ class Binary < Predicate
4
+ def eval(row)
5
+ operand1.eval(row).send(operator, operand2.eval(row))
6
+ end
5
7
  end
6
- end
7
8
 
8
- class Equality < Binary
9
- def operator; :== end
10
- end
9
+ class Equality < Binary
10
+ def operator; :== end
11
+ end
11
12
 
12
- class GreaterThanOrEqualTo < Binary
13
- def operator; :>= end
14
- end
13
+ class GreaterThanOrEqualTo < Binary
14
+ def operator; :>= end
15
+ end
15
16
 
16
- class GreaterThan < Binary
17
- def operator; :> end
18
- end
17
+ class GreaterThan < Binary
18
+ def operator; :> end
19
+ end
19
20
 
20
- class LessThanOrEqualTo < Binary
21
- def operator; :<= end
22
- end
21
+ class LessThanOrEqualTo < Binary
22
+ def operator; :<= end
23
+ end
23
24
 
24
- class LessThan < Binary
25
- def operator; :< end
26
- end
25
+ class LessThan < Binary
26
+ def operator; :< end
27
+ end
27
28
 
28
- class Match < Binary
29
- def operator; :=~ end
30
- end
29
+ class Match < Binary
30
+ def operator; :=~ end
31
+ end
31
32
 
32
- class In < Binary
33
- def operator; :include? end
33
+ class In < Binary
34
+ def operator; :include? end
35
+ end
34
36
  end
35
37
  end
@@ -32,6 +32,12 @@ module Arel
32
32
  end
33
33
  end
34
34
 
35
+ class From < Compound
36
+ def eval
37
+ unoperated_rows[sources..-1]
38
+ end
39
+ end
40
+
35
41
  class Group < Compound
36
42
  def eval
37
43
  raise NotImplementedError
@@ -4,7 +4,8 @@ module Arel
4
4
  def name_for(relation)
5
5
  @used_names ||= Hash.new(0)
6
6
  (@relation_names ||= Hash.new do |hash, relation|
7
- @used_names[name = relation.name] += 1
7
+ name = relation.table_alias ? relation.table_alias : relation.name
8
+ @used_names[name] += 1
8
9
  hash[relation] = name + (@used_names[name] > 1 ? "_#{@used_names[name]}" : '')
9
10
  end)[relation.table]
10
11
  end
@@ -2,13 +2,17 @@ module Arel
2
2
  module Sql
3
3
  module ArrayExtensions
4
4
  def to_sql(formatter = nil)
5
- "(" + collect { |e| e.to_sql(formatter) }.join(', ') + ")"
5
+ if any?
6
+ "(" + collect { |e| e.to_sql(formatter) }.join(', ') + ")"
7
+ else
8
+ "(NULL)"
9
+ end
6
10
  end
7
11
 
8
12
  def inclusion_predicate_sql
9
13
  "IN"
10
14
  end
11
-
15
+
12
16
  Array.send(:include, self)
13
17
  end
14
18
  end
@@ -4,7 +4,7 @@ module Arel
4
4
  def equality_predicate_sql
5
5
  'IS'
6
6
  end
7
-
7
+
8
8
  NilClass.send(:include, self)
9
9
  end
10
10
  end
@@ -8,7 +8,7 @@ module Arel
8
8
  def equality_predicate_sql
9
9
  '='
10
10
  end
11
-
11
+
12
12
  Object.send(:include, self)
13
13
  end
14
14
  end
@@ -8,7 +8,7 @@ module Arel
8
8
  def inclusion_predicate_sql
9
9
  "BETWEEN"
10
10
  end
11
-
11
+
12
12
  Range.send(:include, self)
13
13
  end
14
14
  end
@@ -0,0 +1,4 @@
1
+ require 'arel/engines/sql/core_extensions/object'
2
+ require 'arel/engines/sql/core_extensions/array'
3
+ require 'arel/engines/sql/core_extensions/range'
4
+ require 'arel/engines/sql/core_extensions/nil_class'
@@ -9,6 +9,10 @@ module Arel
9
9
  @ar.connection
10
10
  end
11
11
 
12
+ def adapter_name
13
+ @adapter_name ||= connection.adapter_name
14
+ end
15
+
12
16
  def method_missing(method, *args, &block)
13
17
  @ar.connection.send(method, *args, &block)
14
18
  end
@@ -13,7 +13,6 @@ module Arel
13
13
 
14
14
  class SelectClause < Formatter
15
15
  def attribute(attribute)
16
- # FIXME this should check that the column exists
17
16
  "#{quote_table_name(name_for(attribute.original_relation))}.#{quote_column_name(attribute.name)}" +
18
17
  (attribute.alias ? " AS #{quote(attribute.alias.to_s)}" : "")
19
18
  end
@@ -58,6 +57,12 @@ module Arel
58
57
  end
59
58
  end
60
59
 
60
+ class HavingClause < PassThrough
61
+ def attribute(attribute)
62
+ attribute
63
+ end
64
+ end
65
+
61
66
  class WhereCondition < Formatter
62
67
  def attribute(attribute)
63
68
  "#{quote_table_name(name_for(attribute.original_relation))}.#{quote_column_name(attribute.name)}"
@@ -92,8 +97,12 @@ module Arel
92
97
  end
93
98
 
94
99
  def table(table)
95
- quote_table_name(table.name) +
96
- (table.name != name_for(table) ? " AS " + quote_table_name(name_for(table)) : '')
100
+ if table.name =~ /\s/
101
+ table.name
102
+ else
103
+ quote_table_name(table.name) +
104
+ (table.name != name_for(table) ? " AS " + quote_table_name(name_for(table)) : '')
105
+ end
97
106
  end
98
107
  end
99
108
 
@@ -1,51 +1,53 @@
1
1
  module Arel
2
- class Binary < Predicate
3
- def to_sql(formatter = nil)
4
- "#{operand1.to_sql} #{predicate_sql} #{operand1.format(operand2)}"
2
+ module Predicates
3
+ class Binary < Predicate
4
+ def to_sql(formatter = nil)
5
+ "#{operand1.to_sql} #{predicate_sql} #{operand1.format(operand2)}"
6
+ end
5
7
  end
6
- end
7
8
 
8
- class CompoundPredicate < Binary
9
- def to_sql(formatter = nil)
10
- "(#{operand1.to_sql(formatter)} #{predicate_sql} #{operand2.to_sql(formatter)})"
9
+ class CompoundPredicate < Binary
10
+ def to_sql(formatter = nil)
11
+ "(#{operand1.to_sql(formatter)} #{predicate_sql} #{operand2.to_sql(formatter)})"
12
+ end
11
13
  end
12
- end
13
14
 
14
- class Or < CompoundPredicate
15
- def predicate_sql; "OR" end
16
- end
15
+ class Or < CompoundPredicate
16
+ def predicate_sql; "OR" end
17
+ end
17
18
 
18
- class And < CompoundPredicate
19
- def predicate_sql; "AND" end
20
- end
19
+ class And < CompoundPredicate
20
+ def predicate_sql; "AND" end
21
+ end
21
22
 
22
- class Equality < Binary
23
- def predicate_sql
24
- operand2.equality_predicate_sql
23
+ class Equality < Binary
24
+ def predicate_sql
25
+ operand2.equality_predicate_sql
26
+ end
25
27
  end
26
- end
27
28
 
28
- class GreaterThanOrEqualTo < Binary
29
- def predicate_sql; '>=' end
30
- end
29
+ class GreaterThanOrEqualTo < Binary
30
+ def predicate_sql; '>=' end
31
+ end
31
32
 
32
- class GreaterThan < Binary
33
- def predicate_sql; '>' end
34
- end
33
+ class GreaterThan < Binary
34
+ def predicate_sql; '>' end
35
+ end
35
36
 
36
- class LessThanOrEqualTo < Binary
37
- def predicate_sql; '<=' end
38
- end
37
+ class LessThanOrEqualTo < Binary
38
+ def predicate_sql; '<=' end
39
+ end
39
40
 
40
- class LessThan < Binary
41
- def predicate_sql; '<' end
42
- end
41
+ class LessThan < Binary
42
+ def predicate_sql; '<' end
43
+ end
43
44
 
44
- class Match < Binary
45
- def predicate_sql; 'LIKE' end
46
- end
45
+ class Match < Binary
46
+ def predicate_sql; 'LIKE' end
47
+ end
47
48
 
48
- class In < Binary
49
- def predicate_sql; operand2.inclusion_predicate_sql end
49
+ class In < Binary
50
+ def predicate_sql; operand2.inclusion_predicate_sql end
51
+ end
50
52
  end
51
53
  end
@@ -3,10 +3,12 @@ module Arel
3
3
  def relation
4
4
  nil
5
5
  end
6
-
6
+
7
7
  def to_sql(formatter = nil)
8
8
  self
9
9
  end
10
+
11
+ include Attribute::Expressions
10
12
  end
11
13
 
12
14
  class Attribute
@@ -28,7 +30,13 @@ module Arel
28
30
  end
29
31
 
30
32
  class Value
31
- delegate :inclusion_predicate_sql, :equality_predicate_sql, :to => :value
33
+ def inclusion_predicate_sql
34
+ value.inclusion_predicate_sql
35
+ end
36
+
37
+ def equality_predicate_sql
38
+ value.equality_predicate_sql
39
+ end
32
40
 
33
41
  def to_sql(formatter = Sql::WhereCondition.new(relation))
34
42
  formatter.value value
@@ -26,8 +26,8 @@ module Arel
26
26
  end
27
27
 
28
28
  class StringJoin < Join
29
- def joins(_, __ = nil)
30
- relation2
29
+ def joins(environment, formatter = Sql::TableReference.new(environment))
30
+ [relation1.joins(environment), relation2].compact.join(" ")
31
31
  end
32
32
  end
33
33
  end
@@ -5,15 +5,40 @@ module Arel
5
5
  end
6
6
 
7
7
  def select_sql
8
- build_query \
9
- "SELECT #{select_clauses.join(', ')}",
10
- "FROM #{table_sql(Sql::TableReference.new(self))}",
11
- (joins(self) unless joins(self).blank? ),
12
- ("WHERE #{where_clauses.join("\n\tAND ")}" unless wheres.blank? ),
13
- ("GROUP BY #{group_clauses.join(', ')}" unless groupings.blank? ),
14
- ("ORDER BY #{order_clauses.join(', ')}" unless orders.blank? ),
15
- ("LIMIT #{taken}" unless taken.blank? ),
16
- ("OFFSET #{skipped}" unless skipped.blank? )
8
+ if engine.adapter_name == "PostgreSQL" && !orders.blank? && using_distinct_on?
9
+ # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
10
+ # by wrapping the +sql+ string as a sub-select and ordering in that query.
11
+ order = order_clauses.join(', ').split(',').map { |s| s.strip }.reject(&:blank?)
12
+ order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{'DESC' if s =~ /\bdesc$/i}" }.join(', ')
13
+
14
+ query = build_query \
15
+ "SELECT #{select_clauses.kind_of?(::Array) ? select_clauses.join("") : select_clauses.to_s}",
16
+ "FROM #{from_clauses}",
17
+ (joins(self) unless joins(self).blank? ),
18
+ ("WHERE #{where_clauses.join(" AND ")}" unless wheres.blank? ),
19
+ ("GROUP BY #{group_clauses.join(', ')}" unless groupings.blank? ),
20
+ ("HAVING #{having_clauses.join(', ')}" unless havings.blank? ),
21
+ ("#{locked}" unless locked.blank? )
22
+
23
+ build_query \
24
+ "SELECT * FROM (#{query}) AS id_list",
25
+ "ORDER BY #{order}",
26
+ ("LIMIT #{taken}" unless taken.blank? ),
27
+ ("OFFSET #{skipped}" unless skipped.blank? )
28
+
29
+ else
30
+ build_query \
31
+ "SELECT #{select_clauses.join(', ')}",
32
+ "FROM #{from_clauses}",
33
+ (joins(self) unless joins(self).blank? ),
34
+ ("WHERE #{where_clauses.join(" AND ")}" unless wheres.blank? ),
35
+ ("GROUP BY #{group_clauses.join(', ')}" unless groupings.blank? ),
36
+ ("HAVING #{having_clauses.join(', ')}" unless havings.blank? ),
37
+ ("ORDER BY #{order_clauses.join(', ')}" unless orders.blank? ),
38
+ ("LIMIT #{taken}" unless taken.blank? ),
39
+ ("OFFSET #{skipped}" unless skipped.blank? ),
40
+ ("#{locked}" unless engine.adapter_name =~ /SQLite/ || locked.blank?)
41
+ end
17
42
  end
18
43
 
19
44
  def inclusion_predicate_sql
@@ -27,7 +52,11 @@ module Arel
27
52
  protected
28
53
 
29
54
  def build_query(*parts)
30
- parts.compact.join("\n")
55
+ parts.compact.join(" ")
56
+ end
57
+
58
+ def from_clauses
59
+ sources.blank? ? table_sql(Sql::TableReference.new(self)) : sources
31
60
  end
32
61
 
33
62
  def select_clauses
@@ -42,9 +71,16 @@ module Arel
42
71
  groupings.collect { |g| g.to_sql(Sql::GroupClause.new(self)) }
43
72
  end
44
73
 
74
+ def having_clauses
75
+ havings.collect { |g| g.to_sql(Sql::HavingClause.new(self)) }
76
+ end
77
+
45
78
  def order_clauses
46
79
  orders.collect { |o| o.to_sql(Sql::OrderClause.new(self)) }
47
80
  end
48
81
 
82
+ def using_distinct_on?
83
+ select_clauses.any? { |x| x =~ /DISTINCT ON/ }
84
+ end
49
85
  end
50
86
  end
@@ -3,10 +3,22 @@ module Arel
3
3
  include Recursion::BaseCase
4
4
 
5
5
  cattr_accessor :engine
6
- attr_reader :name, :engine
6
+ attr_reader :name, :engine, :table_alias, :options
7
7
 
8
- def initialize(name, engine = Table.engine)
9
- @name, @engine = name.to_s, engine
8
+ def initialize(name, options = {})
9
+ @name = name.to_s
10
+
11
+ if options.is_a?(Hash)
12
+ @options = options
13
+ @engine = options[:engine] || Table.engine
14
+ @table_alias = options[:as].to_s if options[:as].present? && options[:as].to_s != @name
15
+ else
16
+ @engine = options # Table.new('foo', engine)
17
+ end
18
+ end
19
+
20
+ def as(table_alias)
21
+ Table.new(name, options.merge(:as => table_alias))
10
22
  end
11
23
 
12
24
  def attributes
@@ -40,8 +52,9 @@ module Arel
40
52
  end
41
53
 
42
54
  def ==(other)
43
- Table === other and
44
- name == other.name
55
+ Table === other and
56
+ name == other.name and
57
+ table_alias == other.table_alias
45
58
  end
46
59
  end
47
60
  end