activerecord 4.2.9 → 6.1.4.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 (374) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +964 -1382
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +15 -14
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +266 -251
  8. data/lib/active_record/association_relation.rb +40 -15
  9. data/lib/active_record/associations/alias_tracker.rb +40 -43
  10. data/lib/active_record/associations/association.rb +162 -69
  11. data/lib/active_record/associations/association_scope.rb +105 -130
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -65
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  14. data/lib/active_record/associations/builder/association.rb +57 -43
  15. data/lib/active_record/associations/builder/belongs_to.rb +74 -57
  16. data/lib/active_record/associations/builder/collection_association.rb +15 -37
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +49 -66
  18. data/lib/active_record/associations/builder/has_many.rb +13 -5
  19. data/lib/active_record/associations/builder/has_one.rb +44 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  21. data/lib/active_record/associations/collection_association.rb +148 -287
  22. data/lib/active_record/associations/collection_proxy.rb +252 -150
  23. data/lib/active_record/associations/foreign_association.rb +23 -1
  24. data/lib/active_record/associations/has_many_association.rb +56 -98
  25. data/lib/active_record/associations/has_many_through_association.rb +68 -89
  26. data/lib/active_record/associations/has_one_association.rb +73 -47
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +54 -81
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  31. data/lib/active_record/associations/join_dependency.rb +174 -169
  32. data/lib/active_record/associations/preloader/association.rb +108 -115
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  34. data/lib/active_record/associations/preloader.rb +97 -94
  35. data/lib/active_record/associations/singular_association.rb +18 -39
  36. data/lib/active_record/associations/through_association.rb +39 -19
  37. data/lib/active_record/associations.rb +1845 -1598
  38. data/lib/active_record/attribute_assignment.rb +59 -185
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +18 -10
  40. data/lib/active_record/attribute_methods/dirty.rb +168 -148
  41. data/lib/active_record/attribute_methods/primary_key.rb +93 -83
  42. data/lib/active_record/attribute_methods/query.rb +8 -10
  43. data/lib/active_record/attribute_methods/read.rb +19 -79
  44. data/lib/active_record/attribute_methods/serialization.rb +49 -24
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +55 -36
  46. data/lib/active_record/attribute_methods/write.rb +24 -55
  47. data/lib/active_record/attribute_methods.rb +149 -154
  48. data/lib/active_record/attributes.rb +234 -78
  49. data/lib/active_record/autosave_association.rb +133 -60
  50. data/lib/active_record/base.rb +46 -46
  51. data/lib/active_record/callbacks.rb +234 -79
  52. data/lib/active_record/coders/json.rb +3 -1
  53. data/lib/active_record/coders/yaml_column.rb +34 -13
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -323
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +292 -124
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +177 -60
  59. data/lib/active_record/connection_adapters/abstract/savepoints.rb +8 -6
  60. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +473 -255
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +869 -286
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +257 -91
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +483 -230
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +557 -640
  67. data/lib/active_record/connection_adapters/column.rb +67 -40
  68. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  69. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +194 -0
  72. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  78. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +80 -192
  80. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  81. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -160
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -19
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -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 +44 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  101. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  109. data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
  110. data/lib/active_record/connection_adapters/postgresql/quoting.rb +145 -48
  111. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  112. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  114. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +496 -298
  116. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  117. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  118. data/lib/active_record/connection_adapters/postgresql_adapter.rb +588 -375
  119. data/lib/active_record/connection_adapters/schema_cache.rb +167 -29
  120. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  121. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  122. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  123. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -373
  129. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  130. data/lib/active_record/connection_adapters.rb +52 -0
  131. data/lib/active_record/connection_handling.rb +314 -41
  132. data/lib/active_record/core.rb +458 -241
  133. data/lib/active_record/counter_cache.rb +70 -49
  134. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  135. data/lib/active_record/database_configurations/database_config.rb +80 -0
  136. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  137. data/lib/active_record/database_configurations/url_config.rb +53 -0
  138. data/lib/active_record/database_configurations.rb +272 -0
  139. data/lib/active_record/delegated_type.rb +209 -0
  140. data/lib/active_record/destroy_association_async_job.rb +36 -0
  141. data/lib/active_record/dynamic_matchers.rb +87 -106
  142. data/lib/active_record/enum.rb +211 -92
  143. data/lib/active_record/errors.rb +224 -54
  144. data/lib/active_record/explain.rb +27 -11
  145. data/lib/active_record/explain_registry.rb +4 -2
  146. data/lib/active_record/explain_subscriber.rb +10 -5
  147. data/lib/active_record/fixture_set/file.rb +33 -14
  148. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  149. data/lib/active_record/fixture_set/render_context.rb +17 -0
  150. data/lib/active_record/fixture_set/table_row.rb +152 -0
  151. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  152. data/lib/active_record/fixtures.rb +275 -500
  153. data/lib/active_record/gem_version.rb +6 -4
  154. data/lib/active_record/inheritance.rb +175 -110
  155. data/lib/active_record/insert_all.rb +212 -0
  156. data/lib/active_record/integration.rb +121 -29
  157. data/lib/active_record/internal_metadata.rb +62 -0
  158. data/lib/active_record/legacy_yaml_adapter.rb +27 -5
  159. data/lib/active_record/locale/en.yml +3 -2
  160. data/lib/active_record/locking/optimistic.rb +98 -92
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +93 -31
  163. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector.rb +77 -0
  166. data/lib/active_record/migration/command_recorder.rb +185 -90
  167. data/lib/active_record/migration/compatibility.rb +295 -0
  168. data/lib/active_record/migration/join_table.rb +8 -7
  169. data/lib/active_record/migration.rb +673 -325
  170. data/lib/active_record/model_schema.rb +418 -113
  171. data/lib/active_record/nested_attributes.rb +263 -224
  172. data/lib/active_record/no_touching.rb +15 -2
  173. data/lib/active_record/null_relation.rb +24 -38
  174. data/lib/active_record/persistence.rb +572 -136
  175. data/lib/active_record/query_cache.rb +29 -23
  176. data/lib/active_record/querying.rb +50 -31
  177. data/lib/active_record/railtie.rb +170 -51
  178. data/lib/active_record/railties/console_sandbox.rb +3 -3
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +523 -199
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +454 -291
  183. data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
  184. data/lib/active_record/relation/batches.rb +217 -59
  185. data/lib/active_record/relation/calculations.rb +324 -249
  186. data/lib/active_record/relation/delegation.rb +76 -84
  187. data/lib/active_record/relation/finder_methods.rb +316 -242
  188. data/lib/active_record/relation/from_clause.rb +30 -0
  189. data/lib/active_record/relation/merger.rb +95 -103
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -26
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  192. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  193. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
  194. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  195. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  196. data/lib/active_record/relation/predicate_builder.rb +136 -122
  197. data/lib/active_record/relation/query_attribute.rb +50 -0
  198. data/lib/active_record/relation/query_methods.rb +757 -413
  199. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  200. data/lib/active_record/relation/spawn_methods.rb +18 -20
  201. data/lib/active_record/relation/where_clause.rb +239 -0
  202. data/lib/active_record/relation.rb +554 -343
  203. data/lib/active_record/result.rb +91 -47
  204. data/lib/active_record/runtime_registry.rb +6 -4
  205. data/lib/active_record/sanitization.rb +134 -122
  206. data/lib/active_record/schema.rb +21 -24
  207. data/lib/active_record/schema_dumper.rb +141 -92
  208. data/lib/active_record/schema_migration.rb +24 -23
  209. data/lib/active_record/scoping/default.rb +96 -83
  210. data/lib/active_record/scoping/named.rb +78 -36
  211. data/lib/active_record/scoping.rb +45 -27
  212. data/lib/active_record/secure_token.rb +48 -0
  213. data/lib/active_record/serialization.rb +8 -6
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +89 -36
  216. data/lib/active_record/store.rb +128 -43
  217. data/lib/active_record/suppressor.rb +61 -0
  218. data/lib/active_record/table_metadata.rb +81 -0
  219. data/lib/active_record/tasks/database_tasks.rb +364 -130
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +67 -113
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +86 -49
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +287 -0
  225. data/lib/active_record/timestamp.rb +86 -43
  226. data/lib/active_record/touch_later.rb +65 -0
  227. data/lib/active_record/transactions.rb +182 -163
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  230. data/lib/active_record/type/date.rb +4 -45
  231. data/lib/active_record/type/date_time.rb +4 -49
  232. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  233. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  234. data/lib/active_record/type/internal/timezone.rb +17 -0
  235. data/lib/active_record/type/json.rb +30 -0
  236. data/lib/active_record/type/serialized.rb +27 -15
  237. data/lib/active_record/type/text.rb +2 -2
  238. data/lib/active_record/type/time.rb +21 -16
  239. data/lib/active_record/type/type_map.rb +16 -19
  240. data/lib/active_record/type/unsigned_integer.rb +9 -8
  241. data/lib/active_record/type.rb +84 -23
  242. data/lib/active_record/type_caster/connection.rb +33 -0
  243. data/lib/active_record/type_caster/map.rb +23 -0
  244. data/lib/active_record/type_caster.rb +9 -0
  245. data/lib/active_record/validations/absence.rb +25 -0
  246. data/lib/active_record/validations/associated.rb +12 -4
  247. data/lib/active_record/validations/length.rb +26 -0
  248. data/lib/active_record/validations/numericality.rb +35 -0
  249. data/lib/active_record/validations/presence.rb +14 -13
  250. data/lib/active_record/validations/uniqueness.rb +63 -56
  251. data/lib/active_record/validations.rb +39 -35
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/active_record.rb +42 -29
  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/and.rb +32 -0
  269. data/lib/arel/nodes/ascending.rb +23 -0
  270. data/lib/arel/nodes/binary.rb +126 -0
  271. data/lib/arel/nodes/bind_param.rb +44 -0
  272. data/lib/arel/nodes/case.rb +55 -0
  273. data/lib/arel/nodes/casted.rb +62 -0
  274. data/lib/arel/nodes/comment.rb +29 -0
  275. data/lib/arel/nodes/count.rb +12 -0
  276. data/lib/arel/nodes/delete_statement.rb +45 -0
  277. data/lib/arel/nodes/descending.rb +23 -0
  278. data/lib/arel/nodes/equality.rb +15 -0
  279. data/lib/arel/nodes/extract.rb +24 -0
  280. data/lib/arel/nodes/false.rb +16 -0
  281. data/lib/arel/nodes/full_outer_join.rb +8 -0
  282. data/lib/arel/nodes/function.rb +44 -0
  283. data/lib/arel/nodes/grouping.rb +11 -0
  284. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  285. data/lib/arel/nodes/in.rb +15 -0
  286. data/lib/arel/nodes/infix_operation.rb +92 -0
  287. data/lib/arel/nodes/inner_join.rb +8 -0
  288. data/lib/arel/nodes/insert_statement.rb +37 -0
  289. data/lib/arel/nodes/join_source.rb +20 -0
  290. data/lib/arel/nodes/matches.rb +18 -0
  291. data/lib/arel/nodes/named_function.rb +23 -0
  292. data/lib/arel/nodes/node.rb +51 -0
  293. data/lib/arel/nodes/node_expression.rb +13 -0
  294. data/lib/arel/nodes/ordering.rb +27 -0
  295. data/lib/arel/nodes/outer_join.rb +8 -0
  296. data/lib/arel/nodes/over.rb +15 -0
  297. data/lib/arel/nodes/regexp.rb +16 -0
  298. data/lib/arel/nodes/right_outer_join.rb +8 -0
  299. data/lib/arel/nodes/select_core.rb +67 -0
  300. data/lib/arel/nodes/select_statement.rb +41 -0
  301. data/lib/arel/nodes/sql_literal.rb +19 -0
  302. data/lib/arel/nodes/string_join.rb +11 -0
  303. data/lib/arel/nodes/table_alias.rb +31 -0
  304. data/lib/arel/nodes/terminal.rb +16 -0
  305. data/lib/arel/nodes/true.rb +16 -0
  306. data/lib/arel/nodes/unary.rb +44 -0
  307. data/lib/arel/nodes/unary_operation.rb +20 -0
  308. data/lib/arel/nodes/unqualified_column.rb +22 -0
  309. data/lib/arel/nodes/update_statement.rb +41 -0
  310. data/lib/arel/nodes/values_list.rb +9 -0
  311. data/lib/arel/nodes/window.rb +126 -0
  312. data/lib/arel/nodes/with.rb +11 -0
  313. data/lib/arel/nodes.rb +70 -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/dot.rb +308 -0
  321. data/lib/arel/visitors/mysql.rb +93 -0
  322. data/lib/arel/visitors/postgresql.rb +120 -0
  323. data/lib/arel/visitors/sqlite.rb +38 -0
  324. data/lib/arel/visitors/to_sql.rb +899 -0
  325. data/lib/arel/visitors/visitor.rb +45 -0
  326. data/lib/arel/visitors.rb +13 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/arel.rb +54 -0
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
  332. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -4
  334. data/lib/rails/generators/active_record/migration.rb +35 -1
  335. data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
  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.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. data/lib/rails/generators/active_record.rb +7 -5
  340. metadata +172 -65
  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_decorators.rb +0 -66
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/attribute_set.rb +0 -81
  352. data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
  353. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  354. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  355. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  356. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  357. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  358. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  359. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  360. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  361. data/lib/active_record/type/big_integer.rb +0 -13
  362. data/lib/active_record/type/binary.rb +0 -50
  363. data/lib/active_record/type/boolean.rb +0 -31
  364. data/lib/active_record/type/decimal.rb +0 -64
  365. data/lib/active_record/type/decorator.rb +0 -14
  366. data/lib/active_record/type/float.rb +0 -19
  367. data/lib/active_record/type/integer.rb +0 -59
  368. data/lib/active_record/type/mutable.rb +0 -16
  369. data/lib/active_record/type/numeric.rb +0 -36
  370. data/lib/active_record/type/string.rb +0 -40
  371. data/lib/active_record/type/time_value.rb +0 -38
  372. data/lib/active_record/type/value.rb +0 -110
  373. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  374. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -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,150 @@ 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
+ schema_cache.clear_data_source_cache!(table_name.to_s)
59
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
128
60
  end
129
61
 
130
62
  # Returns true if schema exists.
131
63
  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
64
+ query_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
137
65
  end
138
66
 
139
- def index_name_exists?(table_name, index_name, default)
140
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
67
+ # Verifies existence of an index with a given name.
68
+ def index_name_exists?(table_name, index_name)
69
+ table = quoted_scope(table_name)
70
+ index = quoted_scope(index_name)
71
+
72
+ query_value(<<~SQL, "SCHEMA").to_i > 0
141
73
  SELECT COUNT(*)
142
74
  FROM pg_class t
143
75
  INNER JOIN pg_index d ON t.oid = d.indrelid
144
76
  INNER JOIN pg_class i ON d.indexrelid = i.oid
145
- 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)) )
77
+ LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
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]}
149
82
  SQL
150
83
  end
151
84
 
152
85
  # 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)) )
86
+ def indexes(table_name) # :nodoc:
87
+ scope = quoted_scope(table_name)
88
+
89
+ result = query(<<~SQL, "SCHEMA")
90
+ SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
91
+ pg_catalog.obj_description(i.oid, 'pg_class') AS comment
92
+ FROM pg_class t
93
+ INNER JOIN pg_index d ON t.oid = d.indrelid
94
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
95
+ LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
96
+ WHERE i.relkind IN ('i', 'I')
97
+ AND d.indisprimary = 'f'
98
+ AND t.relname = #{scope[:name]}
99
+ AND n.nspname = #{scope[:schema]}
163
100
  ORDER BY i.relname
164
101
  SQL
165
102
 
166
103
  result.map do |row|
167
104
  index_name = row[0]
168
- unique = row[1] == 't'
169
- indkey = row[2].split(" ")
105
+ unique = row[1]
106
+ indkey = row[2].split(" ").map(&:to_i)
170
107
  inddef = row[3]
171
108
  oid = row[4]
109
+ comment = row[5]
172
110
 
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
111
+ using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
181
112
 
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
113
+ orders = {}
114
+ opclasses = {}
188
115
 
189
- IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
116
+ if indkey.include?(0)
117
+ columns = expressions
118
+ else
119
+ columns = Hash[query(<<~SQL, "SCHEMA")].values_at(*indkey).compact
120
+ SELECT a.attnum, a.attname
121
+ FROM pg_attribute a
122
+ WHERE a.attrelid = #{oid}
123
+ AND a.attnum IN (#{indkey.join(",")})
124
+ SQL
125
+
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
190
136
  end
191
- end.compact
137
+
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
+ )
149
+ end
192
150
  end
193
151
 
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)
152
+ def table_options(table_name) # :nodoc:
153
+ if comment = table_comment(table_name)
154
+ { comment: comment }
202
155
  end
203
156
  end
204
157
 
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)
158
+ # Returns a comment stored in database for given table
159
+ def table_comment(table_name) # :nodoc:
160
+ scope = quoted_scope(table_name, type: "BASE TABLE")
161
+ if scope[:name]
162
+ query_value(<<~SQL, "SCHEMA")
163
+ SELECT pg_catalog.obj_description(c.oid, 'pg_class')
164
+ FROM pg_catalog.pg_class c
165
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
166
+ WHERE c.relname = #{scope[:name]}
167
+ AND c.relkind IN (#{scope[:type]})
168
+ AND n.nspname = #{scope[:schema]}
169
+ SQL
170
+ end
207
171
  end
208
172
 
209
173
  # Returns the current database name.
210
174
  def current_database
211
- query('select current_database()', 'SCHEMA')[0][0]
175
+ query_value("SELECT current_database()", "SCHEMA")
212
176
  end
213
177
 
214
178
  # Returns the current schema name.
215
179
  def current_schema
216
- query('SELECT current_schema', 'SCHEMA')[0][0]
180
+ query_value("SELECT current_schema", "SCHEMA")
217
181
  end
218
182
 
219
183
  # Returns the current database encoding format.
220
184
  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
185
+ query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
225
186
  end
226
187
 
227
188
  # Returns the current database collation.
228
189
  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
190
+ query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
232
191
  end
233
192
 
234
193
  # Returns the current database ctype.
235
194
  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
195
+ query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
239
196
  end
240
197
 
241
198
  # Returns an array of schema names.
242
199
  def schema_names
243
- query(<<-SQL, 'SCHEMA').flatten
200
+ query_values(<<~SQL, "SCHEMA")
244
201
  SELECT nspname
245
202
  FROM pg_namespace
246
203
  WHERE nspname !~ '^pg_.*'
@@ -250,56 +207,53 @@ module ActiveRecord
250
207
  end
251
208
 
252
209
  # Creates a schema for the given schema name.
253
- def create_schema schema_name
254
- execute "CREATE SCHEMA #{schema_name}"
210
+ def create_schema(schema_name)
211
+ execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
255
212
  end
256
213
 
257
214
  # Drops the schema for the given schema name.
258
- def drop_schema schema_name
259
- execute "DROP SCHEMA #{schema_name} CASCADE"
215
+ def drop_schema(schema_name, **options)
216
+ execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
260
217
  end
261
218
 
262
219
  # Sets the schema search path to a string of comma-separated schema names.
263
220
  # Names beginning with $ have to be quoted (e.g. $user => '$user').
264
- # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
221
+ # See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
265
222
  #
266
223
  # This should be not be called manually but set in database.yml.
267
224
  def schema_search_path=(schema_csv)
268
225
  if schema_csv
269
- execute("SET search_path TO #{schema_csv}", 'SCHEMA')
226
+ execute("SET search_path TO #{schema_csv}", "SCHEMA")
270
227
  @schema_search_path = schema_csv
271
228
  end
272
229
  end
273
230
 
274
231
  # Returns the active schema search path.
275
232
  def schema_search_path
276
- @schema_search_path ||= query('SHOW search_path', 'SCHEMA')[0][0]
233
+ @schema_search_path ||= query_value("SHOW search_path", "SCHEMA")
277
234
  end
278
235
 
279
236
  # Returns the current client message level.
280
237
  def client_min_messages
281
- query('SHOW client_min_messages', 'SCHEMA')[0][0]
238
+ query_value("SHOW client_min_messages", "SCHEMA")
282
239
  end
283
240
 
284
241
  # Set the client message level.
285
242
  def client_min_messages=(level)
286
- execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
243
+ execute("SET client_min_messages TO '#{level}'", "SCHEMA")
287
244
  end
288
245
 
289
246
  # 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')
247
+ def default_sequence_name(table_name, pk = "id") #:nodoc:
248
+ result = serial_sequence(table_name, pk)
292
249
  return nil unless result
293
250
  Utils.extract_schema_qualified_name(result).to_s
294
251
  rescue ActiveRecord::StatementInvalid
295
- PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq").to_s
252
+ PostgreSQL::Name.new(nil, "#{table_name}_#{pk}_seq").to_s
296
253
  end
297
254
 
298
255
  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
256
+ query_value("SELECT pg_get_serial_sequence(#{quote(table)}, #{quote(column)})", "SCHEMA")
303
257
  end
304
258
 
305
259
  # Sets the sequence of a table's primary key to the specified value.
@@ -310,18 +264,16 @@ module ActiveRecord
310
264
  if sequence
311
265
  quoted_sequence = quote_table_name(sequence)
312
266
 
313
- select_value <<-end_sql, 'SCHEMA'
314
- SELECT setval('#{quoted_sequence}', #{value})
315
- end_sql
267
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
316
268
  else
317
- @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
269
+ @logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
318
270
  end
319
271
  end
320
272
  end
321
273
 
322
274
  # Resets the sequence of a table's primary key to the maximum value.
323
275
  def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
324
- unless pk and sequence
276
+ unless pk && sequence
325
277
  default_pk, default_sequence = pk_and_sequence_for(table)
326
278
 
327
279
  pk ||= default_pk
@@ -329,15 +281,21 @@ module ActiveRecord
329
281
  end
330
282
 
331
283
  if @logger && pk && !sequence
332
- @logger.warn "#{table} has primary key #{pk} with no default sequence"
284
+ @logger.warn "#{table} has primary key #{pk} with no default sequence."
333
285
  end
334
286
 
335
287
  if pk && sequence
336
288
  quoted_sequence = quote_table_name(sequence)
289
+ max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
290
+ if max_pk.nil?
291
+ if database_version >= 100000
292
+ minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
293
+ else
294
+ minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
295
+ end
296
+ end
337
297
 
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
298
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
341
299
  end
342
300
  end
343
301
 
@@ -345,7 +303,7 @@ module ActiveRecord
345
303
  def pk_and_sequence_for(table) #:nodoc:
346
304
  # First try looking for a sequence with a dependency on the
347
305
  # given table's primary key.
348
- result = query(<<-end_sql, 'SCHEMA')[0]
306
+ result = query(<<~SQL, "SCHEMA")[0]
349
307
  SELECT attr.attname, nsp.nspname, seq.relname
350
308
  FROM pg_class seq,
351
309
  pg_attribute attr,
@@ -361,11 +319,11 @@ module ActiveRecord
361
319
  AND seq.relnamespace = nsp.oid
362
320
  AND cons.contype = 'p'
363
321
  AND dep.classid = 'pg_class'::regclass
364
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
365
- end_sql
322
+ AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
323
+ SQL
366
324
 
367
- if result.nil? or result.empty?
368
- result = query(<<-end_sql, 'SCHEMA')[0]
325
+ if result.nil? || result.empty?
326
+ result = query(<<~SQL, "SCHEMA")[0]
369
327
  SELECT attr.attname, nsp.nspname,
370
328
  CASE
371
329
  WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
@@ -379,10 +337,10 @@ module ActiveRecord
379
337
  JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
380
338
  JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
381
339
  JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
382
- WHERE t.oid = '#{quote_table_name(table)}'::regclass
340
+ WHERE t.oid = #{quote(quote_table_name(table))}::regclass
383
341
  AND cons.contype = 'p'
384
342
  AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
385
- end_sql
343
+ SQL
386
344
  end
387
345
 
388
346
  pk = result.shift
@@ -395,17 +353,20 @@ module ActiveRecord
395
353
  nil
396
354
  end
397
355
 
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]
356
+ def primary_keys(table_name) # :nodoc:
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
369
+ SQL
409
370
  end
410
371
 
411
372
  # Renames a table.
@@ -416,82 +377,103 @@ module ActiveRecord
416
377
  # rename_table('octopuses', 'octopi')
417
378
  def rename_table(table_name, new_name)
418
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)
419
382
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
420
383
  pk, seq = pk_and_sequence_for(new_name)
421
- if seq && seq.identifier == "#{table_name}_#{pk}_seq"
422
- new_seq = "#{new_name}_#{pk}_seq"
384
+ if pk
423
385
  idx = "#{table_name}_pkey"
424
386
  new_idx = "#{new_name}_pkey"
425
- execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
426
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
427
392
  end
428
-
429
393
  rename_table_indexes(table_name, new_name)
430
394
  end
431
395
 
432
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
396
+ def add_column(table_name, column_name, type, **options) #:nodoc:
433
397
  clear_cache!
434
398
  super
399
+ change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
435
400
  end
436
401
 
437
- # Changes the column of a table.
438
- def change_column(table_name, column_name, type, options = {})
402
+ def change_column(table_name, column_name, type, **options) #:nodoc:
439
403
  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)
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)
452
407
  end
453
408
 
454
409
  # 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
410
+ def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
411
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
468
412
  end
469
413
 
470
- def change_column_null(table_name, column_name, null, default = nil)
414
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
471
415
  clear_cache!
472
416
  unless null || default.nil?
473
417
  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
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
475
419
  end
476
- 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)}"
421
+ end
422
+
423
+ # Adds comment for given table column or drops it if +comment+ is a +nil+
424
+ def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
425
+ clear_cache!
426
+ comment = extract_new_comment_value(comment_or_changes)
427
+ execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
428
+ end
429
+
430
+ # Adds comment for given table or drops it if +comment+ is a +nil+
431
+ def change_table_comment(table_name, comment_or_changes) # :nodoc:
432
+ clear_cache!
433
+ comment = extract_new_comment_value(comment_or_changes)
434
+ execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
477
435
  end
478
436
 
479
437
  # Renames a column in a table.
480
438
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
481
439
  clear_cache!
482
- 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)}")
483
441
  rename_column_indexes(table_name, column_name, new_column_name)
484
442
  end
485
443
 
486
- 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}"
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
489
452
  end
490
453
 
491
- def remove_index!(table_name, index_name) #:nodoc:
492
- execute "DROP INDEX #{quote_table_name(index_name)}"
454
+ def remove_index(table_name, column_name = nil, **options) # :nodoc:
455
+ table = Utils.extract_schema_qualified_name(table_name.to_s)
456
+
457
+ if options.key?(:name)
458
+ provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
459
+
460
+ options[:name] = provided_index.identifier
461
+ table = PostgreSQL::Name.new(provided_index.schema, table.identifier) unless table.schema.present?
462
+
463
+ if provided_index.schema.present? && table.schema != provided_index.schema
464
+ raise ArgumentError.new("Index schema '#{provided_index.schema}' does not match table schema '#{table.schema}'")
465
+ end
466
+ end
467
+
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)}"
493
473
  end
494
474
 
475
+ # Renames an index of a table. Raises error if length of new
476
+ # index name is greater than allowed limit.
495
477
  def rename_index(table_name, old_name, new_name)
496
478
  validate_index_length!(table_name, new_name)
497
479
 
@@ -499,8 +481,9 @@ module ActiveRecord
499
481
  end
500
482
 
501
483
  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
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
504
487
  FROM pg_constraint c
505
488
  JOIN pg_class t1 ON c.conrelid = t1.oid
506
489
  JOIN pg_class t2 ON c.confrelid = t2.oid
@@ -508,88 +491,303 @@ module ActiveRecord
508
491
  JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
509
492
  JOIN pg_namespace t3 ON c.connamespace = t3.oid
510
493
  WHERE c.contype = 'f'
511
- AND t1.relname = #{quote(table_name)}
512
- AND t3.nspname = ANY (current_schemas(false))
494
+ AND t1.relname = #{scope[:name]}
495
+ AND t3.nspname = #{scope[:schema]}
513
496
  ORDER BY c.conname
514
497
  SQL
515
498
 
516
499
  fk_info.map do |row|
517
500
  options = {
518
- column: row['column'],
519
- name: row['name'],
520
- primary_key: row['primary_key']
501
+ column: row["column"],
502
+ name: row["name"],
503
+ primary_key: row["primary_key"]
521
504
  }
522
505
 
523
- options[:on_delete] = extract_foreign_key_action(row['on_delete'])
524
- 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"]
525
509
 
526
- ForeignKeyDefinition.new(table_name, row['to_table'], options)
510
+ ForeignKeyDefinition.new(table_name, row["to_table"], options)
527
511
  end
528
512
  end
529
513
 
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
514
+ def foreign_tables
515
+ query_values(data_source_sql(type: "FOREIGN TABLE"), "SCHEMA")
536
516
  end
537
517
 
538
- def index_name_length
539
- 63
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)
541
+ end
540
542
  end
541
543
 
542
544
  # 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.")
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
567
571
  end
568
- when 'datetime'
569
- return super unless precision
570
572
 
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
573
+ sql = "#{sql}[]" if array && type != :primary_key
574
+ sql
578
575
  end
579
576
 
580
577
  # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
581
578
  # requires that the ORDER BY include the distinct column.
582
579
  def columns_for_distinct(columns, orders) #:nodoc:
583
- order_columns = orders.reject(&:blank?).map{ |s|
584
- # Convert Arel node to string
585
- s = s.to_sql unless s.is_a?(String)
586
- # Remove any ASC/DESC modifiers
587
- s.gsub(/\s+(?:ASC|DESC)\b/i, '')
588
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
589
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
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
590
 
591
- [super, *order_columns].join(', ')
591
+ def update_table_definition(table_name, base) # :nodoc:
592
+ PostgreSQL::Table.new(table_name, base)
592
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)
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
593
791
  end
594
792
  end
595
793
  end