activerecord 6.0.1 → 6.1.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 (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +843 -626
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -2
  6. data/lib/active_record/association_relation.rb +18 -17
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +49 -37
  9. data/lib/active_record/associations/association_scope.rb +17 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  12. data/lib/active_record/associations/builder/association.rb +9 -3
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +25 -8
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +73 -42
  28. data/lib/active_record/associations/preloader/association.rb +51 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +12 -7
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +115 -12
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +32 -8
  45. data/lib/active_record/autosave_association.rb +56 -41
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +1 -2
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +82 -37
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -8
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +263 -107
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +60 -73
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +28 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  75. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  91. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  93. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  98. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  100. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +77 -57
  103. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +36 -12
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  107. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
  110. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  111. data/lib/active_record/connection_adapters.rb +50 -0
  112. data/lib/active_record/connection_handling.rb +210 -87
  113. data/lib/active_record/core.rb +229 -65
  114. data/lib/active_record/counter_cache.rb +4 -1
  115. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  116. data/lib/active_record/database_configurations/database_config.rb +52 -9
  117. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  118. data/lib/active_record/database_configurations/url_config.rb +15 -41
  119. data/lib/active_record/database_configurations.rb +124 -85
  120. data/lib/active_record/delegated_type.rb +209 -0
  121. data/lib/active_record/destroy_association_async_job.rb +36 -0
  122. data/lib/active_record/dynamic_matchers.rb +2 -3
  123. data/lib/active_record/enum.rb +40 -16
  124. data/lib/active_record/errors.rb +47 -12
  125. data/lib/active_record/explain.rb +9 -5
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +10 -17
  128. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  129. data/lib/active_record/fixture_set/render_context.rb +1 -1
  130. data/lib/active_record/fixture_set/table_row.rb +2 -3
  131. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  132. data/lib/active_record/fixtures.rb +54 -11
  133. data/lib/active_record/gem_version.rb +2 -2
  134. data/lib/active_record/inheritance.rb +40 -21
  135. data/lib/active_record/insert_all.rb +38 -9
  136. data/lib/active_record/integration.rb +3 -5
  137. data/lib/active_record/internal_metadata.rb +16 -7
  138. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  139. data/lib/active_record/locking/optimistic.rb +22 -17
  140. data/lib/active_record/locking/pessimistic.rb +6 -2
  141. data/lib/active_record/log_subscriber.rb +27 -9
  142. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  143. data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
  144. data/lib/active_record/middleware/database_selector.rb +4 -2
  145. data/lib/active_record/migration/command_recorder.rb +53 -45
  146. data/lib/active_record/migration/compatibility.rb +70 -20
  147. data/lib/active_record/migration/join_table.rb +0 -1
  148. data/lib/active_record/migration.rb +114 -84
  149. data/lib/active_record/model_schema.rb +117 -15
  150. data/lib/active_record/nested_attributes.rb +2 -5
  151. data/lib/active_record/no_touching.rb +1 -1
  152. data/lib/active_record/null_relation.rb +0 -1
  153. data/lib/active_record/persistence.rb +50 -46
  154. data/lib/active_record/query_cache.rb +15 -5
  155. data/lib/active_record/querying.rb +12 -7
  156. data/lib/active_record/railtie.rb +65 -45
  157. data/lib/active_record/railties/databases.rake +267 -93
  158. data/lib/active_record/readonly_attributes.rb +4 -0
  159. data/lib/active_record/reflection.rb +77 -63
  160. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  161. data/lib/active_record/relation/batches.rb +38 -32
  162. data/lib/active_record/relation/calculations.rb +102 -45
  163. data/lib/active_record/relation/delegation.rb +9 -7
  164. data/lib/active_record/relation/finder_methods.rb +45 -16
  165. data/lib/active_record/relation/from_clause.rb +5 -1
  166. data/lib/active_record/relation/merger.rb +27 -26
  167. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  168. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  169. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  170. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  171. data/lib/active_record/relation/predicate_builder.rb +55 -35
  172. data/lib/active_record/relation/query_methods.rb +335 -187
  173. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  174. data/lib/active_record/relation/spawn_methods.rb +8 -8
  175. data/lib/active_record/relation/where_clause.rb +104 -58
  176. data/lib/active_record/relation.rb +108 -68
  177. data/lib/active_record/result.rb +41 -34
  178. data/lib/active_record/runtime_registry.rb +2 -2
  179. data/lib/active_record/sanitization.rb +6 -17
  180. data/lib/active_record/schema_dumper.rb +34 -4
  181. data/lib/active_record/schema_migration.rb +2 -8
  182. data/lib/active_record/scoping/default.rb +0 -1
  183. data/lib/active_record/scoping/named.rb +7 -18
  184. data/lib/active_record/scoping.rb +0 -1
  185. data/lib/active_record/secure_token.rb +16 -8
  186. data/lib/active_record/serialization.rb +5 -3
  187. data/lib/active_record/signed_id.rb +116 -0
  188. data/lib/active_record/statement_cache.rb +20 -4
  189. data/lib/active_record/store.rb +3 -3
  190. data/lib/active_record/suppressor.rb +2 -2
  191. data/lib/active_record/table_metadata.rb +39 -36
  192. data/lib/active_record/tasks/database_tasks.rb +139 -113
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  196. data/lib/active_record/test_databases.rb +5 -4
  197. data/lib/active_record/test_fixtures.rb +38 -16
  198. data/lib/active_record/timestamp.rb +4 -7
  199. data/lib/active_record/touch_later.rb +20 -21
  200. data/lib/active_record/transactions.rb +21 -70
  201. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  202. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  203. data/lib/active_record/type/serialized.rb +6 -3
  204. data/lib/active_record/type/time.rb +10 -0
  205. data/lib/active_record/type/type_map.rb +0 -1
  206. data/lib/active_record/type/unsigned_integer.rb +0 -1
  207. data/lib/active_record/type.rb +8 -2
  208. data/lib/active_record/type_caster/connection.rb +0 -1
  209. data/lib/active_record/type_caster/map.rb +8 -5
  210. data/lib/active_record/validations/associated.rb +1 -2
  211. data/lib/active_record/validations/numericality.rb +35 -0
  212. data/lib/active_record/validations/uniqueness.rb +24 -4
  213. data/lib/active_record/validations.rb +3 -3
  214. data/lib/active_record.rb +7 -13
  215. data/lib/arel/attributes/attribute.rb +4 -0
  216. data/lib/arel/collectors/bind.rb +5 -0
  217. data/lib/arel/collectors/composite.rb +8 -0
  218. data/lib/arel/collectors/sql_string.rb +7 -0
  219. data/lib/arel/collectors/substitute_binds.rb +7 -0
  220. data/lib/arel/nodes/binary.rb +82 -8
  221. data/lib/arel/nodes/bind_param.rb +8 -0
  222. data/lib/arel/nodes/casted.rb +21 -9
  223. data/lib/arel/nodes/equality.rb +6 -9
  224. data/lib/arel/nodes/grouping.rb +3 -0
  225. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  226. data/lib/arel/nodes/in.rb +8 -1
  227. data/lib/arel/nodes/infix_operation.rb +13 -1
  228. data/lib/arel/nodes/join_source.rb +1 -1
  229. data/lib/arel/nodes/node.rb +7 -6
  230. data/lib/arel/nodes/ordering.rb +27 -0
  231. data/lib/arel/nodes/sql_literal.rb +3 -0
  232. data/lib/arel/nodes/table_alias.rb +7 -3
  233. data/lib/arel/nodes/unary.rb +0 -1
  234. data/lib/arel/nodes.rb +3 -1
  235. data/lib/arel/predications.rb +17 -24
  236. data/lib/arel/select_manager.rb +1 -2
  237. data/lib/arel/table.rb +13 -5
  238. data/lib/arel/visitors/dot.rb +14 -3
  239. data/lib/arel/visitors/mysql.rb +11 -1
  240. data/lib/arel/visitors/postgresql.rb +15 -5
  241. data/lib/arel/visitors/sqlite.rb +0 -1
  242. data/lib/arel/visitors/to_sql.rb +89 -79
  243. data/lib/arel/visitors/visitor.rb +0 -1
  244. data/lib/arel/visitors.rb +0 -7
  245. data/lib/arel.rb +5 -9
  246. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  247. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  248. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  249. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  250. data/lib/rails/generators/active_record/migration.rb +6 -2
  251. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  252. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  253. metadata +26 -26
  254. data/lib/active_record/attribute_decorators.rb +0 -90
  255. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  256. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  257. data/lib/active_record/define_callbacks.rb +0 -22
  258. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  259. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  260. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  261. data/lib/arel/attributes.rb +0 -22
  262. data/lib/arel/visitors/depth_first.rb +0 -204
  263. data/lib/arel/visitors/ibm_db.rb +0 -34
  264. data/lib/arel/visitors/informix.rb +0 -62
  265. data/lib/arel/visitors/mssql.rb +0 -157
  266. data/lib/arel/visitors/oracle.rb +0 -159
  267. data/lib/arel/visitors/oracle12.rb +0 -66
  268. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -54,7 +54,8 @@ module ActiveRecord
54
54
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
55
55
  end
56
56
 
57
- def drop_table(table_name, options = {}) # :nodoc:
57
+ def drop_table(table_name, **options) # :nodoc:
58
+ schema_cache.clear_data_source_cache!(table_name.to_s)
58
59
  execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
59
60
  end
60
61
 
@@ -74,7 +75,7 @@ module ActiveRecord
74
75
  INNER JOIN pg_index d ON t.oid = d.indrelid
75
76
  INNER JOIN pg_class i ON d.indexrelid = i.oid
76
77
  LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
77
- WHERE i.relkind = 'i'
78
+ WHERE i.relkind IN ('i', 'I')
78
79
  AND i.relname = #{index[:name]}
79
80
  AND t.relname = #{table[:name]}
80
81
  AND n.nspname = #{index[:schema]}
@@ -92,7 +93,7 @@ module ActiveRecord
92
93
  INNER JOIN pg_index d ON t.oid = d.indrelid
93
94
  INNER JOIN pg_class i ON d.indexrelid = i.oid
94
95
  LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
95
- WHERE i.relkind = 'i'
96
+ WHERE i.relkind IN ('i', 'I')
96
97
  AND d.indisprimary = 'f'
97
98
  AND t.relname = #{scope[:name]}
98
99
  AND n.nspname = #{scope[:schema]}
@@ -211,7 +212,7 @@ module ActiveRecord
211
212
  end
212
213
 
213
214
  # Drops the schema for the given schema name.
214
- def drop_schema(schema_name, options = {})
215
+ def drop_schema(schema_name, **options)
215
216
  execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
216
217
  end
217
218
 
@@ -376,6 +377,8 @@ module ActiveRecord
376
377
  # rename_table('octopuses', 'octopi')
377
378
  def rename_table(table_name, new_name)
378
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)
379
382
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
380
383
  pk, seq = pk_and_sequence_for(new_name)
381
384
  if pk
@@ -390,15 +393,15 @@ module ActiveRecord
390
393
  rename_table_indexes(table_name, new_name)
391
394
  end
392
395
 
393
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
396
+ def add_column(table_name, column_name, type, **options) #:nodoc:
394
397
  clear_cache!
395
398
  super
396
399
  change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
397
400
  end
398
401
 
399
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
402
+ def change_column(table_name, column_name, type, **options) #:nodoc:
400
403
  clear_cache!
401
- sqls, procs = Array(change_column_for_alter(table_name, column_name, type, options)).partition { |v| v.is_a?(String) }
404
+ sqls, procs = Array(change_column_for_alter(table_name, column_name, type, **options)).partition { |v| v.is_a?(String) }
402
405
  execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
403
406
  procs.each(&:call)
404
407
  end
@@ -434,21 +437,24 @@ module ActiveRecord
434
437
  # Renames a column in a table.
435
438
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
436
439
  clear_cache!
437
- execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
440
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
438
441
  rename_column_indexes(table_name, column_name, new_column_name)
439
442
  end
440
443
 
441
- def add_index(table_name, column_name, options = {}) #:nodoc:
442
- index_name, index_type, index_columns_and_opclasses, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
443
- execute("CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns_and_opclasses})#{index_options}").tap do
444
- execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
445
- end
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
446
452
  end
447
453
 
448
- def remove_index(table_name, options = {}) #:nodoc:
454
+ def remove_index(table_name, column_name = nil, **options) # :nodoc:
449
455
  table = Utils.extract_schema_qualified_name(table_name.to_s)
450
456
 
451
- if options.is_a?(Hash) && options.key?(:name)
457
+ if options.key?(:name)
452
458
  provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
453
459
 
454
460
  options[:name] = provided_index.identifier
@@ -459,14 +465,11 @@ module ActiveRecord
459
465
  end
460
466
  end
461
467
 
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)}"
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)}"
470
473
  end
471
474
 
472
475
  # Renames an index of a table. Raises error if length of new
@@ -516,6 +519,28 @@ module ActiveRecord
516
519
  query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
517
520
  end
518
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
542
+ end
543
+
519
544
  # Maps logical Rails types to PostgreSQL-specific data types.
520
545
  def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
521
546
  sql = \
@@ -552,13 +577,13 @@ module ActiveRecord
552
577
  # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
553
578
  # requires that the ORDER BY include the distinct column.
554
579
  def columns_for_distinct(columns, orders) #:nodoc:
555
- order_columns = orders.reject(&:blank?).map { |s|
556
- # Convert Arel node to string
557
- s = s.to_sql unless s.is_a?(String)
558
- # Remove any ASC/DESC modifiers
559
- s.gsub(/\s+(?:ASC|DESC)\b/i, "")
560
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
561
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
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}" }
562
587
 
563
588
  (order_columns << super).join(", ")
564
589
  end
@@ -577,8 +602,6 @@ module ActiveRecord
577
602
  #
578
603
  # validate_constraint :accounts, :constraint_name
579
604
  def validate_constraint(table_name, constraint_name)
580
- return unless supports_validate_constraints?
581
-
582
605
  at = create_alter_table table_name
583
606
  at.validate_constraint constraint_name
584
607
 
@@ -601,20 +624,29 @@ module ActiveRecord
601
624
  #
602
625
  # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
603
626
  def validate_foreign_key(from_table, to_table = nil, **options)
604
- return unless supports_validate_constraints?
605
-
606
627
  fk_name_to_validate = foreign_key_for!(from_table, to_table: to_table, **options).name
607
628
 
608
629
  validate_constraint from_table, fk_name_to_validate
609
630
  end
610
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
+
611
643
  private
612
644
  def schema_creation
613
645
  PostgreSQL::SchemaCreation.new(self)
614
646
  end
615
647
 
616
- def create_table_definition(*args)
617
- PostgreSQL::TableDefinition.new(self, *args)
648
+ def create_table_definition(name, **options)
649
+ PostgreSQL::TableDefinition.new(self, name, **options)
618
650
  end
619
651
 
620
652
  def create_alter_table(name)
@@ -679,14 +711,14 @@ module ActiveRecord
679
711
  end
680
712
  end
681
713
 
682
- def add_column_for_alter(table_name, column_name, type, options = {})
714
+ def add_column_for_alter(table_name, column_name, type, **options)
683
715
  return super unless options.key?(:comment)
684
716
  [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
685
717
  end
686
718
 
687
- def change_column_for_alter(table_name, column_name, type, options = {})
719
+ def change_column_for_alter(table_name, column_name, type, **options)
688
720
  td = create_table_definition(table_name)
689
- cd = td.new_column_definition(column_name, type, options)
721
+ cd = td.new_column_definition(column_name, type, **options)
690
722
  sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
691
723
  sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
692
724
  sqls
@@ -711,20 +743,6 @@ module ActiveRecord
711
743
  "ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
712
744
  end
713
745
 
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
746
  def add_index_opclass(quoted_columns, **options)
729
747
  opclasses = options_for_index_columns(options[:opclass])
730
748
  quoted_columns.each do |name, column|
@@ -733,7 +751,7 @@ module ActiveRecord
733
751
  end
734
752
 
735
753
  def add_options_for_index_columns(quoted_columns, **options)
736
- quoted_columns = add_index_opclass(quoted_columns, options)
754
+ quoted_columns = add_index_opclass(quoted_columns, **options)
737
755
  super
738
756
  end
739
757
 
@@ -7,6 +7,8 @@ module ActiveRecord
7
7
  class TypeMetadata < DelegateClass(SqlTypeMetadata)
8
8
  undef to_yaml if method_defined?(:to_yaml)
9
9
 
10
+ include Deduplicable
11
+
10
12
  attr_reader :oid, :fmod
11
13
 
12
14
  def initialize(type_metadata, oid: nil, fmod: nil)
@@ -29,6 +31,12 @@ module ActiveRecord
29
31
  oid.hash ^
30
32
  fmod.hash
31
33
  end
34
+
35
+ private
36
+ def deduplicated
37
+ __setobj__(__getobj__.deduplicate)
38
+ super
39
+ end
32
40
  end
33
41
  end
34
42
  PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
@@ -37,7 +37,6 @@ module ActiveRecord
37
37
  end
38
38
 
39
39
  protected
40
-
41
40
  def parts
42
41
  @parts ||= [@schema, @identifier].compact
43
42
  end
@@ -1,17 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
4
- gem "pg", ">= 0.18", "< 2.0"
3
+ gem "pg", "~> 1.1"
5
4
  require "pg"
6
5
 
7
- # Use async_exec instead of exec_params on pg versions before 1.1
8
- class ::PG::Connection # :nodoc:
9
- unless self.public_method_defined?(:async_exec_params)
10
- remove_method :exec_params
11
- alias exec_params async_exec
12
- end
13
- end
14
-
6
+ require "active_support/core_ext/object/try"
15
7
  require "active_record/connection_adapters/abstract_adapter"
16
8
  require "active_record/connection_adapters/statement_pool"
17
9
  require "active_record/connection_adapters/postgresql/column"
@@ -31,9 +23,7 @@ module ActiveRecord
31
23
  module ConnectionHandling # :nodoc:
32
24
  # Establishes a connection to the database that's used by all Active Record objects
33
25
  def postgresql_connection(config)
34
- conn_params = config.symbolize_keys
35
-
36
- conn_params.delete_if { |_, v| v.nil? }
26
+ conn_params = config.symbolize_keys.compact
37
27
 
38
28
  # Map ActiveRecords param names to PGs.
39
29
  conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
@@ -43,19 +33,17 @@ module ActiveRecord
43
33
  valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
44
34
  conn_params.slice!(*valid_conn_param_keys)
45
35
 
46
- conn = PG.connect(conn_params)
47
- ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
48
- rescue ::PG::Error => error
49
- if error.message.include?(conn_params[:dbname])
50
- raise ActiveRecord::NoDatabaseError
51
- else
52
- raise
53
- end
36
+ ConnectionAdapters::PostgreSQLAdapter.new(
37
+ ConnectionAdapters::PostgreSQLAdapter.new_client(conn_params),
38
+ logger,
39
+ conn_params,
40
+ config,
41
+ )
54
42
  end
55
43
  end
56
44
 
57
45
  module ConnectionAdapters
58
- # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
46
+ # The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
59
47
  #
60
48
  # Options:
61
49
  #
@@ -85,6 +73,18 @@ module ActiveRecord
85
73
  class PostgreSQLAdapter < AbstractAdapter
86
74
  ADAPTER_NAME = "PostgreSQL"
87
75
 
76
+ class << self
77
+ def new_client(conn_params)
78
+ PG.connect(conn_params)
79
+ rescue ::PG::Error => error
80
+ if conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
81
+ raise ActiveRecord::NoDatabaseError
82
+ else
83
+ raise ActiveRecord::ConnectionNotEstablished, error.message
84
+ end
85
+ end
86
+ end
87
+
88
88
  ##
89
89
  # :singleton-method:
90
90
  # PostgreSQL allows the creation of "unlogged" tables, which do not record
@@ -156,6 +156,10 @@ module ActiveRecord
156
156
  true
157
157
  end
158
158
 
159
+ def supports_partitioned_indexes?
160
+ database_version >= 110_000
161
+ end
162
+
159
163
  def supports_partial_index?
160
164
  true
161
165
  end
@@ -172,6 +176,10 @@ module ActiveRecord
172
176
  true
173
177
  end
174
178
 
179
+ def supports_check_constraints?
180
+ true
181
+ end
182
+
175
183
  def supports_validate_constraints?
176
184
  true
177
185
  end
@@ -337,11 +345,6 @@ module ActiveRecord
337
345
  true
338
346
  end
339
347
 
340
- def supports_ranges?
341
- true
342
- end
343
- deprecate :supports_ranges?
344
-
345
348
  def supports_materialized_views?
346
349
  true
347
350
  end
@@ -422,16 +425,6 @@ module ActiveRecord
422
425
  @use_insert_returning
423
426
  end
424
427
 
425
- def column_name_for_operation(operation, node) # :nodoc:
426
- OPERATION_ALIASES.fetch(operation) { operation.downcase }
427
- end
428
-
429
- OPERATION_ALIASES = { # :nodoc:
430
- "maximum" => "max",
431
- "minimum" => "min",
432
- "average" => "avg",
433
- }
434
-
435
428
  # Returns the version of the connected PostgreSQL server.
436
429
  def get_database_version # :nodoc:
437
430
  @connection.server_version
@@ -449,6 +442,7 @@ module ActiveRecord
449
442
  sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
450
443
  elsif insert.update_duplicates?
451
444
  sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
445
+ sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
452
446
  sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
453
447
  end
454
448
 
@@ -463,7 +457,6 @@ module ActiveRecord
463
457
  end
464
458
 
465
459
  private
466
-
467
460
  # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
468
461
  VALUE_LIMIT_VIOLATION = "22001"
469
462
  NUMERIC_VALUE_OUT_OF_RANGE = "22003"
@@ -472,6 +465,7 @@ module ActiveRecord
472
465
  UNIQUE_VIOLATION = "23505"
473
466
  SERIALIZATION_FAILURE = "40001"
474
467
  DEADLOCK_DETECTED = "40P01"
468
+ DUPLICATE_DATABASE = "42P04"
475
469
  LOCK_NOT_AVAILABLE = "55P03"
476
470
  QUERY_CANCELED = "57014"
477
471
 
@@ -479,6 +473,12 @@ module ActiveRecord
479
473
  return exception unless exception.respond_to?(:result)
480
474
 
481
475
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
476
+ when nil
477
+ if exception.message.match?(/connection is closed/i)
478
+ ConnectionNotEstablished.new(exception)
479
+ else
480
+ super
481
+ end
482
482
  when UNIQUE_VIOLATION
483
483
  RecordNotUnique.new(message, sql: sql, binds: binds)
484
484
  when FOREIGN_KEY_VIOLATION
@@ -493,6 +493,8 @@ module ActiveRecord
493
493
  SerializationFailure.new(message, sql: sql, binds: binds)
494
494
  when DEADLOCK_DETECTED
495
495
  Deadlocked.new(message, sql: sql, binds: binds)
496
+ when DUPLICATE_DATABASE
497
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
496
498
  when LOCK_NOT_AVAILABLE
497
499
  LockWaitTimeout.new(message, sql: sql, binds: binds)
498
500
  when QUERY_CANCELED
@@ -544,7 +546,7 @@ module ActiveRecord
544
546
  m.register_type "uuid", OID::Uuid.new
545
547
  m.register_type "xml", OID::Xml.new
546
548
  m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
547
- m.register_type "macaddr", OID::SpecializedString.new(:macaddr)
549
+ m.register_type "macaddr", OID::Macaddr.new
548
550
  m.register_type "citext", OID::SpecializedString.new(:citext)
549
551
  m.register_type "ltree", OID::SpecializedString.new(:ltree)
550
552
  m.register_type "line", OID::SpecializedString.new(:line)
@@ -554,11 +556,6 @@ module ActiveRecord
554
556
  m.register_type "polygon", OID::SpecializedString.new(:polygon)
555
557
  m.register_type "circle", OID::SpecializedString.new(:circle)
556
558
 
557
- m.register_type "interval" do |_, _, sql_type|
558
- precision = extract_precision(sql_type)
559
- OID::SpecializedString.new(:interval, precision: precision)
560
- end
561
-
562
559
  register_class_with_precision m, "time", Type::Time
563
560
  register_class_with_precision m, "timestamp", OID::DateTime
564
561
 
@@ -582,6 +579,11 @@ module ActiveRecord
582
579
  end
583
580
  end
584
581
 
582
+ m.register_type "interval" do |*args, sql_type|
583
+ precision = extract_precision(sql_type)
584
+ OID::Interval.new(precision: precision)
585
+ end
586
+
585
587
  load_additional_types
586
588
  end
587
589
 
@@ -630,7 +632,7 @@ module ActiveRecord
630
632
  SQL
631
633
 
632
634
  if oids
633
- query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
635
+ query += "WHERE t.oid IN (%s)" % oids.join(", ")
634
636
  else
635
637
  query += initializer.query_conditions_for_initial_load
636
638
  end
@@ -654,13 +656,17 @@ module ActiveRecord
654
656
  else
655
657
  result = exec_cache(sql, name, binds)
656
658
  end
657
- ret = yield result
658
- result.clear
659
+ begin
660
+ ret = yield result
661
+ ensure
662
+ result.clear
663
+ end
659
664
  ret
660
665
  end
661
666
 
662
667
  def exec_no_cache(sql, name, binds)
663
668
  materialize_transactions
669
+ mark_transaction_written_if_write(sql)
664
670
 
665
671
  # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
666
672
  # made since we established the connection
@@ -676,6 +682,7 @@ module ActiveRecord
676
682
 
677
683
  def exec_cache(sql, name, binds)
678
684
  materialize_transactions
685
+ mark_transaction_written_if_write(sql)
679
686
  update_typemap_for_default_timezone
680
687
 
681
688
  stmt_key = prepare_statement(sql, binds)
@@ -711,11 +718,10 @@ module ActiveRecord
711
718
  #
712
719
  # Check here for more details:
713
720
  # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
714
- CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
715
721
  def is_cached_plan_failure?(e)
716
722
  pgerror = e.cause
717
- code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
718
- code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
723
+ pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
724
+ pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
719
725
  rescue
720
726
  false
721
727
  end
@@ -753,7 +759,7 @@ module ActiveRecord
753
759
  # Connects to a PostgreSQL server and sets up the adapter depending on the
754
760
  # connected server's characteristics.
755
761
  def connect
756
- @connection = PG.connect(@connection_parameters)
762
+ @connection = self.class.new_client(@connection_parameters)
757
763
  configure_connection
758
764
  add_pg_encoders
759
765
  add_pg_decoders
@@ -783,6 +789,9 @@ module ActiveRecord
783
789
  end
784
790
  end
785
791
 
792
+ # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
793
+ execute("SET intervalstyle = iso_8601", "SCHEMA")
794
+
786
795
  # SET statements from :variables config hash
787
796
  # https://www.postgresql.org/docs/current/static/sql-set.html
788
797
  variables.map do |k, v|
@@ -894,15 +903,12 @@ module ActiveRecord
894
903
  "oid" => PG::TextDecoder::Integer,
895
904
  "float4" => PG::TextDecoder::Float,
896
905
  "float8" => PG::TextDecoder::Float,
906
+ "numeric" => PG::TextDecoder::Numeric,
897
907
  "bool" => PG::TextDecoder::Boolean,
908
+ "timestamp" => PG::TextDecoder::TimestampUtc,
909
+ "timestamptz" => PG::TextDecoder::TimestampWithTimeZone,
898
910
  }
899
911
 
900
- if defined?(PG::TextDecoder::TimestampUtc)
901
- # Use native PG encoders available since pg-1.1
902
- coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
903
- coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
904
- end
905
-
906
912
  known_coder_types = coders_by_name.keys.map { |n| quote(n) }
907
913
  query = <<~SQL % known_coder_types.join(", ")
908
914
  SELECT t.oid, t.typname
@@ -919,6 +925,11 @@ module ActiveRecord
919
925
  coders.each { |coder| map.add_coder(coder) }
920
926
  @connection.type_map_for_results = map
921
927
 
928
+ @type_map_for_results = PG::TypeMapByOid.new
929
+ @type_map_for_results.default_type_map = map
930
+ @type_map_for_results.add_coder(PG::TextDecoder::Bytea.new(oid: 17, name: "bytea"))
931
+ @type_map_for_results.add_coder(MoneyDecoder.new(oid: 790, name: "money"))
932
+
922
933
  # extract timestamp decoder for use in update_typemap_for_default_timezone
923
934
  @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
924
935
  update_typemap_for_default_timezone
@@ -929,6 +940,14 @@ module ActiveRecord
929
940
  coder_class.new(oid: row["oid"].to_i, name: row["typname"])
930
941
  end
931
942
 
943
+ class MoneyDecoder < PG::SimpleDecoder # :nodoc:
944
+ TYPE = OID::Money.new
945
+
946
+ def decode(value, tuple = nil, field = nil)
947
+ TYPE.deserialize(value)
948
+ end
949
+ end
950
+
932
951
  ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
933
952
  ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
934
953
  ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
@@ -941,6 +960,7 @@ module ActiveRecord
941
960
  ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
942
961
  ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
943
962
  ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
963
+ ActiveRecord::Type.register(:interval, OID::Interval, adapter: :postgresql)
944
964
  ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
945
965
  ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
946
966
  ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)