activerecord 5.2.6 → 6.0.0

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 (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +609 -622
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +4 -2
  7. data/lib/active_record/associations/association.rb +52 -19
  8. data/lib/active_record/associations/association_scope.rb +4 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  11. data/lib/active_record/associations/builder/association.rb +14 -18
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  13. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  15. data/lib/active_record/associations/builder/has_many.rb +2 -0
  16. data/lib/active_record/associations/builder/has_one.rb +35 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  18. data/lib/active_record/associations/collection_association.rb +6 -21
  19. data/lib/active_record/associations/collection_proxy.rb +12 -15
  20. data/lib/active_record/associations/foreign_association.rb +7 -0
  21. data/lib/active_record/associations/has_many_association.rb +2 -10
  22. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  23. data/lib/active_record/associations/has_one_association.rb +28 -30
  24. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  25. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  26. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  27. data/lib/active_record/associations/join_dependency.rb +24 -28
  28. data/lib/active_record/associations/preloader/association.rb +38 -36
  29. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  30. data/lib/active_record/associations/preloader.rb +40 -32
  31. data/lib/active_record/associations/singular_association.rb +2 -16
  32. data/lib/active_record/associations.rb +19 -14
  33. data/lib/active_record/attribute_assignment.rb +7 -10
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  35. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  36. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  37. data/lib/active_record/attribute_methods/query.rb +2 -3
  38. data/lib/active_record/attribute_methods/read.rb +15 -53
  39. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  41. data/lib/active_record/attribute_methods/write.rb +17 -24
  42. data/lib/active_record/attribute_methods.rb +28 -100
  43. data/lib/active_record/attributes.rb +13 -0
  44. data/lib/active_record/autosave_association.rb +5 -9
  45. data/lib/active_record/base.rb +2 -3
  46. data/lib/active_record/callbacks.rb +5 -19
  47. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +94 -16
  48. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  49. data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
  50. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -8
  51. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  52. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  53. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  54. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +132 -53
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +180 -47
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
  59. data/lib/active_record/connection_adapters/column.rb +17 -13
  60. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  61. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  64. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  65. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  68. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
  72. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  77. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  79. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  80. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  81. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  84. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  85. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
  87. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  88. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  89. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  90. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  91. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -141
  93. data/lib/active_record/connection_handling.rb +149 -27
  94. data/lib/active_record/core.rb +100 -60
  95. data/lib/active_record/counter_cache.rb +4 -29
  96. data/lib/active_record/database_configurations/database_config.rb +37 -0
  97. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  98. data/lib/active_record/database_configurations/url_config.rb +79 -0
  99. data/lib/active_record/database_configurations.rb +233 -0
  100. data/lib/active_record/dynamic_matchers.rb +1 -1
  101. data/lib/active_record/enum.rb +37 -7
  102. data/lib/active_record/errors.rb +15 -7
  103. data/lib/active_record/explain.rb +1 -1
  104. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  105. data/lib/active_record/fixture_set/render_context.rb +17 -0
  106. data/lib/active_record/fixture_set/table_row.rb +153 -0
  107. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  108. data/lib/active_record/fixtures.rb +145 -472
  109. data/lib/active_record/gem_version.rb +3 -3
  110. data/lib/active_record/inheritance.rb +13 -3
  111. data/lib/active_record/insert_all.rb +179 -0
  112. data/lib/active_record/integration.rb +68 -16
  113. data/lib/active_record/internal_metadata.rb +10 -2
  114. data/lib/active_record/locking/optimistic.rb +5 -6
  115. data/lib/active_record/locking/pessimistic.rb +3 -3
  116. data/lib/active_record/log_subscriber.rb +7 -26
  117. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  118. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  119. data/lib/active_record/middleware/database_selector.rb +75 -0
  120. data/lib/active_record/migration/command_recorder.rb +50 -6
  121. data/lib/active_record/migration/compatibility.rb +76 -49
  122. data/lib/active_record/migration.rb +100 -81
  123. data/lib/active_record/model_schema.rb +30 -9
  124. data/lib/active_record/nested_attributes.rb +2 -2
  125. data/lib/active_record/no_touching.rb +7 -0
  126. data/lib/active_record/persistence.rb +228 -24
  127. data/lib/active_record/query_cache.rb +11 -4
  128. data/lib/active_record/querying.rb +32 -20
  129. data/lib/active_record/railtie.rb +80 -43
  130. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  131. data/lib/active_record/railties/controller_runtime.rb +30 -35
  132. data/lib/active_record/railties/databases.rake +196 -46
  133. data/lib/active_record/reflection.rb +32 -30
  134. data/lib/active_record/relation/batches.rb +13 -10
  135. data/lib/active_record/relation/calculations.rb +53 -47
  136. data/lib/active_record/relation/delegation.rb +26 -43
  137. data/lib/active_record/relation/finder_methods.rb +13 -26
  138. data/lib/active_record/relation/merger.rb +11 -20
  139. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  140. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  141. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  142. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  143. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  144. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  145. data/lib/active_record/relation/predicate_builder.rb +4 -6
  146. data/lib/active_record/relation/query_attribute.rb +13 -8
  147. data/lib/active_record/relation/query_methods.rb +189 -63
  148. data/lib/active_record/relation/spawn_methods.rb +1 -1
  149. data/lib/active_record/relation/where_clause.rb +14 -10
  150. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  151. data/lib/active_record/relation.rb +310 -80
  152. data/lib/active_record/result.rb +30 -11
  153. data/lib/active_record/sanitization.rb +32 -40
  154. data/lib/active_record/schema.rb +2 -11
  155. data/lib/active_record/schema_dumper.rb +22 -7
  156. data/lib/active_record/schema_migration.rb +5 -1
  157. data/lib/active_record/scoping/default.rb +4 -5
  158. data/lib/active_record/scoping/named.rb +19 -15
  159. data/lib/active_record/scoping.rb +8 -8
  160. data/lib/active_record/statement_cache.rb +30 -3
  161. data/lib/active_record/store.rb +87 -8
  162. data/lib/active_record/table_metadata.rb +10 -17
  163. data/lib/active_record/tasks/database_tasks.rb +194 -25
  164. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  165. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  166. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  167. data/lib/active_record/test_databases.rb +23 -0
  168. data/lib/active_record/test_fixtures.rb +224 -0
  169. data/lib/active_record/timestamp.rb +39 -25
  170. data/lib/active_record/touch_later.rb +4 -2
  171. data/lib/active_record/transactions.rb +57 -66
  172. data/lib/active_record/translation.rb +1 -1
  173. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  174. data/lib/active_record/type.rb +3 -4
  175. data/lib/active_record/type_caster/connection.rb +15 -14
  176. data/lib/active_record/type_caster/map.rb +1 -4
  177. data/lib/active_record/validations/uniqueness.rb +15 -27
  178. data/lib/active_record/validations.rb +1 -0
  179. data/lib/active_record.rb +9 -2
  180. data/lib/arel/alias_predication.rb +9 -0
  181. data/lib/arel/attributes/attribute.rb +37 -0
  182. data/lib/arel/attributes.rb +22 -0
  183. data/lib/arel/collectors/bind.rb +24 -0
  184. data/lib/arel/collectors/composite.rb +31 -0
  185. data/lib/arel/collectors/plain_string.rb +20 -0
  186. data/lib/arel/collectors/sql_string.rb +20 -0
  187. data/lib/arel/collectors/substitute_binds.rb +28 -0
  188. data/lib/arel/crud.rb +42 -0
  189. data/lib/arel/delete_manager.rb +18 -0
  190. data/lib/arel/errors.rb +9 -0
  191. data/lib/arel/expressions.rb +29 -0
  192. data/lib/arel/factory_methods.rb +49 -0
  193. data/lib/arel/insert_manager.rb +49 -0
  194. data/lib/arel/math.rb +45 -0
  195. data/lib/arel/nodes/and.rb +32 -0
  196. data/lib/arel/nodes/ascending.rb +23 -0
  197. data/lib/arel/nodes/binary.rb +52 -0
  198. data/lib/arel/nodes/bind_param.rb +36 -0
  199. data/lib/arel/nodes/case.rb +55 -0
  200. data/lib/arel/nodes/casted.rb +50 -0
  201. data/lib/arel/nodes/comment.rb +29 -0
  202. data/lib/arel/nodes/count.rb +12 -0
  203. data/lib/arel/nodes/delete_statement.rb +45 -0
  204. data/lib/arel/nodes/descending.rb +23 -0
  205. data/lib/arel/nodes/equality.rb +18 -0
  206. data/lib/arel/nodes/extract.rb +24 -0
  207. data/lib/arel/nodes/false.rb +16 -0
  208. data/lib/arel/nodes/full_outer_join.rb +8 -0
  209. data/lib/arel/nodes/function.rb +44 -0
  210. data/lib/arel/nodes/grouping.rb +8 -0
  211. data/lib/arel/nodes/in.rb +8 -0
  212. data/lib/arel/nodes/infix_operation.rb +80 -0
  213. data/lib/arel/nodes/inner_join.rb +8 -0
  214. data/lib/arel/nodes/insert_statement.rb +37 -0
  215. data/lib/arel/nodes/join_source.rb +20 -0
  216. data/lib/arel/nodes/matches.rb +18 -0
  217. data/lib/arel/nodes/named_function.rb +23 -0
  218. data/lib/arel/nodes/node.rb +50 -0
  219. data/lib/arel/nodes/node_expression.rb +13 -0
  220. data/lib/arel/nodes/outer_join.rb +8 -0
  221. data/lib/arel/nodes/over.rb +15 -0
  222. data/lib/arel/nodes/regexp.rb +16 -0
  223. data/lib/arel/nodes/right_outer_join.rb +8 -0
  224. data/lib/arel/nodes/select_core.rb +67 -0
  225. data/lib/arel/nodes/select_statement.rb +41 -0
  226. data/lib/arel/nodes/sql_literal.rb +16 -0
  227. data/lib/arel/nodes/string_join.rb +11 -0
  228. data/lib/arel/nodes/table_alias.rb +27 -0
  229. data/lib/arel/nodes/terminal.rb +16 -0
  230. data/lib/arel/nodes/true.rb +16 -0
  231. data/lib/arel/nodes/unary.rb +45 -0
  232. data/lib/arel/nodes/unary_operation.rb +20 -0
  233. data/lib/arel/nodes/unqualified_column.rb +22 -0
  234. data/lib/arel/nodes/update_statement.rb +41 -0
  235. data/lib/arel/nodes/values_list.rb +9 -0
  236. data/lib/arel/nodes/window.rb +126 -0
  237. data/lib/arel/nodes/with.rb +11 -0
  238. data/lib/arel/nodes.rb +68 -0
  239. data/lib/arel/order_predications.rb +13 -0
  240. data/lib/arel/predications.rb +257 -0
  241. data/lib/arel/select_manager.rb +271 -0
  242. data/lib/arel/table.rb +110 -0
  243. data/lib/arel/tree_manager.rb +72 -0
  244. data/lib/arel/update_manager.rb +34 -0
  245. data/lib/arel/visitors/depth_first.rb +204 -0
  246. data/lib/arel/visitors/dot.rb +297 -0
  247. data/lib/arel/visitors/ibm_db.rb +34 -0
  248. data/lib/arel/visitors/informix.rb +62 -0
  249. data/lib/arel/visitors/mssql.rb +157 -0
  250. data/lib/arel/visitors/mysql.rb +83 -0
  251. data/lib/arel/visitors/oracle.rb +159 -0
  252. data/lib/arel/visitors/oracle12.rb +66 -0
  253. data/lib/arel/visitors/postgresql.rb +110 -0
  254. data/lib/arel/visitors/sqlite.rb +39 -0
  255. data/lib/arel/visitors/to_sql.rb +889 -0
  256. data/lib/arel/visitors/visitor.rb +46 -0
  257. data/lib/arel/visitors/where_sql.rb +23 -0
  258. data/lib/arel/visitors.rb +20 -0
  259. data/lib/arel/window_predications.rb +9 -0
  260. data/lib/arel.rb +51 -0
  261. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  262. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  263. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  264. data/lib/rails/generators/active_record/migration.rb +14 -1
  265. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  266. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  267. metadata +108 -26
  268. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Visitors
5
+ class MSSQL < Arel::Visitors::ToSql
6
+ RowNumber = Struct.new :children
7
+
8
+ def initialize(*)
9
+ @primary_keys = {}
10
+ super
11
+ end
12
+
13
+ private
14
+
15
+ def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
16
+ right = o.right
17
+
18
+ if right.nil?
19
+ collector = visit o.left, collector
20
+ collector << " IS NULL"
21
+ else
22
+ collector << "EXISTS (VALUES ("
23
+ collector = visit o.left, collector
24
+ collector << ") INTERSECT VALUES ("
25
+ collector = visit right, collector
26
+ collector << "))"
27
+ end
28
+ end
29
+
30
+ def visit_Arel_Nodes_IsDistinctFrom(o, collector)
31
+ if o.right.nil?
32
+ collector = visit o.left, collector
33
+ collector << " IS NOT NULL"
34
+ else
35
+ collector << "NOT "
36
+ visit_Arel_Nodes_IsNotDistinctFrom o, collector
37
+ end
38
+ end
39
+
40
+ def visit_Arel_Visitors_MSSQL_RowNumber(o, collector)
41
+ collector << "ROW_NUMBER() OVER (ORDER BY "
42
+ inject_join(o.children, collector, ", ") << ") as _row_num"
43
+ end
44
+
45
+ def visit_Arel_Nodes_SelectStatement(o, collector)
46
+ if !o.limit && !o.offset
47
+ return super
48
+ end
49
+
50
+ is_select_count = false
51
+ o.cores.each { |x|
52
+ core_order_by = row_num_literal determine_order_by(o.orders, x)
53
+ if select_count? x
54
+ x.projections = [core_order_by]
55
+ is_select_count = true
56
+ else
57
+ x.projections << core_order_by
58
+ end
59
+ }
60
+
61
+ if is_select_count
62
+ # fixme count distinct wouldn't work with limit or offset
63
+ collector << "SELECT COUNT(1) as count_id FROM ("
64
+ end
65
+
66
+ collector << "SELECT _t.* FROM ("
67
+ collector = o.cores.inject(collector) { |c, x|
68
+ visit_Arel_Nodes_SelectCore x, c
69
+ }
70
+ collector << ") as _t WHERE #{get_offset_limit_clause(o)}"
71
+
72
+ if is_select_count
73
+ collector << ") AS subquery"
74
+ else
75
+ collector
76
+ end
77
+ end
78
+
79
+ def visit_Arel_Nodes_SelectCore(o, collector)
80
+ collector = super
81
+ maybe_visit o.optimizer_hints, collector
82
+ end
83
+
84
+ def visit_Arel_Nodes_OptimizerHints(o, collector)
85
+ hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(", ")
86
+ collector << "OPTION (#{hints})"
87
+ end
88
+
89
+ def get_offset_limit_clause(o)
90
+ first_row = o.offset ? o.offset.expr.to_i + 1 : 1
91
+ last_row = o.limit ? o.limit.expr.to_i - 1 + first_row : nil
92
+ if last_row
93
+ " _row_num BETWEEN #{first_row} AND #{last_row}"
94
+ else
95
+ " _row_num >= #{first_row}"
96
+ end
97
+ end
98
+
99
+ def visit_Arel_Nodes_DeleteStatement(o, collector)
100
+ collector << "DELETE "
101
+ if o.limit
102
+ collector << "TOP ("
103
+ visit o.limit.expr, collector
104
+ collector << ") "
105
+ end
106
+ collector << "FROM "
107
+ collector = visit o.relation, collector
108
+ if o.wheres.any?
109
+ collector << " WHERE "
110
+ inject_join o.wheres, collector, " AND "
111
+ else
112
+ collector
113
+ end
114
+ end
115
+
116
+ def collect_optimizer_hints(o, collector)
117
+ collector
118
+ end
119
+
120
+ def determine_order_by(orders, x)
121
+ if orders.any?
122
+ orders
123
+ elsif x.groups.any?
124
+ x.groups
125
+ else
126
+ pk = find_left_table_pk(x.froms)
127
+ pk ? [pk] : []
128
+ end
129
+ end
130
+
131
+ def row_num_literal(order_by)
132
+ RowNumber.new order_by
133
+ end
134
+
135
+ def select_count?(x)
136
+ x.projections.length == 1 && Arel::Nodes::Count === x.projections.first
137
+ end
138
+
139
+ # FIXME raise exception of there is no pk?
140
+ def find_left_table_pk(o)
141
+ if o.kind_of?(Arel::Nodes::Join)
142
+ find_left_table_pk(o.left)
143
+ elsif o.instance_of?(Arel::Table)
144
+ find_primary_key(o)
145
+ end
146
+ end
147
+
148
+ def find_primary_key(o)
149
+ @primary_keys[o.name] ||= begin
150
+ primary_key_name = @connection.primary_key(o.name)
151
+ # some tables might be without primary key
152
+ primary_key_name && o[primary_key_name]
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Visitors
5
+ class MySQL < Arel::Visitors::ToSql
6
+ private
7
+ def visit_Arel_Nodes_Bin(o, collector)
8
+ collector << "BINARY "
9
+ visit o.expr, collector
10
+ end
11
+
12
+ def visit_Arel_Nodes_UnqualifiedColumn(o, collector)
13
+ visit o.expr, collector
14
+ end
15
+
16
+ ###
17
+ # :'(
18
+ # http://dev.mysql.com/doc/refman/5.0/en/select.html#id3482214
19
+ def visit_Arel_Nodes_SelectStatement(o, collector)
20
+ if o.offset && !o.limit
21
+ o.limit = Arel::Nodes::Limit.new(18446744073709551615)
22
+ end
23
+ super
24
+ end
25
+
26
+ def visit_Arel_Nodes_SelectCore(o, collector)
27
+ o.froms ||= Arel.sql("DUAL")
28
+ super
29
+ end
30
+
31
+ def visit_Arel_Nodes_Concat(o, collector)
32
+ collector << " CONCAT("
33
+ visit o.left, collector
34
+ collector << ", "
35
+ visit o.right, collector
36
+ collector << ") "
37
+ collector
38
+ end
39
+
40
+ def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
41
+ collector = visit o.left, collector
42
+ collector << " <=> "
43
+ visit o.right, collector
44
+ end
45
+
46
+ def visit_Arel_Nodes_IsDistinctFrom(o, collector)
47
+ collector << "NOT "
48
+ visit_Arel_Nodes_IsNotDistinctFrom o, collector
49
+ end
50
+
51
+ # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
52
+ # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
53
+ # these, we must use a subquery.
54
+ def prepare_update_statement(o)
55
+ if o.offset || has_join_sources?(o) && has_limit_or_offset_or_orders?(o)
56
+ super
57
+ else
58
+ o
59
+ end
60
+ end
61
+ alias :prepare_delete_statement :prepare_update_statement
62
+
63
+ # MySQL is too stupid to create a temporary table for use subquery, so we have
64
+ # to give it some prompting in the form of a subsubquery.
65
+ def build_subselect(key, o)
66
+ subselect = super
67
+
68
+ # Materialize subquery by adding distinct
69
+ # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
70
+ unless has_limit_or_offset_or_orders?(subselect)
71
+ core = subselect.cores.last
72
+ core.set_quantifier = Arel::Nodes::Distinct.new
73
+ end
74
+
75
+ Nodes::SelectStatement.new.tap do |stmt|
76
+ core = stmt.cores.last
77
+ core.froms = Nodes::Grouping.new(subselect).as("__active_record_temp")
78
+ core.projections = [Arel.sql(quote_column_name(key.name))]
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Visitors
5
+ class Oracle < Arel::Visitors::ToSql
6
+ private
7
+
8
+ def visit_Arel_Nodes_SelectStatement(o, collector)
9
+ o = order_hacks(o)
10
+
11
+ # if need to select first records without ORDER BY and GROUP BY and without DISTINCT
12
+ # then can use simple ROWNUM in WHERE clause
13
+ if o.limit && o.orders.empty? && o.cores.first.groups.empty? && !o.offset && o.cores.first.set_quantifier.class.to_s !~ /Distinct/
14
+ o.cores.last.wheres.push Nodes::LessThanOrEqual.new(
15
+ Nodes::SqlLiteral.new("ROWNUM"), o.limit.expr
16
+ )
17
+ return super
18
+ end
19
+
20
+ if o.limit && o.offset
21
+ o = o.dup
22
+ limit = o.limit.expr
23
+ offset = o.offset
24
+ o.offset = nil
25
+ collector << "
26
+ SELECT * FROM (
27
+ SELECT raw_sql_.*, rownum raw_rnum_
28
+ FROM ("
29
+
30
+ collector = super(o, collector)
31
+
32
+ if offset.expr.is_a? Nodes::BindParam
33
+ collector << ") raw_sql_ WHERE rownum <= ("
34
+ collector = visit offset.expr, collector
35
+ collector << " + "
36
+ collector = visit limit, collector
37
+ collector << ") ) WHERE raw_rnum_ > "
38
+ collector = visit offset.expr, collector
39
+ return collector
40
+ else
41
+ collector << ") raw_sql_
42
+ WHERE rownum <= #{offset.expr.to_i + limit}
43
+ )
44
+ WHERE "
45
+ return visit(offset, collector)
46
+ end
47
+ end
48
+
49
+ if o.limit
50
+ o = o.dup
51
+ limit = o.limit.expr
52
+ collector << "SELECT * FROM ("
53
+ collector = super(o, collector)
54
+ collector << ") WHERE ROWNUM <= "
55
+ return visit limit, collector
56
+ end
57
+
58
+ if o.offset
59
+ o = o.dup
60
+ offset = o.offset
61
+ o.offset = nil
62
+ collector << "SELECT * FROM (
63
+ SELECT raw_sql_.*, rownum raw_rnum_
64
+ FROM ("
65
+ collector = super(o, collector)
66
+ collector << ") raw_sql_
67
+ )
68
+ WHERE "
69
+ return visit offset, collector
70
+ end
71
+
72
+ super
73
+ end
74
+
75
+ def visit_Arel_Nodes_Limit(o, collector)
76
+ collector
77
+ end
78
+
79
+ def visit_Arel_Nodes_Offset(o, collector)
80
+ collector << "raw_rnum_ > "
81
+ visit o.expr, collector
82
+ end
83
+
84
+ def visit_Arel_Nodes_Except(o, collector)
85
+ collector << "( "
86
+ collector = infix_value o, collector, " MINUS "
87
+ collector << " )"
88
+ end
89
+
90
+ def visit_Arel_Nodes_UpdateStatement(o, collector)
91
+ # Oracle does not allow ORDER BY/LIMIT in UPDATEs.
92
+ if o.orders.any? && o.limit.nil?
93
+ # However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided,
94
+ # otherwise let the user deal with the error
95
+ o = o.dup
96
+ o.orders = []
97
+ end
98
+
99
+ super
100
+ end
101
+
102
+ ###
103
+ # Hacks for the order clauses specific to Oracle
104
+ def order_hacks(o)
105
+ return o if o.orders.empty?
106
+ return o unless o.cores.any? do |core|
107
+ core.projections.any? do |projection|
108
+ /FIRST_VALUE/ === projection
109
+ end
110
+ end
111
+ # Previous version with join and split broke ORDER BY clause
112
+ # if it contained functions with several arguments (separated by ',').
113
+ #
114
+ # orders = o.orders.map { |x| visit x }.join(', ').split(',')
115
+ orders = o.orders.map do |x|
116
+ string = visit(x, Arel::Collectors::SQLString.new).value
117
+ if string.include?(",")
118
+ split_order_string(string)
119
+ else
120
+ string
121
+ end
122
+ end.flatten
123
+ o.orders = []
124
+ orders.each_with_index do |order, i|
125
+ o.orders <<
126
+ Nodes::SqlLiteral.new("alias_#{i}__#{' DESC' if /\bdesc$/i === order}")
127
+ end
128
+ o
129
+ end
130
+
131
+ # Split string by commas but count opening and closing brackets
132
+ # and ignore commas inside brackets.
133
+ def split_order_string(string)
134
+ array = []
135
+ i = 0
136
+ string.split(",").each do |part|
137
+ if array[i]
138
+ array[i] << "," << part
139
+ else
140
+ # to ensure that array[i] will be String and not Arel::Nodes::SqlLiteral
141
+ array[i] = part.to_s
142
+ end
143
+ i += 1 if array[i].count("(") == array[i].count(")")
144
+ end
145
+ array
146
+ end
147
+
148
+ def visit_Arel_Nodes_BindParam(o, collector)
149
+ collector.add_bind(o.value) { |i| ":a#{i}" }
150
+ end
151
+
152
+ def is_distinct_from(o, collector)
153
+ collector << "DECODE("
154
+ collector = visit [o.left, o.right, 0, 1], collector
155
+ collector << ")"
156
+ end
157
+ end
158
+ end
159
+ end
@@ -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