activerecord 3.2.6 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (371) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +611 -6417
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +44 -47
  5. data/examples/performance.rb +79 -71
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +268 -238
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +173 -81
  11. data/lib/active_record/associations/association_scope.rb +124 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -38
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +11 -9
  14. data/lib/active_record/associations/builder/association.rb +113 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +105 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +53 -56
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +98 -41
  18. data/lib/active_record/associations/builder/has_many.rb +11 -63
  19. data/lib/active_record/associations/builder/has_one.rb +47 -45
  20. data/lib/active_record/associations/builder/singular_association.rb +30 -18
  21. data/lib/active_record/associations/collection_association.rb +217 -295
  22. data/lib/active_record/associations/collection_proxy.rb +1074 -77
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +78 -50
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -61
  26. data/lib/active_record/associations/has_one_association.rb +75 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +45 -119
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +208 -164
  32. data/lib/active_record/associations/preloader/association.rb +93 -87
  33. data/lib/active_record/associations/preloader/through_association.rb +87 -38
  34. data/lib/active_record/associations/preloader.rb +134 -110
  35. data/lib/active_record/associations/singular_association.rb +19 -24
  36. data/lib/active_record/associations/through_association.rb +61 -27
  37. data/lib/active_record/associations.rb +1766 -1505
  38. data/lib/active_record/attribute_assignment.rb +57 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +58 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +187 -67
  42. data/lib/active_record/attribute_methods/primary_key.rb +100 -78
  43. data/lib/active_record/attribute_methods/query.rb +10 -8
  44. data/lib/active_record/attribute_methods/read.rb +29 -118
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -72
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -42
  47. data/lib/active_record/attribute_methods/write.rb +36 -44
  48. data/lib/active_record/attribute_methods.rb +306 -161
  49. data/lib/active_record/attributes.rb +279 -0
  50. data/lib/active_record/autosave_association.rb +324 -238
  51. data/lib/active_record/base.rb +114 -507
  52. data/lib/active_record/callbacks.rb +147 -83
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +962 -279
  56. data/lib/active_record/connection_adapters/abstract/database_limits.rb +32 -5
  57. data/lib/active_record/connection_adapters/abstract/database_statements.rb +331 -209
  58. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -23
  59. data/lib/active_record/connection_adapters/abstract/quoting.rb +201 -65
  60. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +510 -289
  63. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1182 -313
  65. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  66. data/lib/active_record/connection_adapters/abstract_adapter.rb +585 -120
  67. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +610 -463
  68. data/lib/active_record/connection_adapters/column.rb +58 -233
  69. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  70. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  73. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  74. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  79. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +75 -207
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  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 +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  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 +41 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +695 -1052
  116. data/lib/active_record/connection_adapters/schema_cache.rb +115 -24
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  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 +528 -26
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +267 -0
  128. data/lib/active_record/core.rb +599 -0
  129. data/lib/active_record/counter_cache.rb +177 -103
  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 +107 -64
  136. data/lib/active_record/enum.rb +274 -0
  137. data/lib/active_record/errors.rb +254 -61
  138. data/lib/active_record/explain.rb +35 -70
  139. data/lib/active_record/explain_registry.rb +32 -0
  140. data/lib/active_record/explain_subscriber.rb +18 -8
  141. data/lib/active_record/fixture_set/file.rb +82 -0
  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 +291 -475
  147. data/lib/active_record/gem_version.rb +17 -0
  148. data/lib/active_record/inheritance.rb +219 -100
  149. data/lib/active_record/insert_all.rb +179 -0
  150. data/lib/active_record/integration.rb +175 -17
  151. data/lib/active_record/internal_metadata.rb +53 -0
  152. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  153. data/lib/active_record/locale/en.yml +9 -1
  154. data/lib/active_record/locking/optimistic.rb +106 -92
  155. data/lib/active_record/locking/pessimistic.rb +23 -11
  156. data/lib/active_record/log_subscriber.rb +80 -30
  157. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  158. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  159. data/lib/active_record/middleware/database_selector.rb +75 -0
  160. data/lib/active_record/migration/command_recorder.rb +235 -56
  161. data/lib/active_record/migration/compatibility.rb +244 -0
  162. data/lib/active_record/migration/join_table.rb +17 -0
  163. data/lib/active_record/migration.rb +917 -301
  164. data/lib/active_record/model_schema.rb +351 -175
  165. data/lib/active_record/nested_attributes.rb +366 -235
  166. data/lib/active_record/no_touching.rb +65 -0
  167. data/lib/active_record/null_relation.rb +68 -0
  168. data/lib/active_record/persistence.rb +761 -166
  169. data/lib/active_record/query_cache.rb +22 -44
  170. data/lib/active_record/querying.rb +55 -31
  171. data/lib/active_record/railtie.rb +185 -47
  172. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  173. data/lib/active_record/railties/console_sandbox.rb +5 -4
  174. data/lib/active_record/railties/controller_runtime.rb +35 -33
  175. data/lib/active_record/railties/databases.rake +366 -463
  176. data/lib/active_record/readonly_attributes.rb +4 -6
  177. data/lib/active_record/reflection.rb +736 -228
  178. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  179. data/lib/active_record/relation/batches.rb +252 -52
  180. data/lib/active_record/relation/calculations.rb +340 -270
  181. data/lib/active_record/relation/delegation.rb +117 -36
  182. data/lib/active_record/relation/finder_methods.rb +439 -286
  183. data/lib/active_record/relation/from_clause.rb +26 -0
  184. data/lib/active_record/relation/merger.rb +184 -0
  185. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  186. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  187. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  188. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  189. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  190. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  191. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder.rb +131 -39
  193. data/lib/active_record/relation/query_attribute.rb +50 -0
  194. data/lib/active_record/relation/query_methods.rb +1163 -221
  195. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  196. data/lib/active_record/relation/spawn_methods.rb +49 -120
  197. data/lib/active_record/relation/where_clause.rb +190 -0
  198. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  199. data/lib/active_record/relation.rb +671 -349
  200. data/lib/active_record/result.rb +149 -15
  201. data/lib/active_record/runtime_registry.rb +24 -0
  202. data/lib/active_record/sanitization.rb +153 -133
  203. data/lib/active_record/schema.rb +22 -19
  204. data/lib/active_record/schema_dumper.rb +178 -112
  205. data/lib/active_record/schema_migration.rb +60 -0
  206. data/lib/active_record/scoping/default.rb +107 -98
  207. data/lib/active_record/scoping/named.rb +130 -115
  208. data/lib/active_record/scoping.rb +77 -123
  209. data/lib/active_record/secure_token.rb +40 -0
  210. data/lib/active_record/serialization.rb +10 -6
  211. data/lib/active_record/statement_cache.rb +148 -0
  212. data/lib/active_record/store.rb +256 -16
  213. data/lib/active_record/suppressor.rb +61 -0
  214. data/lib/active_record/table_metadata.rb +75 -0
  215. data/lib/active_record/tasks/database_tasks.rb +506 -0
  216. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  217. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  218. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  219. data/lib/active_record/test_databases.rb +23 -0
  220. data/lib/active_record/test_fixtures.rb +224 -0
  221. data/lib/active_record/timestamp.rb +93 -39
  222. data/lib/active_record/touch_later.rb +66 -0
  223. data/lib/active_record/transactions.rb +260 -129
  224. data/lib/active_record/translation.rb +3 -1
  225. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  226. data/lib/active_record/type/date.rb +9 -0
  227. data/lib/active_record/type/date_time.rb +9 -0
  228. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  229. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  230. data/lib/active_record/type/internal/timezone.rb +17 -0
  231. data/lib/active_record/type/json.rb +30 -0
  232. data/lib/active_record/type/serialized.rb +71 -0
  233. data/lib/active_record/type/text.rb +11 -0
  234. data/lib/active_record/type/time.rb +21 -0
  235. data/lib/active_record/type/type_map.rb +62 -0
  236. data/lib/active_record/type/unsigned_integer.rb +17 -0
  237. data/lib/active_record/type.rb +78 -0
  238. data/lib/active_record/type_caster/connection.rb +34 -0
  239. data/lib/active_record/type_caster/map.rb +20 -0
  240. data/lib/active_record/type_caster.rb +9 -0
  241. data/lib/active_record/validations/absence.rb +25 -0
  242. data/lib/active_record/validations/associated.rb +35 -18
  243. data/lib/active_record/validations/length.rb +26 -0
  244. data/lib/active_record/validations/presence.rb +68 -0
  245. data/lib/active_record/validations/uniqueness.rb +123 -77
  246. data/lib/active_record/validations.rb +54 -43
  247. data/lib/active_record/version.rb +7 -7
  248. data/lib/active_record.rb +97 -49
  249. data/lib/arel/alias_predication.rb +9 -0
  250. data/lib/arel/attributes/attribute.rb +37 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/collectors/bind.rb +24 -0
  253. data/lib/arel/collectors/composite.rb +31 -0
  254. data/lib/arel/collectors/plain_string.rb +20 -0
  255. data/lib/arel/collectors/sql_string.rb +20 -0
  256. data/lib/arel/collectors/substitute_binds.rb +28 -0
  257. data/lib/arel/crud.rb +42 -0
  258. data/lib/arel/delete_manager.rb +18 -0
  259. data/lib/arel/errors.rb +9 -0
  260. data/lib/arel/expressions.rb +29 -0
  261. data/lib/arel/factory_methods.rb +49 -0
  262. data/lib/arel/insert_manager.rb +49 -0
  263. data/lib/arel/math.rb +45 -0
  264. data/lib/arel/nodes/and.rb +32 -0
  265. data/lib/arel/nodes/ascending.rb +23 -0
  266. data/lib/arel/nodes/binary.rb +52 -0
  267. data/lib/arel/nodes/bind_param.rb +36 -0
  268. data/lib/arel/nodes/case.rb +55 -0
  269. data/lib/arel/nodes/casted.rb +50 -0
  270. data/lib/arel/nodes/comment.rb +29 -0
  271. data/lib/arel/nodes/count.rb +12 -0
  272. data/lib/arel/nodes/delete_statement.rb +45 -0
  273. data/lib/arel/nodes/descending.rb +23 -0
  274. data/lib/arel/nodes/equality.rb +18 -0
  275. data/lib/arel/nodes/extract.rb +24 -0
  276. data/lib/arel/nodes/false.rb +16 -0
  277. data/lib/arel/nodes/full_outer_join.rb +8 -0
  278. data/lib/arel/nodes/function.rb +44 -0
  279. data/lib/arel/nodes/grouping.rb +8 -0
  280. data/lib/arel/nodes/in.rb +8 -0
  281. data/lib/arel/nodes/infix_operation.rb +80 -0
  282. data/lib/arel/nodes/inner_join.rb +8 -0
  283. data/lib/arel/nodes/insert_statement.rb +37 -0
  284. data/lib/arel/nodes/join_source.rb +20 -0
  285. data/lib/arel/nodes/matches.rb +18 -0
  286. data/lib/arel/nodes/named_function.rb +23 -0
  287. data/lib/arel/nodes/node.rb +50 -0
  288. data/lib/arel/nodes/node_expression.rb +13 -0
  289. data/lib/arel/nodes/outer_join.rb +8 -0
  290. data/lib/arel/nodes/over.rb +15 -0
  291. data/lib/arel/nodes/regexp.rb +16 -0
  292. data/lib/arel/nodes/right_outer_join.rb +8 -0
  293. data/lib/arel/nodes/select_core.rb +67 -0
  294. data/lib/arel/nodes/select_statement.rb +41 -0
  295. data/lib/arel/nodes/sql_literal.rb +16 -0
  296. data/lib/arel/nodes/string_join.rb +11 -0
  297. data/lib/arel/nodes/table_alias.rb +27 -0
  298. data/lib/arel/nodes/terminal.rb +16 -0
  299. data/lib/arel/nodes/true.rb +16 -0
  300. data/lib/arel/nodes/unary.rb +45 -0
  301. data/lib/arel/nodes/unary_operation.rb +20 -0
  302. data/lib/arel/nodes/unqualified_column.rb +22 -0
  303. data/lib/arel/nodes/update_statement.rb +41 -0
  304. data/lib/arel/nodes/values_list.rb +9 -0
  305. data/lib/arel/nodes/window.rb +126 -0
  306. data/lib/arel/nodes/with.rb +11 -0
  307. data/lib/arel/nodes.rb +68 -0
  308. data/lib/arel/order_predications.rb +13 -0
  309. data/lib/arel/predications.rb +257 -0
  310. data/lib/arel/select_manager.rb +271 -0
  311. data/lib/arel/table.rb +110 -0
  312. data/lib/arel/tree_manager.rb +72 -0
  313. data/lib/arel/update_manager.rb +34 -0
  314. data/lib/arel/visitors/depth_first.rb +204 -0
  315. data/lib/arel/visitors/dot.rb +297 -0
  316. data/lib/arel/visitors/ibm_db.rb +34 -0
  317. data/lib/arel/visitors/informix.rb +62 -0
  318. data/lib/arel/visitors/mssql.rb +157 -0
  319. data/lib/arel/visitors/mysql.rb +83 -0
  320. data/lib/arel/visitors/oracle.rb +159 -0
  321. data/lib/arel/visitors/oracle12.rb +66 -0
  322. data/lib/arel/visitors/postgresql.rb +110 -0
  323. data/lib/arel/visitors/sqlite.rb +39 -0
  324. data/lib/arel/visitors/to_sql.rb +889 -0
  325. data/lib/arel/visitors/visitor.rb +46 -0
  326. data/lib/arel/visitors/where_sql.rb +23 -0
  327. data/lib/arel/visitors.rb +20 -0
  328. data/lib/arel/window_predications.rb +9 -0
  329. data/lib/arel.rb +51 -0
  330. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  331. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +59 -9
  333. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  334. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  335. data/lib/rails/generators/active_record/migration.rb +41 -8
  336. data/lib/rails/generators/active_record/model/model_generator.rb +24 -22
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  339. data/lib/rails/generators/active_record.rb +10 -16
  340. metadata +285 -149
  341. data/examples/associations.png +0 -0
  342. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  343. data/lib/active_record/associations/join_helper.rb +0 -55
  344. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  345. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  346. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  347. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  348. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  349. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  350. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  351. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  352. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  353. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -188
  354. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -426
  355. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -579
  356. data/lib/active_record/dynamic_finder_match.rb +0 -68
  357. data/lib/active_record/dynamic_scope_match.rb +0 -23
  358. data/lib/active_record/fixtures/file.rb +0 -65
  359. data/lib/active_record/identity_map.rb +0 -162
  360. data/lib/active_record/observer.rb +0 -121
  361. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  362. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  363. data/lib/active_record/session_store.rb +0 -358
  364. data/lib/active_record/test_case.rb +0 -73
  365. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  366. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  367. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  368. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  369. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  370. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  371. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -0,0 +1,776 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostgreSQL
6
+ module SchemaStatements
7
+ # Drops the database specified on the +name+ attribute
8
+ # and creates it again using the provided +options+.
9
+ def recreate_database(name, options = {}) #:nodoc:
10
+ drop_database(name)
11
+ create_database(name, options)
12
+ end
13
+
14
+ # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
15
+ # <tt>:encoding</tt> (defaults to utf8), <tt>:collation</tt>, <tt>:ctype</tt>,
16
+ # <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
17
+ # <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
18
+ #
19
+ # Example:
20
+ # create_database config[:database], config
21
+ # create_database 'foo_development', encoding: 'unicode'
22
+ def create_database(name, options = {})
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
+ ""
43
+ end
44
+ end
45
+
46
+ execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
47
+ end
48
+
49
+ # Drops a PostgreSQL database.
50
+ #
51
+ # Example:
52
+ # drop_database 'matt_development'
53
+ def drop_database(name) #:nodoc:
54
+ execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
55
+ end
56
+
57
+ def drop_table(table_name, options = {}) # :nodoc:
58
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
59
+ end
60
+
61
+ # Returns true if schema exists.
62
+ def schema_exists?(name)
63
+ query_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
64
+ end
65
+
66
+ # Verifies existence of an index with a given name.
67
+ def index_name_exists?(table_name, index_name)
68
+ table = quoted_scope(table_name)
69
+ index = quoted_scope(index_name)
70
+
71
+ query_value(<<~SQL, "SCHEMA").to_i > 0
72
+ SELECT COUNT(*)
73
+ FROM pg_class t
74
+ INNER JOIN pg_index d ON t.oid = d.indrelid
75
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
76
+ LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
77
+ WHERE i.relkind = 'i'
78
+ AND i.relname = #{index[:name]}
79
+ AND t.relname = #{table[:name]}
80
+ AND n.nspname = #{index[:schema]}
81
+ SQL
82
+ end
83
+
84
+ # Returns an array of indexes for the given table.
85
+ def indexes(table_name) # :nodoc:
86
+ scope = quoted_scope(table_name)
87
+
88
+ result = query(<<~SQL, "SCHEMA")
89
+ SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
90
+ pg_catalog.obj_description(i.oid, 'pg_class') AS comment
91
+ FROM pg_class t
92
+ INNER JOIN pg_index d ON t.oid = d.indrelid
93
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
94
+ LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
95
+ WHERE i.relkind = 'i'
96
+ AND d.indisprimary = 'f'
97
+ AND t.relname = #{scope[:name]}
98
+ AND n.nspname = #{scope[:schema]}
99
+ ORDER BY i.relname
100
+ SQL
101
+
102
+ result.map do |row|
103
+ index_name = row[0]
104
+ unique = row[1]
105
+ indkey = row[2].split(" ").map(&:to_i)
106
+ inddef = row[3]
107
+ oid = row[4]
108
+ comment = row[5]
109
+
110
+ using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
111
+
112
+ orders = {}
113
+ opclasses = {}
114
+
115
+ if indkey.include?(0)
116
+ columns = expressions
117
+ else
118
+ columns = Hash[query(<<~SQL, "SCHEMA")].values_at(*indkey).compact
119
+ SELECT a.attnum, a.attname
120
+ FROM pg_attribute a
121
+ WHERE a.attrelid = #{oid}
122
+ AND a.attnum IN (#{indkey.join(",")})
123
+ SQL
124
+
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
135
+ end
136
+
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
+ )
148
+ end
149
+ end
150
+
151
+ def table_options(table_name) # :nodoc:
152
+ if comment = table_comment(table_name)
153
+ { comment: comment }
154
+ end
155
+ end
156
+
157
+ # Returns a comment stored in database for given table
158
+ def table_comment(table_name) # :nodoc:
159
+ scope = quoted_scope(table_name, type: "BASE TABLE")
160
+ if scope[:name]
161
+ query_value(<<~SQL, "SCHEMA")
162
+ SELECT pg_catalog.obj_description(c.oid, 'pg_class')
163
+ FROM pg_catalog.pg_class c
164
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
165
+ WHERE c.relname = #{scope[:name]}
166
+ AND c.relkind IN (#{scope[:type]})
167
+ AND n.nspname = #{scope[:schema]}
168
+ SQL
169
+ end
170
+ end
171
+
172
+ # Returns the current database name.
173
+ def current_database
174
+ query_value("SELECT current_database()", "SCHEMA")
175
+ end
176
+
177
+ # Returns the current schema name.
178
+ def current_schema
179
+ query_value("SELECT current_schema", "SCHEMA")
180
+ end
181
+
182
+ # Returns the current database encoding format.
183
+ def encoding
184
+ query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
185
+ end
186
+
187
+ # Returns the current database collation.
188
+ def collation
189
+ query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
190
+ end
191
+
192
+ # Returns the current database ctype.
193
+ def ctype
194
+ query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
195
+ end
196
+
197
+ # Returns an array of schema names.
198
+ def schema_names
199
+ query_values(<<~SQL, "SCHEMA")
200
+ SELECT nspname
201
+ FROM pg_namespace
202
+ WHERE nspname !~ '^pg_.*'
203
+ AND nspname NOT IN ('information_schema')
204
+ ORDER by nspname;
205
+ SQL
206
+ end
207
+
208
+ # Creates a schema for the given schema name.
209
+ def create_schema(schema_name)
210
+ execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
211
+ end
212
+
213
+ # Drops the schema for the given schema name.
214
+ def drop_schema(schema_name, options = {})
215
+ execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
216
+ end
217
+
218
+ # Sets the schema search path to a string of comma-separated schema names.
219
+ # Names beginning with $ have to be quoted (e.g. $user => '$user').
220
+ # See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
221
+ #
222
+ # This should be not be called manually but set in database.yml.
223
+ def schema_search_path=(schema_csv)
224
+ if schema_csv
225
+ execute("SET search_path TO #{schema_csv}", "SCHEMA")
226
+ @schema_search_path = schema_csv
227
+ end
228
+ end
229
+
230
+ # Returns the active schema search path.
231
+ def schema_search_path
232
+ @schema_search_path ||= query_value("SHOW search_path", "SCHEMA")
233
+ end
234
+
235
+ # Returns the current client message level.
236
+ def client_min_messages
237
+ query_value("SHOW client_min_messages", "SCHEMA")
238
+ end
239
+
240
+ # Set the client message level.
241
+ def client_min_messages=(level)
242
+ execute("SET client_min_messages TO '#{level}'", "SCHEMA")
243
+ end
244
+
245
+ # Returns the sequence name for a table's primary key or some other specified key.
246
+ def default_sequence_name(table_name, pk = "id") #:nodoc:
247
+ result = serial_sequence(table_name, pk)
248
+ return nil unless result
249
+ Utils.extract_schema_qualified_name(result).to_s
250
+ rescue ActiveRecord::StatementInvalid
251
+ PostgreSQL::Name.new(nil, "#{table_name}_#{pk}_seq").to_s
252
+ end
253
+
254
+ def serial_sequence(table, column)
255
+ query_value("SELECT pg_get_serial_sequence(#{quote(table)}, #{quote(column)})", "SCHEMA")
256
+ end
257
+
258
+ # Sets the sequence of a table's primary key to the specified value.
259
+ def set_pk_sequence!(table, value) #:nodoc:
260
+ pk, sequence = pk_and_sequence_for(table)
261
+
262
+ if pk
263
+ if sequence
264
+ quoted_sequence = quote_table_name(sequence)
265
+
266
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
267
+ else
268
+ @logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
269
+ end
270
+ end
271
+ end
272
+
273
+ # Resets the sequence of a table's primary key to the maximum value.
274
+ def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
275
+ unless pk && sequence
276
+ default_pk, default_sequence = pk_and_sequence_for(table)
277
+
278
+ pk ||= default_pk
279
+ sequence ||= default_sequence
280
+ end
281
+
282
+ if @logger && pk && !sequence
283
+ @logger.warn "#{table} has primary key #{pk} with no default sequence."
284
+ end
285
+
286
+ if pk && sequence
287
+ quoted_sequence = quote_table_name(sequence)
288
+ max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
289
+ if max_pk.nil?
290
+ if database_version >= 100000
291
+ minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
292
+ else
293
+ minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
294
+ end
295
+ end
296
+
297
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
298
+ end
299
+ end
300
+
301
+ # Returns a table's primary key and belonging sequence.
302
+ def pk_and_sequence_for(table) #:nodoc:
303
+ # First try looking for a sequence with a dependency on the
304
+ # given table's primary key.
305
+ result = query(<<~SQL, "SCHEMA")[0]
306
+ SELECT attr.attname, nsp.nspname, seq.relname
307
+ FROM pg_class seq,
308
+ pg_attribute attr,
309
+ pg_depend dep,
310
+ pg_constraint cons,
311
+ pg_namespace nsp
312
+ WHERE seq.oid = dep.objid
313
+ AND seq.relkind = 'S'
314
+ AND attr.attrelid = dep.refobjid
315
+ AND attr.attnum = dep.refobjsubid
316
+ AND attr.attrelid = cons.conrelid
317
+ AND attr.attnum = cons.conkey[1]
318
+ AND seq.relnamespace = nsp.oid
319
+ AND cons.contype = 'p'
320
+ AND dep.classid = 'pg_class'::regclass
321
+ AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
322
+ SQL
323
+
324
+ if result.nil? || result.empty?
325
+ result = query(<<~SQL, "SCHEMA")[0]
326
+ SELECT attr.attname, nsp.nspname,
327
+ CASE
328
+ WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
329
+ WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
330
+ substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
331
+ strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
332
+ ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
333
+ END
334
+ FROM pg_class t
335
+ JOIN pg_attribute attr ON (t.oid = attrelid)
336
+ JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
337
+ JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
338
+ JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
339
+ WHERE t.oid = #{quote(quote_table_name(table))}::regclass
340
+ AND cons.contype = 'p'
341
+ AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
342
+ SQL
343
+ end
344
+
345
+ pk = result.shift
346
+ if result.last
347
+ [pk, PostgreSQL::Name.new(*result)]
348
+ else
349
+ [pk, nil]
350
+ end
351
+ rescue
352
+ nil
353
+ end
354
+
355
+ def primary_keys(table_name) # :nodoc:
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
368
+ SQL
369
+ end
370
+
371
+ # Renames a table.
372
+ # Also renames a table's primary key sequence if the sequence name exists and
373
+ # matches the Active Record default.
374
+ #
375
+ # Example:
376
+ # rename_table('octopuses', 'octopi')
377
+ def rename_table(table_name, new_name)
378
+ clear_cache!
379
+ execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
380
+ pk, seq = pk_and_sequence_for(new_name)
381
+ if pk
382
+ idx = "#{table_name}_pkey"
383
+ new_idx = "#{new_name}_pkey"
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
389
+ end
390
+ rename_table_indexes(table_name, new_name)
391
+ end
392
+
393
+ def add_column(table_name, column_name, type, options = {}) #:nodoc:
394
+ clear_cache!
395
+ super
396
+ change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
397
+ end
398
+
399
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
400
+ clear_cache!
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)
404
+ end
405
+
406
+ # Changes the default value of a table column.
407
+ def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
408
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
409
+ end
410
+
411
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
412
+ clear_cache!
413
+ unless null || default.nil?
414
+ column = column_for(table_name, column_name)
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
416
+ end
417
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_null_for_alter(table_name, column_name, null, default)}"
418
+ end
419
+
420
+ # Adds comment for given table column or drops it if +comment+ is a +nil+
421
+ def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
422
+ clear_cache!
423
+ comment = extract_new_comment_value(comment_or_changes)
424
+ execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
425
+ end
426
+
427
+ # Adds comment for given table or drops it if +comment+ is a +nil+
428
+ def change_table_comment(table_name, comment_or_changes) # :nodoc:
429
+ clear_cache!
430
+ comment = extract_new_comment_value(comment_or_changes)
431
+ execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
432
+ end
433
+
434
+ # Renames a column in a table.
435
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
436
+ clear_cache!
437
+ execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
438
+ rename_column_indexes(table_name, column_name, new_column_name)
439
+ end
440
+
441
+ def add_index(table_name, column_name, options = {}) #:nodoc:
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
444
+ execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
445
+ end
446
+ end
447
+
448
+ def remove_index(table_name, options = {}) #:nodoc:
449
+ table = Utils.extract_schema_qualified_name(table_name.to_s)
450
+
451
+ if options.is_a?(Hash) && options.key?(:name)
452
+ provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
453
+
454
+ options[:name] = provided_index.identifier
455
+ table = PostgreSQL::Name.new(provided_index.schema, table.identifier) unless table.schema.present?
456
+
457
+ if provided_index.schema.present? && table.schema != provided_index.schema
458
+ raise ArgumentError.new("Index schema '#{provided_index.schema}' does not match table schema '#{table.schema}'")
459
+ end
460
+ end
461
+
462
+ index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, options))
463
+ algorithm =
464
+ if options.is_a?(Hash) && options.key?(:algorithm)
465
+ index_algorithms.fetch(options[:algorithm]) do
466
+ raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
467
+ end
468
+ end
469
+ execute "DROP INDEX #{algorithm} #{quote_table_name(index_to_remove)}"
470
+ end
471
+
472
+ # Renames an index of a table. Raises error if length of new
473
+ # index name is greater than allowed limit.
474
+ def rename_index(table_name, old_name, new_name)
475
+ validate_index_length!(table_name, new_name)
476
+
477
+ execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
478
+ end
479
+
480
+ def foreign_keys(table_name)
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
484
+ FROM pg_constraint c
485
+ JOIN pg_class t1 ON c.conrelid = t1.oid
486
+ JOIN pg_class t2 ON c.confrelid = t2.oid
487
+ JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
488
+ JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
489
+ JOIN pg_namespace t3 ON c.connamespace = t3.oid
490
+ WHERE c.contype = 'f'
491
+ AND t1.relname = #{scope[:name]}
492
+ AND t3.nspname = #{scope[:schema]}
493
+ ORDER BY c.conname
494
+ SQL
495
+
496
+ fk_info.map do |row|
497
+ options = {
498
+ column: row["column"],
499
+ name: row["name"],
500
+ primary_key: row["primary_key"]
501
+ }
502
+
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"]
506
+
507
+ ForeignKeyDefinition.new(table_name, row["to_table"], options)
508
+ end
509
+ end
510
+
511
+ def foreign_tables
512
+ query_values(data_source_sql(type: "FOREIGN TABLE"), "SCHEMA")
513
+ end
514
+
515
+ def foreign_table_exists?(table_name)
516
+ query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
517
+ end
518
+
519
+ # Maps logical Rails types to PostgreSQL-specific data types.
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
546
+ end
547
+
548
+ sql = "#{sql}[]" if array && type != :primary_key
549
+ sql
550
+ end
551
+
552
+ # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
553
+ # requires that the ORDER BY include the distinct column.
554
+ def columns_for_distinct(columns, orders) #:nodoc:
555
+ order_columns = orders.reject(&:blank?).map { |s|
556
+ # Convert Arel node to string
557
+ s = s.to_sql unless s.is_a?(String)
558
+ # Remove any ASC/DESC modifiers
559
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
560
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
561
+ }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
562
+
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)
586
+ end
587
+
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
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
773
+ end
774
+ end
775
+ end
776
+ end