activerecord 7.2.0 → 8.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +189 -745
- data/README.rdoc +1 -1
- data/lib/active_record/associations/association.rb +25 -5
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +10 -8
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +5 -5
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +34 -4
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +9 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
- data/lib/active_record/attributes.rb +6 -5
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +23 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +53 -18
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +26 -5
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -25
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +20 -38
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +44 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +50 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +38 -90
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -12
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
- data/lib/active_record/connection_handling.rb +22 -0
- data/lib/active_record/core.rb +16 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +12 -3
- data/lib/active_record/encryption/encryptor.rb +16 -9
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/encryption.rb +2 -0
- data/lib/active_record/enum.rb +8 -0
- data/lib/active_record/errors.rb +13 -5
- data/lib/active_record/fixtures.rb +0 -1
- data/lib/active_record/future_result.rb +14 -10
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/insert_all.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +22 -5
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +35 -33
- data/lib/active_record/model_schema.rb +6 -3
- data/lib/active_record/nested_attributes.rb +11 -2
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_logs.rb +97 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +6 -6
- data/lib/active_record/railtie.rb +8 -14
- data/lib/active_record/reflection.rb +19 -10
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +135 -75
- data/lib/active_record/relation/calculations.rb +24 -19
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +18 -18
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +6 -1
- data/lib/active_record/relation/query_methods.rb +58 -37
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation.rb +72 -61
- data/lib/active_record/result.rb +68 -7
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +5 -0
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/statement_cache.rb +12 -12
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +36 -16
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +9 -8
- data/lib/active_record.rb +15 -0
- data/lib/arel/collectors/bind.rb +1 -1
- metadata +14 -14
@@ -576,23 +576,31 @@ module ActiveRecord
|
|
576
576
|
end
|
577
577
|
|
578
578
|
# This is meant to be implemented by the adapters that support custom enum types
|
579
|
-
def create_enum(
|
579
|
+
def create_enum(...) # :nodoc:
|
580
580
|
end
|
581
581
|
|
582
582
|
# This is meant to be implemented by the adapters that support custom enum types
|
583
|
-
def drop_enum(
|
583
|
+
def drop_enum(...) # :nodoc:
|
584
584
|
end
|
585
585
|
|
586
586
|
# This is meant to be implemented by the adapters that support custom enum types
|
587
|
-
def rename_enum(
|
587
|
+
def rename_enum(...) # :nodoc:
|
588
588
|
end
|
589
589
|
|
590
590
|
# This is meant to be implemented by the adapters that support custom enum types
|
591
|
-
def add_enum_value(
|
591
|
+
def add_enum_value(...) # :nodoc:
|
592
592
|
end
|
593
593
|
|
594
594
|
# This is meant to be implemented by the adapters that support custom enum types
|
595
|
-
def rename_enum_value(
|
595
|
+
def rename_enum_value(...) # :nodoc:
|
596
|
+
end
|
597
|
+
|
598
|
+
# This is meant to be implemented by the adapters that support virtual tables
|
599
|
+
def create_virtual_table(*) # :nodoc:
|
600
|
+
end
|
601
|
+
|
602
|
+
# This is meant to be implemented by the adapters that support virtual tables
|
603
|
+
def drop_virtual_table(*) # :nodoc:
|
596
604
|
end
|
597
605
|
|
598
606
|
def advisory_locks_enabled? # :nodoc:
|
@@ -1044,7 +1052,8 @@ module ActiveRecord
|
|
1044
1052
|
end
|
1045
1053
|
|
1046
1054
|
def retryable_connection_error?(exception)
|
1047
|
-
exception.is_a?(ConnectionNotEstablished)
|
1055
|
+
(exception.is_a?(ConnectionNotEstablished) && !exception.is_a?(ConnectionNotDefined)) ||
|
1056
|
+
exception.is_a?(ConnectionFailed)
|
1048
1057
|
end
|
1049
1058
|
|
1050
1059
|
def invalidate_transaction(exception)
|
@@ -1105,24 +1114,25 @@ module ActiveRecord
|
|
1105
1114
|
end
|
1106
1115
|
end
|
1107
1116
|
|
1108
|
-
def translate_exception_class(
|
1109
|
-
|
1117
|
+
def translate_exception_class(native_error, sql, binds)
|
1118
|
+
return native_error if native_error.is_a?(ActiveRecordError)
|
1119
|
+
|
1120
|
+
message = "#{native_error.class.name}: #{native_error.message}"
|
1110
1121
|
|
1111
|
-
|
1112
|
-
|
1122
|
+
active_record_error = translate_exception(
|
1123
|
+
native_error, message: message, sql: sql, binds: binds
|
1113
1124
|
)
|
1114
|
-
|
1115
|
-
|
1125
|
+
active_record_error.set_backtrace(native_error.backtrace)
|
1126
|
+
active_record_error
|
1116
1127
|
end
|
1117
1128
|
|
1118
|
-
def log(sql, name = "SQL", binds = [], type_casted_binds = [],
|
1129
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
|
1119
1130
|
@instrumenter.instrument(
|
1120
1131
|
"sql.active_record",
|
1121
1132
|
sql: sql,
|
1122
1133
|
name: name,
|
1123
1134
|
binds: binds,
|
1124
1135
|
type_casted_binds: type_casted_binds,
|
1125
|
-
statement_name: statement_name,
|
1126
1136
|
async: async,
|
1127
1137
|
connection: self,
|
1128
1138
|
transaction: current_transaction.user_transaction.presence,
|
@@ -1133,13 +1143,6 @@ module ActiveRecord
|
|
1133
1143
|
raise ex.set_query(sql, binds)
|
1134
1144
|
end
|
1135
1145
|
|
1136
|
-
def transform_query(sql)
|
1137
|
-
ActiveRecord.query_transformers.each do |transformer|
|
1138
|
-
sql = transformer.call(sql, self)
|
1139
|
-
end
|
1140
|
-
sql
|
1141
|
-
end
|
1142
|
-
|
1143
1146
|
def translate_exception(exception, message:, sql:, binds:)
|
1144
1147
|
# override in derived class
|
1145
1148
|
case exception
|
@@ -1150,10 +1153,6 @@ module ActiveRecord
|
|
1150
1153
|
end
|
1151
1154
|
end
|
1152
1155
|
|
1153
|
-
def without_prepared_statement?(binds)
|
1154
|
-
!prepared_statements || binds.empty?
|
1155
|
-
end
|
1156
|
-
|
1157
1156
|
def column_for(table_name, column_name)
|
1158
1157
|
column_name = column_name.to_s
|
1159
1158
|
columns(table_name).detect { |c| c.name == column_name } ||
|
@@ -79,7 +79,7 @@ module ActiveRecord
|
|
79
79
|
|
80
80
|
args << config.database
|
81
81
|
|
82
|
-
find_cmd_and_exec([
|
82
|
+
find_cmd_and_exec(ActiveRecord.database_cli[:mysql], *args)
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
@@ -138,7 +138,7 @@ module ActiveRecord
|
|
138
138
|
end
|
139
139
|
|
140
140
|
def supports_datetime_with_precision?
|
141
|
-
|
141
|
+
true
|
142
142
|
end
|
143
143
|
|
144
144
|
def supports_virtual_columns?
|
@@ -197,12 +197,6 @@ module ActiveRecord
|
|
197
197
|
|
198
198
|
# HELPER METHODS ===========================================
|
199
199
|
|
200
|
-
# The two drivers have slightly different ways of yielding hashes of results, so
|
201
|
-
# this method must be implemented to provide a uniform interface.
|
202
|
-
def each_hash(result) # :nodoc:
|
203
|
-
raise NotImplementedError
|
204
|
-
end
|
205
|
-
|
206
200
|
# Must return the MySQL error number from the exception, if the exception has an
|
207
201
|
# error number.
|
208
202
|
def error_number(exception) # :nodoc:
|
@@ -226,24 +220,19 @@ module ActiveRecord
|
|
226
220
|
# DATABASE STATEMENTS ======================================
|
227
221
|
#++
|
228
222
|
|
229
|
-
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
230
|
-
# to write stuff in an abstract way without concerning ourselves about whether it
|
231
|
-
# needs to be explicitly freed or not.
|
232
|
-
def execute_and_free(sql, name = nil, async: false, allow_retry: false) # :nodoc:
|
233
|
-
sql = transform_query(sql)
|
234
|
-
check_if_write_query(sql)
|
235
|
-
|
236
|
-
mark_transaction_written_if_write(sql)
|
237
|
-
yield raw_execute(sql, name, async: async, allow_retry: allow_retry)
|
238
|
-
end
|
239
|
-
|
240
223
|
def begin_db_transaction # :nodoc:
|
241
224
|
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
242
225
|
end
|
243
226
|
|
244
227
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
245
|
-
|
246
|
-
|
228
|
+
# From MySQL manual: The [SET TRANSACTION] statement applies only to the next single transaction performed within the session.
|
229
|
+
# So we don't need to implement #reset_isolation_level
|
230
|
+
execute_batch(
|
231
|
+
["SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "BEGIN"],
|
232
|
+
"TRANSACTION",
|
233
|
+
allow_retry: true,
|
234
|
+
materialize_transactions: false,
|
235
|
+
)
|
247
236
|
end
|
248
237
|
|
249
238
|
def commit_db_transaction # :nodoc:
|
@@ -343,7 +332,7 @@ module ActiveRecord
|
|
343
332
|
rename_table_indexes(table_name, new_name, **options)
|
344
333
|
end
|
345
334
|
|
346
|
-
# Drops a table from the database.
|
335
|
+
# Drops a table or tables from the database.
|
347
336
|
#
|
348
337
|
# [<tt>:force</tt>]
|
349
338
|
# Set to +:cascade+ to drop dependent objects as well.
|
@@ -357,10 +346,10 @@ module ActiveRecord
|
|
357
346
|
#
|
358
347
|
# Although this command ignores most +options+ and the block if one is given,
|
359
348
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
360
|
-
# In that case, +options+ and the block will be used by create_table.
|
361
|
-
def drop_table(
|
362
|
-
schema_cache.clear_data_source_cache!(table_name.to_s)
|
363
|
-
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
349
|
+
# In that case, +options+ and the block will be used by #create_table except if you provide more than one table which is not supported.
|
350
|
+
def drop_table(*table_names, **options)
|
351
|
+
table_names.each { |table_name| schema_cache.clear_data_source_cache!(table_name.to_s) }
|
352
|
+
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{table_names.map { |table_name| quote_table_name(table_name) }.join(', ')}#{' CASCADE' if options[:force] == :cascade}"
|
364
353
|
end
|
365
354
|
|
366
355
|
def rename_index(table_name, old_name, new_name)
|
@@ -580,7 +569,7 @@ module ActiveRecord
|
|
580
569
|
|
581
570
|
# SHOW VARIABLES LIKE 'name'
|
582
571
|
def show_variable(name)
|
583
|
-
query_value("SELECT @@#{name}", "SCHEMA")
|
572
|
+
query_value("SELECT @@#{name}", "SCHEMA", materialize_transactions: false, allow_retry: true)
|
584
573
|
rescue ActiveRecord::StatementInvalid
|
585
574
|
nil
|
586
575
|
end
|
@@ -679,8 +668,8 @@ module ActiveRecord
|
|
679
668
|
end
|
680
669
|
|
681
670
|
def check_version # :nodoc:
|
682
|
-
if database_version < "5.
|
683
|
-
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.
|
671
|
+
if database_version < "5.6.4"
|
672
|
+
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.6.4."
|
684
673
|
end
|
685
674
|
end
|
686
675
|
|
@@ -787,11 +776,6 @@ module ActiveRecord
|
|
787
776
|
warning.level == "Note" || super
|
788
777
|
end
|
789
778
|
|
790
|
-
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
791
|
-
# made since we established the connection
|
792
|
-
def sync_timezone_changes(raw_connection)
|
793
|
-
end
|
794
|
-
|
795
779
|
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
796
780
|
ER_DB_CREATE_EXISTS = 1007
|
797
781
|
ER_FILSORT_ABORT = 1028
|
@@ -961,13 +945,11 @@ module ActiveRecord
|
|
961
945
|
end.join(", ")
|
962
946
|
|
963
947
|
# ...and send them all in one query
|
964
|
-
|
948
|
+
raw_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
|
965
949
|
end
|
966
950
|
|
967
951
|
def column_definitions(table_name) # :nodoc:
|
968
|
-
|
969
|
-
each_hash(result)
|
970
|
-
end
|
952
|
+
internal_exec_query("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA")
|
971
953
|
end
|
972
954
|
|
973
955
|
def create_table_info(table_name) # :nodoc:
|
@@ -42,18 +42,12 @@ module ActiveRecord
|
|
42
42
|
# :method: unsigned_bigint
|
43
43
|
# :call-seq: unsigned_bigint(*names, **options)
|
44
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)
|
52
|
-
|
53
45
|
included do
|
54
46
|
define_column_methods :blob, :tinyblob, :mediumblob, :longblob,
|
55
47
|
:tinytext, :mediumtext, :longtext, :unsigned_integer, :unsigned_bigint,
|
56
48
|
:unsigned_float, :unsigned_decimal
|
49
|
+
|
50
|
+
deprecate :unsigned_float, :unsigned_decimal, deprecator: ActiveRecord.deprecator
|
57
51
|
end
|
58
52
|
end
|
59
53
|
|
@@ -8,45 +8,43 @@ module ActiveRecord
|
|
8
8
|
def indexes(table_name)
|
9
9
|
indexes = []
|
10
10
|
current_index = nil
|
11
|
-
|
12
|
-
|
13
|
-
if
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
index_using = mysql_index_type
|
23
|
-
end
|
24
|
-
|
25
|
-
indexes << [
|
26
|
-
row[:Table],
|
27
|
-
row[:Key_name],
|
28
|
-
row[:Non_unique].to_i == 0,
|
29
|
-
[],
|
30
|
-
lengths: {},
|
31
|
-
orders: {},
|
32
|
-
type: index_type,
|
33
|
-
using: index_using,
|
34
|
-
comment: row[:Index_comment].presence
|
35
|
-
]
|
11
|
+
internal_exec_query("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA").each do |row|
|
12
|
+
if current_index != row["Key_name"]
|
13
|
+
next if row["Key_name"] == "PRIMARY" # skip the primary key
|
14
|
+
current_index = row["Key_name"]
|
15
|
+
|
16
|
+
mysql_index_type = row["Index_type"].downcase.to_sym
|
17
|
+
case mysql_index_type
|
18
|
+
when :fulltext, :spatial
|
19
|
+
index_type = mysql_index_type
|
20
|
+
when :btree, :hash
|
21
|
+
index_using = mysql_index_type
|
36
22
|
end
|
37
23
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
24
|
+
indexes << [
|
25
|
+
row["Table"],
|
26
|
+
row["Key_name"],
|
27
|
+
row["Non_unique"].to_i == 0,
|
28
|
+
[],
|
29
|
+
lengths: {},
|
30
|
+
orders: {},
|
31
|
+
type: index_type,
|
32
|
+
using: index_using,
|
33
|
+
comment: row["Index_comment"].presence
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
if expression = row["Expression"]
|
38
|
+
expression = expression.gsub("\\'", "'")
|
39
|
+
expression = +"(#{expression})" unless expression.start_with?("(")
|
40
|
+
indexes.last[-2] << expression
|
41
|
+
indexes.last[-1][:expressions] ||= {}
|
42
|
+
indexes.last[-1][:expressions][expression] = expression
|
43
|
+
indexes.last[-1][:orders][expression] = :desc if row["Collation"] == "D"
|
44
|
+
else
|
45
|
+
indexes.last[-2] << row["Column_name"]
|
46
|
+
indexes.last[-1][:lengths][row["Column_name"]] = row["Sub_part"].to_i if row["Sub_part"]
|
47
|
+
indexes.last[-1][:orders][row["Column_name"]] = :desc if row["Collation"] == "D"
|
50
48
|
end
|
51
49
|
end
|
52
50
|
|
@@ -161,7 +159,7 @@ module ActiveRecord
|
|
161
159
|
end
|
162
160
|
|
163
161
|
def valid_primary_key_options
|
164
|
-
super + [:unsigned]
|
162
|
+
super + [:unsigned, :auto_increment]
|
165
163
|
end
|
166
164
|
|
167
165
|
def create_table_definition(name, **options)
|
@@ -182,12 +180,12 @@ module ActiveRecord
|
|
182
180
|
end
|
183
181
|
|
184
182
|
def new_column_from_field(table_name, field, _definitions)
|
185
|
-
field_name = field.fetch(
|
186
|
-
type_metadata = fetch_type_metadata(field[
|
187
|
-
default, default_function = field[
|
183
|
+
field_name = field.fetch("Field")
|
184
|
+
type_metadata = fetch_type_metadata(field["Type"], field["Extra"])
|
185
|
+
default, default_function = field["Default"], nil
|
188
186
|
|
189
187
|
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
|
190
|
-
default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field[
|
188
|
+
default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field["Extra"])
|
191
189
|
default, default_function = nil, default
|
192
190
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
193
191
|
default = +"(#{default})" unless default.start_with?("(")
|
@@ -203,13 +201,13 @@ module ActiveRecord
|
|
203
201
|
end
|
204
202
|
|
205
203
|
MySQL::Column.new(
|
206
|
-
field[
|
204
|
+
field["Field"],
|
207
205
|
default,
|
208
206
|
type_metadata,
|
209
|
-
field[
|
207
|
+
field["Null"] == "YES",
|
210
208
|
default_function,
|
211
|
-
collation: field[
|
212
|
-
comment: field[
|
209
|
+
collation: field["Collation"],
|
210
|
+
comment: field["Comment"].presence
|
213
211
|
)
|
214
212
|
end
|
215
213
|
|
@@ -13,49 +13,10 @@ module ActiveRecord
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
|
17
|
-
if without_prepared_statement?(binds)
|
18
|
-
execute_and_free(sql, name, async: async, allow_retry: allow_retry) do |result|
|
19
|
-
if result
|
20
|
-
build_result(columns: result.fields, rows: result.to_a)
|
21
|
-
else
|
22
|
-
build_result(columns: [], rows: [])
|
23
|
-
end
|
24
|
-
end
|
25
|
-
else
|
26
|
-
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
|
27
|
-
if result
|
28
|
-
build_result(columns: result.fields, rows: result.to_a)
|
29
|
-
else
|
30
|
-
build_result(columns: [], rows: [])
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
37
|
-
if without_prepared_statement?(binds)
|
38
|
-
with_raw_connection do |conn|
|
39
|
-
@affected_rows_before_warnings = nil
|
40
|
-
execute_and_free(sql, name) { @affected_rows_before_warnings || conn.affected_rows }
|
41
|
-
end
|
42
|
-
else
|
43
|
-
exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
|
44
|
-
end
|
45
|
-
end
|
46
|
-
alias :exec_update :exec_delete
|
47
|
-
|
48
16
|
private
|
49
|
-
def
|
50
|
-
raw_connection.query_options[:database_timezone] = default_timezone
|
51
|
-
end
|
52
|
-
|
53
|
-
def execute_batch(statements, name = nil)
|
54
|
-
statements = statements.map { |sql| transform_query(sql) }
|
17
|
+
def execute_batch(statements, name = nil, **kwargs)
|
55
18
|
combine_multi_statements(statements).each do |statement|
|
56
|
-
|
57
|
-
raw_execute(statement, name)
|
58
|
-
end
|
19
|
+
raw_execute(statement, name, batch: true, **kwargs)
|
59
20
|
end
|
60
21
|
end
|
61
22
|
|
@@ -77,75 +38,58 @@ module ActiveRecord
|
|
77
38
|
end
|
78
39
|
end
|
79
40
|
|
80
|
-
def
|
81
|
-
if multi_statements_enabled?
|
82
|
-
|
41
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
42
|
+
reset_multi_statement = if batch && !multi_statements_enabled?
|
43
|
+
raw_connection.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
|
44
|
+
true
|
83
45
|
end
|
84
46
|
|
85
|
-
|
86
|
-
|
47
|
+
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
48
|
+
# made since we established the connection
|
49
|
+
raw_connection.query_options[:database_timezone] = default_timezone
|
87
50
|
|
88
|
-
|
89
|
-
|
90
|
-
conn.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
91
|
-
end
|
92
|
-
end
|
51
|
+
result = if prepare
|
52
|
+
stmt = @statements[sql] ||= raw_connection.prepare(sql)
|
93
53
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
notification_payload[:row_count] = result&.size || 0
|
103
|
-
result
|
54
|
+
begin
|
55
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
56
|
+
stmt.execute(*type_casted_binds)
|
57
|
+
end
|
58
|
+
rescue ::Mysql2::Error
|
59
|
+
@statements.delete(sql)
|
60
|
+
stmt.close
|
61
|
+
raise
|
104
62
|
end
|
63
|
+
verified!
|
64
|
+
else
|
65
|
+
raw_connection.query(sql)
|
105
66
|
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
|
109
|
-
sql = transform_query(sql)
|
110
|
-
check_if_write_query(sql)
|
111
67
|
|
112
|
-
|
68
|
+
notification_payload[:row_count] = result&.size || 0
|
113
69
|
|
114
|
-
|
70
|
+
@affected_rows_before_warnings = raw_connection.affected_rows
|
71
|
+
raw_connection.abandon_results!
|
115
72
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
begin
|
127
|
-
result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
128
|
-
stmt.execute(*type_casted_binds)
|
129
|
-
end
|
130
|
-
verified!
|
131
|
-
result
|
132
|
-
rescue ::Mysql2::Error => e
|
133
|
-
if cache_stmt
|
134
|
-
@statements.delete(sql)
|
135
|
-
else
|
136
|
-
stmt.close
|
137
|
-
end
|
138
|
-
raise e
|
139
|
-
end
|
73
|
+
verified!
|
74
|
+
handle_warnings(sql)
|
75
|
+
result
|
76
|
+
ensure
|
77
|
+
if reset_multi_statement && active?
|
78
|
+
raw_connection.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
79
|
+
end
|
80
|
+
end
|
140
81
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
end
|
82
|
+
def cast_result(result)
|
83
|
+
if result.nil? || result.fields.empty?
|
84
|
+
ActiveRecord::Result.empty
|
85
|
+
else
|
86
|
+
ActiveRecord::Result.new(result.fields, result.to_a)
|
147
87
|
end
|
148
88
|
end
|
89
|
+
|
90
|
+
def affected_rows(result)
|
91
|
+
@affected_rows_before_warnings
|
92
|
+
end
|
149
93
|
end
|
150
94
|
end
|
151
95
|
end
|
@@ -55,6 +55,7 @@ module ActiveRecord
|
|
55
55
|
def initialize(...)
|
56
56
|
super
|
57
57
|
|
58
|
+
@affected_rows_before_warnings = nil
|
58
59
|
@config[:flags] ||= 0
|
59
60
|
|
60
61
|
if @config[:flags].kind_of? Array
|
@@ -92,14 +93,6 @@ module ActiveRecord
|
|
92
93
|
|
93
94
|
# HELPER METHODS ===========================================
|
94
95
|
|
95
|
-
def each_hash(result, &block) # :nodoc:
|
96
|
-
if block_given?
|
97
|
-
result.each(as: :hash, symbolize_keys: true, &block)
|
98
|
-
else
|
99
|
-
to_enum(:each_hash, result)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
96
|
def error_number(exception)
|
104
97
|
exception.error_number if exception.respond_to?(:error_number)
|
105
98
|
end
|