square-arel 2.0.9.20110222133018

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 (125) hide show
  1. data/.autotest +26 -0
  2. data/History.txt +105 -0
  3. data/MIT-LICENSE.txt +20 -0
  4. data/Manifest.txt +124 -0
  5. data/README.markdown +94 -0
  6. data/Rakefile +20 -0
  7. data/lib/arel.rb +39 -0
  8. data/lib/arel/attributes.rb +20 -0
  9. data/lib/arel/attributes/attribute.rb +18 -0
  10. data/lib/arel/compatibility/wheres.rb +33 -0
  11. data/lib/arel/crud.rb +37 -0
  12. data/lib/arel/delete_manager.rb +18 -0
  13. data/lib/arel/deprecated.rb +4 -0
  14. data/lib/arel/expression.rb +4 -0
  15. data/lib/arel/expressions.rb +23 -0
  16. data/lib/arel/insert_manager.rb +34 -0
  17. data/lib/arel/nodes.rb +53 -0
  18. data/lib/arel/nodes/and.rb +6 -0
  19. data/lib/arel/nodes/as.rb +6 -0
  20. data/lib/arel/nodes/assignment.rb +6 -0
  21. data/lib/arel/nodes/avg.rb +6 -0
  22. data/lib/arel/nodes/between.rb +6 -0
  23. data/lib/arel/nodes/binary.rb +12 -0
  24. data/lib/arel/nodes/count.rb +13 -0
  25. data/lib/arel/nodes/delete_statement.rb +19 -0
  26. data/lib/arel/nodes/does_not_match.rb +6 -0
  27. data/lib/arel/nodes/equality.rb +9 -0
  28. data/lib/arel/nodes/except.rb +7 -0
  29. data/lib/arel/nodes/exists.rb +7 -0
  30. data/lib/arel/nodes/function.rb +18 -0
  31. data/lib/arel/nodes/greater_than.rb +6 -0
  32. data/lib/arel/nodes/greater_than_or_equal.rb +6 -0
  33. data/lib/arel/nodes/group.rb +6 -0
  34. data/lib/arel/nodes/grouping.rb +6 -0
  35. data/lib/arel/nodes/having.rb +6 -0
  36. data/lib/arel/nodes/in.rb +6 -0
  37. data/lib/arel/nodes/inner_join.rb +6 -0
  38. data/lib/arel/nodes/insert_statement.rb +19 -0
  39. data/lib/arel/nodes/intersect.rb +7 -0
  40. data/lib/arel/nodes/join.rb +13 -0
  41. data/lib/arel/nodes/less_than.rb +6 -0
  42. data/lib/arel/nodes/less_than_or_equal.rb +6 -0
  43. data/lib/arel/nodes/limit.rb +7 -0
  44. data/lib/arel/nodes/lock.rb +6 -0
  45. data/lib/arel/nodes/matches.rb +6 -0
  46. data/lib/arel/nodes/max.rb +6 -0
  47. data/lib/arel/nodes/min.rb +6 -0
  48. data/lib/arel/nodes/node.rb +44 -0
  49. data/lib/arel/nodes/not.rb +6 -0
  50. data/lib/arel/nodes/not_equal.rb +6 -0
  51. data/lib/arel/nodes/not_in.rb +6 -0
  52. data/lib/arel/nodes/offset.rb +7 -0
  53. data/lib/arel/nodes/on.rb +6 -0
  54. data/lib/arel/nodes/or.rb +6 -0
  55. data/lib/arel/nodes/ordering.rb +20 -0
  56. data/lib/arel/nodes/outer_join.rb +6 -0
  57. data/lib/arel/nodes/select_core.rb +26 -0
  58. data/lib/arel/nodes/select_statement.rb +22 -0
  59. data/lib/arel/nodes/sql_literal.rb +8 -0
  60. data/lib/arel/nodes/string_join.rb +11 -0
  61. data/lib/arel/nodes/sum.rb +6 -0
  62. data/lib/arel/nodes/table_alias.rb +13 -0
  63. data/lib/arel/nodes/top.rb +6 -0
  64. data/lib/arel/nodes/unary.rb +11 -0
  65. data/lib/arel/nodes/union.rb +7 -0
  66. data/lib/arel/nodes/union_all.rb +7 -0
  67. data/lib/arel/nodes/unqualified_column.rb +16 -0
  68. data/lib/arel/nodes/update_statement.rb +21 -0
  69. data/lib/arel/nodes/values.rb +14 -0
  70. data/lib/arel/predications.rb +183 -0
  71. data/lib/arel/relation.rb +6 -0
  72. data/lib/arel/select_manager.rb +237 -0
  73. data/lib/arel/sql/engine.rb +10 -0
  74. data/lib/arel/sql_literal.rb +4 -0
  75. data/lib/arel/table.rb +134 -0
  76. data/lib/arel/tree_manager.rb +36 -0
  77. data/lib/arel/update_manager.rb +49 -0
  78. data/lib/arel/visitors.rb +38 -0
  79. data/lib/arel/visitors/depth_first.rb +154 -0
  80. data/lib/arel/visitors/dot.rb +230 -0
  81. data/lib/arel/visitors/join_sql.rb +40 -0
  82. data/lib/arel/visitors/mssql.rb +16 -0
  83. data/lib/arel/visitors/mysql.rb +34 -0
  84. data/lib/arel/visitors/oracle.rb +116 -0
  85. data/lib/arel/visitors/order_clauses.rb +11 -0
  86. data/lib/arel/visitors/postgresql.rb +58 -0
  87. data/lib/arel/visitors/sqlite.rb +11 -0
  88. data/lib/arel/visitors/to_sql.rb +331 -0
  89. data/lib/arel/visitors/visitor.rb +27 -0
  90. data/lib/arel/visitors/where_sql.rb +9 -0
  91. data/square-arel.gemspec +36 -0
  92. data/test/attributes/test_attribute.rb +664 -0
  93. data/test/helper.rb +13 -0
  94. data/test/nodes/test_as.rb +16 -0
  95. data/test/nodes/test_count.rb +18 -0
  96. data/test/nodes/test_delete_statement.rb +14 -0
  97. data/test/nodes/test_equality.rb +74 -0
  98. data/test/nodes/test_insert_statement.rb +18 -0
  99. data/test/nodes/test_node.rb +33 -0
  100. data/test/nodes/test_not.rb +20 -0
  101. data/test/nodes/test_or.rb +22 -0
  102. data/test/nodes/test_select_core.rb +22 -0
  103. data/test/nodes/test_select_statement.rb +13 -0
  104. data/test/nodes/test_sql_literal.rb +52 -0
  105. data/test/nodes/test_sum.rb +12 -0
  106. data/test/nodes/test_update_statement.rb +18 -0
  107. data/test/support/fake_record.rb +91 -0
  108. data/test/test_activerecord_compat.rb +18 -0
  109. data/test/test_attributes.rb +46 -0
  110. data/test/test_crud.rb +69 -0
  111. data/test/test_delete_manager.rb +42 -0
  112. data/test/test_insert_manager.rb +125 -0
  113. data/test/test_select_manager.rb +659 -0
  114. data/test/test_table.rb +193 -0
  115. data/test/test_update_manager.rb +86 -0
  116. data/test/visitors/test_depth_first.rb +212 -0
  117. data/test/visitors/test_dot.rb +29 -0
  118. data/test/visitors/test_join_sql.rb +35 -0
  119. data/test/visitors/test_mssql.rb +18 -0
  120. data/test/visitors/test_mysql.rb +45 -0
  121. data/test/visitors/test_oracle.rb +147 -0
  122. data/test/visitors/test_postgres.rb +36 -0
  123. data/test/visitors/test_sqlite.rb +18 -0
  124. data/test/visitors/test_to_sql.rb +255 -0
  125. metadata +261 -0
@@ -0,0 +1,6 @@
1
+ module Arel
2
+ ###
3
+ # This is deprecated. Fix rails, then remove this.
4
+ module Relation
5
+ end
6
+ end
@@ -0,0 +1,237 @@
1
+ module Arel
2
+ class SelectManager < Arel::TreeManager
3
+ include Arel::Crud
4
+
5
+ def initialize engine, table = nil
6
+ super(engine)
7
+ @ast = Nodes::SelectStatement.new
8
+ @ctx = @ast.cores.last
9
+ from table
10
+ end
11
+
12
+ def taken
13
+ @ast.limit && @ast.limit.expr
14
+ end
15
+
16
+ def constraints
17
+ @ctx.wheres
18
+ end
19
+
20
+ def skip amount
21
+ @ast.offset = Nodes::Offset.new(amount)
22
+ self
23
+ end
24
+
25
+ ###
26
+ # Produces an Arel::Nodes::Exists node
27
+ def exists
28
+ Arel::Nodes::Exists.new @ast
29
+ end
30
+
31
+ def where_clauses
32
+ #warn "where_clauses is deprecated" if $VERBOSE
33
+ to_sql = Visitors::ToSql.new @engine
34
+ @ctx.wheres.map { |c| to_sql.accept c }
35
+ end
36
+
37
+ def lock locking = Arel.sql('FOR UPDATE')
38
+ case locking
39
+ when true
40
+ locking = Arel.sql('FOR UPDATE')
41
+ when Arel::Nodes::SqlLiteral
42
+ when String
43
+ locking = Arel.sql locking
44
+ end
45
+
46
+ @ast.lock = Nodes::Lock.new(locking)
47
+ self
48
+ end
49
+
50
+ def locked
51
+ @ast.lock
52
+ end
53
+
54
+ def on *exprs
55
+ @ctx.froms.constraint = Nodes::On.new(collapse(exprs))
56
+ self
57
+ end
58
+
59
+ def group *columns
60
+ columns.each do |column|
61
+ # FIXME: backwards compat
62
+ column = Nodes::SqlLiteral.new(column) if String === column
63
+ column = Nodes::SqlLiteral.new(column.to_s) if Symbol === column
64
+
65
+ @ctx.groups.push Nodes::Group.new column
66
+ end
67
+ self
68
+ end
69
+
70
+ def from table
71
+ table = Nodes::SqlLiteral.new(table) if String === table
72
+ # FIXME: this is a hack to support
73
+ # test_with_two_tables_in_from_without_getting_double_quoted
74
+ # from the AR tests.
75
+ if @ctx.froms
76
+ source = @ctx.froms
77
+
78
+ if Nodes::SqlLiteral === table && Nodes::Join === source
79
+ source.left = table
80
+ table = source
81
+ end
82
+ end
83
+
84
+ @ctx.froms = table
85
+ self
86
+ end
87
+
88
+ def join relation, klass = Nodes::InnerJoin
89
+ return self unless relation
90
+
91
+ case relation
92
+ when String, Nodes::SqlLiteral
93
+ raise if relation.blank?
94
+ from Nodes::StringJoin.new(@ctx.froms, relation)
95
+ else
96
+ from klass.new(@ctx.froms, relation, nil)
97
+ end
98
+ end
99
+
100
+ def having expr
101
+ expr = Nodes::SqlLiteral.new(expr) if String === expr
102
+
103
+ @ctx.having = Nodes::Having.new(expr)
104
+ self
105
+ end
106
+
107
+ def project *projections
108
+ # FIXME: converting these to SQLLiterals is probably not good, but
109
+ # rails tests require it.
110
+ @ctx.projections.concat projections.map { |x|
111
+ [Symbol, String].include?(x.class) ? SqlLiteral.new(x.to_s) : x
112
+ }
113
+ self
114
+ end
115
+
116
+ def order *expr
117
+ # FIXME: We SHOULD NOT be converting these to SqlLiteral automatically
118
+ @ast.orders.concat expr.map { |x|
119
+ String === x || Symbol === x ? Nodes::SqlLiteral.new(x.to_s) : x
120
+ }
121
+ self
122
+ end
123
+
124
+ def orders
125
+ @ast.orders
126
+ end
127
+
128
+ def wheres
129
+ Compatibility::Wheres.new @engine, @ctx.wheres
130
+ end
131
+
132
+ def where_sql
133
+ return if @ctx.wheres.empty?
134
+
135
+ viz = Visitors::WhereSql.new @engine
136
+ Nodes::SqlLiteral.new viz.accept @ctx
137
+ end
138
+
139
+ def union operation, other = nil
140
+ if other
141
+ node_class = Nodes.const_get("Union#{operation.to_s.capitalize}")
142
+ else
143
+ other = operation
144
+ node_class = Nodes::Union
145
+ end
146
+
147
+ node_class.new self.ast, other.ast
148
+ end
149
+
150
+ def intersect other
151
+ Nodes::Intersect.new ast, other.ast
152
+ end
153
+
154
+ def except other
155
+ Nodes::Except.new ast, other.ast
156
+ end
157
+ alias :minus :except
158
+
159
+ def take limit
160
+ @ast.limit = Nodes::Limit.new(limit)
161
+ @ctx.top = Nodes::Top.new(limit)
162
+ self
163
+ end
164
+
165
+ def join_sql
166
+ return nil unless @ctx.froms
167
+
168
+ viz = Visitors::JoinSql.new @engine
169
+ Nodes::SqlLiteral.new viz.accept @ctx
170
+ end
171
+
172
+ def order_clauses
173
+ Visitors::OrderClauses.new(@engine).accept(@ast).map { |x|
174
+ Nodes::SqlLiteral.new x
175
+ }
176
+ end
177
+
178
+ def joins manager
179
+ if $VERBOSE
180
+ warn "joins is deprecated and will be removed in 2.2"
181
+ warn "please remove your call to joins from #{caller.first}"
182
+ end
183
+ manager.join_sql
184
+ end
185
+
186
+ class Row < Struct.new(:data) # :nodoc:
187
+ def id
188
+ data['id']
189
+ end
190
+
191
+ def method_missing(name, *args)
192
+ name = name.to_s
193
+ return data[name] if data.key?(name)
194
+ super
195
+ end
196
+ end
197
+
198
+ def to_a # :nodoc:
199
+ warn "to_a is deprecated. Please remove it from #{caller[0]}"
200
+ # FIXME: I think `select` should be made public...
201
+ @engine.connection.send(:select, to_sql, 'AREL').map { |x| Row.new(x) }
202
+ end
203
+
204
+ # FIXME: this method should go away
205
+ def insert values
206
+ im = InsertManager.new @engine
207
+ table = @ctx.froms
208
+ primary_key_name = (primary_key = table.primary_key) && primary_key.name
209
+ # FIXME: in AR tests values sometimes were Array and not Hash therefore is_a?(Hash) check is added
210
+ primary_key_value = primary_key && values.is_a?(Hash) && values[primary_key]
211
+ im.into table
212
+ im.insert values
213
+ # Oracle adapter needs primary key name to generate RETURNING ... INTO ... clause
214
+ # for tables which assign primary key value using trigger.
215
+ # RETURNING ... INTO ... clause will be added only if primary_key_value is nil
216
+ # therefore it is necessary to pass primary key value as well
217
+ @engine.connection.insert im.to_sql, 'AREL', primary_key_name, primary_key_value
218
+ end
219
+
220
+ private
221
+ def collapse exprs
222
+ exprs.map! { |x| x.class == ::String ? Arel.sql(x) : x }
223
+
224
+ return exprs.first if exprs.length == 1
225
+
226
+ right = exprs.pop
227
+ left = exprs.pop
228
+
229
+ right = Nodes::SqlLiteral.new(right) if String === right
230
+
231
+ right = Nodes::And.new left, right
232
+ exprs.reverse.inject(right) { |memo,expr|
233
+ Nodes::And.new(expr, memo)
234
+ }
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,10 @@
1
+ module Arel
2
+ module Sql
3
+ class Engine
4
+ def self.new thing
5
+ #warn "#{caller.first} -- Engine will be removed"
6
+ thing
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ module Arel
2
+ class SqlLiteral < Nodes::SqlLiteral
3
+ end
4
+ end
@@ -0,0 +1,134 @@
1
+ module Arel
2
+ class Table
3
+ include Arel::Crud
4
+
5
+ @engine = nil
6
+ class << self; attr_accessor :engine; end
7
+
8
+ attr_accessor :name, :engine, :aliases, :table_alias
9
+
10
+ def initialize name, engine = Table.engine
11
+ @name = name.to_s
12
+ @engine = engine
13
+ @columns = nil
14
+ @aliases = []
15
+ @table_alias = nil
16
+ @primary_key = nil
17
+
18
+ if Hash === engine
19
+ @engine = engine[:engine] || Table.engine
20
+ @columns = attributes_for engine[:columns]
21
+
22
+ # Sometime AR sends an :as parameter to table, to let the table know
23
+ # that it is an Alias. We may want to override new, and return a
24
+ # TableAlias node?
25
+ @table_alias = engine[:as] unless engine[:as].to_s == @name
26
+ end
27
+ end
28
+
29
+ def primary_key
30
+ @primary_key ||= begin
31
+ primary_key_name = @engine.connection.primary_key(name)
32
+ # some tables might be without primary key
33
+ primary_key_name && self[primary_key_name]
34
+ end
35
+ end
36
+
37
+ def alias name = "#{self.name}_2"
38
+ Nodes::TableAlias.new(name, self).tap do |node|
39
+ @aliases << node
40
+ end
41
+ end
42
+
43
+ def from table
44
+ SelectManager.new(@engine, table)
45
+ end
46
+
47
+ def joins manager
48
+ if $VERBOSE
49
+ warn "joins is deprecated and will be removed in 2.2"
50
+ warn "please remove your call to joins from #{caller.first}"
51
+ end
52
+ nil
53
+ end
54
+
55
+ def join relation, klass = Nodes::InnerJoin
56
+ return from(self) unless relation
57
+
58
+ case relation
59
+ when String, Nodes::SqlLiteral
60
+ raise if relation.blank?
61
+ from Nodes::StringJoin.new(self, relation)
62
+ else
63
+ from klass.new(self, relation, nil)
64
+ end
65
+ end
66
+
67
+ def group *columns
68
+ from(self).group(*columns)
69
+ end
70
+
71
+ def order *expr
72
+ from(self).order(*expr)
73
+ end
74
+
75
+ def where condition
76
+ from(self).where condition
77
+ end
78
+
79
+ def project *things
80
+ from(self).project(*things)
81
+ end
82
+
83
+ def take amount
84
+ from(self).take amount
85
+ end
86
+
87
+ def skip amount
88
+ from(self).skip amount
89
+ end
90
+
91
+ def having expr
92
+ from(self).having expr
93
+ end
94
+
95
+ def columns
96
+ @columns ||=
97
+ attributes_for @engine.connection.columns(@name, "#{@name} Columns")
98
+ end
99
+
100
+ def [] name
101
+ return nil unless table_exists?
102
+
103
+ name = name.to_sym
104
+ columns.find { |column| column.name == name }
105
+ end
106
+
107
+ def select_manager
108
+ SelectManager.new(@engine)
109
+ end
110
+
111
+ private
112
+
113
+ def attributes_for columns
114
+ return nil unless columns
115
+
116
+ columns.map do |column|
117
+ Attributes.for(column).new self, column.name.to_sym, column
118
+ end
119
+ end
120
+
121
+ def table_exists?
122
+ @table_exists ||= tables.key?(@name) || engine.connection.table_exists?(name)
123
+ end
124
+
125
+ def tables
126
+ self.class.table_cache(@engine)
127
+ end
128
+
129
+ @@table_cache = nil
130
+ def self.table_cache engine # :nodoc:
131
+ @@table_cache ||= Hash[engine.connection.tables.map { |x| [x,true] }]
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,36 @@
1
+ module Arel
2
+ class TreeManager
3
+ # FIXME: Remove this.
4
+ include Arel::Relation
5
+
6
+ attr_accessor :visitor
7
+ attr_reader :ast, :engine
8
+
9
+ def initialize engine
10
+ @engine = engine
11
+ @visitor = Visitors.visitor_for @engine
12
+ @ctx = nil
13
+ end
14
+
15
+ def to_dot
16
+ Visitors::Dot.new.accept @ast
17
+ end
18
+
19
+ def to_sql
20
+ @visitor.accept @ast
21
+ end
22
+
23
+ def initialize_copy other
24
+ super
25
+ @ast = @ast.clone
26
+ end
27
+
28
+ def where expr
29
+ if Arel::TreeManager === expr
30
+ expr = expr.ast
31
+ end
32
+ @ctx.wheres << expr
33
+ self
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,49 @@
1
+ module Arel
2
+ class UpdateManager < Arel::TreeManager
3
+ def initialize engine
4
+ super
5
+ @ast = Nodes::UpdateStatement.new
6
+ @ctx = @ast
7
+ end
8
+
9
+ def take limit
10
+ @ast.limit = Nodes::Limit.new(limit) if limit
11
+ self
12
+ end
13
+
14
+ def order *expr
15
+ @ast.orders = expr
16
+ self
17
+ end
18
+
19
+ ###
20
+ # UPDATE +table+
21
+ def table table
22
+ @ast.relation = table
23
+ self
24
+ end
25
+
26
+ def wheres= exprs
27
+ @ast.wheres = exprs
28
+ end
29
+
30
+ def where expr
31
+ @ast.wheres << expr
32
+ self
33
+ end
34
+
35
+ def set values
36
+ if String === values
37
+ @ast.values = [values]
38
+ else
39
+ @ast.values = values.map { |column,value|
40
+ Nodes::Assignment.new(
41
+ Nodes::UnqualifiedColumn.new(column),
42
+ value
43
+ )
44
+ }
45
+ end
46
+ self
47
+ end
48
+ end
49
+ end