activerecord 5.0.6 → 6.0.1

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 (358) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +638 -2023
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +8 -6
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record/aggregations.rb +249 -246
  8. data/lib/active_record/association_relation.rb +24 -13
  9. data/lib/active_record/associations/alias_tracker.rb +24 -33
  10. data/lib/active_record/associations/association.rb +119 -56
  11. data/lib/active_record/associations/association_scope.rb +94 -94
  12. data/lib/active_record/associations/belongs_to_association.rb +58 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  14. data/lib/active_record/associations/builder/association.rb +18 -25
  15. data/lib/active_record/associations/builder/belongs_to.rb +43 -54
  16. data/lib/active_record/associations/builder/collection_association.rb +7 -18
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -61
  18. data/lib/active_record/associations/builder/has_many.rb +4 -0
  19. data/lib/active_record/associations/builder/has_one.rb +37 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  21. data/lib/active_record/associations/collection_association.rb +80 -252
  22. data/lib/active_record/associations/collection_proxy.rb +158 -121
  23. data/lib/active_record/associations/foreign_association.rb +9 -0
  24. data/lib/active_record/associations/has_many_association.rb +23 -29
  25. data/lib/active_record/associations/has_many_through_association.rb +58 -44
  26. data/lib/active_record/associations/has_one_association.rb +59 -54
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +38 -90
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +134 -176
  32. data/lib/active_record/associations/preloader/association.rb +84 -125
  33. data/lib/active_record/associations/preloader/through_association.rb +82 -75
  34. data/lib/active_record/associations/preloader.rb +90 -102
  35. data/lib/active_record/associations/singular_association.rb +12 -45
  36. data/lib/active_record/associations/through_association.rb +26 -14
  37. data/lib/active_record/associations.rb +1603 -1592
  38. data/lib/active_record/attribute_assignment.rb +54 -60
  39. data/lib/active_record/attribute_decorators.rb +38 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -7
  41. data/lib/active_record/attribute_methods/dirty.rb +179 -109
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -91
  43. data/lib/active_record/attribute_methods/query.rb +4 -3
  44. data/lib/active_record/attribute_methods/read.rb +21 -49
  45. data/lib/active_record/attribute_methods/serialization.rb +30 -7
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -64
  47. data/lib/active_record/attribute_methods/write.rb +35 -33
  48. data/lib/active_record/attribute_methods.rb +66 -106
  49. data/lib/active_record/attributes.rb +38 -24
  50. data/lib/active_record/autosave_association.rb +53 -32
  51. data/lib/active_record/base.rb +27 -24
  52. data/lib/active_record/callbacks.rb +63 -33
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +11 -11
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +553 -321
  56. data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
  57. data/lib/active_record/connection_adapters/abstract/database_statements.rb +213 -94
  58. data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -28
  59. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -75
  60. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -27
  62. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -126
  63. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  64. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +369 -199
  65. data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
  66. data/lib/active_record/connection_adapters/abstract_adapter.rb +363 -202
  67. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -551
  68. data/lib/active_record/connection_adapters/column.rb +41 -13
  69. data/lib/active_record/connection_adapters/connection_specification.rb +172 -138
  70. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -4
  71. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +143 -49
  73. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -22
  74. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  75. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +50 -45
  76. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +58 -56
  77. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  78. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  79. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +49 -30
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +22 -7
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +60 -54
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -10
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +4 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -17
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
  98. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +31 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +34 -30
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -4
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +24 -21
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +95 -35
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +35 -32
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +380 -300
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +10 -6
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +382 -275
  116. data/lib/active_record/connection_adapters/schema_cache.rb +46 -12
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +13 -8
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +74 -19
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +3 -8
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +254 -262
  126. data/lib/active_record/connection_adapters/statement_pool.rb +9 -7
  127. data/lib/active_record/connection_handling.rb +159 -40
  128. data/lib/active_record/core.rb +202 -162
  129. data/lib/active_record/counter_cache.rb +57 -28
  130. data/lib/active_record/database_configurations/database_config.rb +37 -0
  131. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  132. data/lib/active_record/database_configurations/url_config.rb +79 -0
  133. data/lib/active_record/database_configurations.rb +233 -0
  134. data/lib/active_record/define_callbacks.rb +22 -0
  135. data/lib/active_record/dynamic_matchers.rb +87 -86
  136. data/lib/active_record/enum.rb +60 -23
  137. data/lib/active_record/errors.rb +114 -18
  138. data/lib/active_record/explain.rb +4 -3
  139. data/lib/active_record/explain_registry.rb +3 -1
  140. data/lib/active_record/explain_subscriber.rb +9 -4
  141. data/lib/active_record/fixture_set/file.rb +13 -8
  142. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  143. data/lib/active_record/fixture_set/render_context.rb +17 -0
  144. data/lib/active_record/fixture_set/table_row.rb +153 -0
  145. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  146. data/lib/active_record/fixtures.rb +195 -502
  147. data/lib/active_record/gem_version.rb +4 -2
  148. data/lib/active_record/inheritance.rb +151 -97
  149. data/lib/active_record/insert_all.rb +179 -0
  150. data/lib/active_record/integration.rb +116 -25
  151. data/lib/active_record/internal_metadata.rb +15 -18
  152. data/lib/active_record/legacy_yaml_adapter.rb +4 -2
  153. data/lib/active_record/locking/optimistic.rb +78 -87
  154. data/lib/active_record/locking/pessimistic.rb +18 -6
  155. data/lib/active_record/log_subscriber.rb +48 -29
  156. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  157. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/migration/command_recorder.rb +143 -97
  160. data/lib/active_record/migration/compatibility.rb +174 -56
  161. data/lib/active_record/migration/join_table.rb +8 -6
  162. data/lib/active_record/migration.rb +367 -300
  163. data/lib/active_record/model_schema.rb +145 -139
  164. data/lib/active_record/nested_attributes.rb +214 -201
  165. data/lib/active_record/no_touching.rb +10 -1
  166. data/lib/active_record/null_relation.rb +13 -34
  167. data/lib/active_record/persistence.rb +442 -72
  168. data/lib/active_record/query_cache.rb +15 -14
  169. data/lib/active_record/querying.rb +36 -23
  170. data/lib/active_record/railtie.rb +128 -36
  171. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  172. data/lib/active_record/railties/console_sandbox.rb +2 -0
  173. data/lib/active_record/railties/controller_runtime.rb +34 -33
  174. data/lib/active_record/railties/databases.rake +309 -177
  175. data/lib/active_record/readonly_attributes.rb +5 -4
  176. data/lib/active_record/reflection.rb +211 -249
  177. data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
  178. data/lib/active_record/relation/batches.rb +99 -52
  179. data/lib/active_record/relation/calculations.rb +211 -172
  180. data/lib/active_record/relation/delegation.rb +67 -65
  181. data/lib/active_record/relation/finder_methods.rb +208 -247
  182. data/lib/active_record/relation/from_clause.rb +2 -8
  183. data/lib/active_record/relation/merger.rb +78 -61
  184. data/lib/active_record/relation/predicate_builder/array_handler.rb +20 -14
  185. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  186. data/lib/active_record/relation/predicate_builder/base_handler.rb +4 -3
  187. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  188. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  189. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  190. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  191. data/lib/active_record/relation/predicate_builder.rb +86 -104
  192. data/lib/active_record/relation/query_attribute.rb +33 -2
  193. data/lib/active_record/relation/query_methods.rb +458 -329
  194. data/lib/active_record/relation/record_fetch_warning.rb +5 -3
  195. data/lib/active_record/relation/spawn_methods.rb +8 -7
  196. data/lib/active_record/relation/where_clause.rb +111 -95
  197. data/lib/active_record/relation/where_clause_factory.rb +6 -11
  198. data/lib/active_record/relation.rb +429 -318
  199. data/lib/active_record/result.rb +69 -39
  200. data/lib/active_record/runtime_registry.rb +5 -3
  201. data/lib/active_record/sanitization.rb +83 -99
  202. data/lib/active_record/schema.rb +7 -14
  203. data/lib/active_record/schema_dumper.rb +71 -69
  204. data/lib/active_record/schema_migration.rb +15 -5
  205. data/lib/active_record/scoping/default.rb +93 -95
  206. data/lib/active_record/scoping/named.rb +45 -25
  207. data/lib/active_record/scoping.rb +20 -19
  208. data/lib/active_record/secure_token.rb +4 -2
  209. data/lib/active_record/serialization.rb +2 -0
  210. data/lib/active_record/statement_cache.rb +63 -28
  211. data/lib/active_record/store.rb +121 -41
  212. data/lib/active_record/suppressor.rb +4 -1
  213. data/lib/active_record/table_metadata.rb +26 -20
  214. data/lib/active_record/tasks/database_tasks.rb +276 -85
  215. data/lib/active_record/tasks/mysql_database_tasks.rb +54 -90
  216. data/lib/active_record/tasks/postgresql_database_tasks.rb +78 -47
  217. data/lib/active_record/tasks/sqlite_database_tasks.rb +34 -16
  218. data/lib/active_record/test_databases.rb +23 -0
  219. data/lib/active_record/test_fixtures.rb +224 -0
  220. data/lib/active_record/timestamp.rb +70 -35
  221. data/lib/active_record/touch_later.rb +7 -4
  222. data/lib/active_record/transactions.rb +133 -149
  223. data/lib/active_record/translation.rb +3 -1
  224. data/lib/active_record/type/adapter_specific_registry.rb +44 -45
  225. data/lib/active_record/type/date.rb +2 -0
  226. data/lib/active_record/type/date_time.rb +2 -0
  227. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  228. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  229. data/lib/active_record/type/internal/timezone.rb +2 -0
  230. data/lib/active_record/type/json.rb +30 -0
  231. data/lib/active_record/type/serialized.rb +16 -8
  232. data/lib/active_record/type/text.rb +11 -0
  233. data/lib/active_record/type/time.rb +2 -1
  234. data/lib/active_record/type/type_map.rb +13 -15
  235. data/lib/active_record/type/unsigned_integer.rb +17 -0
  236. data/lib/active_record/type.rb +23 -17
  237. data/lib/active_record/type_caster/connection.rb +17 -12
  238. data/lib/active_record/type_caster/map.rb +5 -4
  239. data/lib/active_record/type_caster.rb +4 -2
  240. data/lib/active_record/validations/absence.rb +2 -0
  241. data/lib/active_record/validations/associated.rb +3 -1
  242. data/lib/active_record/validations/length.rb +2 -0
  243. data/lib/active_record/validations/presence.rb +4 -2
  244. data/lib/active_record/validations/uniqueness.rb +29 -42
  245. data/lib/active_record/validations.rb +7 -4
  246. data/lib/active_record/version.rb +3 -1
  247. data/lib/active_record.rb +36 -22
  248. data/lib/arel/alias_predication.rb +9 -0
  249. data/lib/arel/attributes/attribute.rb +37 -0
  250. data/lib/arel/attributes.rb +22 -0
  251. data/lib/arel/collectors/bind.rb +24 -0
  252. data/lib/arel/collectors/composite.rb +31 -0
  253. data/lib/arel/collectors/plain_string.rb +20 -0
  254. data/lib/arel/collectors/sql_string.rb +20 -0
  255. data/lib/arel/collectors/substitute_binds.rb +28 -0
  256. data/lib/arel/crud.rb +42 -0
  257. data/lib/arel/delete_manager.rb +18 -0
  258. data/lib/arel/errors.rb +9 -0
  259. data/lib/arel/expressions.rb +29 -0
  260. data/lib/arel/factory_methods.rb +49 -0
  261. data/lib/arel/insert_manager.rb +49 -0
  262. data/lib/arel/math.rb +45 -0
  263. data/lib/arel/nodes/and.rb +32 -0
  264. data/lib/arel/nodes/ascending.rb +23 -0
  265. data/lib/arel/nodes/binary.rb +52 -0
  266. data/lib/arel/nodes/bind_param.rb +36 -0
  267. data/lib/arel/nodes/case.rb +55 -0
  268. data/lib/arel/nodes/casted.rb +50 -0
  269. data/lib/arel/nodes/comment.rb +29 -0
  270. data/lib/arel/nodes/count.rb +12 -0
  271. data/lib/arel/nodes/delete_statement.rb +45 -0
  272. data/lib/arel/nodes/descending.rb +23 -0
  273. data/lib/arel/nodes/equality.rb +18 -0
  274. data/lib/arel/nodes/extract.rb +24 -0
  275. data/lib/arel/nodes/false.rb +16 -0
  276. data/lib/arel/nodes/full_outer_join.rb +8 -0
  277. data/lib/arel/nodes/function.rb +44 -0
  278. data/lib/arel/nodes/grouping.rb +8 -0
  279. data/lib/arel/nodes/in.rb +8 -0
  280. data/lib/arel/nodes/infix_operation.rb +80 -0
  281. data/lib/arel/nodes/inner_join.rb +8 -0
  282. data/lib/arel/nodes/insert_statement.rb +37 -0
  283. data/lib/arel/nodes/join_source.rb +20 -0
  284. data/lib/arel/nodes/matches.rb +18 -0
  285. data/lib/arel/nodes/named_function.rb +23 -0
  286. data/lib/arel/nodes/node.rb +50 -0
  287. data/lib/arel/nodes/node_expression.rb +13 -0
  288. data/lib/arel/nodes/outer_join.rb +8 -0
  289. data/lib/arel/nodes/over.rb +15 -0
  290. data/lib/arel/nodes/regexp.rb +16 -0
  291. data/lib/arel/nodes/right_outer_join.rb +8 -0
  292. data/lib/arel/nodes/select_core.rb +67 -0
  293. data/lib/arel/nodes/select_statement.rb +41 -0
  294. data/lib/arel/nodes/sql_literal.rb +16 -0
  295. data/lib/arel/nodes/string_join.rb +11 -0
  296. data/lib/arel/nodes/table_alias.rb +27 -0
  297. data/lib/arel/nodes/terminal.rb +16 -0
  298. data/lib/arel/nodes/true.rb +16 -0
  299. data/lib/arel/nodes/unary.rb +45 -0
  300. data/lib/arel/nodes/unary_operation.rb +20 -0
  301. data/lib/arel/nodes/unqualified_column.rb +22 -0
  302. data/lib/arel/nodes/update_statement.rb +41 -0
  303. data/lib/arel/nodes/values_list.rb +9 -0
  304. data/lib/arel/nodes/window.rb +126 -0
  305. data/lib/arel/nodes/with.rb +11 -0
  306. data/lib/arel/nodes.rb +68 -0
  307. data/lib/arel/order_predications.rb +13 -0
  308. data/lib/arel/predications.rb +257 -0
  309. data/lib/arel/select_manager.rb +271 -0
  310. data/lib/arel/table.rb +110 -0
  311. data/lib/arel/tree_manager.rb +72 -0
  312. data/lib/arel/update_manager.rb +34 -0
  313. data/lib/arel/visitors/depth_first.rb +204 -0
  314. data/lib/arel/visitors/dot.rb +297 -0
  315. data/lib/arel/visitors/ibm_db.rb +34 -0
  316. data/lib/arel/visitors/informix.rb +62 -0
  317. data/lib/arel/visitors/mssql.rb +157 -0
  318. data/lib/arel/visitors/mysql.rb +83 -0
  319. data/lib/arel/visitors/oracle.rb +159 -0
  320. data/lib/arel/visitors/oracle12.rb +66 -0
  321. data/lib/arel/visitors/postgresql.rb +110 -0
  322. data/lib/arel/visitors/sqlite.rb +39 -0
  323. data/lib/arel/visitors/to_sql.rb +889 -0
  324. data/lib/arel/visitors/visitor.rb +46 -0
  325. data/lib/arel/visitors/where_sql.rb +23 -0
  326. data/lib/arel/visitors.rb +20 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/arel.rb +58 -0
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  330. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -35
  332. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +1 -1
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +4 -2
  334. data/lib/rails/generators/active_record/migration.rb +17 -2
  335. data/lib/rails/generators/active_record/model/model_generator.rb +9 -29
  336. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  337. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  338. data/lib/rails/generators/active_record.rb +7 -5
  339. metadata +133 -50
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  347. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  348. data/lib/active_record/attribute.rb +0 -213
  349. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  350. data/lib/active_record/attribute_set/builder.rb +0 -130
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/collection_cache_key.rb +0 -50
  353. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  354. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  355. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  356. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  357. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  358. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,24 +1,8 @@
1
- require 'active_support/core_ext/string/strip'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module PostgreSQL
6
- class SchemaCreation < AbstractAdapter::SchemaCreation
7
- private
8
-
9
- def visit_ColumnDefinition(o)
10
- o.sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale, o.array)
11
- super
12
- end
13
-
14
- def add_column_options!(sql, options)
15
- if options[:collation]
16
- sql << " COLLATE \"#{options[:collation]}\""
17
- end
18
- super
19
- end
20
- end
21
-
22
6
  module SchemaStatements
23
7
  # Drops the database specified on the +name+ attribute
24
8
  # and creates it again using the provided +options+.
@@ -36,26 +20,26 @@ module ActiveRecord
36
20
  # create_database config[:database], config
37
21
  # create_database 'foo_development', encoding: 'unicode'
38
22
  def create_database(name, options = {})
39
- options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
40
-
41
- option_string = options.inject("") do |memo, (key, value)|
42
- memo += case key
43
- when :owner
44
- " OWNER = \"#{value}\""
45
- when :template
46
- " TEMPLATE = \"#{value}\""
47
- when :encoding
48
- " ENCODING = '#{value}'"
49
- when :collation
50
- " LC_COLLATE = '#{value}'"
51
- when :ctype
52
- " LC_CTYPE = '#{value}'"
53
- when :tablespace
54
- " TABLESPACE = \"#{value}\""
55
- when :connection_limit
56
- " CONNECTION LIMIT = #{value}"
57
- else
58
- ""
23
+ options = { encoding: "utf8" }.merge!(options.symbolize_keys)
24
+
25
+ option_string = options.each_with_object(+"") do |(key, value), memo|
26
+ memo << case key
27
+ when :owner
28
+ " OWNER = \"#{value}\""
29
+ when :template
30
+ " TEMPLATE = \"#{value}\""
31
+ when :encoding
32
+ " ENCODING = '#{value}'"
33
+ when :collation
34
+ " LC_COLLATE = '#{value}'"
35
+ when :ctype
36
+ " LC_CTYPE = '#{value}'"
37
+ when :tablespace
38
+ " TABLESPACE = \"#{value}\""
39
+ when :connection_limit
40
+ " CONNECTION LIMIT = #{value}"
41
+ else
42
+ ""
59
43
  end
60
44
  end
61
45
 
@@ -70,123 +54,48 @@ module ActiveRecord
70
54
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
71
55
  end
72
56
 
73
- # Returns the list of all tables in the schema search path.
74
- def tables(name = nil)
75
- if name
76
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
77
- Passing arguments to #tables is deprecated without replacement.
78
- MSG
79
- end
80
-
81
- select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", 'SCHEMA')
82
- end
83
-
84
- def data_sources # :nodoc
85
- select_values(<<-SQL, 'SCHEMA')
86
- SELECT c.relname
87
- FROM pg_class c
88
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
89
- WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
90
- AND n.nspname = ANY (current_schemas(false))
91
- SQL
92
- end
93
-
94
- # Returns true if table exists.
95
- # If the schema is not specified as part of +name+ then it will only find tables within
96
- # the current schema search path (regardless of permissions to access tables in other schemas)
97
- def table_exists?(name)
98
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
99
- #table_exists? currently checks both tables and views.
100
- This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
101
- Use #data_source_exists? instead.
102
- MSG
103
-
104
- data_source_exists?(name)
105
- end
106
-
107
- def data_source_exists?(name)
108
- name = Utils.extract_schema_qualified_name(name.to_s)
109
- return false unless name.identifier
110
-
111
- select_value(<<-SQL, 'SCHEMA').to_i > 0
112
- SELECT COUNT(*)
113
- FROM pg_class c
114
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
115
- WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
116
- AND c.relname = '#{name.identifier}'
117
- AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
118
- SQL
119
- end
120
-
121
- def views # :nodoc:
122
- select_values(<<-SQL, 'SCHEMA')
123
- SELECT c.relname
124
- FROM pg_class c
125
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
126
- WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
127
- AND n.nspname = ANY (current_schemas(false))
128
- SQL
129
- end
130
-
131
- def view_exists?(view_name) # :nodoc:
132
- name = Utils.extract_schema_qualified_name(view_name.to_s)
133
- return false unless name.identifier
134
-
135
- select_values(<<-SQL, 'SCHEMA').any?
136
- SELECT c.relname
137
- FROM pg_class c
138
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
139
- WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
140
- AND c.relname = '#{name.identifier}'
141
- AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
142
- SQL
143
- end
144
-
145
57
  def drop_table(table_name, options = {}) # :nodoc:
146
58
  execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
147
59
  end
148
60
 
149
61
  # Returns true if schema exists.
150
62
  def schema_exists?(name)
151
- select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", 'SCHEMA').to_i > 0
63
+ query_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
152
64
  end
153
65
 
154
66
  # Verifies existence of an index with a given name.
155
- def index_name_exists?(table_name, index_name, default)
156
- table = Utils.extract_schema_qualified_name(table_name.to_s)
157
- index = Utils.extract_schema_qualified_name(index_name.to_s)
67
+ def index_name_exists?(table_name, index_name)
68
+ table = quoted_scope(table_name)
69
+ index = quoted_scope(index_name)
158
70
 
159
- select_value(<<-SQL, 'SCHEMA').to_i > 0
71
+ query_value(<<~SQL, "SCHEMA").to_i > 0
160
72
  SELECT COUNT(*)
161
73
  FROM pg_class t
162
74
  INNER JOIN pg_index d ON t.oid = d.indrelid
163
75
  INNER JOIN pg_class i ON d.indexrelid = i.oid
164
76
  LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
165
77
  WHERE i.relkind = 'i'
166
- AND i.relname = '#{index.identifier}'
167
- AND t.relname = '#{table.identifier}'
168
- AND n.nspname = #{index.schema ? "'#{index.schema}'" : 'ANY (current_schemas(false))'}
78
+ AND i.relname = #{index[:name]}
79
+ AND t.relname = #{table[:name]}
80
+ AND n.nspname = #{index[:schema]}
169
81
  SQL
170
82
  end
171
83
 
172
84
  # Returns an array of indexes for the given table.
173
- def indexes(table_name, name = nil)
174
- table = Utils.extract_schema_qualified_name(table_name.to_s)
85
+ def indexes(table_name) # :nodoc:
86
+ scope = quoted_scope(table_name)
175
87
 
176
- result = query(<<-SQL, 'SCHEMA')
88
+ result = query(<<~SQL, "SCHEMA")
177
89
  SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
178
- pg_catalog.obj_description(i.oid, 'pg_class') AS comment,
179
- (SELECT COUNT(*) FROM pg_opclass o
180
- JOIN (SELECT unnest(string_to_array(d.indclass::text, ' '))::int oid) c
181
- ON o.oid = c.oid WHERE o.opcdefault = 'f')
90
+ pg_catalog.obj_description(i.oid, 'pg_class') AS comment
182
91
  FROM pg_class t
183
92
  INNER JOIN pg_index d ON t.oid = d.indrelid
184
93
  INNER JOIN pg_class i ON d.indexrelid = i.oid
185
94
  LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
186
95
  WHERE i.relkind = 'i'
187
96
  AND d.indisprimary = 'f'
188
- AND t.relname = '#{table.identifier}'
189
- AND n.nspname = #{table.schema ? "'#{table.schema}'" : 'ANY (current_schemas(false))'}
97
+ AND t.relname = #{scope[:name]}
98
+ AND n.nspname = #{scope[:schema]}
190
99
  ORDER BY i.relname
191
100
  SQL
192
101
 
@@ -197,47 +106,48 @@ module ActiveRecord
197
106
  inddef = row[3]
198
107
  oid = row[4]
199
108
  comment = row[5]
200
- opclass = row[6]
201
109
 
202
- using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten
110
+ using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
111
+
112
+ orders = {}
113
+ opclasses = {}
203
114
 
204
- if indkey.include?(0) || opclass > 0
115
+ if indkey.include?(0)
205
116
  columns = expressions
206
117
  else
207
- columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
118
+ columns = Hash[query(<<~SQL, "SCHEMA")].values_at(*indkey).compact
208
119
  SELECT a.attnum, a.attname
209
120
  FROM pg_attribute a
210
121
  WHERE a.attrelid = #{oid}
211
122
  AND a.attnum IN (#{indkey.join(",")})
212
123
  SQL
213
124
 
214
- # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
215
- orders = Hash[
216
- expressions.scan(/(\w+) DESC/).flatten.map { |order_column| [order_column, :desc] }
217
- ]
125
+ # add info on sort order (only desc order is explicitly specified, asc is the default)
126
+ # and non-default opclasses
127
+ expressions.scan(/(?<column>\w+)"?\s?(?<opclass>\w+_ops)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
128
+ opclasses[column] = opclass.to_sym if opclass
129
+ if nulls
130
+ orders[column] = [desc, nulls].compact.join(" ")
131
+ else
132
+ orders[column] = :desc if desc
133
+ end
134
+ end
218
135
  end
219
136
 
220
- IndexDefinition.new(table_name, index_name, unique, columns, [], orders, where, nil, using.to_sym, comment.presence)
221
- end.compact
222
- end
223
-
224
- # Returns the list of all column definitions for a table.
225
- def columns(table_name) # :nodoc:
226
- table_name = table_name.to_s
227
- column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod, collation, comment|
228
- oid = oid.to_i
229
- fmod = fmod.to_i
230
- type_metadata = fetch_type_metadata(column_name, type, oid, fmod)
231
- default_value = extract_value_from_default(default)
232
- default_function = extract_default_function(default_value, default)
233
- new_column(column_name, default_value, type_metadata, !notnull, table_name, default_function, collation, comment: comment.presence)
137
+ IndexDefinition.new(
138
+ table_name,
139
+ index_name,
140
+ unique,
141
+ columns,
142
+ orders: orders,
143
+ opclasses: opclasses,
144
+ where: where,
145
+ using: using.to_sym,
146
+ comment: comment.presence
147
+ )
234
148
  end
235
149
  end
236
150
 
237
- def new_column(*args) # :nodoc:
238
- PostgreSQLColumn.new(*args)
239
- end
240
-
241
151
  def table_options(table_name) # :nodoc:
242
152
  if comment = table_comment(table_name)
243
153
  { comment: comment }
@@ -246,47 +156,47 @@ module ActiveRecord
246
156
 
247
157
  # Returns a comment stored in database for given table
248
158
  def table_comment(table_name) # :nodoc:
249
- name = Utils.extract_schema_qualified_name(table_name.to_s)
250
- if name.identifier
251
- select_value(<<-SQL.strip_heredoc, 'SCHEMA')
159
+ scope = quoted_scope(table_name, type: "BASE TABLE")
160
+ if scope[:name]
161
+ query_value(<<~SQL, "SCHEMA")
252
162
  SELECT pg_catalog.obj_description(c.oid, 'pg_class')
253
163
  FROM pg_catalog.pg_class c
254
164
  LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
255
- WHERE c.relname = #{quote(name.identifier)}
256
- AND c.relkind IN ('r') -- (r)elation/table
257
- AND n.nspname = #{name.schema ? quote(name.schema) : 'ANY (current_schemas(false))'}
165
+ WHERE c.relname = #{scope[:name]}
166
+ AND c.relkind IN (#{scope[:type]})
167
+ AND n.nspname = #{scope[:schema]}
258
168
  SQL
259
169
  end
260
170
  end
261
171
 
262
172
  # Returns the current database name.
263
173
  def current_database
264
- select_value('select current_database()', 'SCHEMA')
174
+ query_value("SELECT current_database()", "SCHEMA")
265
175
  end
266
176
 
267
177
  # Returns the current schema name.
268
178
  def current_schema
269
- select_value('SELECT current_schema', 'SCHEMA')
179
+ query_value("SELECT current_schema", "SCHEMA")
270
180
  end
271
181
 
272
182
  # Returns the current database encoding format.
273
183
  def encoding
274
- select_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
184
+ query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
275
185
  end
276
186
 
277
187
  # Returns the current database collation.
278
188
  def collation
279
- select_value("SELECT datcollate FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
189
+ query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
280
190
  end
281
191
 
282
192
  # Returns the current database ctype.
283
193
  def ctype
284
- select_value("SELECT datctype FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
194
+ query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
285
195
  end
286
196
 
287
197
  # Returns an array of schema names.
288
198
  def schema_names
289
- select_values(<<-SQL, 'SCHEMA')
199
+ query_values(<<~SQL, "SCHEMA")
290
200
  SELECT nspname
291
201
  FROM pg_namespace
292
202
  WHERE nspname !~ '^pg_.*'
@@ -296,7 +206,7 @@ module ActiveRecord
296
206
  end
297
207
 
298
208
  # Creates a schema for the given schema name.
299
- def create_schema schema_name
209
+ def create_schema(schema_name)
300
210
  execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
301
211
  end
302
212
 
@@ -307,42 +217,42 @@ module ActiveRecord
307
217
 
308
218
  # Sets the schema search path to a string of comma-separated schema names.
309
219
  # Names beginning with $ have to be quoted (e.g. $user => '$user').
310
- # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
220
+ # See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
311
221
  #
312
222
  # This should be not be called manually but set in database.yml.
313
223
  def schema_search_path=(schema_csv)
314
224
  if schema_csv
315
- execute("SET search_path TO #{schema_csv}", 'SCHEMA')
225
+ execute("SET search_path TO #{schema_csv}", "SCHEMA")
316
226
  @schema_search_path = schema_csv
317
227
  end
318
228
  end
319
229
 
320
230
  # Returns the active schema search path.
321
231
  def schema_search_path
322
- @schema_search_path ||= select_value('SHOW search_path', 'SCHEMA')
232
+ @schema_search_path ||= query_value("SHOW search_path", "SCHEMA")
323
233
  end
324
234
 
325
235
  # Returns the current client message level.
326
236
  def client_min_messages
327
- select_value('SHOW client_min_messages', 'SCHEMA')
237
+ query_value("SHOW client_min_messages", "SCHEMA")
328
238
  end
329
239
 
330
240
  # Set the client message level.
331
241
  def client_min_messages=(level)
332
- execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
242
+ execute("SET client_min_messages TO '#{level}'", "SCHEMA")
333
243
  end
334
244
 
335
245
  # Returns the sequence name for a table's primary key or some other specified key.
336
- def default_sequence_name(table_name, pk = nil) #:nodoc:
337
- result = serial_sequence(table_name, pk || 'id')
246
+ def default_sequence_name(table_name, pk = "id") #:nodoc:
247
+ result = serial_sequence(table_name, pk)
338
248
  return nil unless result
339
249
  Utils.extract_schema_qualified_name(result).to_s
340
250
  rescue ActiveRecord::StatementInvalid
341
- PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq").to_s
251
+ PostgreSQL::Name.new(nil, "#{table_name}_#{pk}_seq").to_s
342
252
  end
343
253
 
344
254
  def serial_sequence(table, column)
345
- select_value("SELECT pg_get_serial_sequence('#{table}', '#{column}')", 'SCHEMA')
255
+ query_value("SELECT pg_get_serial_sequence(#{quote(table)}, #{quote(column)})", "SCHEMA")
346
256
  end
347
257
 
348
258
  # Sets the sequence of a table's primary key to the specified value.
@@ -353,7 +263,7 @@ module ActiveRecord
353
263
  if sequence
354
264
  quoted_sequence = quote_table_name(sequence)
355
265
 
356
- select_value("SELECT setval('#{quoted_sequence}', #{value})", 'SCHEMA')
266
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
357
267
  else
358
268
  @logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
359
269
  end
@@ -362,7 +272,7 @@ module ActiveRecord
362
272
 
363
273
  # Resets the sequence of a table's primary key to the maximum value.
364
274
  def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
365
- unless pk and sequence
275
+ unless pk && sequence
366
276
  default_pk, default_sequence = pk_and_sequence_for(table)
367
277
 
368
278
  pk ||= default_pk
@@ -375,18 +285,16 @@ module ActiveRecord
375
285
 
376
286
  if pk && sequence
377
287
  quoted_sequence = quote_table_name(sequence)
378
- max_pk = select_value("select MAX(#{quote_column_name pk}) from #{quote_table_name(table)}")
288
+ max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
379
289
  if max_pk.nil?
380
- if postgresql_version >= 100000
381
- minvalue = select_value("SELECT seqmin from pg_sequence where seqrelid = '#{quoted_sequence}'::regclass")
290
+ if database_version >= 100000
291
+ minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
382
292
  else
383
- minvalue = select_value("SELECT min_value FROM #{quoted_sequence}")
293
+ minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
384
294
  end
385
295
  end
386
296
 
387
- select_value(<<-end_sql, "SCHEMA")
388
- SELECT setval('#{quoted_sequence}', #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})
389
- end_sql
297
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
390
298
  end
391
299
  end
392
300
 
@@ -394,7 +302,7 @@ module ActiveRecord
394
302
  def pk_and_sequence_for(table) #:nodoc:
395
303
  # First try looking for a sequence with a dependency on the
396
304
  # given table's primary key.
397
- result = query(<<-end_sql, 'SCHEMA')[0]
305
+ result = query(<<~SQL, "SCHEMA")[0]
398
306
  SELECT attr.attname, nsp.nspname, seq.relname
399
307
  FROM pg_class seq,
400
308
  pg_attribute attr,
@@ -410,11 +318,11 @@ module ActiveRecord
410
318
  AND seq.relnamespace = nsp.oid
411
319
  AND cons.contype = 'p'
412
320
  AND dep.classid = 'pg_class'::regclass
413
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
414
- end_sql
321
+ AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
322
+ SQL
415
323
 
416
- if result.nil? or result.empty?
417
- result = query(<<-end_sql, 'SCHEMA')[0]
324
+ if result.nil? || result.empty?
325
+ result = query(<<~SQL, "SCHEMA")[0]
418
326
  SELECT attr.attname, nsp.nspname,
419
327
  CASE
420
328
  WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
@@ -428,10 +336,10 @@ module ActiveRecord
428
336
  JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
429
337
  JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
430
338
  JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
431
- WHERE t.oid = '#{quote_table_name(table)}'::regclass
339
+ WHERE t.oid = #{quote(quote_table_name(table))}::regclass
432
340
  AND cons.contype = 'p'
433
341
  AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
434
- end_sql
342
+ SQL
435
343
  end
436
344
 
437
345
  pk = result.shift
@@ -445,17 +353,18 @@ module ActiveRecord
445
353
  end
446
354
 
447
355
  def primary_keys(table_name) # :nodoc:
448
- select_values(<<-SQL.strip_heredoc, 'SCHEMA')
449
- WITH pk_constraint AS (
450
- SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
451
- WHERE contype = 'p'
452
- AND conrelid = '#{quote_table_name(table_name)}'::regclass
453
- ), cons AS (
454
- SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
455
- )
456
- SELECT attr.attname FROM pg_attribute attr
457
- INNER JOIN cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.connum
458
- ORDER BY cons.rownum
356
+ query_values(<<~SQL, "SCHEMA")
357
+ SELECT a.attname
358
+ FROM (
359
+ SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
360
+ FROM pg_index
361
+ WHERE indrelid = #{quote(quote_table_name(table_name))}::regclass
362
+ AND indisprimary
363
+ ) i
364
+ JOIN pg_attribute a
365
+ ON a.attrelid = i.indrelid
366
+ AND a.attnum = i.indkey[i.idx]
367
+ ORDER BY i.idx
459
368
  SQL
460
369
  end
461
370
 
@@ -469,14 +378,15 @@ module ActiveRecord
469
378
  clear_cache!
470
379
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
471
380
  pk, seq = pk_and_sequence_for(new_name)
472
- if seq && seq.identifier == "#{table_name}_#{pk}_seq"
473
- new_seq = "#{new_name}_#{pk}_seq"
381
+ if pk
474
382
  idx = "#{table_name}_pkey"
475
383
  new_idx = "#{new_name}_pkey"
476
- execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
477
384
  execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
385
+ if seq && seq.identifier == "#{table_name}_#{pk}_seq"
386
+ new_seq = "#{new_name}_#{pk}_seq"
387
+ execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
388
+ end
478
389
  end
479
-
480
390
  rename_table_indexes(table_name, new_name)
481
391
  end
482
392
 
@@ -488,61 +398,36 @@ module ActiveRecord
488
398
 
489
399
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
490
400
  clear_cache!
491
- quoted_table_name = quote_table_name(table_name)
492
- quoted_column_name = quote_column_name(column_name)
493
- sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale], options[:array])
494
- sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
495
- if options[:collation]
496
- sql << " COLLATE \"#{options[:collation]}\""
497
- end
498
- if options[:using]
499
- sql << " USING #{options[:using]}"
500
- elsif options[:cast_as]
501
- cast_as_type = type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale], options[:array])
502
- sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
503
- end
504
- execute sql
505
-
506
- change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
507
- change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
508
- change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
401
+ sqls, procs = Array(change_column_for_alter(table_name, column_name, type, options)).partition { |v| v.is_a?(String) }
402
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
403
+ procs.each(&:call)
509
404
  end
510
405
 
511
406
  # Changes the default value of a table column.
512
407
  def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
513
- clear_cache!
514
- column = column_for(table_name, column_name)
515
- return unless column
516
-
517
- default = extract_new_default_value(default_or_changes)
518
- alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
519
- if default.nil?
520
- # <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
521
- # cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
522
- execute alter_column_query % "DROP DEFAULT"
523
- else
524
- execute alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
525
- end
408
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
526
409
  end
527
410
 
528
411
  def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
529
412
  clear_cache!
530
413
  unless null || default.nil?
531
414
  column = column_for(table_name, column_name)
532
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
415
+ execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(default, column)} WHERE #{quote_column_name(column_name)} IS NULL" if column
533
416
  end
534
- execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
417
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_null_for_alter(table_name, column_name, null, default)}"
535
418
  end
536
419
 
537
420
  # Adds comment for given table column or drops it if +comment+ is a +nil+
538
- def change_column_comment(table_name, column_name, comment) # :nodoc:
421
+ def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
539
422
  clear_cache!
423
+ comment = extract_new_comment_value(comment_or_changes)
540
424
  execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
541
425
  end
542
426
 
543
427
  # Adds comment for given table or drops it if +comment+ is a +nil+
544
- def change_table_comment(table_name, comment) # :nodoc:
428
+ def change_table_comment(table_name, comment_or_changes) # :nodoc:
545
429
  clear_cache!
430
+ comment = extract_new_comment_value(comment_or_changes)
546
431
  execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
547
432
  end
548
433
 
@@ -554,8 +439,8 @@ module ActiveRecord
554
439
  end
555
440
 
556
441
  def add_index(table_name, column_name, options = {}) #:nodoc:
557
- index_name, index_type, index_columns, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
558
- execute("CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}").tap do
442
+ index_name, index_type, index_columns_and_opclasses, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
443
+ execute("CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns_and_opclasses})#{index_options}").tap do
559
444
  execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
560
445
  end
561
446
  end
@@ -593,8 +478,9 @@ module ActiveRecord
593
478
  end
594
479
 
595
480
  def foreign_keys(table_name)
596
- fk_info = select_all(<<-SQL.strip_heredoc, 'SCHEMA')
597
- SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
481
+ scope = quoted_scope(table_name)
482
+ fk_info = exec_query(<<~SQL, "SCHEMA")
483
+ SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid
598
484
  FROM pg_constraint c
599
485
  JOIN pg_class t1 ON c.conrelid = t1.oid
600
486
  JOIN pg_class t2 ON c.confrelid = t2.oid
@@ -602,94 +488,288 @@ module ActiveRecord
602
488
  JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
603
489
  JOIN pg_namespace t3 ON c.connamespace = t3.oid
604
490
  WHERE c.contype = 'f'
605
- AND t1.relname = #{quote(table_name)}
606
- AND t3.nspname = ANY (current_schemas(false))
491
+ AND t1.relname = #{scope[:name]}
492
+ AND t3.nspname = #{scope[:schema]}
607
493
  ORDER BY c.conname
608
494
  SQL
609
495
 
610
496
  fk_info.map do |row|
611
497
  options = {
612
- column: row['column'],
613
- name: row['name'],
614
- primary_key: row['primary_key']
498
+ column: row["column"],
499
+ name: row["name"],
500
+ primary_key: row["primary_key"]
615
501
  }
616
502
 
617
- options[:on_delete] = extract_foreign_key_action(row['on_delete'])
618
- options[:on_update] = extract_foreign_key_action(row['on_update'])
503
+ options[:on_delete] = extract_foreign_key_action(row["on_delete"])
504
+ options[:on_update] = extract_foreign_key_action(row["on_update"])
505
+ options[:validate] = row["valid"]
619
506
 
620
- ForeignKeyDefinition.new(table_name, row['to_table'], options)
507
+ ForeignKeyDefinition.new(table_name, row["to_table"], options)
621
508
  end
622
509
  end
623
510
 
624
- def extract_foreign_key_action(specifier) # :nodoc:
625
- case specifier
626
- when 'c'; :cascade
627
- when 'n'; :nullify
628
- when 'r'; :restrict
629
- end
511
+ def foreign_tables
512
+ query_values(data_source_sql(type: "FOREIGN TABLE"), "SCHEMA")
630
513
  end
631
514
 
632
- def index_name_length
633
- 63
515
+ def foreign_table_exists?(table_name)
516
+ query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
634
517
  end
635
518
 
636
519
  # Maps logical Rails types to PostgreSQL-specific data types.
637
- def type_to_sql(type, limit = nil, precision = nil, scale = nil, array = nil)
638
- sql = case type.to_s
639
- when 'binary'
640
- # PostgreSQL doesn't support limits on binary (bytea) columns.
641
- # The hard limit is 1GB, because of a 32-bit size field, and TOAST.
642
- case limit
643
- when nil, 0..0x3fffffff; super(type)
644
- else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
645
- end
646
- when 'text'
647
- # PostgreSQL doesn't support limits on text columns.
648
- # The hard limit is 1GB, according to section 8.3 in the manual.
649
- case limit
650
- when nil, 0..0x3fffffff; super(type)
651
- else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
652
- end
653
- when 'integer'
654
- case limit
655
- when 1, 2; 'smallint'
656
- when nil, 3, 4; 'integer'
657
- when 5..8; 'bigint'
658
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
520
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
521
+ sql = \
522
+ case type.to_s
523
+ when "binary"
524
+ # PostgreSQL doesn't support limits on binary (bytea) columns.
525
+ # The hard limit is 1GB, because of a 32-bit size field, and TOAST.
526
+ case limit
527
+ when nil, 0..0x3fffffff; super(type)
528
+ else raise ArgumentError, "No binary type has byte size #{limit}. The limit on binary can be at most 1GB - 1byte."
529
+ end
530
+ when "text"
531
+ # PostgreSQL doesn't support limits on text columns.
532
+ # The hard limit is 1GB, according to section 8.3 in the manual.
533
+ case limit
534
+ when nil, 0..0x3fffffff; super(type)
535
+ else raise ArgumentError, "No text type has byte size #{limit}. The limit on text can be at most 1GB - 1byte."
536
+ end
537
+ when "integer"
538
+ case limit
539
+ when 1, 2; "smallint"
540
+ when nil, 3, 4; "integer"
541
+ when 5..8; "bigint"
542
+ else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
543
+ end
544
+ else
545
+ super
659
546
  end
660
- else
661
- super(type, limit, precision, scale)
662
- end
663
547
 
664
- sql << '[]' if array && type != :primary_key
548
+ sql = "#{sql}[]" if array && type != :primary_key
665
549
  sql
666
550
  end
667
551
 
668
552
  # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
669
553
  # requires that the ORDER BY include the distinct column.
670
554
  def columns_for_distinct(columns, orders) #:nodoc:
671
- order_columns = orders.reject(&:blank?).map{ |s|
555
+ order_columns = orders.reject(&:blank?).map { |s|
672
556
  # Convert Arel node to string
673
557
  s = s.to_sql unless s.is_a?(String)
674
558
  # Remove any ASC/DESC modifiers
675
- s.gsub(/\s+(?:ASC|DESC)\b/i, '')
676
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
559
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
560
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
677
561
  }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
678
562
 
679
- [super, *order_columns].join(', ')
563
+ (order_columns << super).join(", ")
564
+ end
565
+
566
+ def update_table_definition(table_name, base) # :nodoc:
567
+ PostgreSQL::Table.new(table_name, base)
568
+ end
569
+
570
+ def create_schema_dumper(options) # :nodoc:
571
+ PostgreSQL::SchemaDumper.create(self, options)
572
+ end
573
+
574
+ # Validates the given constraint.
575
+ #
576
+ # Validates the constraint named +constraint_name+ on +accounts+.
577
+ #
578
+ # validate_constraint :accounts, :constraint_name
579
+ def validate_constraint(table_name, constraint_name)
580
+ return unless supports_validate_constraints?
581
+
582
+ at = create_alter_table table_name
583
+ at.validate_constraint constraint_name
584
+
585
+ execute schema_creation.accept(at)
680
586
  end
681
587
 
682
- def fetch_type_metadata(column_name, sql_type, oid, fmod)
683
- cast_type = get_oid_type(oid, fmod, column_name, sql_type)
684
- simple_type = SqlTypeMetadata.new(
685
- sql_type: sql_type,
686
- type: cast_type.type,
687
- limit: cast_type.limit,
688
- precision: cast_type.precision,
689
- scale: cast_type.scale,
690
- )
691
- PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
588
+ # Validates the given foreign key.
589
+ #
590
+ # Validates the foreign key on +accounts.branch_id+.
591
+ #
592
+ # validate_foreign_key :accounts, :branches
593
+ #
594
+ # Validates the foreign key on +accounts.owner_id+.
595
+ #
596
+ # validate_foreign_key :accounts, column: :owner_id
597
+ #
598
+ # Validates the foreign key named +special_fk_name+ on the +accounts+ table.
599
+ #
600
+ # validate_foreign_key :accounts, name: :special_fk_name
601
+ #
602
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
603
+ def validate_foreign_key(from_table, to_table = nil, **options)
604
+ return unless supports_validate_constraints?
605
+
606
+ fk_name_to_validate = foreign_key_for!(from_table, to_table: to_table, **options).name
607
+
608
+ validate_constraint from_table, fk_name_to_validate
692
609
  end
610
+
611
+ private
612
+ def schema_creation
613
+ PostgreSQL::SchemaCreation.new(self)
614
+ end
615
+
616
+ def create_table_definition(*args)
617
+ PostgreSQL::TableDefinition.new(self, *args)
618
+ end
619
+
620
+ def create_alter_table(name)
621
+ PostgreSQL::AlterTable.new create_table_definition(name)
622
+ end
623
+
624
+ def new_column_from_field(table_name, field)
625
+ column_name, type, default, notnull, oid, fmod, collation, comment = field
626
+ type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
627
+ default_value = extract_value_from_default(default)
628
+ default_function = extract_default_function(default_value, default)
629
+
630
+ if match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/)
631
+ serial = sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
632
+ end
633
+
634
+ PostgreSQL::Column.new(
635
+ column_name,
636
+ default_value,
637
+ type_metadata,
638
+ !notnull,
639
+ default_function,
640
+ collation: collation,
641
+ comment: comment.presence,
642
+ serial: serial
643
+ )
644
+ end
645
+
646
+ def fetch_type_metadata(column_name, sql_type, oid, fmod)
647
+ cast_type = get_oid_type(oid, fmod, column_name, sql_type)
648
+ simple_type = SqlTypeMetadata.new(
649
+ sql_type: sql_type,
650
+ type: cast_type.type,
651
+ limit: cast_type.limit,
652
+ precision: cast_type.precision,
653
+ scale: cast_type.scale,
654
+ )
655
+ PostgreSQL::TypeMetadata.new(simple_type, oid: oid, fmod: fmod)
656
+ end
657
+
658
+ def sequence_name_from_parts(table_name, column_name, suffix)
659
+ over_length = [table_name, column_name, suffix].sum(&:length) + 2 - max_identifier_length
660
+
661
+ if over_length > 0
662
+ column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
663
+ over_length -= column_name.length - column_name_length
664
+ column_name = column_name[0, column_name_length - [over_length, 0].min]
665
+ end
666
+
667
+ if over_length > 0
668
+ table_name = table_name[0, table_name.length - over_length]
669
+ end
670
+
671
+ "#{table_name}_#{column_name}_#{suffix}"
672
+ end
673
+
674
+ def extract_foreign_key_action(specifier)
675
+ case specifier
676
+ when "c"; :cascade
677
+ when "n"; :nullify
678
+ when "r"; :restrict
679
+ end
680
+ end
681
+
682
+ def add_column_for_alter(table_name, column_name, type, options = {})
683
+ return super unless options.key?(:comment)
684
+ [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
685
+ end
686
+
687
+ def change_column_for_alter(table_name, column_name, type, options = {})
688
+ td = create_table_definition(table_name)
689
+ cd = td.new_column_definition(column_name, type, options)
690
+ sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
691
+ sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
692
+ sqls
693
+ end
694
+
695
+ def change_column_default_for_alter(table_name, column_name, default_or_changes)
696
+ column = column_for(table_name, column_name)
697
+ return unless column
698
+
699
+ default = extract_new_default_value(default_or_changes)
700
+ alter_column_query = "ALTER COLUMN #{quote_column_name(column_name)} %s"
701
+ if default.nil?
702
+ # <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
703
+ # cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
704
+ alter_column_query % "DROP DEFAULT"
705
+ else
706
+ alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
707
+ end
708
+ end
709
+
710
+ def change_column_null_for_alter(table_name, column_name, null, default = nil)
711
+ "ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
712
+ end
713
+
714
+ def add_timestamps_for_alter(table_name, options = {})
715
+ options[:null] = false if options[:null].nil?
716
+
717
+ if !options.key?(:precision) && supports_datetime_with_precision?
718
+ options[:precision] = 6
719
+ end
720
+
721
+ [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
722
+ end
723
+
724
+ def remove_timestamps_for_alter(table_name, options = {})
725
+ [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
726
+ end
727
+
728
+ def add_index_opclass(quoted_columns, **options)
729
+ opclasses = options_for_index_columns(options[:opclass])
730
+ quoted_columns.each do |name, column|
731
+ column << " #{opclasses[name]}" if opclasses[name].present?
732
+ end
733
+ end
734
+
735
+ def add_options_for_index_columns(quoted_columns, **options)
736
+ quoted_columns = add_index_opclass(quoted_columns, options)
737
+ super
738
+ end
739
+
740
+ def data_source_sql(name = nil, type: nil)
741
+ scope = quoted_scope(name, type: type)
742
+ scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
743
+
744
+ sql = +"SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
745
+ sql << " WHERE n.nspname = #{scope[:schema]}"
746
+ sql << " AND c.relname = #{scope[:name]}" if scope[:name]
747
+ sql << " AND c.relkind IN (#{scope[:type]})"
748
+ sql
749
+ end
750
+
751
+ def quoted_scope(name = nil, type: nil)
752
+ schema, name = extract_schema_qualified_name(name)
753
+ type = \
754
+ case type
755
+ when "BASE TABLE"
756
+ "'r','p'"
757
+ when "VIEW"
758
+ "'v','m'"
759
+ when "FOREIGN TABLE"
760
+ "'f'"
761
+ end
762
+ scope = {}
763
+ scope[:schema] = schema ? quote(schema) : "ANY (current_schemas(false))"
764
+ scope[:name] = quote(name) if name
765
+ scope[:type] = type if type
766
+ scope
767
+ end
768
+
769
+ def extract_schema_qualified_name(string)
770
+ name = Utils.extract_schema_qualified_name(string.to_s)
771
+ [name.schema, name.identifier]
772
+ end
693
773
  end
694
774
  end
695
775
  end