activerecord 6.0.0.beta1 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +455 -9
  3. data/README.rdoc +3 -1
  4. data/lib/active_record/associations/association.rb +18 -1
  5. data/lib/active_record/associations/builder/association.rb +14 -18
  6. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  7. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  8. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
  9. data/lib/active_record/associations/builder/has_many.rb +2 -0
  10. data/lib/active_record/associations/builder/has_one.rb +35 -1
  11. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  12. data/lib/active_record/associations/collection_association.rb +5 -6
  13. data/lib/active_record/associations/collection_proxy.rb +13 -42
  14. data/lib/active_record/associations/has_many_association.rb +1 -9
  15. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  16. data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
  17. data/lib/active_record/associations/join_dependency.rb +10 -9
  18. data/lib/active_record/associations/preloader/association.rb +37 -34
  19. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  20. data/lib/active_record/associations/preloader.rb +11 -6
  21. data/lib/active_record/associations.rb +3 -2
  22. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  23. data/lib/active_record/attribute_methods/dirty.rb +47 -14
  24. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  25. data/lib/active_record/attribute_methods/query.rb +2 -3
  26. data/lib/active_record/attribute_methods/read.rb +3 -9
  27. data/lib/active_record/attribute_methods/write.rb +6 -12
  28. data/lib/active_record/attribute_methods.rb +3 -53
  29. data/lib/active_record/attributes.rb +13 -0
  30. data/lib/active_record/autosave_association.rb +15 -5
  31. data/lib/active_record/base.rb +0 -1
  32. data/lib/active_record/callbacks.rb +3 -3
  33. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +124 -23
  34. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  35. data/lib/active_record/connection_adapters/abstract/database_statements.rb +101 -70
  36. data/lib/active_record/connection_adapters/abstract/query_cache.rb +11 -5
  37. data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
  38. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
  39. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
  40. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
  42. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  43. data/lib/active_record/connection_adapters/abstract_adapter.rb +108 -39
  44. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +93 -134
  45. data/lib/active_record/connection_adapters/column.rb +17 -13
  46. data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
  47. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
  48. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
  49. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  50. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  51. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  52. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
  53. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  54. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
  55. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  56. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  58. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  59. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  60. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
  61. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
  62. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
  63. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
  64. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  65. data/lib/active_record/connection_adapters/postgresql_adapter.rb +91 -24
  66. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  67. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  68. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  69. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +69 -118
  72. data/lib/active_record/connection_handling.rb +32 -16
  73. data/lib/active_record/core.rb +27 -20
  74. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  75. data/lib/active_record/database_configurations/url_config.rb +21 -16
  76. data/lib/active_record/database_configurations.rb +99 -50
  77. data/lib/active_record/dynamic_matchers.rb +1 -1
  78. data/lib/active_record/enum.rb +15 -0
  79. data/lib/active_record/errors.rb +18 -13
  80. data/lib/active_record/fixtures.rb +11 -6
  81. data/lib/active_record/gem_version.rb +1 -1
  82. data/lib/active_record/inheritance.rb +1 -1
  83. data/lib/active_record/insert_all.rb +179 -0
  84. data/lib/active_record/integration.rb +13 -1
  85. data/lib/active_record/internal_metadata.rb +5 -1
  86. data/lib/active_record/locking/optimistic.rb +3 -4
  87. data/lib/active_record/log_subscriber.rb +1 -1
  88. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  89. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  90. data/lib/active_record/middleware/database_selector.rb +75 -0
  91. data/lib/active_record/migration/command_recorder.rb +28 -14
  92. data/lib/active_record/migration/compatibility.rb +72 -63
  93. data/lib/active_record/migration.rb +62 -44
  94. data/lib/active_record/persistence.rb +212 -19
  95. data/lib/active_record/querying.rb +18 -14
  96. data/lib/active_record/railtie.rb +9 -1
  97. data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
  98. data/lib/active_record/railties/databases.rake +124 -25
  99. data/lib/active_record/reflection.rb +18 -32
  100. data/lib/active_record/relation/calculations.rb +40 -44
  101. data/lib/active_record/relation/delegation.rb +23 -31
  102. data/lib/active_record/relation/finder_methods.rb +13 -13
  103. data/lib/active_record/relation/merger.rb +11 -16
  104. data/lib/active_record/relation/query_attribute.rb +5 -3
  105. data/lib/active_record/relation/query_methods.rb +217 -68
  106. data/lib/active_record/relation/spawn_methods.rb +1 -1
  107. data/lib/active_record/relation/where_clause.rb +10 -10
  108. data/lib/active_record/relation.rb +184 -35
  109. data/lib/active_record/sanitization.rb +33 -4
  110. data/lib/active_record/schema.rb +1 -1
  111. data/lib/active_record/schema_dumper.rb +10 -1
  112. data/lib/active_record/schema_migration.rb +1 -1
  113. data/lib/active_record/scoping/default.rb +7 -15
  114. data/lib/active_record/scoping/named.rb +10 -2
  115. data/lib/active_record/scoping.rb +6 -7
  116. data/lib/active_record/statement_cache.rb +2 -2
  117. data/lib/active_record/store.rb +48 -0
  118. data/lib/active_record/table_metadata.rb +9 -13
  119. data/lib/active_record/tasks/database_tasks.rb +109 -6
  120. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
  121. data/lib/active_record/test_databases.rb +1 -16
  122. data/lib/active_record/test_fixtures.rb +2 -2
  123. data/lib/active_record/timestamp.rb +35 -19
  124. data/lib/active_record/touch_later.rb +4 -2
  125. data/lib/active_record/transactions.rb +55 -45
  126. data/lib/active_record/type_caster/connection.rb +16 -10
  127. data/lib/active_record/validations/uniqueness.rb +4 -4
  128. data/lib/active_record/validations.rb +1 -0
  129. data/lib/active_record.rb +7 -1
  130. data/lib/arel/insert_manager.rb +3 -3
  131. data/lib/arel/nodes/and.rb +1 -1
  132. data/lib/arel/nodes/case.rb +1 -1
  133. data/lib/arel/nodes/comment.rb +29 -0
  134. data/lib/arel/nodes/select_core.rb +16 -12
  135. data/lib/arel/nodes/unary.rb +1 -0
  136. data/lib/arel/nodes/values_list.rb +2 -17
  137. data/lib/arel/nodes.rb +2 -1
  138. data/lib/arel/select_manager.rb +10 -10
  139. data/lib/arel/visitors/depth_first.rb +7 -2
  140. data/lib/arel/visitors/dot.rb +7 -2
  141. data/lib/arel/visitors/ibm_db.rb +13 -0
  142. data/lib/arel/visitors/informix.rb +6 -0
  143. data/lib/arel/visitors/mssql.rb +15 -1
  144. data/lib/arel/visitors/oracle12.rb +4 -5
  145. data/lib/arel/visitors/postgresql.rb +4 -10
  146. data/lib/arel/visitors/to_sql.rb +107 -131
  147. data/lib/arel/visitors/visitor.rb +9 -5
  148. data/lib/arel.rb +7 -0
  149. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  150. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  151. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  152. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  153. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  154. metadata +17 -13
  155. data/lib/active_record/collection_cache_key.rb +0 -53
  156. data/lib/arel/nodes/values.rb +0 -16
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  module ConnectionAdapters
6
6
  # An abstract definition of a column in a table.
7
7
  class Column
8
- attr_reader :name, :default, :sql_type_metadata, :null, :table_name, :default_function, :collation, :comment
8
+ attr_reader :name, :default, :sql_type_metadata, :null, :default_function, :collation, :comment
9
9
 
10
10
  delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
11
11
 
@@ -15,9 +15,8 @@ module ActiveRecord
15
15
  # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
16
16
  # +sql_type_metadata+ is various information about the type of the column
17
17
  # +null+ determines if this column allows +NULL+ values.
18
- def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil, **)
18
+ def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **)
19
19
  @name = name.freeze
20
- @table_name = table_name
21
20
  @sql_type_metadata = sql_type_metadata
22
21
  @null = null
23
22
  @default = default
@@ -44,7 +43,6 @@ module ActiveRecord
44
43
 
45
44
  def init_with(coder)
46
45
  @name = coder["name"]
47
- @table_name = coder["table_name"]
48
46
  @sql_type_metadata = coder["sql_type_metadata"]
49
47
  @null = coder["null"]
50
48
  @default = coder["default"]
@@ -55,7 +53,6 @@ module ActiveRecord
55
53
 
56
54
  def encode_with(coder)
57
55
  coder["name"] = @name
58
- coder["table_name"] = @table_name
59
56
  coder["sql_type_metadata"] = @sql_type_metadata
60
57
  coder["null"] = @null
61
58
  coder["default"] = @default
@@ -66,19 +63,26 @@ module ActiveRecord
66
63
 
67
64
  def ==(other)
68
65
  other.is_a?(Column) &&
69
- attributes_for_hash == other.attributes_for_hash
66
+ name == other.name &&
67
+ default == other.default &&
68
+ sql_type_metadata == other.sql_type_metadata &&
69
+ null == other.null &&
70
+ default_function == other.default_function &&
71
+ collation == other.collation &&
72
+ comment == other.comment
70
73
  end
71
74
  alias :eql? :==
72
75
 
73
76
  def hash
74
- attributes_for_hash.hash
77
+ Column.hash ^
78
+ name.hash ^
79
+ default.hash ^
80
+ sql_type_metadata.hash ^
81
+ null.hash ^
82
+ default_function.hash ^
83
+ collation.hash ^
84
+ comment.hash
75
85
  end
76
-
77
- protected
78
-
79
- def attributes_for_hash
80
- [self.class, name, default, sql_type_metadata, null, table_name, default_function, collation]
81
- end
82
86
  end
83
87
 
84
88
  class NullColumn < Column
@@ -222,7 +222,7 @@ module ActiveRecord
222
222
  when Hash
223
223
  resolve_hash_connection config_or_env
224
224
  else
225
- resolve_connection config_or_env
225
+ raise TypeError, "Invalid type for configuration. Expected Symbol, String, or Hash. Got #{config_or_env.inspect}"
226
226
  end
227
227
  end
228
228
 
@@ -3,9 +3,9 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module DetermineIfPreparableVisitor
6
- attr_reader :preparable
6
+ attr_accessor :preparable
7
7
 
8
- def accept(*)
8
+ def accept(object, collector)
9
9
  @preparable = true
10
10
  super
11
11
  end
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  super
21
21
  end
22
22
 
23
- def visit_Arel_Nodes_SqlLiteral(*)
23
+ def visit_Arel_Nodes_SqlLiteral(o, collector)
24
24
  @preparable = false
25
25
  super
26
26
  end
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  else
12
12
  super
13
13
  end
14
- discard_remaining_results
14
+ @connection.abandon_results!
15
15
  result
16
16
  end
17
17
 
@@ -61,7 +61,9 @@ module ActiveRecord
61
61
 
62
62
  def exec_delete(sql, name = nil, binds = [])
63
63
  if without_prepared_statement?(binds)
64
- execute_and_free(sql, name) { @connection.affected_rows }
64
+ @lock.synchronize do
65
+ execute_and_free(sql, name) { @connection.affected_rows }
66
+ end
65
67
  else
66
68
  exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
67
69
  end
@@ -69,22 +71,31 @@ module ActiveRecord
69
71
  alias :exec_update :exec_delete
70
72
 
71
73
  private
74
+ def execute_batch(sql, name = nil)
75
+ super
76
+ @connection.abandon_results!
77
+ end
78
+
72
79
  def default_insert_value(column)
73
- Arel.sql("DEFAULT") unless column.auto_increment?
80
+ super unless column.auto_increment?
74
81
  end
75
82
 
76
83
  def last_inserted_id(result)
77
84
  @connection.last_id
78
85
  end
79
86
 
80
- def discard_remaining_results
81
- @connection.abandon_results!
82
- end
83
-
84
87
  def supports_set_server_option?
85
88
  @connection.respond_to?(:set_server_option)
86
89
  end
87
90
 
91
+ def build_truncate_statements(*table_names)
92
+ if table_names.size == 1
93
+ super.first
94
+ else
95
+ super
96
+ end
97
+ end
98
+
88
99
  def multi_statements_enabled?(flags)
89
100
  if flags.is_a?(Array)
90
101
  flags.include?("MULTI_STATEMENTS")
@@ -117,6 +128,33 @@ module ActiveRecord
117
128
  end
118
129
  end
119
130
 
131
+ def combine_multi_statements(total_sql)
132
+ total_sql.each_with_object([]) do |sql, total_sql_chunks|
133
+ previous_packet = total_sql_chunks.last
134
+ if max_allowed_packet_reached?(sql, previous_packet)
135
+ total_sql_chunks << +sql
136
+ else
137
+ previous_packet << ";\n"
138
+ previous_packet << sql
139
+ end
140
+ end
141
+ end
142
+
143
+ def max_allowed_packet_reached?(current_packet, previous_packet)
144
+ if current_packet.bytesize > max_allowed_packet
145
+ raise ActiveRecordError,
146
+ "Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
147
+ elsif previous_packet.nil?
148
+ true
149
+ else
150
+ (current_packet.bytesize + previous_packet.bytesize + 2) > max_allowed_packet
151
+ end
152
+ end
153
+
154
+ def max_allowed_packet
155
+ @max_allowed_packet ||= show_variable("max_allowed_packet")
156
+ end
157
+
120
158
  def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
121
159
  if preventing_writes? && write_query?(sql)
122
160
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
@@ -5,11 +5,11 @@ module ActiveRecord
5
5
  module MySQL
6
6
  module Quoting # :nodoc:
7
7
  def quote_column_name(name)
8
- @quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
8
+ self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
9
9
  end
10
10
 
11
11
  def quote_table_name(name)
12
- @quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
12
+ self.class.quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
13
13
  end
14
14
 
15
15
  def unquoted_true
@@ -32,12 +32,49 @@ module ActiveRecord
32
32
  "x'#{value.hex}'"
33
33
  end
34
34
 
35
- def _type_cast(value)
36
- case value
37
- when Date, Time then value
38
- else super
39
- end
35
+ def column_name_matcher
36
+ COLUMN_NAME
37
+ end
38
+
39
+ def column_name_with_order_matcher
40
+ COLUMN_NAME_WITH_ORDER
40
41
  end
42
+
43
+ COLUMN_NAME = /
44
+ \A
45
+ (
46
+ (?:
47
+ # `table_name`.`column_name` | function(one or no argument)
48
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
49
+ )
50
+ (?:\s+AS\s+(?:\w+|`\w+`))?
51
+ )
52
+ (?:\s*,\s*\g<1>)*
53
+ \z
54
+ /ix
55
+
56
+ COLUMN_NAME_WITH_ORDER = /
57
+ \A
58
+ (
59
+ (?:
60
+ # `table_name`.`column_name` | function(one or no argument)
61
+ ((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
62
+ )
63
+ (?:\s+ASC|\s+DESC)?
64
+ )
65
+ (?:\s*,\s*\g<1>)*
66
+ \z
67
+ /ix
68
+
69
+ private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
70
+
71
+ private
72
+ def _type_cast(value)
73
+ case value
74
+ when Date, Time then value
75
+ else super
76
+ end
77
+ end
41
78
  end
42
79
  end
43
80
  end
@@ -4,48 +4,56 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module MySQL
6
6
  module ColumnMethods
7
- def blob(*args, **options)
8
- args.each { |name| column(name, :blob, options) }
9
- end
7
+ extend ActiveSupport::Concern
10
8
 
11
- def tinyblob(*args, **options)
12
- args.each { |name| column(name, :tinyblob, options) }
13
- end
9
+ ##
10
+ # :method: blob
11
+ # :call-seq: blob(*names, **options)
14
12
 
15
- def mediumblob(*args, **options)
16
- args.each { |name| column(name, :mediumblob, options) }
17
- end
13
+ ##
14
+ # :method: tinyblob
15
+ # :call-seq: tinyblob(*names, **options)
18
16
 
19
- def longblob(*args, **options)
20
- args.each { |name| column(name, :longblob, options) }
21
- end
17
+ ##
18
+ # :method: mediumblob
19
+ # :call-seq: mediumblob(*names, **options)
22
20
 
23
- def tinytext(*args, **options)
24
- args.each { |name| column(name, :tinytext, options) }
25
- end
21
+ ##
22
+ # :method: longblob
23
+ # :call-seq: longblob(*names, **options)
26
24
 
27
- def mediumtext(*args, **options)
28
- args.each { |name| column(name, :mediumtext, options) }
29
- end
25
+ ##
26
+ # :method: tinytext
27
+ # :call-seq: tinytext(*names, **options)
30
28
 
31
- def longtext(*args, **options)
32
- args.each { |name| column(name, :longtext, options) }
33
- end
29
+ ##
30
+ # :method: mediumtext
31
+ # :call-seq: mediumtext(*names, **options)
34
32
 
35
- def unsigned_integer(*args, **options)
36
- args.each { |name| column(name, :unsigned_integer, options) }
37
- end
33
+ ##
34
+ # :method: longtext
35
+ # :call-seq: longtext(*names, **options)
38
36
 
39
- def unsigned_bigint(*args, **options)
40
- args.each { |name| column(name, :unsigned_bigint, options) }
41
- end
37
+ ##
38
+ # :method: unsigned_integer
39
+ # :call-seq: unsigned_integer(*names, **options)
42
40
 
43
- def unsigned_float(*args, **options)
44
- args.each { |name| column(name, :unsigned_float, options) }
45
- end
41
+ ##
42
+ # :method: unsigned_bigint
43
+ # :call-seq: unsigned_bigint(*names, **options)
44
+
45
+ ##
46
+ # :method: unsigned_float
47
+ # :call-seq: unsigned_float(*names, **options)
48
+
49
+ ##
50
+ # :method: unsigned_decimal
51
+ # :call-seq: unsigned_decimal(*names, **options)
46
52
 
47
- def unsigned_decimal(*args, **options)
48
- args.each { |name| column(name, :unsigned_decimal, options) }
53
+ included do
54
+ define_column_methods :blob, :tinyblob, :mediumblob, :longblob,
55
+ :tinytext, :mediumtext, :longtext, :unsigned_integer, :unsigned_bigint,
56
+ :unsigned_float, :unsigned_decimal
49
57
  end
50
58
  end
51
59
 
@@ -10,6 +10,10 @@ module ActiveRecord
10
10
  spec[:unsigned] = "true" if column.unsigned?
11
11
  spec[:auto_increment] = "true" if column.auto_increment?
12
12
 
13
+ if /\A(?<size>tiny|medium|long)(?:text|blob)/ =~ column.sql_type
14
+ spec = { size: size.to_sym.inspect }.merge!(spec)
15
+ end
16
+
13
17
  if @connection.supports_virtual_columns? && column.virtual?
14
18
  spec[:as] = extract_expression_for_virtual_column(column)
15
19
  spec[:stored] = "true" if /\b(?:STORED|PERSISTENT)\b/.match?(column.extra)
@@ -37,19 +41,23 @@ module ActiveRecord
37
41
  case column.sql_type
38
42
  when /\Atimestamp\b/
39
43
  :timestamp
40
- when "tinyblob"
41
- :blob
44
+ when /\A(?:enum|set)\b/
45
+ column.sql_type
42
46
  else
43
47
  super
44
48
  end
45
49
  end
46
50
 
51
+ def schema_limit(column)
52
+ super unless /\A(?:enum|set|(?:tiny|medium|long)?(?:text|blob))\b/.match?(column.sql_type)
53
+ end
54
+
47
55
  def schema_precision(column)
48
56
  super unless /\A(?:date)?time(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
49
57
  end
50
58
 
51
59
  def schema_collation(column)
52
- if column.collation && table_name = column.table_name
60
+ if column.collation
53
61
  @table_collation_cache ||= {}
54
62
  @table_collation_cache[table_name] ||=
55
63
  @connection.exec_query("SHOW TABLE STATUS LIKE #{@connection.quote(table_name)}", "SCHEMA").first["Collation"]
@@ -58,14 +66,14 @@ module ActiveRecord
58
66
  end
59
67
 
60
68
  def extract_expression_for_virtual_column(column)
61
- if @connection.mariadb? && @connection.version < "10.2.5"
62
- create_table_info = @connection.send(:create_table_info, column.table_name)
69
+ if @connection.mariadb? && @connection.database_version < "10.2.5"
70
+ create_table_info = @connection.send(:create_table_info, table_name)
63
71
  column_name = @connection.quote_column_name(column.name)
64
72
  if %r/#{column_name} #{Regexp.quote(column.sql_type)}(?: COLLATE \w+)? AS \((?<expression>.+?)\) #{column.extra}/ =~ create_table_info
65
73
  $~[:expression].inspect
66
74
  end
67
75
  else
68
- scope = @connection.send(:quoted_scope, column.table_name)
76
+ scope = @connection.send(:quoted_scope, table_name)
69
77
  column_name = @connection.quote(column.name)
70
78
  sql = "SELECT generation_expression FROM information_schema.columns" \
71
79
  " WHERE table_schema = #{scope[:schema]}" \
@@ -97,14 +97,42 @@ module ActiveRecord
97
97
  MySQL::SchemaDumper.create(self, options)
98
98
  end
99
99
 
100
+ # Maps logical Rails types to MySQL-specific data types.
101
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, size: limit_to_size(limit, type), unsigned: nil, **)
102
+ sql =
103
+ case type.to_s
104
+ when "integer"
105
+ integer_to_sql(limit)
106
+ when "text"
107
+ type_with_size_to_sql("text", size)
108
+ when "blob"
109
+ type_with_size_to_sql("blob", size)
110
+ when "binary"
111
+ if (0..0xfff) === limit
112
+ "varbinary(#{limit})"
113
+ else
114
+ type_with_size_to_sql("blob", size)
115
+ end
116
+ else
117
+ super
118
+ end
119
+
120
+ sql = "#{sql} unsigned" if unsigned && type != :primary_key
121
+ sql
122
+ end
123
+
124
+ def table_alias_length
125
+ 256 # https://dev.mysql.com/doc/refman/8.0/en/identifiers.html
126
+ end
127
+
100
128
  private
101
129
  CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
102
130
 
103
131
  def row_format_dynamic_by_default?
104
132
  if mariadb?
105
- version >= "10.2.2"
133
+ database_version >= "10.2.2"
106
134
  else
107
- version >= "5.7.9"
135
+ database_version >= "5.7.9"
108
136
  end
109
137
  end
110
138
 
@@ -127,7 +155,7 @@ module ActiveRecord
127
155
  end
128
156
 
129
157
  def create_table_definition(*args)
130
- MySQL::TableDefinition.new(*args)
158
+ MySQL::TableDefinition.new(self, *args)
131
159
  end
132
160
 
133
161
  def new_column_from_field(table_name, field)
@@ -146,9 +174,8 @@ module ActiveRecord
146
174
  default,
147
175
  type_metadata,
148
176
  field[:Null] == "YES",
149
- table_name,
150
177
  default_function,
151
- field[:Collation],
178
+ collation: field[:Collation],
152
179
  comment: field[:Comment].presence
153
180
  )
154
181
  end
@@ -197,6 +224,40 @@ module ActiveRecord
197
224
  schema, name = nil, schema unless name
198
225
  [schema, name]
199
226
  end
227
+
228
+ def type_with_size_to_sql(type, size)
229
+ case size&.to_s
230
+ when nil, "tiny", "medium", "long"
231
+ "#{size}#{type}"
232
+ else
233
+ raise ArgumentError,
234
+ "#{size.inspect} is invalid :size value. Only :tiny, :medium, and :long are allowed."
235
+ end
236
+ end
237
+
238
+ def limit_to_size(limit, type)
239
+ case type.to_s
240
+ when "text", "blob", "binary"
241
+ case limit
242
+ when 0..0xff; "tiny"
243
+ when nil, 0x100..0xffff; nil
244
+ when 0x10000..0xffffff; "medium"
245
+ when 0x1000000..0xffffffff; "long"
246
+ else raise ArgumentError, "No #{type} type has byte size #{limit}"
247
+ end
248
+ end
249
+ end
250
+
251
+ def integer_to_sql(limit)
252
+ case limit
253
+ when 1; "tinyint"
254
+ when 2; "smallint"
255
+ when 3; "mediumint"
256
+ when nil, 4; "int"
257
+ when 5..8; "bigint"
258
+ else raise ArgumentError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead."
259
+ end
260
+ end
200
261
  end
201
262
  end
202
263
  end
@@ -10,25 +10,21 @@ module ActiveRecord
10
10
 
11
11
  def initialize(type_metadata, extra: "")
12
12
  super(type_metadata)
13
- @type_metadata = type_metadata
14
13
  @extra = extra
15
14
  end
16
15
 
17
16
  def ==(other)
18
- other.is_a?(MySQL::TypeMetadata) &&
19
- attributes_for_hash == other.attributes_for_hash
17
+ other.is_a?(TypeMetadata) &&
18
+ __getobj__ == other.__getobj__ &&
19
+ extra == other.extra
20
20
  end
21
21
  alias eql? ==
22
22
 
23
23
  def hash
24
- attributes_for_hash.hash
24
+ TypeMetadata.hash ^
25
+ __getobj__.hash ^
26
+ extra.hash
25
27
  end
26
-
27
- protected
28
-
29
- def attributes_for_hash
30
- [self.class, @type_metadata, extra]
31
- end
32
28
  end
33
29
  end
34
30
  end
@@ -8,6 +8,8 @@ require "mysql2"
8
8
 
9
9
  module ActiveRecord
10
10
  module ConnectionHandling # :nodoc:
11
+ ER_BAD_DB_ERROR = 1049
12
+
11
13
  # Establishes a connection to the database that's used by all Active Record objects.
12
14
  def mysql2_connection(config)
13
15
  config = config.symbolize_keys
@@ -22,7 +24,7 @@ module ActiveRecord
22
24
  client = Mysql2::Client.new(config)
23
25
  ConnectionAdapters::Mysql2Adapter.new(client, logger, nil, config)
24
26
  rescue Mysql2::Error => error
25
- if error.message.include?("Unknown database")
27
+ if error.error_number == ER_BAD_DB_ERROR
26
28
  raise ActiveRecord::NoDatabaseError
27
29
  else
28
30
  raise
@@ -37,13 +39,19 @@ module ActiveRecord
37
39
  include MySQL::DatabaseStatements
38
40
 
39
41
  def initialize(connection, logger, connection_options, config)
40
- super
41
- @prepared_statements = false unless config.key?(:prepared_statements)
42
+ superclass_config = config.reverse_merge(prepared_statements: false)
43
+ super(connection, logger, connection_options, superclass_config)
42
44
  configure_connection
43
45
  end
44
46
 
47
+ def self.database_exists?(config)
48
+ !!ActiveRecord::Base.mysql2_connection(config)
49
+ rescue ActiveRecord::NoDatabaseError
50
+ false
51
+ end
52
+
45
53
  def supports_json?
46
- !mariadb? && version >= "5.7.8"
54
+ !mariadb? && database_version >= "5.7.8"
47
55
  end
48
56
 
49
57
  def supports_comments?
@@ -109,6 +117,7 @@ module ActiveRecord
109
117
  end
110
118
 
111
119
  def discard! # :nodoc:
120
+ super
112
121
  @connection.automatic_close = false
113
122
  @connection = nil
114
123
  end
@@ -126,7 +135,11 @@ module ActiveRecord
126
135
  end
127
136
 
128
137
  def full_version
129
- @full_version ||= @connection.server_info[:version]
138
+ schema_cache.database_version.full_version_string
139
+ end
140
+
141
+ def get_full_version
142
+ @connection.server_info[:version]
130
143
  end
131
144
  end
132
145
  end
@@ -2,42 +2,29 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
- # PostgreSQL-specific extensions to column definitions in a table.
6
- class PostgreSQLColumn < Column #:nodoc:
7
- delegate :array, :oid, :fmod, to: :sql_type_metadata
8
- alias :array? :array
5
+ module PostgreSQL
6
+ class Column < ConnectionAdapters::Column # :nodoc:
7
+ delegate :oid, :fmod, to: :sql_type_metadata
9
8
 
10
- def initialize(*, max_identifier_length: 63, **)
11
- super
12
- @max_identifier_length = max_identifier_length
13
- end
14
-
15
- def serial?
16
- return unless default_function
17
-
18
- if %r{\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z} =~ default_function
19
- sequence_name_from_parts(table_name, name, suffix) == sequence_name
9
+ def initialize(*, serial: nil, **)
10
+ super
11
+ @serial = serial
20
12
  end
21
- end
22
-
23
- private
24
- attr_reader :max_identifier_length
25
13
 
26
- def sequence_name_from_parts(table_name, column_name, suffix)
27
- over_length = [table_name, column_name, suffix].map(&:length).sum + 2 - max_identifier_length
28
-
29
- if over_length > 0
30
- column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
31
- over_length -= column_name.length - column_name_length
32
- column_name = column_name[0, column_name_length - [over_length, 0].min]
33
- end
14
+ def serial?
15
+ @serial
16
+ end
34
17
 
35
- if over_length > 0
36
- table_name = table_name[0, table_name.length - over_length]
37
- end
18
+ def array
19
+ sql_type_metadata.sql_type.end_with?("[]")
20
+ end
21
+ alias :array? :array
38
22
 
39
- "#{table_name}_#{column_name}_#{suffix}"
23
+ def sql_type
24
+ super.sub(/\[\]\z/, "")
40
25
  end
26
+ end
41
27
  end
28
+ PostgreSQLColumn = PostgreSQL::Column # :nodoc:
42
29
  end
43
30
  end