activerecord 5.2.4.2 → 6.0.2.2

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