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