activerecord 6.0.6.1 → 6.1.0.rc1

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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +764 -942
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +22 -14
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +39 -27
  9. data/lib/active_record/associations/association_scope.rb +11 -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 -1
  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 +19 -13
  20. data/lib/active_record/associations/collection_proxy.rb +12 -5
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -2
  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 +29 -14
  26. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +63 -49
  28. data/lib/active_record/associations/preloader/association.rb +13 -5
  29. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  30. data/lib/active_record/associations/preloader.rb +5 -3
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations.rb +114 -11
  33. data/lib/active_record/attribute_assignment.rb +10 -8
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  35. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  36. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  37. data/lib/active_record/attribute_methods/query.rb +3 -6
  38. data/lib/active_record/attribute_methods/read.rb +8 -11
  39. data/lib/active_record/attribute_methods/serialization.rb +4 -4
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  41. data/lib/active_record/attribute_methods/write.rb +12 -20
  42. data/lib/active_record/attribute_methods.rb +52 -48
  43. data/lib/active_record/attributes.rb +27 -7
  44. data/lib/active_record/autosave_association.rb +47 -30
  45. data/lib/active_record/base.rb +2 -14
  46. data/lib/active_record/callbacks.rb +32 -22
  47. data/lib/active_record/coders/yaml_column.rb +2 -24
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +180 -134
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +35 -44
  53. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +110 -30
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +31 -70
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
  61. data/lib/active_record/connection_adapters/column.rb +15 -1
  62. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  63. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
  65. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  69. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
  71. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  73. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -53
  77. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  78. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -10
  80. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  91. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  93. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  94. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  95. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
  96. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  98. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  99. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  100. data/lib/active_record/connection_adapters.rb +50 -0
  101. data/lib/active_record/connection_handling.rb +210 -71
  102. data/lib/active_record/core.rb +214 -58
  103. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  104. data/lib/active_record/database_configurations/database_config.rb +52 -9
  105. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  106. data/lib/active_record/database_configurations/url_config.rb +15 -40
  107. data/lib/active_record/database_configurations.rb +124 -85
  108. data/lib/active_record/delegated_type.rb +209 -0
  109. data/lib/active_record/destroy_association_async_job.rb +36 -0
  110. data/lib/active_record/enum.rb +33 -23
  111. data/lib/active_record/errors.rb +47 -12
  112. data/lib/active_record/explain.rb +9 -4
  113. data/lib/active_record/explain_subscriber.rb +1 -1
  114. data/lib/active_record/fixture_set/file.rb +10 -17
  115. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  116. data/lib/active_record/fixture_set/render_context.rb +1 -1
  117. data/lib/active_record/fixture_set/table_row.rb +2 -2
  118. data/lib/active_record/fixtures.rb +54 -8
  119. data/lib/active_record/gem_version.rb +3 -3
  120. data/lib/active_record/inheritance.rb +40 -18
  121. data/lib/active_record/insert_all.rb +32 -5
  122. data/lib/active_record/integration.rb +3 -5
  123. data/lib/active_record/internal_metadata.rb +15 -4
  124. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  125. data/lib/active_record/locking/optimistic.rb +13 -16
  126. data/lib/active_record/locking/pessimistic.rb +6 -2
  127. data/lib/active_record/log_subscriber.rb +26 -8
  128. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  129. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  130. data/lib/active_record/middleware/database_selector.rb +4 -1
  131. data/lib/active_record/migration/command_recorder.rb +47 -27
  132. data/lib/active_record/migration/compatibility.rb +67 -17
  133. data/lib/active_record/migration.rb +113 -83
  134. data/lib/active_record/model_schema.rb +88 -42
  135. data/lib/active_record/nested_attributes.rb +2 -3
  136. data/lib/active_record/no_touching.rb +1 -1
  137. data/lib/active_record/persistence.rb +50 -45
  138. data/lib/active_record/query_cache.rb +15 -5
  139. data/lib/active_record/querying.rb +11 -6
  140. data/lib/active_record/railtie.rb +64 -44
  141. data/lib/active_record/railties/databases.rake +253 -98
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +59 -44
  144. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  145. data/lib/active_record/relation/batches.rb +38 -31
  146. data/lib/active_record/relation/calculations.rb +100 -43
  147. data/lib/active_record/relation/finder_methods.rb +44 -14
  148. data/lib/active_record/relation/from_clause.rb +1 -1
  149. data/lib/active_record/relation/merger.rb +20 -23
  150. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  151. data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -2
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +57 -33
  155. data/lib/active_record/relation/query_methods.rb +319 -198
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +6 -5
  158. data/lib/active_record/relation/where_clause.rb +104 -57
  159. data/lib/active_record/relation.rb +90 -64
  160. data/lib/active_record/result.rb +41 -33
  161. data/lib/active_record/runtime_registry.rb +2 -2
  162. data/lib/active_record/sanitization.rb +6 -17
  163. data/lib/active_record/schema_dumper.rb +34 -4
  164. data/lib/active_record/schema_migration.rb +0 -4
  165. data/lib/active_record/scoping/named.rb +1 -17
  166. data/lib/active_record/secure_token.rb +16 -8
  167. data/lib/active_record/serialization.rb +5 -3
  168. data/lib/active_record/signed_id.rb +116 -0
  169. data/lib/active_record/statement_cache.rb +20 -4
  170. data/lib/active_record/store.rb +2 -2
  171. data/lib/active_record/suppressor.rb +2 -2
  172. data/lib/active_record/table_metadata.rb +36 -52
  173. data/lib/active_record/tasks/database_tasks.rb +139 -113
  174. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  175. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  176. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  177. data/lib/active_record/test_databases.rb +5 -4
  178. data/lib/active_record/test_fixtures.rb +36 -33
  179. data/lib/active_record/timestamp.rb +4 -6
  180. data/lib/active_record/touch_later.rb +21 -21
  181. data/lib/active_record/transactions.rb +15 -64
  182. data/lib/active_record/type/serialized.rb +6 -2
  183. data/lib/active_record/type.rb +8 -1
  184. data/lib/active_record/type_caster/connection.rb +0 -1
  185. data/lib/active_record/type_caster/map.rb +8 -5
  186. data/lib/active_record/validations/associated.rb +1 -1
  187. data/lib/active_record/validations/numericality.rb +35 -0
  188. data/lib/active_record/validations/uniqueness.rb +24 -4
  189. data/lib/active_record/validations.rb +1 -0
  190. data/lib/active_record.rb +7 -14
  191. data/lib/arel/attributes/attribute.rb +4 -0
  192. data/lib/arel/collectors/bind.rb +5 -0
  193. data/lib/arel/collectors/composite.rb +8 -0
  194. data/lib/arel/collectors/sql_string.rb +7 -0
  195. data/lib/arel/collectors/substitute_binds.rb +7 -0
  196. data/lib/arel/nodes/binary.rb +82 -8
  197. data/lib/arel/nodes/bind_param.rb +8 -0
  198. data/lib/arel/nodes/casted.rb +21 -9
  199. data/lib/arel/nodes/equality.rb +6 -9
  200. data/lib/arel/nodes/grouping.rb +3 -0
  201. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  202. data/lib/arel/nodes/in.rb +8 -1
  203. data/lib/arel/nodes/infix_operation.rb +13 -1
  204. data/lib/arel/nodes/join_source.rb +1 -1
  205. data/lib/arel/nodes/node.rb +7 -6
  206. data/lib/arel/nodes/ordering.rb +27 -0
  207. data/lib/arel/nodes/sql_literal.rb +3 -0
  208. data/lib/arel/nodes/table_alias.rb +7 -3
  209. data/lib/arel/nodes/unary.rb +0 -1
  210. data/lib/arel/nodes.rb +3 -1
  211. data/lib/arel/predications.rb +12 -18
  212. data/lib/arel/select_manager.rb +1 -2
  213. data/lib/arel/table.rb +13 -5
  214. data/lib/arel/visitors/dot.rb +14 -2
  215. data/lib/arel/visitors/mysql.rb +11 -1
  216. data/lib/arel/visitors/postgresql.rb +15 -4
  217. data/lib/arel/visitors/to_sql.rb +89 -78
  218. data/lib/arel/visitors.rb +0 -7
  219. data/lib/arel.rb +5 -13
  220. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  221. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  222. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  223. data/lib/rails/generators/active_record/migration.rb +6 -1
  224. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  225. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  226. metadata +30 -32
  227. data/lib/active_record/advisory_lock_base.rb +0 -18
  228. data/lib/active_record/attribute_decorators.rb +0 -88
  229. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  230. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  231. data/lib/active_record/define_callbacks.rb +0 -22
  232. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  233. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  234. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  235. data/lib/arel/attributes.rb +0 -22
  236. data/lib/arel/visitors/depth_first.rb +0 -203
  237. data/lib/arel/visitors/ibm_db.rb +0 -34
  238. data/lib/arel/visitors/informix.rb +0 -62
  239. data/lib/arel/visitors/mssql.rb +0 -156
  240. data/lib/arel/visitors/oracle.rb +0 -158
  241. data/lib/arel/visitors/oracle12.rb +0 -65
  242. 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,16 +71,14 @@ module ActiveRecord
63
71
  end
64
72
  CODE
65
73
  end
66
-
67
- def aliased_types(name, fallback)
68
- "timestamp" == name ? :datetime : fallback
69
- end
70
74
  end
71
75
 
72
76
  AddColumnDefinition = Struct.new(:column) # :nodoc:
73
77
 
74
78
  ChangeColumnDefinition = Struct.new(:column, :name) #:nodoc:
75
79
 
80
+ CreateIndexDefinition = Struct.new(:index, :algorithm, :if_not_exists) # :nodoc:
81
+
76
82
  PrimaryKeyDefinition = Struct.new(:name) # :nodoc:
77
83
 
78
84
  ForeignKeyDefinition = Struct.new(:from_table, :to_table, :options) do #:nodoc:
@@ -121,6 +127,21 @@ module ActiveRecord
121
127
  end
122
128
  end
123
129
 
130
+ CheckConstraintDefinition = Struct.new(:table_name, :expression, :options) do
131
+ def name
132
+ options[:name]
133
+ end
134
+
135
+ def validate?
136
+ options.fetch(:validate, true)
137
+ end
138
+ alias validated? validate?
139
+
140
+ def export_name_on_schema_dump?
141
+ !ActiveRecord::SchemaDumper.chk_ignore_pattern.match?(name) if name
142
+ end
143
+ end
144
+
124
145
  class ReferenceDefinition # :nodoc:
125
146
  def initialize(
126
147
  name,
@@ -143,13 +164,12 @@ module ActiveRecord
143
164
  end
144
165
 
145
166
  def add_to(table)
146
- columns.each do |column_options|
147
- kwargs = column_options.extract_options!
148
- table.column(*column_options, **kwargs)
167
+ columns.each do |name, type, options|
168
+ table.column(name, type, **options)
149
169
  end
150
170
 
151
171
  if index
152
- table.index(column_names, index_options)
172
+ table.index(column_names, **index_options(table.name))
153
173
  end
154
174
 
155
175
  if foreign_key
@@ -168,8 +188,14 @@ module ActiveRecord
168
188
  as_options(polymorphic).merge(options.slice(:null, :first, :after))
169
189
  end
170
190
 
171
- def index_options
172
- as_options(index)
191
+ def polymorphic_index_name(table_name)
192
+ "index_#{table_name}_on_#{name}"
193
+ end
194
+
195
+ def index_options(table_name)
196
+ index_options = as_options(index)
197
+ index_options[:name] ||= polymorphic_index_name(table_name) if polymorphic
198
+ index_options
173
199
  end
174
200
 
175
201
  def foreign_key_options
@@ -227,7 +253,7 @@ module ActiveRecord
227
253
  end
228
254
 
229
255
  class_methods do
230
- private def define_column_methods(*column_types) # :nodoc:
256
+ def define_column_methods(*column_types) # :nodoc:
231
257
  column_types.each do |column_type|
232
258
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
233
259
  def #{column_type}(*names, **options)
@@ -237,6 +263,7 @@ module ActiveRecord
237
263
  RUBY
238
264
  end
239
265
  end
266
+ private :define_column_methods
240
267
  end
241
268
  end
242
269
 
@@ -246,7 +273,7 @@ module ActiveRecord
246
273
  # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
247
274
  # is actually of this type:
248
275
  #
249
- # class SomeMigration < ActiveRecord::Migration[5.0]
276
+ # class SomeMigration < ActiveRecord::Migration[6.0]
250
277
  # def up
251
278
  # create_table :foo do |t|
252
279
  # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
@@ -261,7 +288,7 @@ module ActiveRecord
261
288
  class TableDefinition
262
289
  include ColumnMethods
263
290
 
264
- attr_reader :name, :temporary, :if_not_exists, :options, :as, :comment, :indexes, :foreign_keys
291
+ attr_reader :name, :temporary, :if_not_exists, :options, :as, :comment, :indexes, :foreign_keys, :check_constraints
265
292
 
266
293
  def initialize(
267
294
  conn,
@@ -278,6 +305,7 @@ module ActiveRecord
278
305
  @indexes = []
279
306
  @foreign_keys = []
280
307
  @primary_keys = nil
308
+ @check_constraints = []
281
309
  @temporary = temporary
282
310
  @if_not_exists = if_not_exists
283
311
  @options = options
@@ -366,7 +394,7 @@ module ActiveRecord
366
394
  # t.references :tagger, polymorphic: true
367
395
  # t.references :taggable, polymorphic: { default: 'Photo' }, index: false
368
396
  # end
369
- def column(name, type, **options)
397
+ def column(name, type, index: nil, **options)
370
398
  name = name.to_s
371
399
  type = type.to_sym if type
372
400
 
@@ -378,9 +406,13 @@ module ActiveRecord
378
406
  end
379
407
  end
380
408
 
381
- index_options = options.delete(:index)
382
- index(name, index_options.is_a?(Hash) ? index_options : {}) if index_options
383
409
  @columns_hash[name] = new_column_definition(name, type, **options)
410
+
411
+ if index
412
+ index_options = index.is_a?(Hash) ? index : {}
413
+ index(name, **index_options)
414
+ end
415
+
384
416
  self
385
417
  end
386
418
 
@@ -394,7 +426,7 @@ module ActiveRecord
394
426
  # This is primarily used to track indexes that need to be created after the table
395
427
  #
396
428
  # index(:account_id, name: 'index_projects_on_account_id')
397
- def index(column_name, options = {})
429
+ def index(column_name, **options)
398
430
  indexes << [column_name, options]
399
431
  end
400
432
 
@@ -402,6 +434,10 @@ module ActiveRecord
402
434
  foreign_keys << [table_name, options]
403
435
  end
404
436
 
437
+ def check_constraint(expression, **options)
438
+ check_constraints << [expression, options]
439
+ end
440
+
405
441
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
406
442
  # <tt>:updated_at</tt> to the table. See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
407
443
  #
@@ -461,14 +497,16 @@ module ActiveRecord
461
497
 
462
498
  class AlterTable # :nodoc:
463
499
  attr_reader :adds
464
- attr_reader :foreign_key_adds
465
- attr_reader :foreign_key_drops
500
+ attr_reader :foreign_key_adds, :foreign_key_drops
501
+ attr_reader :check_constraint_adds, :check_constraint_drops
466
502
 
467
503
  def initialize(td)
468
504
  @td = td
469
505
  @adds = []
470
506
  @foreign_key_adds = []
471
507
  @foreign_key_drops = []
508
+ @check_constraint_adds = []
509
+ @check_constraint_drops = []
472
510
  end
473
511
 
474
512
  def name; @td.name; end
@@ -481,6 +519,14 @@ module ActiveRecord
481
519
  @foreign_key_drops << name
482
520
  end
483
521
 
522
+ def add_check_constraint(expression, options)
523
+ @check_constraint_adds << CheckConstraintDefinition.new(name, expression, options)
524
+ end
525
+
526
+ def drop_check_constraint(constraint_name)
527
+ @check_constraint_drops << constraint_name
528
+ end
529
+
484
530
  def add_column(name, type, **options)
485
531
  name = name.to_s
486
532
  type = type.to_sym
@@ -501,9 +547,11 @@ module ActiveRecord
501
547
  # t.timestamps
502
548
  # t.change
503
549
  # t.change_default
550
+ # t.change_null
504
551
  # t.rename
505
552
  # t.references
506
553
  # t.belongs_to
554
+ # t.check_constraint
507
555
  # t.string
508
556
  # t.text
509
557
  # t.integer
@@ -525,6 +573,7 @@ module ActiveRecord
525
573
  # t.remove_references
526
574
  # t.remove_belongs_to
527
575
  # t.remove_index
576
+ # t.remove_check_constraint
528
577
  # t.remove_timestamps
529
578
  # end
530
579
  #
@@ -543,10 +592,12 @@ module ActiveRecord
543
592
  # t.column(:name, :string)
544
593
  #
545
594
  # See TableDefinition#column for details of the options you can use.
546
- def column(column_name, type, **options)
547
- index_options = options.delete(:index)
595
+ def column(column_name, type, index: nil, **options)
548
596
  @base.add_column(name, column_name, type, **options)
549
- index(column_name, index_options.is_a?(Hash) ? index_options : {}) if index_options
597
+ if index
598
+ index_options = index.is_a?(Hash) ? index : {}
599
+ index(column_name, **index_options)
600
+ end
550
601
  end
551
602
 
552
603
  # Checks to see if a column exists.
@@ -566,8 +617,8 @@ module ActiveRecord
566
617
  # t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')
567
618
  #
568
619
  # See {connection.add_index}[rdoc-ref:SchemaStatements#add_index] for details of the options you can use.
569
- def index(column_name, options = {})
570
- @base.add_index(name, column_name, options)
620
+ def index(column_name, **options)
621
+ @base.add_index(name, column_name, **options)
571
622
  end
572
623
 
573
624
  # Checks to see if an index exists.
@@ -605,8 +656,8 @@ module ActiveRecord
605
656
  # t.change(:description, :text)
606
657
  #
607
658
  # See TableDefinition#column for details of the options you can use.
608
- def change(column_name, type, options = {})
609
- @base.change_column(name, column_name, type, options)
659
+ def change(column_name, type, **options)
660
+ @base.change_column(name, column_name, type, **options)
610
661
  end
611
662
 
612
663
  # Sets a new default value for a column.
@@ -620,14 +671,24 @@ module ActiveRecord
620
671
  @base.change_column_default(name, column_name, default_or_changes)
621
672
  end
622
673
 
674
+ # Sets or removes a NOT NULL constraint on a column.
675
+ #
676
+ # t.change_null(:qualification, true)
677
+ # t.change_null(:qualification, false, 0)
678
+ #
679
+ # See {connection.change_column_null}[rdoc-ref:SchemaStatements#change_column_null]
680
+ def change_null(column_name, null, default = nil)
681
+ @base.change_column_null(name, column_name, null, default)
682
+ end
683
+
623
684
  # Removes the column(s) from the table definition.
624
685
  #
625
686
  # t.remove(:qualification)
626
687
  # t.remove(:qualification, :experience)
627
688
  #
628
689
  # See {connection.remove_columns}[rdoc-ref:SchemaStatements#remove_columns]
629
- def remove(*column_names)
630
- @base.remove_columns(name, *column_names)
690
+ def remove(*column_names, **options)
691
+ @base.remove_columns(name, *column_names, **options)
631
692
  end
632
693
 
633
694
  # Removes the given index from the table.
@@ -635,10 +696,11 @@ module ActiveRecord
635
696
  # t.remove_index(:branch_id)
636
697
  # t.remove_index(column: [:branch_id, :party_id])
637
698
  # t.remove_index(name: :by_branch_party)
699
+ # t.remove_index(:branch_id, name: :by_branch_party)
638
700
  #
639
701
  # See {connection.remove_index}[rdoc-ref:SchemaStatements#remove_index]
640
- def remove_index(options = {})
641
- @base.remove_index(name, options)
702
+ def remove_index(column_name = nil, **options)
703
+ @base.remove_index(name, column_name, **options)
642
704
  end
643
705
 
644
706
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
@@ -713,6 +775,24 @@ module ActiveRecord
713
775
  def foreign_key_exists?(*args, **options)
714
776
  @base.foreign_key_exists?(name, *args, **options)
715
777
  end
778
+
779
+ # Adds a check constraint.
780
+ #
781
+ # t.check_constraint("price > 0", name: "price_check")
782
+ #
783
+ # See {connection.add_check_constraint}[rdoc-ref:SchemaStatements#add_check_constraint]
784
+ def check_constraint(*args)
785
+ @base.add_check_constraint(name, *args)
786
+ end
787
+
788
+ # Removes the given check constraint from the table.
789
+ #
790
+ # t.remove_check_constraint(name: "price_check")
791
+ #
792
+ # See {connection.remove_check_constraint}[rdoc-ref:SchemaStatements#remove_check_constraint]
793
+ def remove_check_constraint(*args)
794
+ @base.remove_check_constraint(name, *args)
795
+ end
716
796
  end
717
797
  end
718
798
  end
@@ -13,9 +13,9 @@ module ActiveRecord
13
13
  end
14
14
 
15
15
  def column_spec_for_primary_key(column)
16
- return {} if default_primary_key?(column)
17
- spec = { id: schema_type(column).inspect }
18
- spec.merge!(prepare_column_options(column).except!(:null, :comment))
16
+ spec = {}
17
+ spec[:id] = schema_type(column).inspect unless default_primary_key?(column)
18
+ spec.merge!(prepare_column_options(column).except!(:null))
19
19
  spec[:default] ||= "nil" if explicit_primary_key_default?(column)
20
20
  spec
21
21
  end