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