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