activerecord 4.2.11.2 → 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 (372) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +613 -1638
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +13 -12
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record.rb +41 -22
  8. data/lib/active_record/aggregations.rb +267 -251
  9. data/lib/active_record/association_relation.rb +11 -6
  10. data/lib/active_record/associations.rb +1737 -1597
  11. data/lib/active_record/associations/alias_tracker.rb +29 -35
  12. data/lib/active_record/associations/association.rb +125 -58
  13. data/lib/active_record/associations/association_scope.rb +103 -132
  14. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -40
  17. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  18. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
  20. data/lib/active_record/associations/builder/has_many.rb +8 -4
  21. data/lib/active_record/associations/builder/has_one.rb +46 -5
  22. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  23. data/lib/active_record/associations/collection_association.rb +131 -287
  24. data/lib/active_record/associations/collection_proxy.rb +241 -146
  25. data/lib/active_record/associations/foreign_association.rb +10 -1
  26. data/lib/active_record/associations/has_many_association.rb +34 -97
  27. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  28. data/lib/active_record/associations/has_one_association.rb +61 -49
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +137 -167
  31. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  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 +90 -92
  35. data/lib/active_record/associations/preloader/association.rb +90 -123
  36. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  37. data/lib/active_record/associations/singular_association.rb +18 -39
  38. data/lib/active_record/associations/through_association.rb +38 -18
  39. data/lib/active_record/attribute_assignment.rb +56 -183
  40. data/lib/active_record/attribute_decorators.rb +39 -15
  41. data/lib/active_record/attribute_methods.rb +120 -135
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
  43. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  44. data/lib/active_record/attribute_methods/primary_key.rb +91 -83
  45. data/lib/active_record/attribute_methods/query.rb +6 -5
  46. data/lib/active_record/attribute_methods/read.rb +20 -76
  47. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  49. data/lib/active_record/attribute_methods/write.rb +32 -54
  50. data/lib/active_record/attributes.rb +214 -82
  51. data/lib/active_record/autosave_association.rb +91 -37
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +100 -74
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +24 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +796 -296
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -23
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -243
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +460 -204
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +510 -635
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +551 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -345
  127. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  128. data/lib/active_record/connection_handling.rb +176 -41
  129. data/lib/active_record/core.rb +251 -231
  130. data/lib/active_record/counter_cache.rb +67 -49
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -105
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +23 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +228 -499
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -112
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +87 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +621 -303
  162. data/lib/active_record/migration/command_recorder.rb +177 -90
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +8 -6
  165. data/lib/active_record/model_schema.rb +312 -112
  166. data/lib/active_record/nested_attributes.rb +264 -222
  167. data/lib/active_record/no_touching.rb +14 -1
  168. data/lib/active_record/null_relation.rb +24 -37
  169. data/lib/active_record/persistence.rb +557 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +43 -29
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +328 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +428 -279
  179. data/lib/active_record/relation.rb +518 -341
  180. data/lib/active_record/relation/batches.rb +207 -55
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +267 -253
  183. data/lib/active_record/relation/delegation.rb +70 -80
  184. data/lib/active_record/relation/finder_methods.rb +277 -241
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +78 -87
  187. data/lib/active_record/relation/predicate_builder.rb +114 -119
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +575 -394
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +11 -13
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +79 -42
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping.rb +45 -26
  208. data/lib/active_record/scoping/default.rb +101 -85
  209. data/lib/active_record/scoping/named.rb +86 -33
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +224 -0
  222. data/lib/active_record/timestamp.rb +86 -40
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +216 -150
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type.rb +78 -23
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +4 -45
  229. data/lib/active_record/type/date_time.rb +4 -49
  230. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  231. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +24 -15
  235. data/lib/active_record/type/text.rb +2 -2
  236. data/lib/active_record/type/time.rb +11 -16
  237. data/lib/active_record/type/type_map.rb +15 -17
  238. data/lib/active_record/type/unsigned_integer.rb +9 -7
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +39 -35
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +13 -4
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +14 -13
  247. data/lib/active_record/validations/uniqueness.rb +42 -55
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/arel.rb +51 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +7 -5
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +31 -1
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  337. data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. metadata +164 -59
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -163
  349. data/lib/active_record/attribute_set.rb +0 -81
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  352. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  353. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  354. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  355. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  356. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  359. data/lib/active_record/type/big_integer.rb +0 -13
  360. data/lib/active_record/type/binary.rb +0 -50
  361. data/lib/active_record/type/boolean.rb +0 -31
  362. data/lib/active_record/type/decimal.rb +0 -64
  363. data/lib/active_record/type/decorator.rb +0 -14
  364. data/lib/active_record/type/float.rb +0 -19
  365. data/lib/active_record/type/integer.rb +0 -59
  366. data/lib/active_record/type/mutable.rb +0 -16
  367. data/lib/active_record/type/numeric.rb +0 -36
  368. data/lib/active_record/type/string.rb +0 -40
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -110
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module PostgreSQL
6
+ class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
7
+ private
8
+
9
+ def extensions(stream)
10
+ extensions = @connection.extensions
11
+ if extensions.any?
12
+ stream.puts " # These are extensions that must be enabled in order to support this database"
13
+ extensions.sort.each do |extension|
14
+ stream.puts " enable_extension #{extension.inspect}"
15
+ end
16
+ stream.puts
17
+ end
18
+ end
19
+
20
+ def prepare_column_options(column)
21
+ spec = super
22
+ spec[:array] = "true" if column.array?
23
+ spec
24
+ end
25
+
26
+ def default_primary_key?(column)
27
+ schema_type(column) == :bigserial
28
+ end
29
+
30
+ def explicit_primary_key_default?(column)
31
+ column.type == :uuid || (column.type == :integer && !column.serial?)
32
+ end
33
+
34
+ def schema_type(column)
35
+ return super unless column.serial?
36
+
37
+ if column.bigint?
38
+ :bigserial
39
+ else
40
+ :serial
41
+ end
42
+ end
43
+
44
+ def schema_expression(column)
45
+ super unless column.serial?
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,40 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module PostgreSQL
4
- class SchemaCreation < AbstractAdapter::SchemaCreation
5
- private
6
-
7
- def visit_ColumnDefinition(o)
8
- sql = super
9
- if o.primary_key? && o.type != :primary_key
10
- sql << " PRIMARY KEY "
11
- add_column_options!(sql, column_options(o))
12
- end
13
- sql
14
- end
15
-
16
- def add_column_options!(sql, options)
17
- if options[:array] || options[:column].try(:array)
18
- sql << '[]'
19
- end
20
-
21
- column = options.fetch(:column) { return super }
22
- if column.type == :uuid && options[:default] =~ /\(\)/
23
- sql << " DEFAULT #{options[:default]}"
24
- else
25
- super
26
- end
27
- end
28
-
29
- def type_for_column(column)
30
- if column.array
31
- @conn.lookup_cast_type("#{column.sql_type}[]")
32
- else
33
- super
34
- end
35
- end
36
- end
37
-
38
6
  module SchemaStatements
39
7
  # Drops the database specified on the +name+ attribute
40
8
  # and creates it again using the provided +options+.
@@ -52,26 +20,26 @@ module ActiveRecord
52
20
  # create_database config[:database], config
53
21
  # create_database 'foo_development', encoding: 'unicode'
54
22
  def create_database(name, options = {})
55
- options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
56
-
57
- option_string = options.inject("") do |memo, (key, value)|
58
- memo += case key
59
- when :owner
60
- " OWNER = \"#{value}\""
61
- when :template
62
- " TEMPLATE = \"#{value}\""
63
- when :encoding
64
- " ENCODING = '#{value}'"
65
- when :collation
66
- " LC_COLLATE = '#{value}'"
67
- when :ctype
68
- " LC_CTYPE = '#{value}'"
69
- when :tablespace
70
- " TABLESPACE = \"#{value}\""
71
- when :connection_limit
72
- " CONNECTION LIMIT = #{value}"
73
- else
74
- ""
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
+ ""
75
43
  end
76
44
  end
77
45
 
@@ -86,161 +54,149 @@ module ActiveRecord
86
54
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
87
55
  end
88
56
 
89
- # Returns the list of all tables in the schema search path.
90
- def tables(name = nil)
91
- query(<<-SQL, 'SCHEMA').map { |row| row[0] }
92
- SELECT tablename
93
- FROM pg_tables
94
- WHERE schemaname = ANY (current_schemas(false))
95
- SQL
96
- end
97
-
98
- def data_sources # :nodoc
99
- select_values(<<-SQL, 'SCHEMA')
100
- SELECT c.relname
101
- FROM pg_class c
102
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
103
- WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
104
- AND n.nspname = ANY (current_schemas(false))
105
- SQL
106
- end
107
-
108
- # Returns true if table exists.
109
- # If the schema is not specified as part of +name+ then it will only find tables within
110
- # the current schema search path (regardless of permissions to access tables in other schemas)
111
- def table_exists?(name)
112
- name = Utils.extract_schema_qualified_name(name.to_s)
113
- return false unless name.identifier
114
-
115
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
116
- SELECT COUNT(*)
117
- FROM pg_class c
118
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
119
- WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
120
- AND c.relname = '#{name.identifier}'
121
- AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
122
- SQL
123
- end
124
- alias data_source_exists? table_exists?
125
-
126
- def drop_table(table_name, options = {})
127
- execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
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}"
128
59
  end
129
60
 
130
61
  # Returns true if schema exists.
131
62
  def schema_exists?(name)
132
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
133
- SELECT COUNT(*)
134
- FROM pg_namespace
135
- WHERE nspname = '#{name}'
136
- SQL
63
+ query_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
137
64
  end
138
65
 
139
- def index_name_exists?(table_name, index_name, default)
140
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
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
141
72
  SELECT COUNT(*)
142
73
  FROM pg_class t
143
74
  INNER JOIN pg_index d ON t.oid = d.indrelid
144
75
  INNER JOIN pg_class i ON d.indexrelid = i.oid
76
+ LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
145
77
  WHERE i.relkind = 'i'
146
- AND i.relname = '#{index_name}'
147
- AND t.relname = '#{table_name}'
148
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
78
+ AND i.relname = #{index[:name]}
79
+ AND t.relname = #{table[:name]}
80
+ AND n.nspname = #{index[:schema]}
149
81
  SQL
150
82
  end
151
83
 
152
84
  # Returns an array of indexes for the given table.
153
- def indexes(table_name, name = nil)
154
- result = query(<<-SQL, 'SCHEMA')
155
- SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
156
- FROM pg_class t
157
- INNER JOIN pg_index d ON t.oid = d.indrelid
158
- INNER JOIN pg_class i ON d.indexrelid = i.oid
159
- WHERE i.relkind = 'i'
160
- AND d.indisprimary = 'f'
161
- AND t.relname = '#{table_name}'
162
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
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]}
163
99
  ORDER BY i.relname
164
100
  SQL
165
101
 
166
102
  result.map do |row|
167
103
  index_name = row[0]
168
- unique = row[1] == 't'
169
- indkey = row[2].split(" ")
104
+ unique = row[1]
105
+ indkey = row[2].split(" ").map(&:to_i)
170
106
  inddef = row[3]
171
107
  oid = row[4]
108
+ comment = row[5]
172
109
 
173
- columns = Hash[query(<<-SQL, "SCHEMA")]
174
- SELECT a.attnum, a.attname
175
- FROM pg_attribute a
176
- WHERE a.attrelid = #{oid}
177
- AND a.attnum IN (#{indkey.join(",")})
178
- SQL
179
-
180
- column_names = columns.values_at(*indkey).compact
110
+ using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
181
111
 
182
- unless column_names.empty?
183
- # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
184
- desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
185
- orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
186
- where = inddef.scan(/WHERE (.+)$/).flatten[0]
187
- using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
112
+ orders = {}
113
+ opclasses = {}
188
114
 
189
- IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
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
190
135
  end
191
- end.compact
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
192
149
  end
193
150
 
194
- # Returns the list of all column definitions for a table.
195
- def columns(table_name)
196
- # Limit, precision, and scale are all handled by the superclass.
197
- column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
198
- oid = get_oid_type(oid.to_i, fmod.to_i, column_name, type)
199
- default_value = extract_value_from_default(oid, default)
200
- default_function = extract_default_function(default_value, default)
201
- new_column(column_name, default_value, oid, type, notnull == 'f', default_function)
151
+ def table_options(table_name) # :nodoc:
152
+ if comment = table_comment(table_name)
153
+ { comment: comment }
202
154
  end
203
155
  end
204
156
 
205
- def new_column(name, default, cast_type, sql_type = nil, null = true, default_function = nil) # :nodoc:
206
- PostgreSQLColumn.new(name, default, cast_type, sql_type, null, default_function)
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
207
170
  end
208
171
 
209
172
  # Returns the current database name.
210
173
  def current_database
211
- query('select current_database()', 'SCHEMA')[0][0]
174
+ query_value("SELECT current_database()", "SCHEMA")
212
175
  end
213
176
 
214
177
  # Returns the current schema name.
215
178
  def current_schema
216
- query('SELECT current_schema', 'SCHEMA')[0][0]
179
+ query_value("SELECT current_schema", "SCHEMA")
217
180
  end
218
181
 
219
182
  # Returns the current database encoding format.
220
183
  def encoding
221
- query(<<-end_sql, 'SCHEMA')[0][0]
222
- SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
223
- WHERE pg_database.datname LIKE '#{current_database}'
224
- end_sql
184
+ query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
225
185
  end
226
186
 
227
187
  # Returns the current database collation.
228
188
  def collation
229
- query(<<-end_sql, 'SCHEMA')[0][0]
230
- SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
231
- end_sql
189
+ query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
232
190
  end
233
191
 
234
192
  # Returns the current database ctype.
235
193
  def ctype
236
- query(<<-end_sql, 'SCHEMA')[0][0]
237
- SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
238
- end_sql
194
+ query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
239
195
  end
240
196
 
241
197
  # Returns an array of schema names.
242
198
  def schema_names
243
- query(<<-SQL, 'SCHEMA').flatten
199
+ query_values(<<~SQL, "SCHEMA")
244
200
  SELECT nspname
245
201
  FROM pg_namespace
246
202
  WHERE nspname !~ '^pg_.*'
@@ -250,56 +206,53 @@ module ActiveRecord
250
206
  end
251
207
 
252
208
  # Creates a schema for the given schema name.
253
- def create_schema schema_name
254
- execute "CREATE SCHEMA #{schema_name}"
209
+ def create_schema(schema_name)
210
+ execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
255
211
  end
256
212
 
257
213
  # Drops the schema for the given schema name.
258
- def drop_schema schema_name
259
- execute "DROP SCHEMA #{schema_name} CASCADE"
214
+ def drop_schema(schema_name, options = {})
215
+ execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
260
216
  end
261
217
 
262
218
  # Sets the schema search path to a string of comma-separated schema names.
263
219
  # Names beginning with $ have to be quoted (e.g. $user => '$user').
264
- # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
220
+ # See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
265
221
  #
266
222
  # This should be not be called manually but set in database.yml.
267
223
  def schema_search_path=(schema_csv)
268
224
  if schema_csv
269
- execute("SET search_path TO #{schema_csv}", 'SCHEMA')
225
+ execute("SET search_path TO #{schema_csv}", "SCHEMA")
270
226
  @schema_search_path = schema_csv
271
227
  end
272
228
  end
273
229
 
274
230
  # Returns the active schema search path.
275
231
  def schema_search_path
276
- @schema_search_path ||= query('SHOW search_path', 'SCHEMA')[0][0]
232
+ @schema_search_path ||= query_value("SHOW search_path", "SCHEMA")
277
233
  end
278
234
 
279
235
  # Returns the current client message level.
280
236
  def client_min_messages
281
- query('SHOW client_min_messages', 'SCHEMA')[0][0]
237
+ query_value("SHOW client_min_messages", "SCHEMA")
282
238
  end
283
239
 
284
240
  # Set the client message level.
285
241
  def client_min_messages=(level)
286
- execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
242
+ execute("SET client_min_messages TO '#{level}'", "SCHEMA")
287
243
  end
288
244
 
289
245
  # Returns the sequence name for a table's primary key or some other specified key.
290
- def default_sequence_name(table_name, pk = nil) #:nodoc:
291
- result = serial_sequence(table_name, pk || 'id')
246
+ def default_sequence_name(table_name, pk = "id") #:nodoc:
247
+ result = serial_sequence(table_name, pk)
292
248
  return nil unless result
293
249
  Utils.extract_schema_qualified_name(result).to_s
294
250
  rescue ActiveRecord::StatementInvalid
295
- PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq").to_s
251
+ PostgreSQL::Name.new(nil, "#{table_name}_#{pk}_seq").to_s
296
252
  end
297
253
 
298
254
  def serial_sequence(table, column)
299
- result = exec_query(<<-eosql, 'SCHEMA')
300
- SELECT pg_get_serial_sequence('#{table}', '#{column}')
301
- eosql
302
- result.rows.first.first
255
+ query_value("SELECT pg_get_serial_sequence(#{quote(table)}, #{quote(column)})", "SCHEMA")
303
256
  end
304
257
 
305
258
  # Sets the sequence of a table's primary key to the specified value.
@@ -310,18 +263,16 @@ module ActiveRecord
310
263
  if sequence
311
264
  quoted_sequence = quote_table_name(sequence)
312
265
 
313
- select_value <<-end_sql, 'SCHEMA'
314
- SELECT setval('#{quoted_sequence}', #{value})
315
- end_sql
266
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
316
267
  else
317
- @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
268
+ @logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
318
269
  end
319
270
  end
320
271
  end
321
272
 
322
273
  # Resets the sequence of a table's primary key to the maximum value.
323
274
  def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
324
- unless pk and sequence
275
+ unless pk && sequence
325
276
  default_pk, default_sequence = pk_and_sequence_for(table)
326
277
 
327
278
  pk ||= default_pk
@@ -329,15 +280,21 @@ module ActiveRecord
329
280
  end
330
281
 
331
282
  if @logger && pk && !sequence
332
- @logger.warn "#{table} has primary key #{pk} with no default sequence"
283
+ @logger.warn "#{table} has primary key #{pk} with no default sequence."
333
284
  end
334
285
 
335
286
  if pk && sequence
336
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
337
296
 
338
- select_value <<-end_sql, 'SCHEMA'
339
- SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
340
- end_sql
297
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
341
298
  end
342
299
  end
343
300
 
@@ -345,7 +302,7 @@ module ActiveRecord
345
302
  def pk_and_sequence_for(table) #:nodoc:
346
303
  # First try looking for a sequence with a dependency on the
347
304
  # given table's primary key.
348
- result = query(<<-end_sql, 'SCHEMA')[0]
305
+ result = query(<<~SQL, "SCHEMA")[0]
349
306
  SELECT attr.attname, nsp.nspname, seq.relname
350
307
  FROM pg_class seq,
351
308
  pg_attribute attr,
@@ -361,11 +318,11 @@ module ActiveRecord
361
318
  AND seq.relnamespace = nsp.oid
362
319
  AND cons.contype = 'p'
363
320
  AND dep.classid = 'pg_class'::regclass
364
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
365
- end_sql
321
+ AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
322
+ SQL
366
323
 
367
- if result.nil? or result.empty?
368
- result = query(<<-end_sql, 'SCHEMA')[0]
324
+ if result.nil? || result.empty?
325
+ result = query(<<~SQL, "SCHEMA")[0]
369
326
  SELECT attr.attname, nsp.nspname,
370
327
  CASE
371
328
  WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
@@ -379,10 +336,10 @@ module ActiveRecord
379
336
  JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
380
337
  JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
381
338
  JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
382
- WHERE t.oid = '#{quote_table_name(table)}'::regclass
339
+ WHERE t.oid = #{quote(quote_table_name(table))}::regclass
383
340
  AND cons.contype = 'p'
384
341
  AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
385
- end_sql
342
+ SQL
386
343
  end
387
344
 
388
345
  pk = result.shift
@@ -395,17 +352,20 @@ module ActiveRecord
395
352
  nil
396
353
  end
397
354
 
398
- # Returns just a table's primary key
399
- def primary_key(table)
400
- pks = exec_query(<<-end_sql, 'SCHEMA').rows
401
- SELECT attr.attname
402
- FROM pg_attribute attr
403
- INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey)
404
- WHERE cons.contype = 'p'
405
- AND cons.conrelid = '#{quote_table_name(table)}'::regclass
406
- end_sql
407
- return nil unless pks.count == 1
408
- pks[0][0]
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
409
369
  end
410
370
 
411
371
  # Renames a table.
@@ -418,62 +378,57 @@ module ActiveRecord
418
378
  clear_cache!
419
379
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
420
380
  pk, seq = pk_and_sequence_for(new_name)
421
- if seq && seq.identifier == "#{table_name}_#{pk}_seq"
422
- new_seq = "#{new_name}_#{pk}_seq"
381
+ if pk
423
382
  idx = "#{table_name}_pkey"
424
383
  new_idx = "#{new_name}_pkey"
425
- execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
426
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
427
389
  end
428
-
429
390
  rename_table_indexes(table_name, new_name)
430
391
  end
431
392
 
432
393
  def add_column(table_name, column_name, type, options = {}) #:nodoc:
433
394
  clear_cache!
434
395
  super
396
+ change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
435
397
  end
436
398
 
437
- # Changes the column of a table.
438
- def change_column(table_name, column_name, type, options = {})
399
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
439
400
  clear_cache!
440
- quoted_table_name = quote_table_name(table_name)
441
- sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
442
- sql_type << "[]" if options[:array]
443
- sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}"
444
- sql << " USING #{options[:using]}" if options[:using]
445
- if options[:cast_as]
446
- sql << " USING CAST(#{quote_column_name(column_name)} AS #{type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale])})"
447
- end
448
- execute sql
449
-
450
- change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
451
- change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
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)
452
404
  end
453
405
 
454
406
  # Changes the default value of a table column.
455
- def change_column_default(table_name, column_name, default)
456
- clear_cache!
457
- column = column_for(table_name, column_name)
458
- return unless column
459
-
460
- alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
461
- if default.nil?
462
- # <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
463
- # cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
464
- execute alter_column_query % "DROP DEFAULT"
465
- else
466
- execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}"
467
- end
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)}"
468
409
  end
469
410
 
470
- def change_column_null(table_name, column_name, null, default = nil)
411
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
471
412
  clear_cache!
472
413
  unless null || default.nil?
473
414
  column = column_for(table_name, column_name)
474
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
415
+ execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(default, column)} WHERE #{quote_column_name(column_name)} IS NULL" if column
475
416
  end
476
- execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
417
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_null_for_alter(table_name, column_name, null, default)}"
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)}"
477
432
  end
478
433
 
479
434
  # Renames a column in a table.
@@ -484,14 +439,38 @@ module ActiveRecord
484
439
  end
485
440
 
486
441
  def add_index(table_name, column_name, options = {}) #:nodoc:
487
- index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
488
- execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
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
489
446
  end
490
447
 
491
- def remove_index!(table_name, index_name) #:nodoc:
492
- execute "DROP INDEX #{quote_table_name(index_name)}"
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)}"
493
470
  end
494
471
 
472
+ # Renames an index of a table. Raises error if length of new
473
+ # index name is greater than allowed limit.
495
474
  def rename_index(table_name, old_name, new_name)
496
475
  validate_index_length!(table_name, new_name)
497
476
 
@@ -499,8 +478,9 @@ module ActiveRecord
499
478
  end
500
479
 
501
480
  def foreign_keys(table_name)
502
- fk_info = select_all <<-SQL.strip_heredoc
503
- SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
481
+ scope = quoted_scope(table_name)
482
+ fk_info = exec_query(<<~SQL, "SCHEMA")
483
+ SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid
504
484
  FROM pg_constraint c
505
485
  JOIN pg_class t1 ON c.conrelid = t1.oid
506
486
  JOIN pg_class t2 ON c.confrelid = t2.oid
@@ -508,88 +488,288 @@ module ActiveRecord
508
488
  JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
509
489
  JOIN pg_namespace t3 ON c.connamespace = t3.oid
510
490
  WHERE c.contype = 'f'
511
- AND t1.relname = #{quote(table_name)}
512
- AND t3.nspname = ANY (current_schemas(false))
491
+ AND t1.relname = #{scope[:name]}
492
+ AND t3.nspname = #{scope[:schema]}
513
493
  ORDER BY c.conname
514
494
  SQL
515
495
 
516
496
  fk_info.map do |row|
517
497
  options = {
518
- column: row['column'],
519
- name: row['name'],
520
- primary_key: row['primary_key']
498
+ column: row["column"],
499
+ name: row["name"],
500
+ primary_key: row["primary_key"]
521
501
  }
522
502
 
523
- options[:on_delete] = extract_foreign_key_action(row['on_delete'])
524
- options[:on_update] = extract_foreign_key_action(row['on_update'])
503
+ options[:on_delete] = extract_foreign_key_action(row["on_delete"])
504
+ options[:on_update] = extract_foreign_key_action(row["on_update"])
505
+ options[:validate] = row["valid"]
525
506
 
526
- ForeignKeyDefinition.new(table_name, row['to_table'], options)
507
+ ForeignKeyDefinition.new(table_name, row["to_table"], options)
527
508
  end
528
509
  end
529
510
 
530
- def extract_foreign_key_action(specifier) # :nodoc:
531
- case specifier
532
- when 'c'; :cascade
533
- when 'n'; :nullify
534
- when 'r'; :restrict
535
- end
511
+ def foreign_tables
512
+ query_values(data_source_sql(type: "FOREIGN TABLE"), "SCHEMA")
536
513
  end
537
514
 
538
- def index_name_length
539
- 63
515
+ def foreign_table_exists?(table_name)
516
+ query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
540
517
  end
541
518
 
542
519
  # Maps logical Rails types to PostgreSQL-specific data types.
543
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
544
- case type.to_s
545
- when 'binary'
546
- # PostgreSQL doesn't support limits on binary (bytea) columns.
547
- # The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
548
- case limit
549
- when nil, 0..0x3fffffff; super(type)
550
- else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
551
- end
552
- when 'text'
553
- # PostgreSQL doesn't support limits on text columns.
554
- # The hard limit is 1Gb, according to section 8.3 in the manual.
555
- case limit
556
- when nil, 0..0x3fffffff; super(type)
557
- else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
558
- end
559
- when 'integer'
560
- return 'integer' unless limit
561
-
562
- case limit
563
- when 1, 2; 'smallint'
564
- when 3, 4; 'integer'
565
- when 5..8; 'bigint'
566
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
520
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
521
+ sql = \
522
+ case type.to_s
523
+ when "binary"
524
+ # PostgreSQL doesn't support limits on binary (bytea) columns.
525
+ # The hard limit is 1GB, because of a 32-bit size field, and TOAST.
526
+ case limit
527
+ when nil, 0..0x3fffffff; super(type)
528
+ else raise ArgumentError, "No binary type has byte size #{limit}. The limit on binary can be at most 1GB - 1byte."
529
+ end
530
+ when "text"
531
+ # PostgreSQL doesn't support limits on text columns.
532
+ # The hard limit is 1GB, according to section 8.3 in the manual.
533
+ case limit
534
+ when nil, 0..0x3fffffff; super(type)
535
+ else raise ArgumentError, "No text type has byte size #{limit}. The limit on text can be at most 1GB - 1byte."
536
+ end
537
+ when "integer"
538
+ case limit
539
+ when 1, 2; "smallint"
540
+ when nil, 3, 4; "integer"
541
+ when 5..8; "bigint"
542
+ else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
543
+ end
544
+ else
545
+ super
567
546
  end
568
- when 'datetime'
569
- return super unless precision
570
547
 
571
- case precision
572
- when 0..6; "timestamp(#{precision})"
573
- else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
574
- end
575
- else
576
- super
577
- end
548
+ sql = "#{sql}[]" if array && type != :primary_key
549
+ sql
578
550
  end
579
551
 
580
552
  # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
581
553
  # requires that the ORDER BY include the distinct column.
582
554
  def columns_for_distinct(columns, orders) #:nodoc:
583
- order_columns = orders.reject(&:blank?).map{ |s|
555
+ order_columns = orders.reject(&:blank?).map { |s|
584
556
  # Convert Arel node to string
585
557
  s = s.to_sql unless s.is_a?(String)
586
558
  # Remove any ASC/DESC modifiers
587
- s.gsub(/\s+(?:ASC|DESC)\b/i, '')
588
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
559
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
560
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
589
561
  }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
590
562
 
591
- [super, *order_columns].join(', ')
563
+ (order_columns << super).join(", ")
564
+ end
565
+
566
+ def update_table_definition(table_name, base) # :nodoc:
567
+ PostgreSQL::Table.new(table_name, base)
568
+ end
569
+
570
+ def create_schema_dumper(options) # :nodoc:
571
+ PostgreSQL::SchemaDumper.create(self, options)
572
+ end
573
+
574
+ # Validates the given constraint.
575
+ #
576
+ # Validates the constraint named +constraint_name+ on +accounts+.
577
+ #
578
+ # validate_constraint :accounts, :constraint_name
579
+ def validate_constraint(table_name, constraint_name)
580
+ return unless supports_validate_constraints?
581
+
582
+ at = create_alter_table table_name
583
+ at.validate_constraint constraint_name
584
+
585
+ execute schema_creation.accept(at)
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
592
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
593
773
  end
594
774
  end
595
775
  end