activerecord 6.0.3 → 6.1.3

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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +968 -682
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record.rb +7 -14
  6. data/lib/active_record/aggregations.rb +5 -5
  7. data/lib/active_record/association_relation.rb +30 -12
  8. data/lib/active_record/associations.rb +118 -11
  9. data/lib/active_record/associations/alias_tracker.rb +19 -15
  10. data/lib/active_record/associations/association.rb +44 -28
  11. data/lib/active_record/associations/association_scope.rb +19 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +22 -8
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  14. data/lib/active_record/associations/builder/association.rb +32 -5
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  18. data/lib/active_record/associations/builder/has_many.rb +6 -2
  19. data/lib/active_record/associations/builder/has_one.rb +11 -14
  20. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  21. data/lib/active_record/associations/collection_association.rb +19 -6
  22. data/lib/active_record/associations/collection_proxy.rb +13 -5
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -2
  25. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  26. data/lib/active_record/associations/has_one_association.rb +15 -1
  27. data/lib/active_record/associations/join_dependency.rb +72 -50
  28. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +11 -5
  31. data/lib/active_record/associations/preloader/association.rb +51 -25
  32. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/attribute_assignment.rb +10 -8
  36. data/lib/active_record/attribute_methods.rb +64 -54
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  38. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -11
  42. data/lib/active_record/attribute_methods/serialization.rb +11 -5
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  44. data/lib/active_record/attribute_methods/write.rb +12 -20
  45. data/lib/active_record/attributes.rb +33 -8
  46. data/lib/active_record/autosave_association.rb +57 -40
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +152 -22
  49. data/lib/active_record/coders/yaml_column.rb +1 -1
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +191 -134
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +116 -27
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +54 -72
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
  64. data/lib/active_record/connection_adapters/column.rb +15 -1
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  67. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -25
  68. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  70. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
  71. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  72. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  73. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
  74. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  76. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  77. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
  80. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  82. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
  90. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  91. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  92. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  95. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  96. data/lib/active_record/connection_adapters/postgresql_adapter.rb +74 -63
  97. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  98. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  99. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
  100. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  101. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  102. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
  103. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
  104. data/lib/active_record/connection_handling.rb +218 -71
  105. data/lib/active_record/core.rb +245 -61
  106. data/lib/active_record/database_configurations.rb +124 -85
  107. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  108. data/lib/active_record/database_configurations/database_config.rb +52 -9
  109. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  110. data/lib/active_record/database_configurations/url_config.rb +15 -40
  111. data/lib/active_record/delegated_type.rb +209 -0
  112. data/lib/active_record/destroy_association_async_job.rb +36 -0
  113. data/lib/active_record/enum.rb +82 -38
  114. data/lib/active_record/errors.rb +47 -12
  115. data/lib/active_record/explain.rb +9 -4
  116. data/lib/active_record/explain_subscriber.rb +1 -1
  117. data/lib/active_record/fixture_set/file.rb +10 -17
  118. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  119. data/lib/active_record/fixture_set/render_context.rb +1 -1
  120. data/lib/active_record/fixture_set/table_row.rb +2 -2
  121. data/lib/active_record/fixtures.rb +58 -9
  122. data/lib/active_record/gem_version.rb +1 -1
  123. data/lib/active_record/inheritance.rb +40 -18
  124. data/lib/active_record/insert_all.rb +35 -6
  125. data/lib/active_record/integration.rb +3 -5
  126. data/lib/active_record/internal_metadata.rb +16 -7
  127. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  128. data/lib/active_record/locking/optimistic.rb +33 -17
  129. data/lib/active_record/locking/pessimistic.rb +6 -2
  130. data/lib/active_record/log_subscriber.rb +27 -8
  131. data/lib/active_record/middleware/database_selector.rb +4 -1
  132. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  133. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  134. data/lib/active_record/migration.rb +114 -84
  135. data/lib/active_record/migration/command_recorder.rb +47 -27
  136. data/lib/active_record/migration/compatibility.rb +68 -17
  137. data/lib/active_record/model_schema.rb +117 -13
  138. data/lib/active_record/nested_attributes.rb +2 -3
  139. data/lib/active_record/no_touching.rb +1 -1
  140. data/lib/active_record/persistence.rb +50 -45
  141. data/lib/active_record/query_cache.rb +15 -5
  142. data/lib/active_record/querying.rb +11 -6
  143. data/lib/active_record/railtie.rb +64 -44
  144. data/lib/active_record/railties/console_sandbox.rb +2 -4
  145. data/lib/active_record/railties/databases.rake +276 -99
  146. data/lib/active_record/readonly_attributes.rb +4 -0
  147. data/lib/active_record/reflection.rb +71 -57
  148. data/lib/active_record/relation.rb +95 -67
  149. data/lib/active_record/relation/batches.rb +38 -31
  150. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  151. data/lib/active_record/relation/calculations.rb +101 -44
  152. data/lib/active_record/relation/delegation.rb +2 -1
  153. data/lib/active_record/relation/finder_methods.rb +45 -15
  154. data/lib/active_record/relation/from_clause.rb +1 -1
  155. data/lib/active_record/relation/merger.rb +27 -25
  156. data/lib/active_record/relation/predicate_builder.rb +61 -38
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  158. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  159. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  160. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  161. data/lib/active_record/relation/query_methods.rb +333 -195
  162. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  163. data/lib/active_record/relation/spawn_methods.rb +8 -7
  164. data/lib/active_record/relation/where_clause.rb +107 -60
  165. data/lib/active_record/result.rb +41 -33
  166. data/lib/active_record/runtime_registry.rb +2 -2
  167. data/lib/active_record/sanitization.rb +6 -17
  168. data/lib/active_record/schema_dumper.rb +34 -4
  169. data/lib/active_record/schema_migration.rb +2 -8
  170. data/lib/active_record/scoping/named.rb +6 -17
  171. data/lib/active_record/secure_token.rb +16 -8
  172. data/lib/active_record/serialization.rb +5 -3
  173. data/lib/active_record/signed_id.rb +116 -0
  174. data/lib/active_record/statement_cache.rb +20 -4
  175. data/lib/active_record/store.rb +2 -2
  176. data/lib/active_record/suppressor.rb +2 -2
  177. data/lib/active_record/table_metadata.rb +42 -51
  178. data/lib/active_record/tasks/database_tasks.rb +140 -113
  179. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  180. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  181. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  182. data/lib/active_record/test_databases.rb +5 -4
  183. data/lib/active_record/test_fixtures.rb +37 -16
  184. data/lib/active_record/timestamp.rb +4 -6
  185. data/lib/active_record/touch_later.rb +21 -21
  186. data/lib/active_record/transactions.rb +19 -66
  187. data/lib/active_record/type.rb +8 -1
  188. data/lib/active_record/type/serialized.rb +6 -2
  189. data/lib/active_record/type/time.rb +10 -0
  190. data/lib/active_record/type_caster/connection.rb +0 -1
  191. data/lib/active_record/type_caster/map.rb +8 -5
  192. data/lib/active_record/validations.rb +1 -0
  193. data/lib/active_record/validations/numericality.rb +35 -0
  194. data/lib/active_record/validations/uniqueness.rb +24 -4
  195. data/lib/arel.rb +5 -13
  196. data/lib/arel/attributes/attribute.rb +4 -0
  197. data/lib/arel/collectors/bind.rb +5 -0
  198. data/lib/arel/collectors/composite.rb +8 -0
  199. data/lib/arel/collectors/sql_string.rb +7 -0
  200. data/lib/arel/collectors/substitute_binds.rb +7 -0
  201. data/lib/arel/nodes.rb +3 -1
  202. data/lib/arel/nodes/binary.rb +82 -8
  203. data/lib/arel/nodes/bind_param.rb +8 -0
  204. data/lib/arel/nodes/casted.rb +21 -9
  205. data/lib/arel/nodes/equality.rb +6 -9
  206. data/lib/arel/nodes/grouping.rb +3 -0
  207. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  208. data/lib/arel/nodes/in.rb +8 -1
  209. data/lib/arel/nodes/infix_operation.rb +13 -1
  210. data/lib/arel/nodes/join_source.rb +1 -1
  211. data/lib/arel/nodes/node.rb +7 -6
  212. data/lib/arel/nodes/ordering.rb +27 -0
  213. data/lib/arel/nodes/sql_literal.rb +3 -0
  214. data/lib/arel/nodes/table_alias.rb +7 -3
  215. data/lib/arel/nodes/unary.rb +0 -1
  216. data/lib/arel/predications.rb +12 -18
  217. data/lib/arel/select_manager.rb +1 -2
  218. data/lib/arel/table.rb +13 -5
  219. data/lib/arel/visitors.rb +0 -7
  220. data/lib/arel/visitors/dot.rb +14 -2
  221. data/lib/arel/visitors/mysql.rb +11 -1
  222. data/lib/arel/visitors/postgresql.rb +15 -4
  223. data/lib/arel/visitors/to_sql.rb +89 -78
  224. data/lib/rails/generators/active_record/migration.rb +6 -1
  225. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  226. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  227. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  228. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  229. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  230. metadata +28 -29
  231. data/lib/active_record/advisory_lock_base.rb +0 -18
  232. data/lib/active_record/attribute_decorators.rb +0 -88
  233. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  234. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  235. data/lib/active_record/define_callbacks.rb +0 -22
  236. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  237. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  238. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  239. data/lib/arel/attributes.rb +0 -22
  240. data/lib/arel/visitors/depth_first.rb +0 -203
  241. data/lib/arel/visitors/ibm_db.rb +0 -34
  242. data/lib/arel/visitors/informix.rb +0 -62
  243. data/lib/arel/visitors/mssql.rb +0 -156
  244. data/lib/arel/visitors/oracle.rb +0 -158
  245. data/lib/arel/visitors/oracle12.rb +0 -65
  246. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -2,151 +2,188 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
- class AbstractAdapter
6
- class SchemaCreation # :nodoc:
7
- def initialize(conn)
8
- @conn = conn
9
- @cache = {}
5
+ class SchemaCreation # :nodoc:
6
+ def initialize(conn)
7
+ @conn = conn
8
+ @cache = {}
9
+ end
10
+
11
+ def accept(o)
12
+ m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}"
13
+ send m, o
14
+ end
15
+
16
+ delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
17
+ :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options,
18
+ :quoted_columns_for_index, :supports_partial_index?, :supports_check_constraints?, :check_constraint_options,
19
+ to: :@conn, private: true
20
+
21
+ private
22
+ def visit_AlterTable(o)
23
+ sql = +"ALTER TABLE #{quote_table_name(o.name)} "
24
+ sql << o.adds.map { |col| accept col }.join(" ")
25
+ sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
26
+ sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
27
+ sql << o.check_constraint_adds.map { |con| visit_AddCheckConstraint con }.join(" ")
28
+ sql << o.check_constraint_drops.map { |con| visit_DropCheckConstraint con }.join(" ")
29
+ end
30
+
31
+ def visit_ColumnDefinition(o)
32
+ o.sql_type = type_to_sql(o.type, **o.options)
33
+ column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
34
+ add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
35
+ column_sql
10
36
  end
11
37
 
12
- def accept(o)
13
- m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}"
14
- send m, o
38
+ def visit_AddColumnDefinition(o)
39
+ +"ADD #{accept(o.column)}"
15
40
  end
16
41
 
17
- delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
18
- :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options,
19
- to: :@conn, private: true
42
+ def visit_TableDefinition(o)
43
+ create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
44
+ create_sql << "IF NOT EXISTS " if o.if_not_exists
45
+ create_sql << "#{quote_table_name(o.name)} "
20
46
 
21
- private
22
- def visit_AlterTable(o)
23
- sql = +"ALTER TABLE #{quote_table_name(o.name)} "
24
- sql << o.adds.map { |col| accept col }.join(" ")
25
- sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
26
- sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
47
+ statements = o.columns.map { |c| accept c }
48
+ statements << accept(o.primary_keys) if o.primary_keys
49
+
50
+ if supports_indexes_in_create?
51
+ statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
27
52
  end
28
53
 
29
- def visit_ColumnDefinition(o)
30
- o.sql_type = type_to_sql(o.type, **o.options)
31
- column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
32
- add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
33
- column_sql
54
+ if supports_foreign_keys?
55
+ statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
34
56
  end
35
57
 
36
- def visit_AddColumnDefinition(o)
37
- +"ADD #{accept(o.column)}"
58
+ if supports_check_constraints?
59
+ statements.concat(o.check_constraints.map { |expression, options| check_constraint_in_create(o.name, expression, options) })
38
60
  end
39
61
 
40
- def visit_TableDefinition(o)
41
- create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
42
- create_sql << "IF NOT EXISTS " if o.if_not_exists
43
- create_sql << "#{quote_table_name(o.name)} "
62
+ create_sql << "(#{statements.join(', ')})" if statements.present?
63
+ add_table_options!(create_sql, o)
64
+ create_sql << " AS #{to_sql(o.as)}" if o.as
65
+ create_sql
66
+ end
44
67
 
45
- statements = o.columns.map { |c| accept c }
46
- statements << accept(o.primary_keys) if o.primary_keys
68
+ def visit_PrimaryKeyDefinition(o)
69
+ "PRIMARY KEY (#{o.name.map { |name| quote_column_name(name) }.join(', ')})"
70
+ end
47
71
 
48
- if supports_indexes_in_create?
49
- statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
50
- end
72
+ def visit_ForeignKeyDefinition(o)
73
+ sql = +<<~SQL
74
+ CONSTRAINT #{quote_column_name(o.name)}
75
+ FOREIGN KEY (#{quote_column_name(o.column)})
76
+ REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
77
+ SQL
78
+ sql << " #{action_sql('DELETE', o.on_delete)}" if o.on_delete
79
+ sql << " #{action_sql('UPDATE', o.on_update)}" if o.on_update
80
+ sql
81
+ end
51
82
 
52
- if supports_foreign_keys?
53
- statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
54
- end
83
+ def visit_AddForeignKey(o)
84
+ "ADD #{accept(o)}"
85
+ end
55
86
 
56
- create_sql << "(#{statements.join(', ')})" if statements.present?
57
- add_table_options!(create_sql, table_options(o))
58
- create_sql << " AS #{to_sql(o.as)}" if o.as
59
- create_sql
60
- end
87
+ def visit_DropForeignKey(name)
88
+ "DROP CONSTRAINT #{quote_column_name(name)}"
89
+ end
61
90
 
62
- def visit_PrimaryKeyDefinition(o)
63
- "PRIMARY KEY (#{o.name.map { |name| quote_column_name(name) }.join(', ')})"
64
- end
91
+ def visit_CreateIndexDefinition(o)
92
+ index = o.index
93
+
94
+ sql = ["CREATE"]
95
+ sql << "UNIQUE" if index.unique
96
+ sql << "INDEX"
97
+ sql << "IF NOT EXISTS" if o.if_not_exists
98
+ sql << o.algorithm if o.algorithm
99
+ sql << index.type if index.type
100
+ sql << "#{quote_column_name(index.name)} ON #{quote_table_name(index.table)}"
101
+ sql << "USING #{index.using}" if supports_index_using? && index.using
102
+ sql << "(#{quoted_columns(index)})"
103
+ sql << "WHERE #{index.where}" if supports_partial_index? && index.where
104
+
105
+ sql.join(" ")
106
+ end
65
107
 
66
- def visit_ForeignKeyDefinition(o)
67
- sql = +<<~SQL
68
- CONSTRAINT #{quote_column_name(o.name)}
69
- FOREIGN KEY (#{quote_column_name(o.column)})
70
- REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
71
- SQL
72
- sql << " #{action_sql('DELETE', o.on_delete)}" if o.on_delete
73
- sql << " #{action_sql('UPDATE', o.on_update)}" if o.on_update
74
- sql
75
- end
108
+ def visit_CheckConstraintDefinition(o)
109
+ "CONSTRAINT #{o.name} CHECK (#{o.expression})"
110
+ end
76
111
 
77
- def visit_AddForeignKey(o)
78
- "ADD #{accept(o)}"
79
- end
112
+ def visit_AddCheckConstraint(o)
113
+ "ADD #{accept(o)}"
114
+ end
80
115
 
81
- def visit_DropForeignKey(name)
82
- "DROP CONSTRAINT #{quote_column_name(name)}"
83
- end
116
+ def visit_DropCheckConstraint(name)
117
+ "DROP CONSTRAINT #{quote_column_name(name)}"
118
+ end
84
119
 
85
- def table_options(o)
86
- table_options = {}
87
- table_options[:comment] = o.comment
88
- table_options[:options] = o.options
89
- table_options
90
- end
120
+ def quoted_columns(o)
121
+ String === o.columns ? o.columns : quoted_columns_for_index(o.columns, o.column_options)
122
+ end
91
123
 
92
- def add_table_options!(create_sql, options)
93
- if options_sql = options[:options]
94
- create_sql << " #{options_sql}"
95
- end
96
- create_sql
97
- end
124
+ def supports_index_using?
125
+ true
126
+ end
98
127
 
99
- def column_options(o)
100
- o.options.merge(column: o)
101
- end
128
+ def add_table_options!(create_sql, o)
129
+ create_sql << " #{o.options}" if o.options
130
+ create_sql
131
+ end
102
132
 
103
- def add_column_options!(sql, options)
104
- sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
105
- # must explicitly check for :null to allow change_column to work on migrations
106
- if options[:null] == false
107
- sql << " NOT NULL"
108
- end
109
- if options[:auto_increment] == true
110
- sql << " AUTO_INCREMENT"
111
- end
112
- if options[:primary_key] == true
113
- sql << " PRIMARY KEY"
114
- end
115
- sql
116
- end
133
+ def column_options(o)
134
+ o.options.merge(column: o)
135
+ end
117
136
 
118
- def to_sql(sql)
119
- sql = sql.to_sql if sql.respond_to?(:to_sql)
120
- sql
137
+ def add_column_options!(sql, options)
138
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
139
+ # must explicitly check for :null to allow change_column to work on migrations
140
+ if options[:null] == false
141
+ sql << " NOT NULL"
121
142
  end
122
-
123
- # Returns any SQL string to go between CREATE and TABLE. May be nil.
124
- def table_modifier_in_create(o)
125
- " TEMPORARY" if o.temporary
143
+ if options[:auto_increment] == true
144
+ sql << " AUTO_INCREMENT"
126
145
  end
127
-
128
- def foreign_key_in_create(from_table, to_table, options)
129
- prefix = ActiveRecord::Base.table_name_prefix
130
- suffix = ActiveRecord::Base.table_name_suffix
131
- to_table = "#{prefix}#{to_table}#{suffix}"
132
- options = foreign_key_options(from_table, to_table, options)
133
- accept ForeignKeyDefinition.new(from_table, to_table, options)
146
+ if options[:primary_key] == true
147
+ sql << " PRIMARY KEY"
134
148
  end
149
+ sql
150
+ end
135
151
 
136
- def action_sql(action, dependency)
137
- case dependency
138
- when :nullify then "ON #{action} SET NULL"
139
- when :cascade then "ON #{action} CASCADE"
140
- when :restrict then "ON #{action} RESTRICT"
141
- else
142
- raise ArgumentError, <<~MSG
143
- '#{dependency}' is not supported for :on_update or :on_delete.
144
- Supported values are: :nullify, :cascade, :restrict
145
- MSG
146
- end
152
+ def to_sql(sql)
153
+ sql = sql.to_sql if sql.respond_to?(:to_sql)
154
+ sql
155
+ end
156
+
157
+ # Returns any SQL string to go between CREATE and TABLE. May be nil.
158
+ def table_modifier_in_create(o)
159
+ " TEMPORARY" if o.temporary
160
+ end
161
+
162
+ def foreign_key_in_create(from_table, to_table, options)
163
+ prefix = ActiveRecord::Base.table_name_prefix
164
+ suffix = ActiveRecord::Base.table_name_suffix
165
+ to_table = "#{prefix}#{to_table}#{suffix}"
166
+ options = foreign_key_options(from_table, to_table, options)
167
+ accept ForeignKeyDefinition.new(from_table, to_table, options)
168
+ end
169
+
170
+ def check_constraint_in_create(table_name, expression, options)
171
+ options = check_constraint_options(table_name, expression, options)
172
+ accept CheckConstraintDefinition.new(table_name, expression, options)
173
+ end
174
+
175
+ def action_sql(action, dependency)
176
+ case dependency
177
+ when :nullify then "ON #{action} SET NULL"
178
+ when :cascade then "ON #{action} CASCADE"
179
+ when :restrict then "ON #{action} RESTRICT"
180
+ else
181
+ raise ArgumentError, <<~MSG
182
+ '#{dependency}' is not supported for :on_update or :on_delete.
183
+ Supported values are: :nullify, :cascade, :restrict
184
+ MSG
147
185
  end
148
- end
186
+ end
149
187
  end
150
- SchemaCreation = AbstractAdapter::SchemaCreation # :nodoc:
151
188
  end
152
189
  end
@@ -33,6 +33,14 @@ module ActiveRecord
33
33
  @comment = comment
34
34
  end
35
35
 
36
+ def column_options
37
+ {
38
+ length: lengths,
39
+ order: orders,
40
+ opclass: opclasses,
41
+ }
42
+ end
43
+
36
44
  private
37
45
  def concise_options(options)
38
46
  if columns.size == options.size && options.values.uniq.size == 1
@@ -63,12 +71,18 @@ module ActiveRecord
63
71
  end
64
72
  CODE
65
73
  end
74
+
75
+ def aliased_types(name, fallback)
76
+ "timestamp" == name ? :datetime : fallback
77
+ end
66
78
  end
67
79
 
68
80
  AddColumnDefinition = Struct.new(:column) # :nodoc:
69
81
 
70
82
  ChangeColumnDefinition = Struct.new(:column, :name) #:nodoc:
71
83
 
84
+ CreateIndexDefinition = Struct.new(:index, :algorithm, :if_not_exists) # :nodoc:
85
+
72
86
  PrimaryKeyDefinition = Struct.new(:name) # :nodoc:
73
87
 
74
88
  ForeignKeyDefinition = Struct.new(:from_table, :to_table, :options) do #:nodoc:
@@ -105,8 +119,9 @@ module ActiveRecord
105
119
  !ActiveRecord::SchemaDumper.fk_ignore_pattern.match?(name) if name
106
120
  end
107
121
 
108
- def defined_for?(to_table: nil, **options)
122
+ def defined_for?(to_table: nil, validate: nil, **options)
109
123
  (to_table.nil? || to_table.to_s == self.to_table) &&
124
+ (validate.nil? || validate == options.fetch(:validate, validate)) &&
110
125
  options.all? { |k, v| self.options[k].to_s == v.to_s }
111
126
  end
112
127
 
@@ -116,6 +131,21 @@ module ActiveRecord
116
131
  end
117
132
  end
118
133
 
134
+ CheckConstraintDefinition = Struct.new(:table_name, :expression, :options) do
135
+ def name
136
+ options[:name]
137
+ end
138
+
139
+ def validate?
140
+ options.fetch(:validate, true)
141
+ end
142
+ alias validated? validate?
143
+
144
+ def export_name_on_schema_dump?
145
+ !ActiveRecord::SchemaDumper.chk_ignore_pattern.match?(name) if name
146
+ end
147
+ end
148
+
119
149
  class ReferenceDefinition # :nodoc:
120
150
  def initialize(
121
151
  name,
@@ -138,13 +168,12 @@ module ActiveRecord
138
168
  end
139
169
 
140
170
  def add_to(table)
141
- columns.each do |column_options|
142
- kwargs = column_options.extract_options!
143
- table.column(*column_options, **kwargs)
171
+ columns.each do |name, type, options|
172
+ table.column(name, type, **options)
144
173
  end
145
174
 
146
175
  if index
147
- table.index(column_names, index_options)
176
+ table.index(column_names, **index_options(table.name))
148
177
  end
149
178
 
150
179
  if foreign_key
@@ -163,8 +192,14 @@ module ActiveRecord
163
192
  as_options(polymorphic).merge(options.slice(:null, :first, :after))
164
193
  end
165
194
 
166
- def index_options
167
- as_options(index)
195
+ def polymorphic_index_name(table_name)
196
+ "index_#{table_name}_on_#{name}"
197
+ end
198
+
199
+ def index_options(table_name)
200
+ index_options = as_options(index)
201
+ index_options[:name] ||= polymorphic_index_name(table_name) if polymorphic
202
+ index_options
168
203
  end
169
204
 
170
205
  def foreign_key_options
@@ -222,7 +257,7 @@ module ActiveRecord
222
257
  end
223
258
 
224
259
  class_methods do
225
- private def define_column_methods(*column_types) # :nodoc:
260
+ def define_column_methods(*column_types) # :nodoc:
226
261
  column_types.each do |column_type|
227
262
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
228
263
  def #{column_type}(*names, **options)
@@ -232,6 +267,7 @@ module ActiveRecord
232
267
  RUBY
233
268
  end
234
269
  end
270
+ private :define_column_methods
235
271
  end
236
272
  end
237
273
 
@@ -241,7 +277,7 @@ module ActiveRecord
241
277
  # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
242
278
  # is actually of this type:
243
279
  #
244
- # class SomeMigration < ActiveRecord::Migration[5.0]
280
+ # class SomeMigration < ActiveRecord::Migration[6.0]
245
281
  # def up
246
282
  # create_table :foo do |t|
247
283
  # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
@@ -256,7 +292,7 @@ module ActiveRecord
256
292
  class TableDefinition
257
293
  include ColumnMethods
258
294
 
259
- attr_reader :name, :temporary, :if_not_exists, :options, :as, :comment, :indexes, :foreign_keys
295
+ attr_reader :name, :temporary, :if_not_exists, :options, :as, :comment, :indexes, :foreign_keys, :check_constraints
260
296
 
261
297
  def initialize(
262
298
  conn,
@@ -273,6 +309,7 @@ module ActiveRecord
273
309
  @indexes = []
274
310
  @foreign_keys = []
275
311
  @primary_keys = nil
312
+ @check_constraints = []
276
313
  @temporary = temporary
277
314
  @if_not_exists = if_not_exists
278
315
  @options = options
@@ -361,7 +398,7 @@ module ActiveRecord
361
398
  # t.references :tagger, polymorphic: true
362
399
  # t.references :taggable, polymorphic: { default: 'Photo' }, index: false
363
400
  # end
364
- def column(name, type, **options)
401
+ def column(name, type, index: nil, **options)
365
402
  name = name.to_s
366
403
  type = type.to_sym if type
367
404
 
@@ -373,9 +410,13 @@ module ActiveRecord
373
410
  end
374
411
  end
375
412
 
376
- index_options = options.delete(:index)
377
- index(name, index_options.is_a?(Hash) ? index_options : {}) if index_options
378
413
  @columns_hash[name] = new_column_definition(name, type, **options)
414
+
415
+ if index
416
+ index_options = index.is_a?(Hash) ? index : {}
417
+ index(name, **index_options)
418
+ end
419
+
379
420
  self
380
421
  end
381
422
 
@@ -389,7 +430,7 @@ module ActiveRecord
389
430
  # This is primarily used to track indexes that need to be created after the table
390
431
  #
391
432
  # index(:account_id, name: 'index_projects_on_account_id')
392
- def index(column_name, options = {})
433
+ def index(column_name, **options)
393
434
  indexes << [column_name, options]
394
435
  end
395
436
 
@@ -397,6 +438,10 @@ module ActiveRecord
397
438
  foreign_keys << [table_name, options]
398
439
  end
399
440
 
441
+ def check_constraint(expression, **options)
442
+ check_constraints << [expression, options]
443
+ end
444
+
400
445
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
401
446
  # <tt>:updated_at</tt> to the table. See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
402
447
  #
@@ -456,14 +501,16 @@ module ActiveRecord
456
501
 
457
502
  class AlterTable # :nodoc:
458
503
  attr_reader :adds
459
- attr_reader :foreign_key_adds
460
- attr_reader :foreign_key_drops
504
+ attr_reader :foreign_key_adds, :foreign_key_drops
505
+ attr_reader :check_constraint_adds, :check_constraint_drops
461
506
 
462
507
  def initialize(td)
463
508
  @td = td
464
509
  @adds = []
465
510
  @foreign_key_adds = []
466
511
  @foreign_key_drops = []
512
+ @check_constraint_adds = []
513
+ @check_constraint_drops = []
467
514
  end
468
515
 
469
516
  def name; @td.name; end
@@ -476,6 +523,14 @@ module ActiveRecord
476
523
  @foreign_key_drops << name
477
524
  end
478
525
 
526
+ def add_check_constraint(expression, options)
527
+ @check_constraint_adds << CheckConstraintDefinition.new(name, expression, options)
528
+ end
529
+
530
+ def drop_check_constraint(constraint_name)
531
+ @check_constraint_drops << constraint_name
532
+ end
533
+
479
534
  def add_column(name, type, **options)
480
535
  name = name.to_s
481
536
  type = type.to_sym
@@ -496,9 +551,11 @@ module ActiveRecord
496
551
  # t.timestamps
497
552
  # t.change
498
553
  # t.change_default
554
+ # t.change_null
499
555
  # t.rename
500
556
  # t.references
501
557
  # t.belongs_to
558
+ # t.check_constraint
502
559
  # t.string
503
560
  # t.text
504
561
  # t.integer
@@ -520,6 +577,7 @@ module ActiveRecord
520
577
  # t.remove_references
521
578
  # t.remove_belongs_to
522
579
  # t.remove_index
580
+ # t.remove_check_constraint
523
581
  # t.remove_timestamps
524
582
  # end
525
583
  #
@@ -538,10 +596,12 @@ module ActiveRecord
538
596
  # t.column(:name, :string)
539
597
  #
540
598
  # See TableDefinition#column for details of the options you can use.
541
- def column(column_name, type, **options)
542
- index_options = options.delete(:index)
599
+ def column(column_name, type, index: nil, **options)
543
600
  @base.add_column(name, column_name, type, **options)
544
- index(column_name, index_options.is_a?(Hash) ? index_options : {}) if index_options
601
+ if index
602
+ index_options = index.is_a?(Hash) ? index : {}
603
+ index(column_name, **index_options)
604
+ end
545
605
  end
546
606
 
547
607
  # Checks to see if a column exists.
@@ -561,8 +621,8 @@ module ActiveRecord
561
621
  # t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')
562
622
  #
563
623
  # See {connection.add_index}[rdoc-ref:SchemaStatements#add_index] for details of the options you can use.
564
- def index(column_name, options = {})
565
- @base.add_index(name, column_name, options)
624
+ def index(column_name, **options)
625
+ @base.add_index(name, column_name, **options)
566
626
  end
567
627
 
568
628
  # Checks to see if an index exists.
@@ -600,8 +660,8 @@ module ActiveRecord
600
660
  # t.change(:description, :text)
601
661
  #
602
662
  # See TableDefinition#column for details of the options you can use.
603
- def change(column_name, type, options = {})
604
- @base.change_column(name, column_name, type, options)
663
+ def change(column_name, type, **options)
664
+ @base.change_column(name, column_name, type, **options)
605
665
  end
606
666
 
607
667
  # Sets a new default value for a column.
@@ -615,14 +675,24 @@ module ActiveRecord
615
675
  @base.change_column_default(name, column_name, default_or_changes)
616
676
  end
617
677
 
678
+ # Sets or removes a NOT NULL constraint on a column.
679
+ #
680
+ # t.change_null(:qualification, true)
681
+ # t.change_null(:qualification, false, 0)
682
+ #
683
+ # See {connection.change_column_null}[rdoc-ref:SchemaStatements#change_column_null]
684
+ def change_null(column_name, null, default = nil)
685
+ @base.change_column_null(name, column_name, null, default)
686
+ end
687
+
618
688
  # Removes the column(s) from the table definition.
619
689
  #
620
690
  # t.remove(:qualification)
621
691
  # t.remove(:qualification, :experience)
622
692
  #
623
693
  # See {connection.remove_columns}[rdoc-ref:SchemaStatements#remove_columns]
624
- def remove(*column_names)
625
- @base.remove_columns(name, *column_names)
694
+ def remove(*column_names, **options)
695
+ @base.remove_columns(name, *column_names, **options)
626
696
  end
627
697
 
628
698
  # Removes the given index from the table.
@@ -630,10 +700,11 @@ module ActiveRecord
630
700
  # t.remove_index(:branch_id)
631
701
  # t.remove_index(column: [:branch_id, :party_id])
632
702
  # t.remove_index(name: :by_branch_party)
703
+ # t.remove_index(:branch_id, name: :by_branch_party)
633
704
  #
634
705
  # See {connection.remove_index}[rdoc-ref:SchemaStatements#remove_index]
635
- def remove_index(options = {})
636
- @base.remove_index(name, options)
706
+ def remove_index(column_name = nil, **options)
707
+ @base.remove_index(name, column_name, **options)
637
708
  end
638
709
 
639
710
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
@@ -708,6 +779,24 @@ module ActiveRecord
708
779
  def foreign_key_exists?(*args, **options)
709
780
  @base.foreign_key_exists?(name, *args, **options)
710
781
  end
782
+
783
+ # Adds a check constraint.
784
+ #
785
+ # t.check_constraint("price > 0", name: "price_check")
786
+ #
787
+ # See {connection.add_check_constraint}[rdoc-ref:SchemaStatements#add_check_constraint]
788
+ def check_constraint(*args)
789
+ @base.add_check_constraint(name, *args)
790
+ end
791
+
792
+ # Removes the given check constraint from the table.
793
+ #
794
+ # t.remove_check_constraint(name: "price_check")
795
+ #
796
+ # See {connection.remove_check_constraint}[rdoc-ref:SchemaStatements#remove_check_constraint]
797
+ def remove_check_constraint(*args)
798
+ @base.remove_check_constraint(name, *args)
799
+ end
711
800
  end
712
801
  end
713
802
  end