activerecord 5.2.8.1 → 6.0.6.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (294) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +938 -573
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +5 -3
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/advisory_lock_base.rb +18 -0
  7. data/lib/active_record/aggregations.rb +4 -3
  8. data/lib/active_record/association_relation.rb +10 -8
  9. data/lib/active_record/associations/alias_tracker.rb +0 -1
  10. data/lib/active_record/associations/association.rb +55 -19
  11. data/lib/active_record/associations/association_scope.rb +11 -7
  12. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  14. data/lib/active_record/associations/builder/association.rb +14 -18
  15. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  16. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -40
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +35 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +19 -23
  22. data/lib/active_record/associations/collection_proxy.rb +14 -17
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +2 -11
  25. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  26. data/lib/active_record/associations/has_one_association.rb +28 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  28. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  29. data/lib/active_record/associations/join_dependency/join_part.rb +4 -4
  30. data/lib/active_record/associations/join_dependency.rb +47 -30
  31. data/lib/active_record/associations/preloader/association.rb +61 -41
  32. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  33. data/lib/active_record/associations/preloader.rb +44 -33
  34. data/lib/active_record/associations/singular_association.rb +2 -16
  35. data/lib/active_record/associations/through_association.rb +1 -1
  36. data/lib/active_record/associations.rb +21 -16
  37. data/lib/active_record/attribute_assignment.rb +7 -11
  38. data/lib/active_record/attribute_decorators.rb +0 -2
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -2
  40. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  41. data/lib/active_record/attribute_methods/primary_key.rb +15 -24
  42. data/lib/active_record/attribute_methods/query.rb +2 -3
  43. data/lib/active_record/attribute_methods/read.rb +15 -54
  44. data/lib/active_record/attribute_methods/serialization.rb +1 -2
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -3
  46. data/lib/active_record/attribute_methods/write.rb +17 -25
  47. data/lib/active_record/attribute_methods.rb +28 -100
  48. data/lib/active_record/attributes.rb +13 -1
  49. data/lib/active_record/autosave_association.rb +12 -14
  50. data/lib/active_record/base.rb +2 -3
  51. data/lib/active_record/callbacks.rb +6 -21
  52. data/lib/active_record/coders/yaml_column.rb +15 -6
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +109 -18
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +102 -124
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -9
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +77 -17
  58. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +20 -14
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +105 -72
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +175 -79
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -57
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -43
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -217
  65. data/lib/active_record/connection_adapters/column.rb +17 -13
  66. data/lib/active_record/connection_adapters/connection_specification.rb +54 -45
  67. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  68. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/database_statements.rb +70 -14
  70. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +0 -1
  71. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +4 -6
  73. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  74. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  75. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
  76. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -10
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +8 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  89. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -3
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +63 -75
  98. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +168 -75
  101. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  102. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -12
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +137 -147
  107. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  108. data/lib/active_record/connection_handling.rb +139 -26
  109. data/lib/active_record/core.rb +108 -67
  110. data/lib/active_record/counter_cache.rb +8 -30
  111. data/lib/active_record/database_configurations/database_config.rb +37 -0
  112. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  113. data/lib/active_record/database_configurations/url_config.rb +78 -0
  114. data/lib/active_record/database_configurations.rb +233 -0
  115. data/lib/active_record/dynamic_matchers.rb +3 -4
  116. data/lib/active_record/enum.rb +44 -7
  117. data/lib/active_record/errors.rb +15 -7
  118. data/lib/active_record/explain.rb +1 -2
  119. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  120. data/lib/active_record/fixture_set/render_context.rb +17 -0
  121. data/lib/active_record/fixture_set/table_row.rb +152 -0
  122. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  123. data/lib/active_record/fixtures.rb +144 -474
  124. data/lib/active_record/gem_version.rb +3 -3
  125. data/lib/active_record/inheritance.rb +13 -6
  126. data/lib/active_record/insert_all.rb +179 -0
  127. data/lib/active_record/integration.rb +68 -16
  128. data/lib/active_record/internal_metadata.rb +11 -3
  129. data/lib/active_record/locking/optimistic.rb +14 -7
  130. data/lib/active_record/locking/pessimistic.rb +3 -3
  131. data/lib/active_record/log_subscriber.rb +8 -27
  132. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  134. data/lib/active_record/middleware/database_selector.rb +74 -0
  135. data/lib/active_record/migration/command_recorder.rb +54 -22
  136. data/lib/active_record/migration/compatibility.rb +79 -52
  137. data/lib/active_record/migration/join_table.rb +0 -1
  138. data/lib/active_record/migration.rb +104 -85
  139. data/lib/active_record/model_schema.rb +62 -11
  140. data/lib/active_record/nested_attributes.rb +2 -4
  141. data/lib/active_record/no_touching.rb +9 -2
  142. data/lib/active_record/null_relation.rb +0 -1
  143. data/lib/active_record/persistence.rb +232 -29
  144. data/lib/active_record/query_cache.rb +11 -4
  145. data/lib/active_record/querying.rb +33 -21
  146. data/lib/active_record/railtie.rb +80 -61
  147. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  148. data/lib/active_record/railties/controller_runtime.rb +30 -35
  149. data/lib/active_record/railties/databases.rake +199 -46
  150. data/lib/active_record/reflection.rb +51 -51
  151. data/lib/active_record/relation/batches.rb +13 -11
  152. data/lib/active_record/relation/calculations.rb +55 -49
  153. data/lib/active_record/relation/delegation.rb +35 -50
  154. data/lib/active_record/relation/finder_methods.rb +23 -28
  155. data/lib/active_record/relation/from_clause.rb +4 -0
  156. data/lib/active_record/relation/merger.rb +12 -17
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  158. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  159. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  160. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  161. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  162. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  163. data/lib/active_record/relation/predicate_builder.rb +5 -11
  164. data/lib/active_record/relation/query_attribute.rb +13 -8
  165. data/lib/active_record/relation/query_methods.rb +234 -69
  166. data/lib/active_record/relation/spawn_methods.rb +1 -2
  167. data/lib/active_record/relation/where_clause.rb +14 -11
  168. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  169. data/lib/active_record/relation.rb +326 -81
  170. data/lib/active_record/result.rb +30 -12
  171. data/lib/active_record/sanitization.rb +32 -40
  172. data/lib/active_record/schema.rb +2 -11
  173. data/lib/active_record/schema_dumper.rb +22 -7
  174. data/lib/active_record/schema_migration.rb +6 -2
  175. data/lib/active_record/scoping/default.rb +4 -6
  176. data/lib/active_record/scoping/named.rb +25 -16
  177. data/lib/active_record/scoping.rb +8 -9
  178. data/lib/active_record/statement_cache.rb +30 -3
  179. data/lib/active_record/store.rb +87 -8
  180. data/lib/active_record/suppressor.rb +2 -2
  181. data/lib/active_record/table_metadata.rb +23 -15
  182. data/lib/active_record/tasks/database_tasks.rb +194 -25
  183. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -6
  184. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -8
  185. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -9
  186. data/lib/active_record/test_databases.rb +23 -0
  187. data/lib/active_record/test_fixtures.rb +243 -0
  188. data/lib/active_record/timestamp.rb +39 -26
  189. data/lib/active_record/touch_later.rb +5 -4
  190. data/lib/active_record/transactions.rb +64 -73
  191. data/lib/active_record/translation.rb +1 -1
  192. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  193. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  194. data/lib/active_record/type/serialized.rb +0 -1
  195. data/lib/active_record/type/time.rb +10 -0
  196. data/lib/active_record/type/type_map.rb +0 -1
  197. data/lib/active_record/type/unsigned_integer.rb +0 -1
  198. data/lib/active_record/type.rb +3 -5
  199. data/lib/active_record/type_caster/connection.rb +15 -14
  200. data/lib/active_record/type_caster/map.rb +1 -4
  201. data/lib/active_record/validations/associated.rb +0 -1
  202. data/lib/active_record/validations/uniqueness.rb +15 -27
  203. data/lib/active_record/validations.rb +3 -3
  204. data/lib/active_record.rb +10 -2
  205. data/lib/arel/alias_predication.rb +9 -0
  206. data/lib/arel/attributes/attribute.rb +37 -0
  207. data/lib/arel/attributes.rb +22 -0
  208. data/lib/arel/collectors/bind.rb +24 -0
  209. data/lib/arel/collectors/composite.rb +31 -0
  210. data/lib/arel/collectors/plain_string.rb +20 -0
  211. data/lib/arel/collectors/sql_string.rb +20 -0
  212. data/lib/arel/collectors/substitute_binds.rb +28 -0
  213. data/lib/arel/crud.rb +42 -0
  214. data/lib/arel/delete_manager.rb +18 -0
  215. data/lib/arel/errors.rb +9 -0
  216. data/lib/arel/expressions.rb +29 -0
  217. data/lib/arel/factory_methods.rb +49 -0
  218. data/lib/arel/insert_manager.rb +49 -0
  219. data/lib/arel/math.rb +45 -0
  220. data/lib/arel/nodes/and.rb +32 -0
  221. data/lib/arel/nodes/ascending.rb +23 -0
  222. data/lib/arel/nodes/binary.rb +52 -0
  223. data/lib/arel/nodes/bind_param.rb +36 -0
  224. data/lib/arel/nodes/case.rb +55 -0
  225. data/lib/arel/nodes/casted.rb +50 -0
  226. data/lib/arel/nodes/comment.rb +29 -0
  227. data/lib/arel/nodes/count.rb +12 -0
  228. data/lib/arel/nodes/delete_statement.rb +45 -0
  229. data/lib/arel/nodes/descending.rb +23 -0
  230. data/lib/arel/nodes/equality.rb +18 -0
  231. data/lib/arel/nodes/extract.rb +24 -0
  232. data/lib/arel/nodes/false.rb +16 -0
  233. data/lib/arel/nodes/full_outer_join.rb +8 -0
  234. data/lib/arel/nodes/function.rb +44 -0
  235. data/lib/arel/nodes/grouping.rb +8 -0
  236. data/lib/arel/nodes/in.rb +8 -0
  237. data/lib/arel/nodes/infix_operation.rb +80 -0
  238. data/lib/arel/nodes/inner_join.rb +8 -0
  239. data/lib/arel/nodes/insert_statement.rb +37 -0
  240. data/lib/arel/nodes/join_source.rb +20 -0
  241. data/lib/arel/nodes/matches.rb +18 -0
  242. data/lib/arel/nodes/named_function.rb +23 -0
  243. data/lib/arel/nodes/node.rb +50 -0
  244. data/lib/arel/nodes/node_expression.rb +13 -0
  245. data/lib/arel/nodes/outer_join.rb +8 -0
  246. data/lib/arel/nodes/over.rb +15 -0
  247. data/lib/arel/nodes/regexp.rb +16 -0
  248. data/lib/arel/nodes/right_outer_join.rb +8 -0
  249. data/lib/arel/nodes/select_core.rb +67 -0
  250. data/lib/arel/nodes/select_statement.rb +41 -0
  251. data/lib/arel/nodes/sql_literal.rb +16 -0
  252. data/lib/arel/nodes/string_join.rb +11 -0
  253. data/lib/arel/nodes/table_alias.rb +27 -0
  254. data/lib/arel/nodes/terminal.rb +16 -0
  255. data/lib/arel/nodes/true.rb +16 -0
  256. data/lib/arel/nodes/unary.rb +45 -0
  257. data/lib/arel/nodes/unary_operation.rb +20 -0
  258. data/lib/arel/nodes/unqualified_column.rb +22 -0
  259. data/lib/arel/nodes/update_statement.rb +41 -0
  260. data/lib/arel/nodes/values_list.rb +9 -0
  261. data/lib/arel/nodes/window.rb +126 -0
  262. data/lib/arel/nodes/with.rb +11 -0
  263. data/lib/arel/nodes.rb +68 -0
  264. data/lib/arel/order_predications.rb +13 -0
  265. data/lib/arel/predications.rb +256 -0
  266. data/lib/arel/select_manager.rb +271 -0
  267. data/lib/arel/table.rb +110 -0
  268. data/lib/arel/tree_manager.rb +72 -0
  269. data/lib/arel/update_manager.rb +34 -0
  270. data/lib/arel/visitors/depth_first.rb +203 -0
  271. data/lib/arel/visitors/dot.rb +296 -0
  272. data/lib/arel/visitors/ibm_db.rb +34 -0
  273. data/lib/arel/visitors/informix.rb +62 -0
  274. data/lib/arel/visitors/mssql.rb +156 -0
  275. data/lib/arel/visitors/mysql.rb +83 -0
  276. data/lib/arel/visitors/oracle.rb +158 -0
  277. data/lib/arel/visitors/oracle12.rb +65 -0
  278. data/lib/arel/visitors/postgresql.rb +109 -0
  279. data/lib/arel/visitors/sqlite.rb +38 -0
  280. data/lib/arel/visitors/to_sql.rb +888 -0
  281. data/lib/arel/visitors/visitor.rb +45 -0
  282. data/lib/arel/visitors/where_sql.rb +22 -0
  283. data/lib/arel/visitors.rb +20 -0
  284. data/lib/arel/window_predications.rb +9 -0
  285. data/lib/arel.rb +62 -0
  286. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  287. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  288. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  289. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  290. data/lib/rails/generators/active_record/migration.rb +14 -2
  291. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  292. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  293. metadata +113 -26
  294. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -0,0 +1,888 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Visitors
5
+ class UnsupportedVisitError < StandardError
6
+ def initialize(object)
7
+ super "Unsupported argument type: #{object.class.name}. Construct an Arel node instead."
8
+ end
9
+ end
10
+
11
+ class ToSql < Arel::Visitors::Visitor
12
+ def initialize(connection)
13
+ super()
14
+ @connection = connection
15
+ end
16
+
17
+ def compile(node, collector = Arel::Collectors::SQLString.new)
18
+ accept(node, collector).value
19
+ end
20
+
21
+ private
22
+ def visit_Arel_Nodes_DeleteStatement(o, collector)
23
+ o = prepare_delete_statement(o)
24
+
25
+ if has_join_sources?(o)
26
+ collector << "DELETE "
27
+ visit o.relation.left, collector
28
+ collector << " FROM "
29
+ else
30
+ collector << "DELETE FROM "
31
+ end
32
+ collector = visit o.relation, collector
33
+
34
+ collect_nodes_for o.wheres, collector, " WHERE ", " AND "
35
+ collect_nodes_for o.orders, collector, " ORDER BY "
36
+ maybe_visit o.limit, collector
37
+ end
38
+
39
+ def visit_Arel_Nodes_UpdateStatement(o, collector)
40
+ o = prepare_update_statement(o)
41
+
42
+ collector << "UPDATE "
43
+ collector = visit o.relation, collector
44
+ collect_nodes_for o.values, collector, " SET "
45
+
46
+ collect_nodes_for o.wheres, collector, " WHERE ", " AND "
47
+ collect_nodes_for o.orders, collector, " ORDER BY "
48
+ maybe_visit o.limit, collector
49
+ end
50
+
51
+ def visit_Arel_Nodes_InsertStatement(o, collector)
52
+ collector << "INSERT INTO "
53
+ collector = visit o.relation, collector
54
+
55
+ unless o.columns.empty?
56
+ collector << " ("
57
+ o.columns.each_with_index do |x, i|
58
+ collector << ", " unless i == 0
59
+ collector << quote_column_name(x.name)
60
+ end
61
+ collector << ")"
62
+ end
63
+
64
+ if o.values
65
+ maybe_visit o.values, collector
66
+ elsif o.select
67
+ maybe_visit o.select, collector
68
+ else
69
+ collector
70
+ end
71
+ end
72
+
73
+ def visit_Arel_Nodes_Exists(o, collector)
74
+ collector << "EXISTS ("
75
+ collector = visit(o.expressions, collector) << ")"
76
+ if o.alias
77
+ collector << " AS "
78
+ visit o.alias, collector
79
+ else
80
+ collector
81
+ end
82
+ end
83
+
84
+ def visit_Arel_Nodes_Casted(o, collector)
85
+ collector << quoted(o.val, o.attribute).to_s
86
+ end
87
+
88
+ def visit_Arel_Nodes_Quoted(o, collector)
89
+ collector << quoted(o.expr, nil).to_s
90
+ end
91
+
92
+ def visit_Arel_Nodes_True(o, collector)
93
+ collector << "TRUE"
94
+ end
95
+
96
+ def visit_Arel_Nodes_False(o, collector)
97
+ collector << "FALSE"
98
+ end
99
+
100
+ def visit_Arel_Nodes_ValuesList(o, collector)
101
+ collector << "VALUES "
102
+
103
+ o.rows.each_with_index do |row, i|
104
+ collector << ", " unless i == 0
105
+ collector << "("
106
+ row.each_with_index do |value, k|
107
+ collector << ", " unless k == 0
108
+ case value
109
+ when Nodes::SqlLiteral, Nodes::BindParam
110
+ collector = visit(value, collector)
111
+ else
112
+ collector << quote(value).to_s
113
+ end
114
+ end
115
+ collector << ")"
116
+ end
117
+ collector
118
+ end
119
+
120
+ def visit_Arel_Nodes_SelectStatement(o, collector)
121
+ if o.with
122
+ collector = visit o.with, collector
123
+ collector << " "
124
+ end
125
+
126
+ collector = o.cores.inject(collector) { |c, x|
127
+ visit_Arel_Nodes_SelectCore(x, c)
128
+ }
129
+
130
+ unless o.orders.empty?
131
+ collector << " ORDER BY "
132
+ o.orders.each_with_index do |x, i|
133
+ collector << ", " unless i == 0
134
+ collector = visit(x, collector)
135
+ end
136
+ end
137
+
138
+ visit_Arel_Nodes_SelectOptions(o, collector)
139
+ end
140
+
141
+ def visit_Arel_Nodes_SelectOptions(o, collector)
142
+ collector = maybe_visit o.limit, collector
143
+ collector = maybe_visit o.offset, collector
144
+ maybe_visit o.lock, collector
145
+ end
146
+
147
+ def visit_Arel_Nodes_SelectCore(o, collector)
148
+ collector << "SELECT"
149
+
150
+ collector = collect_optimizer_hints(o, collector)
151
+ collector = maybe_visit o.set_quantifier, collector
152
+
153
+ collect_nodes_for o.projections, collector, " "
154
+
155
+ if o.source && !o.source.empty?
156
+ collector << " FROM "
157
+ collector = visit o.source, collector
158
+ end
159
+
160
+ collect_nodes_for o.wheres, collector, " WHERE ", " AND "
161
+ collect_nodes_for o.groups, collector, " GROUP BY "
162
+ collect_nodes_for o.havings, collector, " HAVING ", " AND "
163
+ collect_nodes_for o.windows, collector, " WINDOW "
164
+
165
+ maybe_visit o.comment, collector
166
+ end
167
+
168
+ def visit_Arel_Nodes_OptimizerHints(o, collector)
169
+ hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(" ")
170
+ collector << "/*+ #{hints} */"
171
+ end
172
+
173
+ def visit_Arel_Nodes_Comment(o, collector)
174
+ collector << o.values.map { |v| "/* #{sanitize_as_sql_comment(v)} */" }.join(" ")
175
+ end
176
+
177
+ def collect_nodes_for(nodes, collector, spacer, connector = ", ")
178
+ unless nodes.empty?
179
+ collector << spacer
180
+ inject_join nodes, collector, connector
181
+ end
182
+ end
183
+
184
+ def visit_Arel_Nodes_Bin(o, collector)
185
+ visit o.expr, collector
186
+ end
187
+
188
+ def visit_Arel_Nodes_Distinct(o, collector)
189
+ collector << "DISTINCT"
190
+ end
191
+
192
+ def visit_Arel_Nodes_DistinctOn(o, collector)
193
+ raise NotImplementedError, "DISTINCT ON not implemented for this db"
194
+ end
195
+
196
+ def visit_Arel_Nodes_With(o, collector)
197
+ collector << "WITH "
198
+ inject_join o.children, collector, ", "
199
+ end
200
+
201
+ def visit_Arel_Nodes_WithRecursive(o, collector)
202
+ collector << "WITH RECURSIVE "
203
+ inject_join o.children, collector, ", "
204
+ end
205
+
206
+ def visit_Arel_Nodes_Union(o, collector)
207
+ infix_value_with_paren(o, collector, " UNION ")
208
+ end
209
+
210
+ def visit_Arel_Nodes_UnionAll(o, collector)
211
+ infix_value_with_paren(o, collector, " UNION ALL ")
212
+ end
213
+
214
+ def visit_Arel_Nodes_Intersect(o, collector)
215
+ collector << "( "
216
+ infix_value(o, collector, " INTERSECT ") << " )"
217
+ end
218
+
219
+ def visit_Arel_Nodes_Except(o, collector)
220
+ collector << "( "
221
+ infix_value(o, collector, " EXCEPT ") << " )"
222
+ end
223
+
224
+ def visit_Arel_Nodes_NamedWindow(o, collector)
225
+ collector << quote_column_name(o.name)
226
+ collector << " AS "
227
+ visit_Arel_Nodes_Window o, collector
228
+ end
229
+
230
+ def visit_Arel_Nodes_Window(o, collector)
231
+ collector << "("
232
+
233
+ collect_nodes_for o.partitions, collector, "PARTITION BY "
234
+
235
+ if o.orders.any?
236
+ collector << " " if o.partitions.any?
237
+ collector << "ORDER BY "
238
+ collector = inject_join o.orders, collector, ", "
239
+ end
240
+
241
+ if o.framing
242
+ collector << " " if o.partitions.any? || o.orders.any?
243
+ collector = visit o.framing, collector
244
+ end
245
+
246
+ collector << ")"
247
+ end
248
+
249
+ def visit_Arel_Nodes_Rows(o, collector)
250
+ if o.expr
251
+ collector << "ROWS "
252
+ visit o.expr, collector
253
+ else
254
+ collector << "ROWS"
255
+ end
256
+ end
257
+
258
+ def visit_Arel_Nodes_Range(o, collector)
259
+ if o.expr
260
+ collector << "RANGE "
261
+ visit o.expr, collector
262
+ else
263
+ collector << "RANGE"
264
+ end
265
+ end
266
+
267
+ def visit_Arel_Nodes_Preceding(o, collector)
268
+ collector = if o.expr
269
+ visit o.expr, collector
270
+ else
271
+ collector << "UNBOUNDED"
272
+ end
273
+
274
+ collector << " PRECEDING"
275
+ end
276
+
277
+ def visit_Arel_Nodes_Following(o, collector)
278
+ collector = if o.expr
279
+ visit o.expr, collector
280
+ else
281
+ collector << "UNBOUNDED"
282
+ end
283
+
284
+ collector << " FOLLOWING"
285
+ end
286
+
287
+ def visit_Arel_Nodes_CurrentRow(o, collector)
288
+ collector << "CURRENT ROW"
289
+ end
290
+
291
+ def visit_Arel_Nodes_Over(o, collector)
292
+ case o.right
293
+ when nil
294
+ visit(o.left, collector) << " OVER ()"
295
+ when Arel::Nodes::SqlLiteral
296
+ infix_value o, collector, " OVER "
297
+ when String, Symbol
298
+ visit(o.left, collector) << " OVER #{quote_column_name o.right.to_s}"
299
+ else
300
+ infix_value o, collector, " OVER "
301
+ end
302
+ end
303
+
304
+ def visit_Arel_Nodes_Offset(o, collector)
305
+ collector << "OFFSET "
306
+ visit o.expr, collector
307
+ end
308
+
309
+ def visit_Arel_Nodes_Limit(o, collector)
310
+ collector << "LIMIT "
311
+ visit o.expr, collector
312
+ end
313
+
314
+ def visit_Arel_Nodes_Lock(o, collector)
315
+ visit o.expr, collector
316
+ end
317
+
318
+ def visit_Arel_Nodes_Grouping(o, collector)
319
+ if o.expr.is_a? Nodes::Grouping
320
+ visit(o.expr, collector)
321
+ else
322
+ collector << "("
323
+ visit(o.expr, collector) << ")"
324
+ end
325
+ end
326
+
327
+ def visit_Arel_SelectManager(o, collector)
328
+ collector << "("
329
+ visit(o.ast, collector) << ")"
330
+ end
331
+
332
+ def visit_Arel_Nodes_Ascending(o, collector)
333
+ visit(o.expr, collector) << " ASC"
334
+ end
335
+
336
+ def visit_Arel_Nodes_Descending(o, collector)
337
+ visit(o.expr, collector) << " DESC"
338
+ end
339
+
340
+ def visit_Arel_Nodes_Group(o, collector)
341
+ visit o.expr, collector
342
+ end
343
+
344
+ def visit_Arel_Nodes_NamedFunction(o, collector)
345
+ collector << o.name
346
+ collector << "("
347
+ collector << "DISTINCT " if o.distinct
348
+ collector = inject_join(o.expressions, collector, ", ") << ")"
349
+ if o.alias
350
+ collector << " AS "
351
+ visit o.alias, collector
352
+ else
353
+ collector
354
+ end
355
+ end
356
+
357
+ def visit_Arel_Nodes_Extract(o, collector)
358
+ collector << "EXTRACT(#{o.field.to_s.upcase} FROM "
359
+ visit(o.expr, collector) << ")"
360
+ end
361
+
362
+ def visit_Arel_Nodes_Count(o, collector)
363
+ aggregate "COUNT", o, collector
364
+ end
365
+
366
+ def visit_Arel_Nodes_Sum(o, collector)
367
+ aggregate "SUM", o, collector
368
+ end
369
+
370
+ def visit_Arel_Nodes_Max(o, collector)
371
+ aggregate "MAX", o, collector
372
+ end
373
+
374
+ def visit_Arel_Nodes_Min(o, collector)
375
+ aggregate "MIN", o, collector
376
+ end
377
+
378
+ def visit_Arel_Nodes_Avg(o, collector)
379
+ aggregate "AVG", o, collector
380
+ end
381
+
382
+ def visit_Arel_Nodes_TableAlias(o, collector)
383
+ collector = visit o.relation, collector
384
+ collector << " "
385
+ collector << quote_table_name(o.name)
386
+ end
387
+
388
+ def visit_Arel_Nodes_Between(o, collector)
389
+ collector = visit o.left, collector
390
+ collector << " BETWEEN "
391
+ visit o.right, collector
392
+ end
393
+
394
+ def visit_Arel_Nodes_GreaterThanOrEqual(o, collector)
395
+ collector = visit o.left, collector
396
+ collector << " >= "
397
+ visit o.right, collector
398
+ end
399
+
400
+ def visit_Arel_Nodes_GreaterThan(o, collector)
401
+ collector = visit o.left, collector
402
+ collector << " > "
403
+ visit o.right, collector
404
+ end
405
+
406
+ def visit_Arel_Nodes_LessThanOrEqual(o, collector)
407
+ collector = visit o.left, collector
408
+ collector << " <= "
409
+ visit o.right, collector
410
+ end
411
+
412
+ def visit_Arel_Nodes_LessThan(o, collector)
413
+ collector = visit o.left, collector
414
+ collector << " < "
415
+ visit o.right, collector
416
+ end
417
+
418
+ def visit_Arel_Nodes_Matches(o, collector)
419
+ collector = visit o.left, collector
420
+ collector << " LIKE "
421
+ collector = visit o.right, collector
422
+ if o.escape
423
+ collector << " ESCAPE "
424
+ visit o.escape, collector
425
+ else
426
+ collector
427
+ end
428
+ end
429
+
430
+ def visit_Arel_Nodes_DoesNotMatch(o, collector)
431
+ collector = visit o.left, collector
432
+ collector << " NOT LIKE "
433
+ collector = visit o.right, collector
434
+ if o.escape
435
+ collector << " ESCAPE "
436
+ visit o.escape, collector
437
+ else
438
+ collector
439
+ end
440
+ end
441
+
442
+ def visit_Arel_Nodes_JoinSource(o, collector)
443
+ if o.left
444
+ collector = visit o.left, collector
445
+ end
446
+ if o.right.any?
447
+ collector << " " if o.left
448
+ collector = inject_join o.right, collector, " "
449
+ end
450
+ collector
451
+ end
452
+
453
+ def visit_Arel_Nodes_Regexp(o, collector)
454
+ raise NotImplementedError, "~ not implemented for this db"
455
+ end
456
+
457
+ def visit_Arel_Nodes_NotRegexp(o, collector)
458
+ raise NotImplementedError, "!~ not implemented for this db"
459
+ end
460
+
461
+ def visit_Arel_Nodes_StringJoin(o, collector)
462
+ visit o.left, collector
463
+ end
464
+
465
+ def visit_Arel_Nodes_FullOuterJoin(o, collector)
466
+ collector << "FULL OUTER JOIN "
467
+ collector = visit o.left, collector
468
+ collector << " "
469
+ visit o.right, collector
470
+ end
471
+
472
+ def visit_Arel_Nodes_OuterJoin(o, collector)
473
+ collector << "LEFT OUTER JOIN "
474
+ collector = visit o.left, collector
475
+ collector << " "
476
+ visit o.right, collector
477
+ end
478
+
479
+ def visit_Arel_Nodes_RightOuterJoin(o, collector)
480
+ collector << "RIGHT OUTER JOIN "
481
+ collector = visit o.left, collector
482
+ collector << " "
483
+ visit o.right, collector
484
+ end
485
+
486
+ def visit_Arel_Nodes_InnerJoin(o, collector)
487
+ collector << "INNER JOIN "
488
+ collector = visit o.left, collector
489
+ if o.right
490
+ collector << " "
491
+ visit(o.right, collector)
492
+ else
493
+ collector
494
+ end
495
+ end
496
+
497
+ def visit_Arel_Nodes_On(o, collector)
498
+ collector << "ON "
499
+ visit o.expr, collector
500
+ end
501
+
502
+ def visit_Arel_Nodes_Not(o, collector)
503
+ collector << "NOT ("
504
+ visit(o.expr, collector) << ")"
505
+ end
506
+
507
+ def visit_Arel_Table(o, collector)
508
+ if o.table_alias
509
+ collector << quote_table_name(o.name) << " " << quote_table_name(o.table_alias)
510
+ else
511
+ collector << quote_table_name(o.name)
512
+ end
513
+ end
514
+
515
+ def visit_Arel_Nodes_In(o, collector)
516
+ unless Array === o.right
517
+ return collect_in_clause(o.left, o.right, collector)
518
+ end
519
+
520
+ unless o.right.empty?
521
+ o.right.delete_if { |value| unboundable?(value) }
522
+ end
523
+
524
+ return collector << "1=0" if o.right.empty?
525
+
526
+ in_clause_length = @connection.in_clause_length
527
+
528
+ if !in_clause_length || o.right.length <= in_clause_length
529
+ collect_in_clause(o.left, o.right, collector)
530
+ else
531
+ collector << "("
532
+ o.right.each_slice(in_clause_length).each_with_index do |right, i|
533
+ collector << " OR " unless i == 0
534
+ collect_in_clause(o.left, right, collector)
535
+ end
536
+ collector << ")"
537
+ end
538
+ end
539
+
540
+ def collect_in_clause(left, right, collector)
541
+ collector = visit left, collector
542
+ collector << " IN ("
543
+ visit(right, collector) << ")"
544
+ end
545
+
546
+ def visit_Arel_Nodes_NotIn(o, collector)
547
+ unless Array === o.right
548
+ return collect_not_in_clause(o.left, o.right, collector)
549
+ end
550
+
551
+ unless o.right.empty?
552
+ o.right.delete_if { |value| unboundable?(value) }
553
+ end
554
+
555
+ return collector << "1=1" if o.right.empty?
556
+
557
+ in_clause_length = @connection.in_clause_length
558
+
559
+ if !in_clause_length || o.right.length <= in_clause_length
560
+ collect_not_in_clause(o.left, o.right, collector)
561
+ else
562
+ o.right.each_slice(in_clause_length).each_with_index do |right, i|
563
+ collector << " AND " unless i == 0
564
+ collect_not_in_clause(o.left, right, collector)
565
+ end
566
+ collector
567
+ end
568
+ end
569
+
570
+ def collect_not_in_clause(left, right, collector)
571
+ collector = visit left, collector
572
+ collector << " NOT IN ("
573
+ visit(right, collector) << ")"
574
+ end
575
+
576
+ def visit_Arel_Nodes_And(o, collector)
577
+ inject_join o.children, collector, " AND "
578
+ end
579
+
580
+ def visit_Arel_Nodes_Or(o, collector)
581
+ collector = visit o.left, collector
582
+ collector << " OR "
583
+ visit o.right, collector
584
+ end
585
+
586
+ def visit_Arel_Nodes_Assignment(o, collector)
587
+ case o.right
588
+ when Arel::Nodes::Node, Arel::Attributes::Attribute
589
+ collector = visit o.left, collector
590
+ collector << " = "
591
+ visit o.right, collector
592
+ else
593
+ collector = visit o.left, collector
594
+ collector << " = "
595
+ collector << quote(o.right).to_s
596
+ end
597
+ end
598
+
599
+ def visit_Arel_Nodes_Equality(o, collector)
600
+ right = o.right
601
+
602
+ return collector << "1=0" if unboundable?(right)
603
+
604
+ collector = visit o.left, collector
605
+
606
+ if right.nil?
607
+ collector << " IS NULL"
608
+ else
609
+ collector << " = "
610
+ visit right, collector
611
+ end
612
+ end
613
+
614
+ def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
615
+ if o.right.nil?
616
+ collector = visit o.left, collector
617
+ collector << " IS NULL"
618
+ else
619
+ collector = is_distinct_from(o, collector)
620
+ collector << " = 0"
621
+ end
622
+ end
623
+
624
+ def visit_Arel_Nodes_IsDistinctFrom(o, collector)
625
+ if o.right.nil?
626
+ collector = visit o.left, collector
627
+ collector << " IS NOT NULL"
628
+ else
629
+ collector = is_distinct_from(o, collector)
630
+ collector << " = 1"
631
+ end
632
+ end
633
+
634
+ def visit_Arel_Nodes_NotEqual(o, collector)
635
+ right = o.right
636
+
637
+ return collector << "1=1" if unboundable?(right)
638
+
639
+ collector = visit o.left, collector
640
+
641
+ if right.nil?
642
+ collector << " IS NOT NULL"
643
+ else
644
+ collector << " != "
645
+ visit right, collector
646
+ end
647
+ end
648
+
649
+ def visit_Arel_Nodes_As(o, collector)
650
+ collector = visit o.left, collector
651
+ collector << " AS "
652
+ visit o.right, collector
653
+ end
654
+
655
+ def visit_Arel_Nodes_Case(o, collector)
656
+ collector << "CASE "
657
+ if o.case
658
+ visit o.case, collector
659
+ collector << " "
660
+ end
661
+ o.conditions.each do |condition|
662
+ visit condition, collector
663
+ collector << " "
664
+ end
665
+ if o.default
666
+ visit o.default, collector
667
+ collector << " "
668
+ end
669
+ collector << "END"
670
+ end
671
+
672
+ def visit_Arel_Nodes_When(o, collector)
673
+ collector << "WHEN "
674
+ visit o.left, collector
675
+ collector << " THEN "
676
+ visit o.right, collector
677
+ end
678
+
679
+ def visit_Arel_Nodes_Else(o, collector)
680
+ collector << "ELSE "
681
+ visit o.expr, collector
682
+ end
683
+
684
+ def visit_Arel_Nodes_UnqualifiedColumn(o, collector)
685
+ collector << quote_column_name(o.name)
686
+ end
687
+
688
+ def visit_Arel_Attributes_Attribute(o, collector)
689
+ join_name = o.relation.table_alias || o.relation.name
690
+ collector << quote_table_name(join_name) << "." << quote_column_name(o.name)
691
+ end
692
+ alias :visit_Arel_Attributes_Integer :visit_Arel_Attributes_Attribute
693
+ alias :visit_Arel_Attributes_Float :visit_Arel_Attributes_Attribute
694
+ alias :visit_Arel_Attributes_Decimal :visit_Arel_Attributes_Attribute
695
+ alias :visit_Arel_Attributes_String :visit_Arel_Attributes_Attribute
696
+ alias :visit_Arel_Attributes_Time :visit_Arel_Attributes_Attribute
697
+ alias :visit_Arel_Attributes_Boolean :visit_Arel_Attributes_Attribute
698
+
699
+ def literal(o, collector); collector << o.to_s; end
700
+
701
+ def visit_Arel_Nodes_BindParam(o, collector)
702
+ collector.add_bind(o.value) { "?" }
703
+ end
704
+
705
+ alias :visit_Arel_Nodes_SqlLiteral :literal
706
+ alias :visit_Integer :literal
707
+
708
+ def quoted(o, a)
709
+ if a && a.able_to_type_cast?
710
+ quote(a.type_cast_for_database(o))
711
+ else
712
+ quote(o)
713
+ end
714
+ end
715
+
716
+ def unsupported(o, collector)
717
+ raise UnsupportedVisitError.new(o)
718
+ end
719
+
720
+ alias :visit_ActiveSupport_Multibyte_Chars :unsupported
721
+ alias :visit_ActiveSupport_StringInquirer :unsupported
722
+ alias :visit_BigDecimal :unsupported
723
+ alias :visit_Class :unsupported
724
+ alias :visit_Date :unsupported
725
+ alias :visit_DateTime :unsupported
726
+ alias :visit_FalseClass :unsupported
727
+ alias :visit_Float :unsupported
728
+ alias :visit_Hash :unsupported
729
+ alias :visit_NilClass :unsupported
730
+ alias :visit_String :unsupported
731
+ alias :visit_Symbol :unsupported
732
+ alias :visit_Time :unsupported
733
+ alias :visit_TrueClass :unsupported
734
+
735
+ def visit_Arel_Nodes_InfixOperation(o, collector)
736
+ collector = visit o.left, collector
737
+ collector << " #{o.operator} "
738
+ visit o.right, collector
739
+ end
740
+
741
+ alias :visit_Arel_Nodes_Addition :visit_Arel_Nodes_InfixOperation
742
+ alias :visit_Arel_Nodes_Subtraction :visit_Arel_Nodes_InfixOperation
743
+ alias :visit_Arel_Nodes_Multiplication :visit_Arel_Nodes_InfixOperation
744
+ alias :visit_Arel_Nodes_Division :visit_Arel_Nodes_InfixOperation
745
+
746
+ def visit_Arel_Nodes_UnaryOperation(o, collector)
747
+ collector << " #{o.operator} "
748
+ visit o.expr, collector
749
+ end
750
+
751
+ def visit_Array(o, collector)
752
+ inject_join o, collector, ", "
753
+ end
754
+ alias :visit_Set :visit_Array
755
+
756
+ def quote(value)
757
+ return value if Arel::Nodes::SqlLiteral === value
758
+ @connection.quote value
759
+ end
760
+
761
+ def quote_table_name(name)
762
+ return name if Arel::Nodes::SqlLiteral === name
763
+ @connection.quote_table_name(name)
764
+ end
765
+
766
+ def quote_column_name(name)
767
+ return name if Arel::Nodes::SqlLiteral === name
768
+ @connection.quote_column_name(name)
769
+ end
770
+
771
+ def sanitize_as_sql_comment(value)
772
+ return value if Arel::Nodes::SqlLiteral === value
773
+ @connection.sanitize_as_sql_comment(value)
774
+ end
775
+
776
+ def collect_optimizer_hints(o, collector)
777
+ maybe_visit o.optimizer_hints, collector
778
+ end
779
+
780
+ def maybe_visit(thing, collector)
781
+ return collector unless thing
782
+ collector << " "
783
+ visit thing, collector
784
+ end
785
+
786
+ def inject_join(list, collector, join_str)
787
+ list.each_with_index do |x, i|
788
+ collector << join_str unless i == 0
789
+ collector = visit(x, collector)
790
+ end
791
+ collector
792
+ end
793
+
794
+ def unboundable?(value)
795
+ value.respond_to?(:unboundable?) && value.unboundable?
796
+ end
797
+
798
+ def has_join_sources?(o)
799
+ o.relation.is_a?(Nodes::JoinSource) && !o.relation.right.empty?
800
+ end
801
+
802
+ def has_limit_or_offset_or_orders?(o)
803
+ o.limit || o.offset || !o.orders.empty?
804
+ end
805
+
806
+ # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
807
+ # on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
808
+ # an UPDATE statement, so in the MySQL visitor we redefine this to do that.
809
+ def prepare_update_statement(o)
810
+ if o.key && (has_limit_or_offset_or_orders?(o) || has_join_sources?(o))
811
+ stmt = o.clone
812
+ stmt.limit = nil
813
+ stmt.offset = nil
814
+ stmt.orders = []
815
+ stmt.wheres = [Nodes::In.new(o.key, [build_subselect(o.key, o)])]
816
+ stmt.relation = o.relation.left if has_join_sources?(o)
817
+ stmt
818
+ else
819
+ o
820
+ end
821
+ end
822
+ alias :prepare_delete_statement :prepare_update_statement
823
+
824
+ # FIXME: we should probably have a 2-pass visitor for this
825
+ def build_subselect(key, o)
826
+ stmt = Nodes::SelectStatement.new
827
+ core = stmt.cores.first
828
+ core.froms = o.relation
829
+ core.wheres = o.wheres
830
+ core.projections = [key]
831
+ stmt.limit = o.limit
832
+ stmt.offset = o.offset
833
+ stmt.orders = o.orders
834
+ stmt
835
+ end
836
+
837
+ def infix_value(o, collector, value)
838
+ collector = visit o.left, collector
839
+ collector << value
840
+ visit o.right, collector
841
+ end
842
+
843
+ def infix_value_with_paren(o, collector, value, suppress_parens = false)
844
+ collector << "( " unless suppress_parens
845
+ collector = if o.left.class == o.class
846
+ infix_value_with_paren(o.left, collector, value, true)
847
+ else
848
+ visit o.left, collector
849
+ end
850
+ collector << value
851
+ collector = if o.right.class == o.class
852
+ infix_value_with_paren(o.right, collector, value, true)
853
+ else
854
+ visit o.right, collector
855
+ end
856
+ collector << " )" unless suppress_parens
857
+ collector
858
+ end
859
+
860
+ def aggregate(name, o, collector)
861
+ collector << "#{name}("
862
+ if o.distinct
863
+ collector << "DISTINCT "
864
+ end
865
+ collector = inject_join(o.expressions, collector, ", ") << ")"
866
+ if o.alias
867
+ collector << " AS "
868
+ visit o.alias, collector
869
+ else
870
+ collector
871
+ end
872
+ end
873
+
874
+ def is_distinct_from(o, collector)
875
+ collector << "CASE WHEN "
876
+ collector = visit o.left, collector
877
+ collector << " = "
878
+ collector = visit o.right, collector
879
+ collector << " OR ("
880
+ collector = visit o.left, collector
881
+ collector << " IS NULL AND "
882
+ collector = visit o.right, collector
883
+ collector << " IS NULL)"
884
+ collector << " THEN 0 ELSE 1 END"
885
+ end
886
+ end
887
+ end
888
+ end