omg-activerecord 8.0.0.alpha1

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 (412) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +355 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +219 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record/aggregations.rb +287 -0
  8. data/lib/active_record/association_relation.rb +50 -0
  9. data/lib/active_record/associations/alias_tracker.rb +90 -0
  10. data/lib/active_record/associations/association.rb +417 -0
  11. data/lib/active_record/associations/association_scope.rb +175 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +163 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  14. data/lib/active_record/associations/builder/association.rb +170 -0
  15. data/lib/active_record/associations/builder/belongs_to.rb +160 -0
  16. data/lib/active_record/associations/builder/collection_association.rb +80 -0
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
  18. data/lib/active_record/associations/builder/has_many.rb +23 -0
  19. data/lib/active_record/associations/builder/has_one.rb +61 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +48 -0
  21. data/lib/active_record/associations/collection_association.rb +535 -0
  22. data/lib/active_record/associations/collection_proxy.rb +1163 -0
  23. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  24. data/lib/active_record/associations/errors.rb +265 -0
  25. data/lib/active_record/associations/foreign_association.rb +40 -0
  26. data/lib/active_record/associations/has_many_association.rb +167 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +232 -0
  28. data/lib/active_record/associations/has_one_association.rb +142 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +106 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/join_dependency.rb +301 -0
  34. data/lib/active_record/associations/nested_error.rb +47 -0
  35. data/lib/active_record/associations/preloader/association.rb +316 -0
  36. data/lib/active_record/associations/preloader/batch.rb +48 -0
  37. data/lib/active_record/associations/preloader/branch.rb +153 -0
  38. data/lib/active_record/associations/preloader/through_association.rb +150 -0
  39. data/lib/active_record/associations/preloader.rb +135 -0
  40. data/lib/active_record/associations/singular_association.rb +76 -0
  41. data/lib/active_record/associations/through_association.rb +132 -0
  42. data/lib/active_record/associations.rb +1897 -0
  43. data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
  44. data/lib/active_record/attribute_assignment.rb +82 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
  46. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  47. data/lib/active_record/attribute_methods/dirty.rb +262 -0
  48. data/lib/active_record/attribute_methods/primary_key.rb +158 -0
  49. data/lib/active_record/attribute_methods/query.rb +50 -0
  50. data/lib/active_record/attribute_methods/read.rb +46 -0
  51. data/lib/active_record/attribute_methods/serialization.rb +232 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
  53. data/lib/active_record/attribute_methods/write.rb +49 -0
  54. data/lib/active_record/attribute_methods.rb +542 -0
  55. data/lib/active_record/attributes.rb +307 -0
  56. data/lib/active_record/autosave_association.rb +586 -0
  57. data/lib/active_record/base.rb +338 -0
  58. data/lib/active_record/callbacks.rb +452 -0
  59. data/lib/active_record/coders/column_serializer.rb +61 -0
  60. data/lib/active_record/coders/json.rb +15 -0
  61. data/lib/active_record/coders/yaml_column.rb +95 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
  64. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
  78. data/lib/active_record/connection_adapters/column.rb +122 -0
  79. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  80. data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
  81. data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
  82. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  83. data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
  84. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  89. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
  90. data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
  91. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  92. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  93. data/lib/active_record/connection_adapters/postgresql/column.rb +82 -0
  94. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
  95. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +54 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
  115. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  116. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  117. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  118. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +125 -0
  119. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -0
  120. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  121. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  122. data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -0
  123. data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
  124. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
  125. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
  126. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
  127. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
  128. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
  129. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  130. data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
  131. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
  132. data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
  133. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  134. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  135. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
  136. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  137. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
  138. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
  139. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
  140. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
  141. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
  142. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
  143. data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
  144. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
  145. data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
  146. data/lib/active_record/connection_adapters.rb +176 -0
  147. data/lib/active_record/connection_handling.rb +413 -0
  148. data/lib/active_record/core.rb +836 -0
  149. data/lib/active_record/counter_cache.rb +230 -0
  150. data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
  151. data/lib/active_record/database_configurations/database_config.rb +104 -0
  152. data/lib/active_record/database_configurations/hash_config.rb +172 -0
  153. data/lib/active_record/database_configurations/url_config.rb +78 -0
  154. data/lib/active_record/database_configurations.rb +309 -0
  155. data/lib/active_record/delegated_type.rb +289 -0
  156. data/lib/active_record/deprecator.rb +7 -0
  157. data/lib/active_record/destroy_association_async_job.rb +38 -0
  158. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  159. data/lib/active_record/dynamic_matchers.rb +121 -0
  160. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  161. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  162. data/lib/active_record/encryption/cipher.rb +53 -0
  163. data/lib/active_record/encryption/config.rb +70 -0
  164. data/lib/active_record/encryption/configurable.rb +60 -0
  165. data/lib/active_record/encryption/context.rb +42 -0
  166. data/lib/active_record/encryption/contexts.rb +76 -0
  167. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  168. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  169. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  170. data/lib/active_record/encryption/encrypted_attribute_type.rb +184 -0
  171. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  172. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  173. data/lib/active_record/encryption/encryptor.rb +177 -0
  174. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  175. data/lib/active_record/encryption/errors.rb +15 -0
  176. data/lib/active_record/encryption/extended_deterministic_queries.rb +159 -0
  177. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  178. data/lib/active_record/encryption/key.rb +28 -0
  179. data/lib/active_record/encryption/key_generator.rb +53 -0
  180. data/lib/active_record/encryption/key_provider.rb +46 -0
  181. data/lib/active_record/encryption/message.rb +33 -0
  182. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  183. data/lib/active_record/encryption/message_serializer.rb +96 -0
  184. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  185. data/lib/active_record/encryption/properties.rb +76 -0
  186. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  187. data/lib/active_record/encryption/scheme.rb +107 -0
  188. data/lib/active_record/encryption.rb +58 -0
  189. data/lib/active_record/enum.rb +424 -0
  190. data/lib/active_record/errors.rb +614 -0
  191. data/lib/active_record/explain.rb +63 -0
  192. data/lib/active_record/explain_registry.rb +37 -0
  193. data/lib/active_record/explain_subscriber.rb +34 -0
  194. data/lib/active_record/fixture_set/file.rb +89 -0
  195. data/lib/active_record/fixture_set/model_metadata.rb +42 -0
  196. data/lib/active_record/fixture_set/render_context.rb +19 -0
  197. data/lib/active_record/fixture_set/table_row.rb +208 -0
  198. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  199. data/lib/active_record/fixtures.rb +850 -0
  200. data/lib/active_record/future_result.rb +182 -0
  201. data/lib/active_record/gem_version.rb +17 -0
  202. data/lib/active_record/inheritance.rb +366 -0
  203. data/lib/active_record/insert_all.rb +328 -0
  204. data/lib/active_record/integration.rb +209 -0
  205. data/lib/active_record/internal_metadata.rb +164 -0
  206. data/lib/active_record/legacy_yaml_adapter.rb +15 -0
  207. data/lib/active_record/locale/en.yml +48 -0
  208. data/lib/active_record/locking/optimistic.rb +228 -0
  209. data/lib/active_record/locking/pessimistic.rb +102 -0
  210. data/lib/active_record/log_subscriber.rb +149 -0
  211. data/lib/active_record/marshalling.rb +56 -0
  212. data/lib/active_record/message_pack.rb +124 -0
  213. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  214. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  215. data/lib/active_record/middleware/database_selector.rb +87 -0
  216. data/lib/active_record/middleware/shard_selector.rb +62 -0
  217. data/lib/active_record/migration/command_recorder.rb +406 -0
  218. data/lib/active_record/migration/compatibility.rb +490 -0
  219. data/lib/active_record/migration/default_strategy.rb +22 -0
  220. data/lib/active_record/migration/execution_strategy.rb +19 -0
  221. data/lib/active_record/migration/join_table.rb +16 -0
  222. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  223. data/lib/active_record/migration.rb +1626 -0
  224. data/lib/active_record/model_schema.rb +635 -0
  225. data/lib/active_record/nested_attributes.rb +633 -0
  226. data/lib/active_record/no_touching.rb +65 -0
  227. data/lib/active_record/normalization.rb +163 -0
  228. data/lib/active_record/persistence.rb +968 -0
  229. data/lib/active_record/promise.rb +84 -0
  230. data/lib/active_record/query_cache.rb +56 -0
  231. data/lib/active_record/query_logs.rb +247 -0
  232. data/lib/active_record/query_logs_formatter.rb +30 -0
  233. data/lib/active_record/querying.rb +122 -0
  234. data/lib/active_record/railtie.rb +440 -0
  235. data/lib/active_record/railties/console_sandbox.rb +5 -0
  236. data/lib/active_record/railties/controller_runtime.rb +65 -0
  237. data/lib/active_record/railties/databases.rake +641 -0
  238. data/lib/active_record/railties/job_runtime.rb +23 -0
  239. data/lib/active_record/readonly_attributes.rb +66 -0
  240. data/lib/active_record/reflection.rb +1287 -0
  241. data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
  242. data/lib/active_record/relation/batches.rb +491 -0
  243. data/lib/active_record/relation/calculations.rb +679 -0
  244. data/lib/active_record/relation/delegation.rb +154 -0
  245. data/lib/active_record/relation/finder_methods.rb +661 -0
  246. data/lib/active_record/relation/from_clause.rb +30 -0
  247. data/lib/active_record/relation/merger.rb +192 -0
  248. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  249. data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -0
  250. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  251. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +60 -0
  252. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  253. data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
  254. data/lib/active_record/relation/predicate_builder.rb +181 -0
  255. data/lib/active_record/relation/query_attribute.rb +68 -0
  256. data/lib/active_record/relation/query_methods.rb +2235 -0
  257. data/lib/active_record/relation/record_fetch_warning.rb +52 -0
  258. data/lib/active_record/relation/spawn_methods.rb +78 -0
  259. data/lib/active_record/relation/where_clause.rb +218 -0
  260. data/lib/active_record/relation.rb +1495 -0
  261. data/lib/active_record/result.rb +249 -0
  262. data/lib/active_record/runtime_registry.rb +82 -0
  263. data/lib/active_record/sanitization.rb +254 -0
  264. data/lib/active_record/schema.rb +77 -0
  265. data/lib/active_record/schema_dumper.rb +364 -0
  266. data/lib/active_record/schema_migration.rb +106 -0
  267. data/lib/active_record/scoping/default.rb +205 -0
  268. data/lib/active_record/scoping/named.rb +202 -0
  269. data/lib/active_record/scoping.rb +136 -0
  270. data/lib/active_record/secure_password.rb +60 -0
  271. data/lib/active_record/secure_token.rb +66 -0
  272. data/lib/active_record/serialization.rb +29 -0
  273. data/lib/active_record/signed_id.rb +137 -0
  274. data/lib/active_record/statement_cache.rb +164 -0
  275. data/lib/active_record/store.rb +299 -0
  276. data/lib/active_record/suppressor.rb +59 -0
  277. data/lib/active_record/table_metadata.rb +85 -0
  278. data/lib/active_record/tasks/database_tasks.rb +681 -0
  279. data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
  280. data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
  281. data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
  282. data/lib/active_record/test_databases.rb +24 -0
  283. data/lib/active_record/test_fixtures.rb +321 -0
  284. data/lib/active_record/testing/query_assertions.rb +121 -0
  285. data/lib/active_record/timestamp.rb +177 -0
  286. data/lib/active_record/token_for.rb +123 -0
  287. data/lib/active_record/touch_later.rb +70 -0
  288. data/lib/active_record/transaction.rb +132 -0
  289. data/lib/active_record/transactions.rb +523 -0
  290. data/lib/active_record/translation.rb +22 -0
  291. data/lib/active_record/type/adapter_specific_registry.rb +144 -0
  292. data/lib/active_record/type/date.rb +9 -0
  293. data/lib/active_record/type/date_time.rb +9 -0
  294. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  295. data/lib/active_record/type/hash_lookup_type_map.rb +57 -0
  296. data/lib/active_record/type/internal/timezone.rb +22 -0
  297. data/lib/active_record/type/json.rb +30 -0
  298. data/lib/active_record/type/serialized.rb +76 -0
  299. data/lib/active_record/type/text.rb +11 -0
  300. data/lib/active_record/type/time.rb +35 -0
  301. data/lib/active_record/type/type_map.rb +58 -0
  302. data/lib/active_record/type/unsigned_integer.rb +16 -0
  303. data/lib/active_record/type.rb +83 -0
  304. data/lib/active_record/type_caster/connection.rb +33 -0
  305. data/lib/active_record/type_caster/map.rb +23 -0
  306. data/lib/active_record/type_caster.rb +9 -0
  307. data/lib/active_record/validations/absence.rb +25 -0
  308. data/lib/active_record/validations/associated.rb +65 -0
  309. data/lib/active_record/validations/length.rb +26 -0
  310. data/lib/active_record/validations/numericality.rb +36 -0
  311. data/lib/active_record/validations/presence.rb +45 -0
  312. data/lib/active_record/validations/uniqueness.rb +295 -0
  313. data/lib/active_record/validations.rb +101 -0
  314. data/lib/active_record/version.rb +10 -0
  315. data/lib/active_record.rb +616 -0
  316. data/lib/arel/alias_predication.rb +9 -0
  317. data/lib/arel/attributes/attribute.rb +33 -0
  318. data/lib/arel/collectors/bind.rb +31 -0
  319. data/lib/arel/collectors/composite.rb +46 -0
  320. data/lib/arel/collectors/plain_string.rb +20 -0
  321. data/lib/arel/collectors/sql_string.rb +27 -0
  322. data/lib/arel/collectors/substitute_binds.rb +35 -0
  323. data/lib/arel/crud.rb +48 -0
  324. data/lib/arel/delete_manager.rb +32 -0
  325. data/lib/arel/errors.rb +19 -0
  326. data/lib/arel/expressions.rb +29 -0
  327. data/lib/arel/factory_methods.rb +53 -0
  328. data/lib/arel/filter_predications.rb +9 -0
  329. data/lib/arel/insert_manager.rb +48 -0
  330. data/lib/arel/math.rb +45 -0
  331. data/lib/arel/nodes/ascending.rb +23 -0
  332. data/lib/arel/nodes/binary.rb +125 -0
  333. data/lib/arel/nodes/bind_param.rb +44 -0
  334. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  335. data/lib/arel/nodes/case.rb +55 -0
  336. data/lib/arel/nodes/casted.rb +62 -0
  337. data/lib/arel/nodes/comment.rb +29 -0
  338. data/lib/arel/nodes/count.rb +12 -0
  339. data/lib/arel/nodes/cte.rb +36 -0
  340. data/lib/arel/nodes/delete_statement.rb +44 -0
  341. data/lib/arel/nodes/descending.rb +23 -0
  342. data/lib/arel/nodes/equality.rb +15 -0
  343. data/lib/arel/nodes/extract.rb +24 -0
  344. data/lib/arel/nodes/false.rb +16 -0
  345. data/lib/arel/nodes/filter.rb +10 -0
  346. data/lib/arel/nodes/fragments.rb +35 -0
  347. data/lib/arel/nodes/full_outer_join.rb +8 -0
  348. data/lib/arel/nodes/function.rb +45 -0
  349. data/lib/arel/nodes/grouping.rb +11 -0
  350. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  351. data/lib/arel/nodes/in.rb +15 -0
  352. data/lib/arel/nodes/infix_operation.rb +92 -0
  353. data/lib/arel/nodes/inner_join.rb +8 -0
  354. data/lib/arel/nodes/insert_statement.rb +37 -0
  355. data/lib/arel/nodes/join_source.rb +20 -0
  356. data/lib/arel/nodes/leading_join.rb +8 -0
  357. data/lib/arel/nodes/matches.rb +18 -0
  358. data/lib/arel/nodes/named_function.rb +23 -0
  359. data/lib/arel/nodes/nary.rb +39 -0
  360. data/lib/arel/nodes/node.rb +161 -0
  361. data/lib/arel/nodes/node_expression.rb +13 -0
  362. data/lib/arel/nodes/ordering.rb +27 -0
  363. data/lib/arel/nodes/outer_join.rb +8 -0
  364. data/lib/arel/nodes/over.rb +15 -0
  365. data/lib/arel/nodes/regexp.rb +16 -0
  366. data/lib/arel/nodes/right_outer_join.rb +8 -0
  367. data/lib/arel/nodes/select_core.rb +67 -0
  368. data/lib/arel/nodes/select_statement.rb +41 -0
  369. data/lib/arel/nodes/sql_literal.rb +32 -0
  370. data/lib/arel/nodes/string_join.rb +11 -0
  371. data/lib/arel/nodes/table_alias.rb +35 -0
  372. data/lib/arel/nodes/terminal.rb +16 -0
  373. data/lib/arel/nodes/true.rb +16 -0
  374. data/lib/arel/nodes/unary.rb +44 -0
  375. data/lib/arel/nodes/unary_operation.rb +20 -0
  376. data/lib/arel/nodes/unqualified_column.rb +22 -0
  377. data/lib/arel/nodes/update_statement.rb +46 -0
  378. data/lib/arel/nodes/values_list.rb +9 -0
  379. data/lib/arel/nodes/window.rb +126 -0
  380. data/lib/arel/nodes/with.rb +11 -0
  381. data/lib/arel/nodes.rb +75 -0
  382. data/lib/arel/order_predications.rb +13 -0
  383. data/lib/arel/predications.rb +260 -0
  384. data/lib/arel/select_manager.rb +276 -0
  385. data/lib/arel/table.rb +121 -0
  386. data/lib/arel/tree_manager.rb +65 -0
  387. data/lib/arel/update_manager.rb +49 -0
  388. data/lib/arel/visitors/dot.rb +299 -0
  389. data/lib/arel/visitors/mysql.rb +111 -0
  390. data/lib/arel/visitors/postgresql.rb +99 -0
  391. data/lib/arel/visitors/sqlite.rb +38 -0
  392. data/lib/arel/visitors/to_sql.rb +1033 -0
  393. data/lib/arel/visitors/visitor.rb +45 -0
  394. data/lib/arel/visitors.rb +13 -0
  395. data/lib/arel/window_predications.rb +9 -0
  396. data/lib/arel.rb +73 -0
  397. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  398. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  399. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  400. data/lib/rails/generators/active_record/migration/migration_generator.rb +76 -0
  401. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
  402. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  403. data/lib/rails/generators/active_record/migration.rb +54 -0
  404. data/lib/rails/generators/active_record/model/USAGE +113 -0
  405. data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
  406. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  407. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  408. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  409. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  410. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  411. data/lib/rails/generators/active_record.rb +19 -0
  412. metadata +505 -0
@@ -0,0 +1,249 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ ###
5
+ # = Active Record \Result
6
+ #
7
+ # This class encapsulates a result returned from calling
8
+ # {#exec_query}[rdoc-ref:ConnectionAdapters::DatabaseStatements#exec_query]
9
+ # on any database connection adapter. For example:
10
+ #
11
+ # result = ActiveRecord::Base.lease_connection.exec_query('SELECT id, title, body FROM posts')
12
+ # result # => #<ActiveRecord::Result:0xdeadbeef>
13
+ #
14
+ # # Get the column names of the result:
15
+ # result.columns
16
+ # # => ["id", "title", "body"]
17
+ #
18
+ # # Get the record values of the result:
19
+ # result.rows
20
+ # # => [[1, "title_1", "body_1"],
21
+ # [2, "title_2", "body_2"],
22
+ # ...
23
+ # ]
24
+ #
25
+ # # Get an array of hashes representing the result (column => value):
26
+ # result.to_a
27
+ # # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
28
+ # {"id" => 2, "title" => "title_2", "body" => "body_2"},
29
+ # ...
30
+ # ]
31
+ #
32
+ # # ActiveRecord::Result also includes Enumerable.
33
+ # result.each do |row|
34
+ # puts row['title'] + " " + row['body']
35
+ # end
36
+ class Result
37
+ include Enumerable
38
+
39
+ class IndexedRow
40
+ def initialize(column_indexes, row)
41
+ @column_indexes = column_indexes
42
+ @row = row
43
+ end
44
+
45
+ def size
46
+ @column_indexes.size
47
+ end
48
+ alias_method :length, :size
49
+
50
+ def each_key(&block)
51
+ @column_indexes.each_key(&block)
52
+ end
53
+
54
+ def keys
55
+ @column_indexes.keys
56
+ end
57
+
58
+ def ==(other)
59
+ if other.is_a?(Hash)
60
+ to_hash == other
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ def key?(column)
67
+ @column_indexes.key?(column)
68
+ end
69
+
70
+ def fetch(column)
71
+ if index = @column_indexes[column]
72
+ @row[index]
73
+ elsif block_given?
74
+ yield
75
+ else
76
+ raise KeyError, "key not found: #{column.inspect}"
77
+ end
78
+ end
79
+
80
+ def [](column)
81
+ if index = @column_indexes[column]
82
+ @row[index]
83
+ end
84
+ end
85
+
86
+ def to_h
87
+ @column_indexes.transform_values { |index| @row[index] }
88
+ end
89
+ alias_method :to_hash, :to_h
90
+ end
91
+
92
+ attr_reader :columns, :rows, :column_types
93
+
94
+ def self.empty(async: false) # :nodoc:
95
+ if async
96
+ EMPTY_ASYNC
97
+ else
98
+ EMPTY
99
+ end
100
+ end
101
+
102
+ def initialize(columns, rows, column_types = nil)
103
+ # We freeze the strings to prevent them getting duped when
104
+ # used as keys in ActiveRecord::Base's @attributes hash
105
+ @columns = columns.each(&:-@).freeze
106
+ @rows = rows
107
+ @hash_rows = nil
108
+ @column_types = column_types || EMPTY_HASH
109
+ @column_indexes = nil
110
+ end
111
+
112
+ # Returns true if this result set includes the column named +name+
113
+ def includes_column?(name)
114
+ @columns.include? name
115
+ end
116
+
117
+ # Returns the number of elements in the rows array.
118
+ def length
119
+ @rows.length
120
+ end
121
+
122
+ # Calls the given block once for each element in row collection, passing
123
+ # row as parameter. Each row is a Hash-like, read only object.
124
+ #
125
+ # To get real hashes, use +.to_a.each+.
126
+ #
127
+ # Returns an +Enumerator+ if no block is given.
128
+ def each(&block)
129
+ if block_given?
130
+ indexed_rows.each(&block)
131
+ else
132
+ indexed_rows.to_enum { @rows.size }
133
+ end
134
+ end
135
+
136
+ # Returns true if there are no records, otherwise false.
137
+ def empty?
138
+ rows.empty?
139
+ end
140
+
141
+ # Returns an array of hashes representing each row record.
142
+ def to_ary
143
+ hash_rows
144
+ end
145
+
146
+ alias :to_a :to_ary
147
+
148
+ def [](idx)
149
+ hash_rows[idx]
150
+ end
151
+
152
+ # Returns the last record from the rows collection.
153
+ def last(n = nil)
154
+ n ? hash_rows.last(n) : hash_rows.last
155
+ end
156
+
157
+ def result # :nodoc:
158
+ self
159
+ end
160
+
161
+ def cancel # :nodoc:
162
+ self
163
+ end
164
+
165
+ def cast_values(type_overrides = {}) # :nodoc:
166
+ if columns.one?
167
+ # Separated to avoid allocating an array per row
168
+
169
+ type = if type_overrides.is_a?(Array)
170
+ type_overrides.first
171
+ else
172
+ column_type(columns.first, 0, type_overrides)
173
+ end
174
+
175
+ rows.map do |(value)|
176
+ type.deserialize(value)
177
+ end
178
+ else
179
+ types = if type_overrides.is_a?(Array)
180
+ type_overrides
181
+ else
182
+ columns.map.with_index { |name, i| column_type(name, i, type_overrides) }
183
+ end
184
+
185
+ rows.map do |values|
186
+ Array.new(values.size) { |i| types[i].deserialize(values[i]) }
187
+ end
188
+ end
189
+ end
190
+
191
+ def initialize_copy(other)
192
+ @rows = rows.dup
193
+ @column_types = column_types.dup
194
+ end
195
+
196
+ def freeze # :nodoc:
197
+ hash_rows.freeze
198
+ indexed_rows.freeze
199
+ super
200
+ end
201
+
202
+ def column_indexes # :nodoc:
203
+ @column_indexes ||= begin
204
+ index = 0
205
+ hash = {}
206
+ length = columns.length
207
+ while index < length
208
+ hash[columns[index]] = index
209
+ index += 1
210
+ end
211
+ hash.freeze
212
+ end
213
+ end
214
+
215
+ private
216
+ def column_type(name, index, type_overrides)
217
+ type_overrides.fetch(name) do
218
+ column_types.fetch(index) do
219
+ column_types.fetch(name, Type.default_value)
220
+ end
221
+ end
222
+ end
223
+
224
+ def indexed_rows
225
+ @indexed_rows ||= begin
226
+ columns = column_indexes
227
+ @rows.map { |row| IndexedRow.new(columns, row) }.freeze
228
+ end
229
+ end
230
+
231
+ def hash_rows
232
+ # We use transform_values to rows.
233
+ # This is faster because we avoid any reallocs and avoid hashing entirely.
234
+ @hash_rows ||= @rows.map do |row|
235
+ column_indexes.transform_values { |index| row[index] }
236
+ end
237
+ end
238
+
239
+ empty_array = [].freeze
240
+ EMPTY_HASH = {}.freeze
241
+ private_constant :EMPTY_HASH
242
+
243
+ EMPTY = new(empty_array, empty_array, EMPTY_HASH).freeze
244
+ private_constant :EMPTY
245
+
246
+ EMPTY_ASYNC = FutureResult.wrap(EMPTY).freeze
247
+ private_constant :EMPTY_ASYNC
248
+ end
249
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ # This is a thread locals registry for Active Record. For example:
5
+ #
6
+ # ActiveRecord::RuntimeRegistry.sql_runtime
7
+ #
8
+ # returns the connection handler local to the current unit of execution (either thread of fiber).
9
+ module RuntimeRegistry # :nodoc:
10
+ extend self
11
+
12
+ def sql_runtime
13
+ ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] ||= 0.0
14
+ end
15
+
16
+ def sql_runtime=(runtime)
17
+ ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] = runtime
18
+ end
19
+
20
+ def async_sql_runtime
21
+ ActiveSupport::IsolatedExecutionState[:active_record_async_sql_runtime] ||= 0.0
22
+ end
23
+
24
+ def async_sql_runtime=(runtime)
25
+ ActiveSupport::IsolatedExecutionState[:active_record_async_sql_runtime] = runtime
26
+ end
27
+
28
+ def queries_count
29
+ ActiveSupport::IsolatedExecutionState[:active_record_queries_count] ||= 0
30
+ end
31
+
32
+ def queries_count=(count)
33
+ ActiveSupport::IsolatedExecutionState[:active_record_queries_count] = count
34
+ end
35
+
36
+ def cached_queries_count
37
+ ActiveSupport::IsolatedExecutionState[:active_record_cached_queries_count] ||= 0
38
+ end
39
+
40
+ def cached_queries_count=(count)
41
+ ActiveSupport::IsolatedExecutionState[:active_record_cached_queries_count] = count
42
+ end
43
+
44
+ def reset
45
+ reset_runtimes
46
+ reset_queries_count
47
+ reset_cached_queries_count
48
+ end
49
+
50
+ def reset_runtimes
51
+ rt, self.sql_runtime = sql_runtime, 0.0
52
+ self.async_sql_runtime = 0.0
53
+ rt
54
+ end
55
+
56
+ def reset_queries_count
57
+ qc = queries_count
58
+ self.queries_count = 0
59
+ qc
60
+ end
61
+
62
+ def reset_cached_queries_count
63
+ qc = cached_queries_count
64
+ self.cached_queries_count = 0
65
+ qc
66
+ end
67
+ end
68
+ end
69
+
70
+ ActiveSupport::Notifications.monotonic_subscribe("sql.active_record") do |name, start, finish, id, payload|
71
+ unless ["SCHEMA", "TRANSACTION"].include?(payload[:name])
72
+ ActiveRecord::RuntimeRegistry.queries_count += 1
73
+ ActiveRecord::RuntimeRegistry.cached_queries_count += 1 if payload[:cached]
74
+ end
75
+
76
+ runtime = (finish - start) * 1_000.0
77
+
78
+ if payload[:async]
79
+ ActiveRecord::RuntimeRegistry.async_sql_runtime += (runtime - payload[:lock_wait])
80
+ end
81
+ ActiveRecord::RuntimeRegistry.sql_runtime += runtime
82
+ end
@@ -0,0 +1,254 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Sanitization
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ # Accepts an array of SQL conditions and sanitizes them into a valid
9
+ # SQL fragment for a WHERE clause.
10
+ #
11
+ # sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
12
+ # # => "name='foo''bar' and group_id=4"
13
+ #
14
+ # sanitize_sql_for_conditions(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
15
+ # # => "name='foo''bar' and group_id='4'"
16
+ #
17
+ # sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
18
+ # # => "name='foo''bar' and group_id='4'"
19
+ #
20
+ # This method will NOT sanitize an SQL string since it won't contain
21
+ # any conditions in it and will return the string as is.
22
+ #
23
+ # sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
24
+ # # => "name='foo''bar' and group_id='4'"
25
+ #
26
+ # Note that this sanitization method is not schema-aware, hence won't do any type casting
27
+ # and will directly use the database adapter's +quote+ method.
28
+ # For MySQL specifically this means that numeric parameters will be quoted as strings
29
+ # to prevent query manipulation attacks.
30
+ #
31
+ # sanitize_sql_for_conditions(["role = ?", 0])
32
+ # # => "role = '0'"
33
+ def sanitize_sql_for_conditions(condition)
34
+ return nil if condition.blank?
35
+
36
+ case condition
37
+ when Array; sanitize_sql_array(condition)
38
+ else condition
39
+ end
40
+ end
41
+ alias :sanitize_sql :sanitize_sql_for_conditions
42
+
43
+ # Accepts an array or hash of SQL conditions and sanitizes them into
44
+ # a valid SQL fragment for a SET clause.
45
+ #
46
+ # sanitize_sql_for_assignment(["name=? and group_id=?", nil, 4])
47
+ # # => "name=NULL and group_id=4"
48
+ #
49
+ # sanitize_sql_for_assignment(["name=:name and group_id=:group_id", name: nil, group_id: 4])
50
+ # # => "name=NULL and group_id=4"
51
+ #
52
+ # Post.sanitize_sql_for_assignment({ name: nil, group_id: 4 })
53
+ # # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
54
+ #
55
+ # This method will NOT sanitize an SQL string since it won't contain
56
+ # any conditions in it and will return the string as is.
57
+ #
58
+ # sanitize_sql_for_assignment("name=NULL and group_id='4'")
59
+ # # => "name=NULL and group_id='4'"
60
+ #
61
+ # Note that this sanitization method is not schema-aware, hence won't do any type casting
62
+ # and will directly use the database adapter's +quote+ method.
63
+ # For MySQL specifically this means that numeric parameters will be quoted as strings
64
+ # to prevent query manipulation attacks.
65
+ #
66
+ # sanitize_sql_for_assignment(["role = ?", 0])
67
+ # # => "role = '0'"
68
+ def sanitize_sql_for_assignment(assignments, default_table_name = table_name)
69
+ case assignments
70
+ when Array; sanitize_sql_array(assignments)
71
+ when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
72
+ else assignments
73
+ end
74
+ end
75
+
76
+ # Accepts an array, or string of SQL conditions and sanitizes
77
+ # them into a valid SQL fragment for an ORDER clause.
78
+ #
79
+ # sanitize_sql_for_order([Arel.sql("field(id, ?)"), [1,3,2]])
80
+ # # => "field(id, 1,3,2)"
81
+ #
82
+ # sanitize_sql_for_order("id ASC")
83
+ # # => "id ASC"
84
+ def sanitize_sql_for_order(condition)
85
+ if condition.is_a?(Array) && condition.first.to_s.include?("?")
86
+ disallow_raw_sql!(
87
+ [condition.first],
88
+ permit: adapter_class.column_name_with_order_matcher
89
+ )
90
+
91
+ # Ensure we aren't dealing with a subclass of String that might
92
+ # override methods we use (e.g. Arel::Nodes::SqlLiteral).
93
+ if condition.first.kind_of?(String) && !condition.first.instance_of?(String)
94
+ condition = [String.new(condition.first), *condition[1..-1]]
95
+ end
96
+
97
+ Arel.sql(sanitize_sql_array(condition))
98
+ else
99
+ condition
100
+ end
101
+ end
102
+
103
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
104
+ #
105
+ # sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
106
+ # # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
107
+ def sanitize_sql_hash_for_assignment(attrs, table)
108
+ with_connection do |c|
109
+ attrs.map do |attr, value|
110
+ type = type_for_attribute(attr)
111
+ value = type.serialize(type.cast(value))
112
+ "#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
113
+ end.join(", ")
114
+ end
115
+ end
116
+
117
+ # Sanitizes a +string+ so that it is safe to use within an SQL
118
+ # LIKE statement. This method uses +escape_character+ to escape all
119
+ # occurrences of itself, "_" and "%".
120
+ #
121
+ # sanitize_sql_like("100% true!")
122
+ # # => "100\\% true!"
123
+ #
124
+ # sanitize_sql_like("snake_cased_string")
125
+ # # => "snake\\_cased\\_string"
126
+ #
127
+ # sanitize_sql_like("100% true!", "!")
128
+ # # => "100!% true!!"
129
+ #
130
+ # sanitize_sql_like("snake_cased_string", "!")
131
+ # # => "snake!_cased!_string"
132
+ def sanitize_sql_like(string, escape_character = "\\")
133
+ if string.include?(escape_character) && escape_character != "%" && escape_character != "_"
134
+ string = string.gsub(escape_character, '\0\0')
135
+ end
136
+
137
+ string.gsub(/(?=[%_])/, escape_character)
138
+ end
139
+
140
+ # Accepts an array of conditions. The array has each value
141
+ # sanitized and interpolated into the SQL statement. If using named bind
142
+ # variables in SQL statements where a colon is required verbatim use a
143
+ # backslash to escape.
144
+ #
145
+ # sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
146
+ # # => "name='foo''bar' and group_id=4"
147
+ #
148
+ # sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
149
+ # # => "name='foo''bar' and group_id=4"
150
+ #
151
+ # sanitize_sql_array(["TO_TIMESTAMP(:date, 'YYYY/MM/DD HH12\\:MI\\:SS')", date: "foo"])
152
+ # # => "TO_TIMESTAMP('foo', 'YYYY/MM/DD HH12:MI:SS')"
153
+ #
154
+ # sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
155
+ # # => "name='foo''bar' and group_id='4'"
156
+ #
157
+ # Note that this sanitization method is not schema-aware, hence won't do any type casting
158
+ # and will directly use the database adapter's +quote+ method.
159
+ # For MySQL specifically this means that numeric parameters will be quoted as strings
160
+ # to prevent query manipulation attacks.
161
+ #
162
+ # sanitize_sql_array(["role = ?", 0])
163
+ # # => "role = '0'"
164
+ def sanitize_sql_array(ary)
165
+ statement, *values = ary
166
+ if values.first.is_a?(Hash) && /:\w+/.match?(statement)
167
+ with_connection do |c|
168
+ replace_named_bind_variables(c, statement, values.first)
169
+ end
170
+ elsif statement.include?("?")
171
+ with_connection do |c|
172
+ replace_bind_variables(c, statement, values)
173
+ end
174
+ elsif statement.blank?
175
+ statement
176
+ else
177
+ with_connection do |c|
178
+ statement % values.collect { |value| c.quote_string(value.to_s) }
179
+ end
180
+ end
181
+ end
182
+
183
+ def disallow_raw_sql!(args, permit: adapter_class.column_name_matcher) # :nodoc:
184
+ unexpected = nil
185
+ args.each do |arg|
186
+ next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s.strip)
187
+ (unexpected ||= []) << arg
188
+ end
189
+
190
+ if unexpected
191
+ raise(ActiveRecord::UnknownAttributeReference,
192
+ "Dangerous query method (method whose arguments are used as raw " \
193
+ "SQL) called with non-attribute argument(s): " \
194
+ "#{unexpected.map(&:inspect).join(", ")}." \
195
+ "This method should not be called with user-provided values, such as request " \
196
+ "parameters or model attributes. Known-safe values can be passed " \
197
+ "by wrapping them in Arel.sql()."
198
+ )
199
+ end
200
+ end
201
+
202
+ private
203
+ def replace_bind_variables(connection, statement, values)
204
+ raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
205
+ bound = values.dup
206
+ statement.gsub(/\?/) do
207
+ replace_bind_variable(connection, bound.shift)
208
+ end
209
+ end
210
+
211
+ def replace_bind_variable(connection, value)
212
+ if ActiveRecord::Relation === value
213
+ value.to_sql
214
+ else
215
+ quote_bound_value(connection, value)
216
+ end
217
+ end
218
+
219
+ def replace_named_bind_variables(connection, statement, bind_vars)
220
+ statement.gsub(/([:\\]?):([a-zA-Z]\w*)/) do |match|
221
+ if $1 == ":" # skip PostgreSQL casts
222
+ match # return the whole match
223
+ elsif $1 == "\\" # escaped literal colon
224
+ match[1..-1] # return match with escaping backlash char removed
225
+ elsif bind_vars.include?(match = $2.to_sym)
226
+ replace_bind_variable(connection, bind_vars[match])
227
+ else
228
+ raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
229
+ end
230
+ end
231
+ end
232
+
233
+ def quote_bound_value(connection, value)
234
+ if value.respond_to?(:map) && !value.acts_like?(:string)
235
+ values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
236
+ if values.empty?
237
+ connection.quote(connection.cast_bound_value(nil))
238
+ else
239
+ values.map! { |v| connection.quote(connection.cast_bound_value(v)) }.join(",")
240
+ end
241
+ else
242
+ value = value.id_for_database if value.respond_to?(:id_for_database)
243
+ connection.quote(connection.cast_bound_value(value))
244
+ end
245
+ end
246
+
247
+ def raise_if_bind_arity_mismatch(statement, expected, provided)
248
+ unless expected == provided
249
+ raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
250
+ end
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ # = Active Record \Schema
5
+ #
6
+ # Allows programmers to programmatically define a schema in a portable
7
+ # DSL. This means you can define tables, indexes, etc. without using SQL
8
+ # directly, so your applications can more easily support multiple
9
+ # databases.
10
+ #
11
+ # Usage:
12
+ #
13
+ # ActiveRecord::Schema[7.0].define do
14
+ # create_table :authors do |t|
15
+ # t.string :name, null: false
16
+ # end
17
+ #
18
+ # add_index :authors, :name, :unique
19
+ #
20
+ # create_table :posts do |t|
21
+ # t.integer :author_id, null: false
22
+ # t.string :subject
23
+ # t.text :body
24
+ # t.boolean :private, default: false
25
+ # end
26
+ #
27
+ # add_index :posts, :author_id
28
+ # end
29
+ #
30
+ # ActiveRecord::Schema is only supported by database adapters that also
31
+ # support migrations, the two features being very similar.
32
+ class Schema < Migration::Current
33
+ module Definition
34
+ extend ActiveSupport::Concern
35
+
36
+ module ClassMethods
37
+ # Eval the given block. All methods available to the current connection
38
+ # adapter are available within the block, so you can easily use the
39
+ # database definition DSL to build up your schema (
40
+ # {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table],
41
+ # {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.).
42
+ #
43
+ # The +info+ hash is optional, and if given is used to define metadata
44
+ # about the current schema (currently, only the schema's version):
45
+ #
46
+ # ActiveRecord::Schema[7.0].define(version: 2038_01_19_000001) do
47
+ # ...
48
+ # end
49
+ def define(info = {}, &block)
50
+ new.define(info, &block)
51
+ end
52
+ end
53
+
54
+ def define(info, &block) # :nodoc:
55
+ connection_pool.with_connection do |connection|
56
+ instance_eval(&block)
57
+
58
+ connection_pool.schema_migration.create_table
59
+ if info[:version].present?
60
+ connection.assume_migrated_upto_version(info[:version])
61
+ end
62
+
63
+ connection_pool.internal_metadata.create_table_and_set_flags(connection_pool.migration_context.current_environment)
64
+ end
65
+ end
66
+ end
67
+
68
+ include Definition
69
+
70
+ def self.[](version)
71
+ @class_for_version ||= {}
72
+ @class_for_version[version] ||= Class.new(Migration::Compatibility.find(version)) do
73
+ include Definition
74
+ end
75
+ end
76
+ end
77
+ end