activerecord 6.1.7 → 7.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +616 -1290
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +17 -14
  7. data/lib/active_record/association_relation.rb +2 -12
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +60 -21
  10. data/lib/active_record/associations/association_scope.rb +17 -12
  11. data/lib/active_record/associations/belongs_to_association.rb +37 -11
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
  13. data/lib/active_record/associations/builder/association.rb +11 -5
  14. data/lib/active_record/associations/builder/belongs_to.rb +41 -14
  15. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +4 -4
  19. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  20. data/lib/active_record/associations/collection_association.rb +46 -36
  21. data/lib/active_record/associations/collection_proxy.rb +44 -16
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +10 -3
  25. data/lib/active_record/associations/has_many_association.rb +29 -19
  26. data/lib/active_record/associations/has_many_through_association.rb +19 -8
  27. data/lib/active_record/associations/has_one_association.rb +20 -10
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  30. data/lib/active_record/associations/join_dependency.rb +28 -20
  31. data/lib/active_record/associations/nested_error.rb +47 -0
  32. data/lib/active_record/associations/preloader/association.rb +212 -53
  33. data/lib/active_record/associations/preloader/batch.rb +48 -0
  34. data/lib/active_record/associations/preloader/branch.rb +153 -0
  35. data/lib/active_record/associations/preloader/through_association.rb +50 -16
  36. data/lib/active_record/associations/preloader.rb +50 -121
  37. data/lib/active_record/associations/singular_association.rb +15 -3
  38. data/lib/active_record/associations/through_association.rb +25 -14
  39. data/lib/active_record/associations.rb +429 -522
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +1 -5
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  43. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  44. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  45. data/lib/active_record/attribute_methods/primary_key.rb +47 -27
  46. data/lib/active_record/attribute_methods/query.rb +31 -19
  47. data/lib/active_record/attribute_methods/read.rb +14 -11
  48. data/lib/active_record/attribute_methods/serialization.rb +174 -37
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +15 -9
  50. data/lib/active_record/attribute_methods/write.rb +12 -15
  51. data/lib/active_record/attribute_methods.rb +164 -52
  52. data/lib/active_record/attributes.rb +57 -54
  53. data/lib/active_record/autosave_association.rb +74 -57
  54. data/lib/active_record/base.rb +27 -5
  55. data/lib/active_record/callbacks.rb +19 -35
  56. data/lib/active_record/coders/column_serializer.rb +61 -0
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +70 -46
  59. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +325 -604
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +230 -64
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -143
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +348 -165
  75. data/lib/active_record/connection_adapters/column.rb +13 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
  78. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
  79. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  81. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  82. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  83. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
  85. data/lib/active_record/connection_adapters/pool_config.rb +26 -16
  86. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  87. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  88. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
  89. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  94. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  100. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
  103. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  104. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  105. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
  106. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  107. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +403 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
  110. data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
  111. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  112. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
  113. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
  114. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
  119. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  120. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  121. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  122. data/lib/active_record/connection_adapters.rb +130 -6
  123. data/lib/active_record/connection_handling.rb +132 -146
  124. data/lib/active_record/core.rb +310 -253
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
  127. data/lib/active_record/database_configurations/database_config.rb +34 -10
  128. data/lib/active_record/database_configurations/hash_config.rb +107 -31
  129. data/lib/active_record/database_configurations/url_config.rb +38 -13
  130. data/lib/active_record/database_configurations.rb +96 -60
  131. data/lib/active_record/delegated_type.rb +90 -20
  132. data/lib/active_record/deprecator.rb +7 -0
  133. data/lib/active_record/destroy_association_async_job.rb +4 -2
  134. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  135. data/lib/active_record/dynamic_matchers.rb +3 -3
  136. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +68 -0
  140. data/lib/active_record/encryption/configurable.rb +60 -0
  141. data/lib/active_record/encryption/context.rb +42 -0
  142. data/lib/active_record/encryption/contexts.rb +76 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +170 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +53 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  159. data/lib/active_record/encryption/message_serializer.rb +96 -0
  160. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  161. data/lib/active_record/encryption/properties.rb +76 -0
  162. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  163. data/lib/active_record/encryption/scheme.rb +100 -0
  164. data/lib/active_record/encryption.rb +58 -0
  165. data/lib/active_record/enum.rb +170 -62
  166. data/lib/active_record/errors.rb +210 -27
  167. data/lib/active_record/explain.rb +21 -12
  168. data/lib/active_record/explain_registry.rb +11 -6
  169. data/lib/active_record/explain_subscriber.rb +1 -1
  170. data/lib/active_record/fixture_set/file.rb +15 -1
  171. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  172. data/lib/active_record/fixture_set/render_context.rb +2 -0
  173. data/lib/active_record/fixture_set/table_row.rb +70 -14
  174. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  175. data/lib/active_record/fixtures.rb +179 -112
  176. data/lib/active_record/future_result.rb +178 -0
  177. data/lib/active_record/gem_version.rb +4 -4
  178. data/lib/active_record/inheritance.rb +85 -31
  179. data/lib/active_record/insert_all.rb +148 -32
  180. data/lib/active_record/integration.rb +14 -10
  181. data/lib/active_record/internal_metadata.rb +123 -23
  182. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  183. data/lib/active_record/locking/optimistic.rb +43 -27
  184. data/lib/active_record/locking/pessimistic.rb +15 -6
  185. data/lib/active_record/log_subscriber.rb +41 -29
  186. data/lib/active_record/marshalling.rb +59 -0
  187. data/lib/active_record/message_pack.rb +124 -0
  188. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  189. data/lib/active_record/middleware/database_selector.rb +23 -13
  190. data/lib/active_record/middleware/shard_selector.rb +62 -0
  191. data/lib/active_record/migration/command_recorder.rb +113 -16
  192. data/lib/active_record/migration/compatibility.rb +235 -46
  193. data/lib/active_record/migration/default_strategy.rb +22 -0
  194. data/lib/active_record/migration/execution_strategy.rb +19 -0
  195. data/lib/active_record/migration/join_table.rb +1 -1
  196. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  197. data/lib/active_record/migration.rb +374 -177
  198. data/lib/active_record/model_schema.rb +145 -158
  199. data/lib/active_record/nested_attributes.rb +61 -23
  200. data/lib/active_record/no_touching.rb +3 -3
  201. data/lib/active_record/normalization.rb +163 -0
  202. data/lib/active_record/persistence.rb +282 -283
  203. data/lib/active_record/promise.rb +84 -0
  204. data/lib/active_record/query_cache.rb +18 -25
  205. data/lib/active_record/query_logs.rb +189 -0
  206. data/lib/active_record/query_logs_formatter.rb +41 -0
  207. data/lib/active_record/querying.rb +44 -9
  208. data/lib/active_record/railtie.rb +229 -71
  209. data/lib/active_record/railties/controller_runtime.rb +25 -11
  210. data/lib/active_record/railties/databases.rake +189 -256
  211. data/lib/active_record/railties/job_runtime.rb +23 -0
  212. data/lib/active_record/readonly_attributes.rb +41 -3
  213. data/lib/active_record/reflection.rb +332 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +200 -65
  216. data/lib/active_record/relation/calculations.rb +301 -112
  217. data/lib/active_record/relation/delegation.rb +33 -22
  218. data/lib/active_record/relation/finder_methods.rb +123 -52
  219. data/lib/active_record/relation/merger.rb +26 -19
  220. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  221. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  222. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  223. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  224. data/lib/active_record/relation/predicate_builder.rb +29 -22
  225. data/lib/active_record/relation/query_attribute.rb +30 -12
  226. data/lib/active_record/relation/query_methods.rb +870 -163
  227. data/lib/active_record/relation/record_fetch_warning.rb +10 -9
  228. data/lib/active_record/relation/spawn_methods.rb +7 -6
  229. data/lib/active_record/relation/where_clause.rb +15 -36
  230. data/lib/active_record/relation.rb +736 -145
  231. data/lib/active_record/result.rb +67 -54
  232. data/lib/active_record/runtime_registry.rb +71 -13
  233. data/lib/active_record/sanitization.rb +84 -34
  234. data/lib/active_record/schema.rb +39 -23
  235. data/lib/active_record/schema_dumper.rb +90 -31
  236. data/lib/active_record/schema_migration.rb +74 -23
  237. data/lib/active_record/scoping/default.rb +72 -15
  238. data/lib/active_record/scoping/named.rb +6 -13
  239. data/lib/active_record/scoping.rb +65 -34
  240. data/lib/active_record/secure_password.rb +60 -0
  241. data/lib/active_record/secure_token.rb +21 -3
  242. data/lib/active_record/serialization.rb +6 -1
  243. data/lib/active_record/signed_id.rb +30 -9
  244. data/lib/active_record/statement_cache.rb +7 -7
  245. data/lib/active_record/store.rb +10 -10
  246. data/lib/active_record/suppressor.rb +13 -15
  247. data/lib/active_record/table_metadata.rb +7 -3
  248. data/lib/active_record/tasks/database_tasks.rb +288 -149
  249. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  250. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  251. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  252. data/lib/active_record/test_databases.rb +1 -1
  253. data/lib/active_record/test_fixtures.rb +173 -155
  254. data/lib/active_record/testing/query_assertions.rb +121 -0
  255. data/lib/active_record/timestamp.rb +32 -19
  256. data/lib/active_record/token_for.rb +123 -0
  257. data/lib/active_record/touch_later.rb +12 -7
  258. data/lib/active_record/transaction.rb +132 -0
  259. data/lib/active_record/transactions.rb +118 -41
  260. data/lib/active_record/translation.rb +3 -5
  261. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  262. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  263. data/lib/active_record/type/internal/timezone.rb +7 -2
  264. data/lib/active_record/type/serialized.rb +9 -7
  265. data/lib/active_record/type/time.rb +4 -0
  266. data/lib/active_record/type/type_map.rb +17 -20
  267. data/lib/active_record/type.rb +1 -2
  268. data/lib/active_record/type_caster/connection.rb +4 -4
  269. data/lib/active_record/validations/absence.rb +1 -1
  270. data/lib/active_record/validations/associated.rb +13 -7
  271. data/lib/active_record/validations/numericality.rb +5 -4
  272. data/lib/active_record/validations/presence.rb +5 -28
  273. data/lib/active_record/validations/uniqueness.rb +65 -15
  274. data/lib/active_record/validations.rb +12 -5
  275. data/lib/active_record/version.rb +1 -1
  276. data/lib/active_record.rb +444 -32
  277. data/lib/arel/alias_predication.rb +1 -1
  278. data/lib/arel/attributes/attribute.rb +0 -8
  279. data/lib/arel/collectors/bind.rb +2 -0
  280. data/lib/arel/collectors/composite.rb +7 -0
  281. data/lib/arel/collectors/sql_string.rb +1 -1
  282. data/lib/arel/collectors/substitute_binds.rb +1 -1
  283. data/lib/arel/crud.rb +28 -22
  284. data/lib/arel/delete_manager.rb +18 -4
  285. data/lib/arel/errors.rb +10 -0
  286. data/lib/arel/factory_methods.rb +4 -0
  287. data/lib/arel/filter_predications.rb +9 -0
  288. data/lib/arel/insert_manager.rb +2 -3
  289. data/lib/arel/nodes/binary.rb +6 -7
  290. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  291. data/lib/arel/nodes/casted.rb +1 -1
  292. data/lib/arel/nodes/cte.rb +36 -0
  293. data/lib/arel/nodes/delete_statement.rb +12 -13
  294. data/lib/arel/nodes/filter.rb +10 -0
  295. data/lib/arel/nodes/fragments.rb +35 -0
  296. data/lib/arel/nodes/function.rb +1 -0
  297. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  298. data/lib/arel/nodes/insert_statement.rb +2 -2
  299. data/lib/arel/nodes/leading_join.rb +8 -0
  300. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  301. data/lib/arel/nodes/node.rb +115 -5
  302. data/lib/arel/nodes/select_core.rb +2 -2
  303. data/lib/arel/nodes/select_statement.rb +2 -2
  304. data/lib/arel/nodes/sql_literal.rb +13 -0
  305. data/lib/arel/nodes/table_alias.rb +4 -0
  306. data/lib/arel/nodes/update_statement.rb +8 -3
  307. data/lib/arel/nodes.rb +7 -2
  308. data/lib/arel/predications.rb +14 -4
  309. data/lib/arel/select_manager.rb +11 -5
  310. data/lib/arel/table.rb +9 -6
  311. data/lib/arel/tree_manager.rb +8 -15
  312. data/lib/arel/update_manager.rb +20 -5
  313. data/lib/arel/visitors/dot.rb +81 -90
  314. data/lib/arel/visitors/mysql.rb +23 -5
  315. data/lib/arel/visitors/postgresql.rb +1 -22
  316. data/lib/arel/visitors/sqlite.rb +25 -0
  317. data/lib/arel/visitors/to_sql.rb +170 -36
  318. data/lib/arel/visitors/visitor.rb +2 -2
  319. data/lib/arel.rb +23 -4
  320. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  321. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  322. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  323. data/lib/rails/generators/active_record/migration.rb +3 -1
  324. data/lib/rails/generators/active_record/model/USAGE +113 -0
  325. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  326. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  328. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  329. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  330. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  331. metadata +103 -17
  332. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  333. data/lib/active_record/null_relation.rb +0 -67
@@ -3,12 +3,15 @@
3
3
  module Arel # :nodoc: all
4
4
  module Nodes
5
5
  class UpdateStatement < Arel::Nodes::Node
6
- attr_accessor :relation, :wheres, :values, :orders, :limit, :offset, :key
6
+ attr_accessor :relation, :wheres, :values, :groups, :havings, :orders, :limit, :offset, :key
7
7
 
8
- def initialize
9
- @relation = nil
8
+ def initialize(relation = nil)
9
+ super()
10
+ @relation = relation
10
11
  @wheres = []
11
12
  @values = []
13
+ @groups = []
14
+ @havings = []
12
15
  @orders = []
13
16
  @limit = nil
14
17
  @offset = nil
@@ -30,6 +33,8 @@ module Arel # :nodoc: all
30
33
  self.relation == other.relation &&
31
34
  self.wheres == other.wheres &&
32
35
  self.values == other.values &&
36
+ self.groups == other.groups &&
37
+ self.havings == other.havings &&
33
38
  self.orders == other.orders &&
34
39
  self.limit == other.limit &&
35
40
  self.offset == other.offset &&
data/lib/arel/nodes.rb CHANGED
@@ -8,6 +8,7 @@ require "arel/nodes/select_core"
8
8
  require "arel/nodes/insert_statement"
9
9
  require "arel/nodes/update_statement"
10
10
  require "arel/nodes/bind_param"
11
+ require "arel/nodes/fragments"
11
12
 
12
13
  # terminal
13
14
 
@@ -28,6 +29,7 @@ require "arel/nodes/with"
28
29
  # binary
29
30
  require "arel/nodes/binary"
30
31
  require "arel/nodes/equality"
32
+ require "arel/nodes/filter"
31
33
  require "arel/nodes/in"
32
34
  require "arel/nodes/join_source"
33
35
  require "arel/nodes/delete_statement"
@@ -37,9 +39,10 @@ require "arel/nodes/unary_operation"
37
39
  require "arel/nodes/over"
38
40
  require "arel/nodes/matches"
39
41
  require "arel/nodes/regexp"
42
+ require "arel/nodes/cte"
40
43
 
41
- # nary
42
- require "arel/nodes/and"
44
+ # nary (And and Or)
45
+ require "arel/nodes/nary"
43
46
 
44
47
  # function
45
48
  # FIXME: Function + Alias can be rewritten as a Function and Alias node.
@@ -62,9 +65,11 @@ require "arel/nodes/inner_join"
62
65
  require "arel/nodes/outer_join"
63
66
  require "arel/nodes/right_outer_join"
64
67
  require "arel/nodes/string_join"
68
+ require "arel/nodes/leading_join"
65
69
 
66
70
  require "arel/nodes/comment"
67
71
 
68
72
  require "arel/nodes/sql_literal"
73
+ require "arel/nodes/bound_sql_literal"
69
74
 
70
75
  require "arel/nodes/casted"
@@ -39,7 +39,11 @@ module Arel # :nodoc: all
39
39
  self.in([])
40
40
  elsif open_ended?(other.begin)
41
41
  if open_ended?(other.end)
42
- not_in([])
42
+ if infinity?(other.begin) == 1 || infinity?(other.end) == -1
43
+ self.in([])
44
+ else
45
+ not_in([])
46
+ end
43
47
  elsif other.exclude_end?
44
48
  lt(other.end)
45
49
  else
@@ -49,10 +53,12 @@ module Arel # :nodoc: all
49
53
  gteq(other.begin)
50
54
  elsif other.exclude_end?
51
55
  gteq(other.begin).and(lt(other.end))
56
+ elsif other.begin == other.end
57
+ eq(other.begin)
52
58
  else
53
59
  left = quoted_node(other.begin)
54
60
  right = quoted_node(other.end)
55
- Nodes::Between.new(self, left.and(right))
61
+ Nodes::Between.new(self, Nodes::And.new([left, right]))
56
62
  end
57
63
  end
58
64
 
@@ -80,7 +86,11 @@ module Arel # :nodoc: all
80
86
  not_in([])
81
87
  elsif open_ended?(other.begin)
82
88
  if open_ended?(other.end)
83
- self.in([])
89
+ if infinity?(other.begin) == 1 || infinity?(other.end) == -1
90
+ not_in([])
91
+ else
92
+ self.in([])
93
+ end
84
94
  elsif other.exclude_end?
85
95
  gteq(other.end)
86
96
  else
@@ -222,7 +232,7 @@ module Arel # :nodoc: all
222
232
  def grouping_any(method_id, others, *extras)
223
233
  nodes = others.map { |expr| send(method_id, expr, *extras) }
224
234
  Nodes::Grouping.new nodes.inject { |memo, node|
225
- Nodes::Or.new(memo, node)
235
+ Nodes::Or.new([memo, node])
226
236
  }
227
237
  end
228
238
 
@@ -7,10 +7,8 @@ module Arel # :nodoc: all
7
7
  STRING_OR_SYMBOL_CLASS = [Symbol, String]
8
8
 
9
9
  def initialize(table = nil)
10
- super()
11
- @ast = Nodes::SelectStatement.new
10
+ @ast = Nodes::SelectStatement.new(table)
12
11
  @ctx = @ast.cores.last
13
- from table
14
12
  end
15
13
 
16
14
  def initialize_copy(other)
@@ -48,7 +46,7 @@ module Arel # :nodoc: all
48
46
  end
49
47
 
50
48
  def as(other)
51
- create_table_alias grouping(@ast), Nodes::SqlLiteral.new(other)
49
+ create_table_alias grouping(@ast), Nodes::SqlLiteral.new(other, retryable: true)
52
50
  end
53
51
 
54
52
  def lock(locking = Arel.sql("FOR UPDATE"))
@@ -98,7 +96,7 @@ module Arel # :nodoc: all
98
96
  end
99
97
 
100
98
  def froms
101
- @ast.cores.map { |x| x.from }.compact
99
+ @ast.cores.filter_map { |x| x.from }
102
100
  end
103
101
 
104
102
  def join(relation, klass = Nodes::InnerJoin)
@@ -183,6 +181,14 @@ module Arel # :nodoc: all
183
181
  @ast.orders
184
182
  end
185
183
 
184
+ def where(expr)
185
+ if Arel::TreeManager === expr
186
+ expr = expr.ast
187
+ end
188
+ @ctx.wheres << expr
189
+ self
190
+ end
191
+
186
192
  def where_sql(engine = Table.engine)
187
193
  return if @ctx.wheres.empty?
188
194
 
data/lib/arel/table.rb CHANGED
@@ -2,20 +2,23 @@
2
2
 
3
3
  module Arel # :nodoc: all
4
4
  class Table
5
- include Arel::Crud
6
5
  include Arel::FactoryMethods
7
6
  include Arel::AliasPredication
8
7
 
9
8
  @engine = nil
10
9
  class << self; attr_accessor :engine; end
11
10
 
12
- attr_accessor :name, :table_alias
13
-
14
- # TableAlias and Table both have a #table_name which is the name of the underlying table
15
- alias :table_name :name
11
+ attr_accessor :name
12
+ attr_reader :table_alias
16
13
 
17
14
  def initialize(name, as: nil, klass: nil, type_caster: klass&.type_caster)
18
- @name = name.to_s
15
+ @name =
16
+ case name
17
+ when Symbol then name.to_s
18
+ else
19
+ name
20
+ end
21
+
19
22
  @klass = klass
20
23
  @type_caster = type_caster
21
24
 
@@ -21,7 +21,11 @@ module Arel # :nodoc: all
21
21
  end
22
22
 
23
23
  def key=(key)
24
- @ast.key = Nodes.build_quoted(key)
24
+ @ast.key = if key.is_a?(Array)
25
+ key.map { |k| Nodes.build_quoted(k) }
26
+ else
27
+ Nodes.build_quoted(key)
28
+ end
25
29
  end
26
30
 
27
31
  def key
@@ -40,10 +44,6 @@ module Arel # :nodoc: all
40
44
 
41
45
  attr_reader :ast
42
46
 
43
- def initialize
44
- @ctx = nil
45
- end
46
-
47
47
  def to_dot
48
48
  collector = Arel::Collectors::PlainString.new
49
49
  collector = Visitors::Dot.new.accept @ast, collector
@@ -52,21 +52,14 @@ module Arel # :nodoc: all
52
52
 
53
53
  def to_sql(engine = Table.engine)
54
54
  collector = Arel::Collectors::SQLString.new
55
- collector = engine.connection.visitor.accept @ast, collector
56
- collector.value
55
+ engine.with_connection do |connection|
56
+ connection.visitor.accept(@ast, collector).value
57
+ end
57
58
  end
58
59
 
59
60
  def initialize_copy(other)
60
61
  super
61
62
  @ast = @ast.clone
62
63
  end
63
-
64
- def where(expr)
65
- if Arel::TreeManager === expr
66
- expr = expr.ast
67
- end
68
- @ctx.wheres << expr
69
- self
70
- end
71
64
  end
72
65
  end
@@ -4,10 +4,8 @@ module Arel # :nodoc: all
4
4
  class UpdateManager < Arel::TreeManager
5
5
  include TreeManager::StatementMethods
6
6
 
7
- def initialize
8
- super
9
- @ast = Nodes::UpdateStatement.new
10
- @ctx = @ast
7
+ def initialize(table = nil)
8
+ @ast = Nodes::UpdateStatement.new(table)
11
9
  end
12
10
 
13
11
  ###
@@ -18,7 +16,8 @@ module Arel # :nodoc: all
18
16
  end
19
17
 
20
18
  def set(values)
21
- if String === values
19
+ case values
20
+ when String, Nodes::BoundSqlLiteral
22
21
  @ast.values = [values]
23
22
  else
24
23
  @ast.values = values.map { |column, value|
@@ -30,5 +29,21 @@ module Arel # :nodoc: all
30
29
  end
31
30
  self
32
31
  end
32
+
33
+ def group(columns)
34
+ columns.each do |column|
35
+ column = Nodes::SqlLiteral.new(column) if String === column
36
+ column = Nodes::SqlLiteral.new(column.to_s) if Symbol === column
37
+
38
+ @ast.groups.push Nodes::Group.new column
39
+ end
40
+
41
+ self
42
+ end
43
+
44
+ def having(expr)
45
+ @ast.havings << expr
46
+ self
47
+ end
33
48
  end
34
49
  end
@@ -31,6 +31,40 @@ module Arel # :nodoc: all
31
31
  end
32
32
 
33
33
  private
34
+ def visit_Arel_Nodes_Function(o)
35
+ visit_edge o, "expressions"
36
+ visit_edge o, "distinct"
37
+ visit_edge o, "alias"
38
+ end
39
+
40
+ def visit_Arel_Nodes_Unary(o)
41
+ visit_edge o, "expr"
42
+ end
43
+
44
+ def visit_Arel_Nodes_Binary(o)
45
+ visit_edge o, "left"
46
+ visit_edge o, "right"
47
+ end
48
+
49
+ def visit_Arel_Nodes_UnaryOperation(o)
50
+ visit_edge o, "operator"
51
+ visit_edge o, "expr"
52
+ end
53
+
54
+ def visit_Arel_Nodes_InfixOperation(o)
55
+ visit_edge o, "operator"
56
+ visit_edge o, "left"
57
+ visit_edge o, "right"
58
+ end
59
+
60
+ def visit__regexp(o)
61
+ visit_edge o, "left"
62
+ visit_edge o, "right"
63
+ visit_edge o, "case_sensitive"
64
+ end
65
+ alias :visit_Arel_Nodes_Regexp :visit__regexp
66
+ alias :visit_Arel_Nodes_NotRegexp :visit__regexp
67
+
34
68
  def visit_Arel_Nodes_Ordering(o)
35
69
  visit_edge o, "expr"
36
70
  end
@@ -53,71 +87,29 @@ module Arel # :nodoc: all
53
87
  visit_edge o, "left"
54
88
  end
55
89
 
56
- def visit_Arel_Nodes_InnerJoin(o)
57
- visit_edge o, "left"
58
- visit_edge o, "right"
59
- end
60
- alias :visit_Arel_Nodes_FullOuterJoin :visit_Arel_Nodes_InnerJoin
61
- alias :visit_Arel_Nodes_OuterJoin :visit_Arel_Nodes_InnerJoin
62
- alias :visit_Arel_Nodes_RightOuterJoin :visit_Arel_Nodes_InnerJoin
63
-
64
- def visit_Arel_Nodes_DeleteStatement(o)
65
- visit_edge o, "relation"
66
- visit_edge o, "wheres"
67
- end
68
-
69
- def unary(o)
70
- visit_edge o, "expr"
71
- end
72
- alias :visit_Arel_Nodes_Group :unary
73
- alias :visit_Arel_Nodes_Cube :unary
74
- alias :visit_Arel_Nodes_RollUp :unary
75
- alias :visit_Arel_Nodes_GroupingSet :unary
76
- alias :visit_Arel_Nodes_GroupingElement :unary
77
- alias :visit_Arel_Nodes_Grouping :unary
78
- alias :visit_Arel_Nodes_Having :unary
79
- alias :visit_Arel_Nodes_Limit :unary
80
- alias :visit_Arel_Nodes_Not :unary
81
- alias :visit_Arel_Nodes_Offset :unary
82
- alias :visit_Arel_Nodes_On :unary
83
- alias :visit_Arel_Nodes_UnqualifiedColumn :unary
84
- alias :visit_Arel_Nodes_OptimizerHints :unary
85
- alias :visit_Arel_Nodes_Preceding :unary
86
- alias :visit_Arel_Nodes_Following :unary
87
- alias :visit_Arel_Nodes_Rows :unary
88
- alias :visit_Arel_Nodes_Range :unary
89
-
90
- def window(o)
90
+ def visit_Arel_Nodes_Window(o)
91
91
  visit_edge o, "partitions"
92
92
  visit_edge o, "orders"
93
93
  visit_edge o, "framing"
94
94
  end
95
- alias :visit_Arel_Nodes_Window :window
96
95
 
97
- def named_window(o)
96
+ def visit_Arel_Nodes_NamedWindow(o)
98
97
  visit_edge o, "partitions"
99
98
  visit_edge o, "orders"
100
99
  visit_edge o, "framing"
101
100
  visit_edge o, "name"
102
101
  end
103
- alias :visit_Arel_Nodes_NamedWindow :named_window
104
102
 
105
- def function(o)
106
- visit_edge o, "expressions"
107
- visit_edge o, "distinct"
108
- visit_edge o, "alias"
103
+ def visit__no_edges(o)
104
+ # intentionally left blank
109
105
  end
110
- alias :visit_Arel_Nodes_Exists :function
111
- alias :visit_Arel_Nodes_Min :function
112
- alias :visit_Arel_Nodes_Max :function
113
- alias :visit_Arel_Nodes_Avg :function
114
- alias :visit_Arel_Nodes_Sum :function
106
+ alias :visit_Arel_Nodes_CurrentRow :visit__no_edges
107
+ alias :visit_Arel_Nodes_Distinct :visit__no_edges
115
108
 
116
- def extract(o)
109
+ def visit_Arel_Nodes_Extract(o)
117
110
  visit_edge o, "expressions"
118
111
  visit_edge o, "alias"
119
112
  end
120
- alias :visit_Arel_Nodes_Extract :extract
121
113
 
122
114
  def visit_Arel_Nodes_NamedFunction(o)
123
115
  visit_edge o, "name"
@@ -130,13 +122,19 @@ module Arel # :nodoc: all
130
122
  visit_edge o, "relation"
131
123
  visit_edge o, "columns"
132
124
  visit_edge o, "values"
125
+ visit_edge o, "select"
133
126
  end
134
127
 
135
128
  def visit_Arel_Nodes_SelectCore(o)
136
129
  visit_edge o, "source"
137
130
  visit_edge o, "projections"
138
131
  visit_edge o, "wheres"
139
- visit_edge o, "windows"
132
+ visit_edge o, "windows"
133
+ visit_edge o, "groups"
134
+ visit_edge o, "comment"
135
+ visit_edge o, "havings"
136
+ visit_edge o, "set_quantifier"
137
+ visit_edge o, "optimizer_hints"
140
138
  end
141
139
 
142
140
  def visit_Arel_Nodes_SelectStatement(o)
@@ -144,12 +142,27 @@ module Arel # :nodoc: all
144
142
  visit_edge o, "limit"
145
143
  visit_edge o, "orders"
146
144
  visit_edge o, "offset"
145
+ visit_edge o, "lock"
146
+ visit_edge o, "with"
147
147
  end
148
148
 
149
149
  def visit_Arel_Nodes_UpdateStatement(o)
150
150
  visit_edge o, "relation"
151
151
  visit_edge o, "wheres"
152
152
  visit_edge o, "values"
153
+ visit_edge o, "orders"
154
+ visit_edge o, "limit"
155
+ visit_edge o, "offset"
156
+ visit_edge o, "key"
157
+ end
158
+
159
+ def visit_Arel_Nodes_DeleteStatement(o)
160
+ visit_edge o, "relation"
161
+ visit_edge o, "wheres"
162
+ visit_edge o, "orders"
163
+ visit_edge o, "limit"
164
+ visit_edge o, "offset"
165
+ visit_edge o, "key"
153
166
  end
154
167
 
155
168
  def visit_Arel_Table(o)
@@ -167,47 +180,19 @@ module Arel # :nodoc: all
167
180
  visit_edge o, "attribute"
168
181
  end
169
182
 
170
- def visit_Arel_Attribute(o)
183
+ def visit_Arel_Attributes_Attribute(o)
171
184
  visit_edge o, "relation"
172
185
  visit_edge o, "name"
173
186
  end
174
- alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
175
- alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
176
- alias :visit_Arel_Attributes_String :visit_Arel_Attribute
177
- alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
178
- alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
179
- alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
180
187
 
181
- def nary(o)
182
- o.children.each_with_index do |x, i|
183
- edge(i) { visit x }
188
+ def visit__children(o)
189
+ o.children.each_with_index do |child, i|
190
+ edge(i) { visit child }
184
191
  end
185
192
  end
186
- alias :visit_Arel_Nodes_And :nary
187
-
188
- def binary(o)
189
- visit_edge o, "left"
190
- visit_edge o, "right"
191
- end
192
- alias :visit_Arel_Nodes_As :binary
193
- alias :visit_Arel_Nodes_Assignment :binary
194
- alias :visit_Arel_Nodes_Between :binary
195
- alias :visit_Arel_Nodes_Concat :binary
196
- alias :visit_Arel_Nodes_DoesNotMatch :binary
197
- alias :visit_Arel_Nodes_Equality :binary
198
- alias :visit_Arel_Nodes_GreaterThan :binary
199
- alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
200
- alias :visit_Arel_Nodes_In :binary
201
- alias :visit_Arel_Nodes_JoinSource :binary
202
- alias :visit_Arel_Nodes_LessThan :binary
203
- alias :visit_Arel_Nodes_LessThanOrEqual :binary
204
- alias :visit_Arel_Nodes_IsNotDistinctFrom :binary
205
- alias :visit_Arel_Nodes_IsDistinctFrom :binary
206
- alias :visit_Arel_Nodes_Matches :binary
207
- alias :visit_Arel_Nodes_NotEqual :binary
208
- alias :visit_Arel_Nodes_NotIn :binary
209
- alias :visit_Arel_Nodes_Or :binary
210
- alias :visit_Arel_Nodes_Over :binary
193
+ alias :visit_Arel_Nodes_And :visit__children
194
+ alias :visit_Arel_Nodes_Or :visit__children
195
+ alias :visit_Arel_Nodes_With :visit__children
211
196
 
212
197
  def visit_String(o)
213
198
  @node_stack.last.fields << o
@@ -225,22 +210,22 @@ module Arel # :nodoc: all
225
210
  alias :visit_Arel_Nodes_SqlLiteral :visit_String
226
211
 
227
212
  def visit_Arel_Nodes_BindParam(o)
228
- edge("value") { visit o.value }
213
+ visit_edge(o, "value")
229
214
  end
230
215
 
231
216
  def visit_ActiveModel_Attribute(o)
232
- edge("value_before_type_cast") { visit o.value_before_type_cast }
217
+ visit_edge(o, "value_before_type_cast")
233
218
  end
234
219
 
235
220
  def visit_Hash(o)
236
221
  o.each_with_index do |pair, i|
237
- edge("pair_#{i}") { visit pair }
222
+ edge("pair_#{i}") { visit pair }
238
223
  end
239
224
  end
240
225
 
241
226
  def visit_Array(o)
242
- o.each_with_index do |x, i|
243
- edge(i) { visit x }
227
+ o.each_with_index do |member, i|
228
+ edge(i) { visit member }
244
229
  end
245
230
  end
246
231
  alias :visit_Set :visit_Array
@@ -249,6 +234,12 @@ module Arel # :nodoc: all
249
234
  visit_edge(o, "values")
250
235
  end
251
236
 
237
+ def visit_Arel_Nodes_Case(o)
238
+ visit_edge(o, "case")
239
+ visit_edge(o, "conditions")
240
+ visit_edge(o, "default")
241
+ end
242
+
252
243
  def visit_edge(o, method)
253
244
  edge(method) { visit o.send(method) }
254
245
  end
@@ -5,8 +5,9 @@ module Arel # :nodoc: all
5
5
  class MySQL < Arel::Visitors::ToSql
6
6
  private
7
7
  def visit_Arel_Nodes_Bin(o, collector)
8
- collector << "BINARY "
8
+ collector << "CAST("
9
9
  visit o.expr, collector
10
+ collector << " AS BINARY)"
10
11
  end
11
12
 
12
13
  def visit_Arel_Nodes_UnqualifiedColumn(o, collector)
@@ -26,7 +27,7 @@ module Arel # :nodoc: all
26
27
  end
27
28
 
28
29
  def visit_Arel_Nodes_SelectCore(o, collector)
29
- o.froms ||= Arel.sql("DUAL")
30
+ o.froms ||= Arel.sql("DUAL", retryable: true)
30
31
  super
31
32
  end
32
33
 
@@ -58,11 +59,28 @@ module Arel # :nodoc: all
58
59
  infix_value o, collector, " NOT REGEXP "
59
60
  end
60
61
 
62
+ def visit_Arel_Nodes_NullsFirst(o, collector)
63
+ visit(o.expr.expr, collector) << " IS NOT NULL, "
64
+ visit(o.expr, collector)
65
+ end
66
+
67
+ def visit_Arel_Nodes_NullsLast(o, collector)
68
+ visit(o.expr.expr, collector) << " IS NULL, "
69
+ visit(o.expr, collector)
70
+ end
71
+
72
+ def visit_Arel_Nodes_Cte(o, collector)
73
+ collector << quote_table_name(o.name)
74
+ collector << " AS "
75
+ visit o.relation, collector
76
+ end
77
+
61
78
  # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
62
79
  # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
63
80
  # these, we must use a subquery.
64
81
  def prepare_update_statement(o)
65
- if o.offset || has_join_sources?(o) && has_limit_or_offset_or_orders?(o)
82
+ if o.offset || has_group_by_and_having?(o) ||
83
+ has_join_sources?(o) && has_limit_or_offset_or_orders?(o)
66
84
  super
67
85
  else
68
86
  o
@@ -70,7 +88,7 @@ module Arel # :nodoc: all
70
88
  end
71
89
  alias :prepare_delete_statement :prepare_update_statement
72
90
 
73
- # MySQL is too stupid to create a temporary table for use subquery, so we have
91
+ # MySQL doesn't automatically create a temporary table for use subquery, so we have
74
92
  # to give it some prompting in the form of a subsubquery.
75
93
  def build_subselect(key, o)
76
94
  subselect = super
@@ -85,7 +103,7 @@ module Arel # :nodoc: all
85
103
  Nodes::SelectStatement.new.tap do |stmt|
86
104
  core = stmt.cores.last
87
105
  core.froms = Nodes::Grouping.new(subselect).as("__active_record_temp")
88
- core.projections = [Arel.sql(quote_column_name(key.name))]
106
+ core.projections = [Arel.sql(quote_column_name(key.name), retryable: true)]
89
107
  end
90
108
  end
91
109
  end
@@ -63,7 +63,7 @@ module Arel # :nodoc: all
63
63
 
64
64
  def visit_Arel_Nodes_Lateral(o, collector)
65
65
  collector << "LATERAL "
66
- grouping_parentheses o, collector
66
+ grouping_parentheses o.expr, collector
67
67
  end
68
68
 
69
69
  def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
@@ -78,32 +78,11 @@ module Arel # :nodoc: all
78
78
  visit o.right, collector
79
79
  end
80
80
 
81
- def visit_Arel_Nodes_NullsFirst(o, collector)
82
- visit o.expr, collector
83
- collector << " NULLS FIRST"
84
- end
85
-
86
- def visit_Arel_Nodes_NullsLast(o, collector)
87
- visit o.expr, collector
88
- collector << " NULLS LAST"
89
- end
90
-
91
81
  BIND_BLOCK = proc { |i| "$#{i}" }
92
82
  private_constant :BIND_BLOCK
93
83
 
94
84
  def bind_block; BIND_BLOCK; end
95
85
 
96
- # Used by Lateral visitor to enclose select queries in parentheses
97
- def grouping_parentheses(o, collector)
98
- if o.expr.is_a? Nodes::SelectStatement
99
- collector << "("
100
- visit o.expr, collector
101
- collector << ")"
102
- else
103
- visit o.expr, collector
104
- end
105
- end
106
-
107
86
  # Utilized by GroupingSet, Cube & RollUp visitors to
108
87
  # handle grouping aggregation semantics
109
88
  def grouping_array_or_grouping_element(o, collector)
@@ -33,6 +33,31 @@ module Arel # :nodoc: all
33
33
  collector << " IS NOT "
34
34
  visit o.right, collector
35
35
  end
36
+
37
+ # Queries used in UNION should not be wrapped by parentheses,
38
+ # because it is an invalid syntax in SQLite.
39
+ def infix_value_with_paren(o, collector, value, suppress_parens = false)
40
+ collector << "( " unless suppress_parens
41
+
42
+ left = o.left.is_a?(Nodes::Grouping) ? o.left.expr : o.left
43
+ collector = if left.class == o.class
44
+ infix_value_with_paren(left, collector, value, true)
45
+ else
46
+ grouping_parentheses left, collector, false
47
+ end
48
+
49
+ collector << value
50
+
51
+ right = o.right.is_a?(Nodes::Grouping) ? o.right.expr : o.right
52
+ collector = if right.class == o.class
53
+ infix_value_with_paren(right, collector, value, true)
54
+ else
55
+ grouping_parentheses right, collector, false
56
+ end
57
+
58
+ collector << " )" unless suppress_parens
59
+ collector
60
+ end
36
61
  end
37
62
  end
38
63
  end