activerecord 7.0.8.6 → 7.2.2.1
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 +631 -1939
- data/MIT-LICENSE +1 -1
- data/README.rdoc +29 -29
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +23 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +26 -14
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +21 -14
- data/lib/active_record/associations/has_many_through_association.rb +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +131 -32
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +148 -33
- data/lib/active_record/attributes.rb +64 -50
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- data/lib/active_record/callbacks.rb +11 -25
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +124 -1
- data/lib/active_record/connection_handling.rb +96 -104
- data/lib/active_record/core.rb +251 -176
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +39 -10
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +45 -21
- data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
- data/lib/active_record/encryption/encryptor.rb +18 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +129 -28
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +167 -97
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +234 -117
- data/lib/active_record/model_schema.rb +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +92 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +33 -8
- data/lib/active_record/railtie.rb +129 -85
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +145 -154
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +267 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +250 -93
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +93 -18
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +28 -16
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +576 -107
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +580 -90
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +63 -14
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +27 -6
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +190 -118
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +106 -24
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +61 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +112 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +56 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -63,6 +63,15 @@ module ActiveRecord
|
|
63
63
|
coder["comment"] = @comment
|
64
64
|
end
|
65
65
|
|
66
|
+
# whether the column is auto-populated by the database using a sequence
|
67
|
+
def auto_incremented_by_db?
|
68
|
+
false
|
69
|
+
end
|
70
|
+
|
71
|
+
def auto_populated?
|
72
|
+
auto_incremented_by_db? || default_function
|
73
|
+
end
|
74
|
+
|
66
75
|
def ==(other)
|
67
76
|
other.is_a?(Column) &&
|
68
77
|
name == other.name &&
|
@@ -4,135 +4,62 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module MySQL
|
6
6
|
module DatabaseStatements
|
7
|
-
|
8
|
-
def select_all(*, **) # :nodoc:
|
9
|
-
result = if ExplainRegistry.collect? && prepared_statements
|
10
|
-
unprepared_statement { super }
|
11
|
-
else
|
12
|
-
super
|
13
|
-
end
|
14
|
-
@connection.abandon_results!
|
15
|
-
result
|
16
|
-
end
|
17
|
-
|
18
|
-
def query(sql, name = nil) # :nodoc:
|
19
|
-
execute(sql, name).to_a
|
20
|
-
end
|
21
|
-
|
22
|
-
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
7
|
+
READ_QUERY = AbstractAdapter.build_read_query_regexp(
|
23
8
|
:desc, :describe, :set, :show, :use, :kill
|
24
9
|
) # :nodoc:
|
25
10
|
private_constant :READ_QUERY
|
26
11
|
|
12
|
+
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
|
13
|
+
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
|
14
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)", retryable: true).freeze # :nodoc:
|
15
|
+
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
16
|
+
|
27
17
|
def write_query?(sql) # :nodoc:
|
28
18
|
!READ_QUERY.match?(sql)
|
29
19
|
rescue ArgumentError # Invalid encoding
|
30
20
|
!READ_QUERY.match?(sql.b)
|
31
21
|
end
|
32
22
|
|
33
|
-
def
|
34
|
-
|
23
|
+
def high_precision_current_timestamp
|
24
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP
|
25
|
+
end
|
26
|
+
|
27
|
+
def explain(arel, binds = [], options = [])
|
28
|
+
sql = build_explain_clause(options) + " " + to_sql(arel, binds)
|
35
29
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
36
|
-
result =
|
30
|
+
result = internal_exec_query(sql, "EXPLAIN", binds)
|
37
31
|
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
38
32
|
|
39
33
|
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
40
34
|
end
|
41
35
|
|
42
|
-
|
43
|
-
|
44
|
-
sql = transform_query(sql)
|
45
|
-
check_if_write_query(sql)
|
46
|
-
|
47
|
-
raw_execute(sql, name, async: async)
|
48
|
-
end
|
36
|
+
def build_explain_clause(options = [])
|
37
|
+
return "EXPLAIN" if options.empty?
|
49
38
|
|
50
|
-
|
51
|
-
if without_prepared_statement?(binds)
|
52
|
-
execute_and_free(sql, name, async: async) do |result|
|
53
|
-
if result
|
54
|
-
build_result(columns: result.fields, rows: result.to_a)
|
55
|
-
else
|
56
|
-
build_result(columns: [], rows: [])
|
57
|
-
end
|
58
|
-
end
|
59
|
-
else
|
60
|
-
exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
|
61
|
-
if result
|
62
|
-
build_result(columns: result.fields, rows: result.to_a)
|
63
|
-
else
|
64
|
-
build_result(columns: [], rows: [])
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
39
|
+
explain_clause = "EXPLAIN #{options.join(" ").upcase}"
|
69
40
|
|
70
|
-
|
71
|
-
|
72
|
-
@lock.synchronize do
|
73
|
-
execute_and_free(sql, name) { @connection.affected_rows }
|
74
|
-
end
|
41
|
+
if analyze_without_explain? && explain_clause.include?("ANALYZE")
|
42
|
+
explain_clause.sub("EXPLAIN ", "")
|
75
43
|
else
|
76
|
-
|
44
|
+
explain_clause
|
77
45
|
end
|
78
46
|
end
|
79
|
-
alias :exec_update :exec_delete
|
80
|
-
|
81
|
-
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
|
82
|
-
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
|
83
|
-
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
|
84
|
-
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
85
|
-
|
86
|
-
def high_precision_current_timestamp
|
87
|
-
HIGH_PRECISION_CURRENT_TIMESTAMP
|
88
|
-
end
|
89
47
|
|
90
48
|
private
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
|
95
|
-
|
96
|
-
super
|
97
|
-
end
|
98
|
-
|
99
|
-
def execute_batch(statements, name = nil)
|
100
|
-
statements = statements.map { |sql| transform_query(sql) }
|
101
|
-
combine_multi_statements(statements).each do |statement|
|
102
|
-
raw_execute(statement, name)
|
103
|
-
@connection.abandon_results!
|
104
|
-
end
|
49
|
+
# https://mariadb.com/kb/en/analyze-statement/
|
50
|
+
def analyze_without_explain?
|
51
|
+
mariadb? && database_version >= "10.1.0"
|
105
52
|
end
|
106
53
|
|
107
54
|
def default_insert_value(column)
|
108
55
|
super unless column.auto_increment?
|
109
56
|
end
|
110
57
|
|
111
|
-
def
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
def multi_statements_enabled?
|
116
|
-
flags = @config[:flags]
|
117
|
-
|
118
|
-
if flags.is_a?(Array)
|
119
|
-
flags.include?("MULTI_STATEMENTS")
|
58
|
+
def returning_column_values(result)
|
59
|
+
if supports_insert_returning?
|
60
|
+
result.rows.first
|
120
61
|
else
|
121
|
-
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def with_multi_statements
|
126
|
-
multi_statements_was = multi_statements_enabled?
|
127
|
-
|
128
|
-
unless multi_statements_was
|
129
|
-
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
|
130
|
-
end
|
131
|
-
|
132
|
-
yield
|
133
|
-
ensure
|
134
|
-
unless multi_statements_was
|
135
|
-
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
62
|
+
super
|
136
63
|
end
|
137
64
|
end
|
138
65
|
|
@@ -162,46 +89,6 @@ module ActiveRecord
|
|
162
89
|
def max_allowed_packet
|
163
90
|
@max_allowed_packet ||= show_variable("max_allowed_packet")
|
164
91
|
end
|
165
|
-
|
166
|
-
def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
|
167
|
-
sql = transform_query(sql)
|
168
|
-
check_if_write_query(sql)
|
169
|
-
|
170
|
-
materialize_transactions
|
171
|
-
mark_transaction_written_if_write(sql)
|
172
|
-
|
173
|
-
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
174
|
-
# made since we established the connection
|
175
|
-
@connection.query_options[:database_timezone] = ActiveRecord.default_timezone
|
176
|
-
|
177
|
-
type_casted_binds = type_casted_binds(binds)
|
178
|
-
|
179
|
-
log(sql, name, binds, type_casted_binds, async: async) do
|
180
|
-
if cache_stmt
|
181
|
-
stmt = @statements[sql] ||= @connection.prepare(sql)
|
182
|
-
else
|
183
|
-
stmt = @connection.prepare(sql)
|
184
|
-
end
|
185
|
-
|
186
|
-
begin
|
187
|
-
result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
188
|
-
stmt.execute(*type_casted_binds)
|
189
|
-
end
|
190
|
-
rescue Mysql2::Error => e
|
191
|
-
if cache_stmt
|
192
|
-
@statements.delete(sql)
|
193
|
-
else
|
194
|
-
stmt.close
|
195
|
-
end
|
196
|
-
raise e
|
197
|
-
end
|
198
|
-
|
199
|
-
ret = yield stmt, result
|
200
|
-
result.free if result
|
201
|
-
stmt.close unless cache_stmt
|
202
|
-
ret
|
203
|
-
end
|
204
|
-
end
|
205
92
|
end
|
206
93
|
end
|
207
94
|
end
|
@@ -6,34 +6,69 @@ module ActiveRecord
|
|
6
6
|
module ConnectionAdapters
|
7
7
|
module MySQL
|
8
8
|
module Quoting # :nodoc:
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
9
11
|
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
10
12
|
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
11
13
|
|
12
|
-
|
14
|
+
module ClassMethods # :nodoc:
|
15
|
+
def column_name_matcher
|
16
|
+
/
|
17
|
+
\A
|
18
|
+
(
|
19
|
+
(?:
|
20
|
+
# `table_name`.`column_name` | function(one or no argument)
|
21
|
+
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
|
22
|
+
)
|
23
|
+
(?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
|
24
|
+
)
|
25
|
+
(?:\s*,\s*\g<1>)*
|
26
|
+
\z
|
27
|
+
/ix
|
28
|
+
end
|
29
|
+
|
30
|
+
def column_name_with_order_matcher
|
31
|
+
/
|
32
|
+
\A
|
33
|
+
(
|
34
|
+
(?:
|
35
|
+
# `table_name`.`column_name` | function(one or no argument)
|
36
|
+
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
|
37
|
+
)
|
38
|
+
(?:\s+COLLATE\s+(?:\w+|"\w+"))?
|
39
|
+
(?:\s+ASC|\s+DESC)?
|
40
|
+
)
|
41
|
+
(?:\s*,\s*\g<1>)*
|
42
|
+
\z
|
43
|
+
/ix
|
44
|
+
end
|
45
|
+
|
46
|
+
def quote_column_name(name)
|
47
|
+
QUOTED_COLUMN_NAMES[name] ||= "`#{name.to_s.gsub('`', '``')}`".freeze
|
48
|
+
end
|
49
|
+
|
50
|
+
def quote_table_name(name)
|
51
|
+
QUOTED_TABLE_NAMES[name] ||= "`#{name.to_s.gsub('`', '``').gsub(".", "`.`")}`".freeze
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def cast_bound_value(value)
|
13
56
|
case value
|
14
57
|
when Rational
|
15
|
-
|
16
|
-
when Numeric
|
17
|
-
|
58
|
+
value.to_f.to_s
|
59
|
+
when Numeric
|
60
|
+
value.to_s
|
18
61
|
when BigDecimal
|
19
|
-
|
62
|
+
value.to_s("F")
|
20
63
|
when true
|
21
|
-
"
|
64
|
+
"1"
|
22
65
|
when false
|
23
|
-
"
|
66
|
+
"0"
|
24
67
|
else
|
25
|
-
|
68
|
+
value
|
26
69
|
end
|
27
70
|
end
|
28
71
|
|
29
|
-
def quote_column_name(name)
|
30
|
-
QUOTED_COLUMN_NAMES[name] ||= "`#{super.gsub('`', '``')}`"
|
31
|
-
end
|
32
|
-
|
33
|
-
def quote_table_name(name)
|
34
|
-
QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "`.`").freeze
|
35
|
-
end
|
36
|
-
|
37
72
|
def unquoted_true
|
38
73
|
1
|
39
74
|
end
|
@@ -63,14 +98,14 @@ module ActiveRecord
|
|
63
98
|
end
|
64
99
|
|
65
100
|
# Override +type_cast+ we pass to mysql2 Date and Time objects instead
|
66
|
-
# of Strings since
|
101
|
+
# of Strings since MySQL adapters are able to handle those classes more efficiently.
|
67
102
|
def type_cast(value) # :nodoc:
|
68
103
|
case value
|
69
104
|
when ActiveSupport::TimeWithZone
|
70
105
|
# We need to check explicitly for ActiveSupport::TimeWithZone because
|
71
106
|
# we need to transform it to Time objects but we don't want to
|
72
107
|
# transform Time objects to themselves.
|
73
|
-
if
|
108
|
+
if default_timezone == :utc
|
74
109
|
value.getutc
|
75
110
|
else
|
76
111
|
value.getlocal
|
@@ -81,42 +116,6 @@ module ActiveRecord
|
|
81
116
|
super
|
82
117
|
end
|
83
118
|
end
|
84
|
-
|
85
|
-
def column_name_matcher
|
86
|
-
COLUMN_NAME
|
87
|
-
end
|
88
|
-
|
89
|
-
def column_name_with_order_matcher
|
90
|
-
COLUMN_NAME_WITH_ORDER
|
91
|
-
end
|
92
|
-
|
93
|
-
COLUMN_NAME = /
|
94
|
-
\A
|
95
|
-
(
|
96
|
-
(?:
|
97
|
-
# `table_name`.`column_name` | function(one or no argument)
|
98
|
-
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
|
99
|
-
)
|
100
|
-
(?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
|
101
|
-
)
|
102
|
-
(?:\s*,\s*\g<1>)*
|
103
|
-
\z
|
104
|
-
/ix
|
105
|
-
|
106
|
-
COLUMN_NAME_WITH_ORDER = /
|
107
|
-
\A
|
108
|
-
(
|
109
|
-
(?:
|
110
|
-
# `table_name`.`column_name` | function(one or no argument)
|
111
|
-
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`)) | \w+\((?:|\g<2>)\)
|
112
|
-
)
|
113
|
-
(?:\s+ASC|\s+DESC)?
|
114
|
-
)
|
115
|
-
(?:\s*,\s*\g<1>)*
|
116
|
-
\z
|
117
|
-
/ix
|
118
|
-
|
119
|
-
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
120
119
|
end
|
121
120
|
end
|
122
121
|
end
|
@@ -24,6 +24,15 @@ module ActiveRecord
|
|
24
24
|
add_column_position!(change_column_sql, column_options(o.column))
|
25
25
|
end
|
26
26
|
|
27
|
+
def visit_ChangeColumnDefaultDefinition(o)
|
28
|
+
sql = +"ALTER COLUMN #{quote_column_name(o.column.name)} "
|
29
|
+
if o.default.nil? && !o.column.null
|
30
|
+
sql << "DROP DEFAULT"
|
31
|
+
else
|
32
|
+
sql << "SET DEFAULT #{quote_default_expression(o.default, o.column)}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
27
36
|
def visit_CreateIndexDefinition(o)
|
28
37
|
sql = visit_IndexDefinition(o.index, true)
|
29
38
|
sql << " #{o.algorithm}" if o.algorithm
|
@@ -57,6 +57,7 @@ module ActiveRecord
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
+
# = Active Record MySQL Adapter \Table Definition
|
60
61
|
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
61
62
|
include ColumnMethods
|
62
63
|
|
@@ -85,6 +86,10 @@ module ActiveRecord
|
|
85
86
|
end
|
86
87
|
|
87
88
|
private
|
89
|
+
def valid_column_definition_options
|
90
|
+
super + [:auto_increment, :charset, :as, :size, :unsigned, :first, :after, :type, :stored]
|
91
|
+
end
|
92
|
+
|
88
93
|
def aliased_types(name, fallback)
|
89
94
|
fallback
|
90
95
|
end
|
@@ -98,6 +103,7 @@ module ActiveRecord
|
|
98
103
|
end
|
99
104
|
end
|
100
105
|
|
106
|
+
# = Active Record MySQL Adapter \Table
|
101
107
|
class Table < ActiveRecord::ConnectionAdapters::Table
|
102
108
|
include ColumnMethods
|
103
109
|
end
|
@@ -66,7 +66,7 @@ module ActiveRecord
|
|
66
66
|
if column.collation
|
67
67
|
@table_collation_cache ||= {}
|
68
68
|
@table_collation_cache[table_name] ||=
|
69
|
-
@connection.
|
69
|
+
@connection.internal_exec_query("SHOW TABLE STATUS LIKE #{@connection.quote(table_name)}", "SCHEMA").first["Collation"]
|
70
70
|
column.collation.inspect if column.collation != @table_collation_cache[table_name]
|
71
71
|
end
|
72
72
|
end
|
@@ -36,7 +36,7 @@ module ActiveRecord
|
|
36
36
|
end
|
37
37
|
|
38
38
|
if row[:Expression]
|
39
|
-
expression = row[:Expression]
|
39
|
+
expression = row[:Expression].gsub("\\'", "'")
|
40
40
|
expression = +"(#{expression})" unless expression.start_with?("(")
|
41
41
|
indexes.last[-2] << expression
|
42
42
|
indexes.last[-1][:expressions] ||= {}
|
@@ -57,9 +57,9 @@ module ActiveRecord
|
|
57
57
|
orders = options.delete(:orders)
|
58
58
|
lengths = options.delete(:lengths)
|
59
59
|
|
60
|
-
columns = index[-1].
|
60
|
+
columns = index[-1].to_h { |name|
|
61
61
|
[ name.to_sym, expressions[name] || +quote_column_name(name) ]
|
62
|
-
}
|
62
|
+
}
|
63
63
|
|
64
64
|
index[-1] = add_options_for_index_columns(
|
65
65
|
columns, order: orders, length: lengths
|
@@ -68,6 +68,12 @@ module ActiveRecord
|
|
68
68
|
|
69
69
|
IndexDefinition.new(*index, **options)
|
70
70
|
end
|
71
|
+
rescue StatementInvalid => e
|
72
|
+
if e.message.match?(/Table '.+' doesn't exist/)
|
73
|
+
[]
|
74
|
+
else
|
75
|
+
raise
|
76
|
+
end
|
71
77
|
end
|
72
78
|
|
73
79
|
def remove_column(table_name, column_name, type = nil, **options)
|
@@ -125,6 +131,10 @@ module ActiveRecord
|
|
125
131
|
256 # https://dev.mysql.com/doc/refman/en/identifiers.html
|
126
132
|
end
|
127
133
|
|
134
|
+
def schema_creation # :nodoc:
|
135
|
+
MySQL::SchemaCreation.new(self)
|
136
|
+
end
|
137
|
+
|
128
138
|
private
|
129
139
|
CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
|
130
140
|
|
@@ -150,8 +160,8 @@ module ActiveRecord
|
|
150
160
|
@default_row_format
|
151
161
|
end
|
152
162
|
|
153
|
-
def
|
154
|
-
|
163
|
+
def valid_primary_key_options
|
164
|
+
super + [:unsigned, :auto_increment]
|
155
165
|
end
|
156
166
|
|
157
167
|
def create_table_definition(name, **options)
|
@@ -171,7 +181,7 @@ module ActiveRecord
|
|
171
181
|
end
|
172
182
|
end
|
173
183
|
|
174
|
-
def new_column_from_field(table_name, field)
|
184
|
+
def new_column_from_field(table_name, field, _definitions)
|
175
185
|
field_name = field.fetch(:Field)
|
176
186
|
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
|
177
187
|
default, default_function = field[:Default], nil
|
@@ -181,6 +191,7 @@ module ActiveRecord
|
|
181
191
|
default, default_function = nil, default
|
182
192
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
183
193
|
default = +"(#{default})" unless default.start_with?("(")
|
194
|
+
default = default.gsub("\\'", "'")
|
184
195
|
default, default_function = nil, default
|
185
196
|
elsif type_metadata.type == :text && default&.start_with?("'")
|
186
197
|
# strip and unescape quotes
|
@@ -225,14 +236,15 @@ module ActiveRecord
|
|
225
236
|
def data_source_sql(name = nil, type: nil)
|
226
237
|
scope = quoted_scope(name, type: type)
|
227
238
|
|
228
|
-
sql = +"SELECT table_name FROM
|
229
|
-
sql << " WHERE table_schema = #{scope[:schema]}
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
sql << " WHERE #{conditions.join(" AND ")}"
|
239
|
+
sql = +"SELECT table_name FROM information_schema.tables"
|
240
|
+
sql << " WHERE table_schema = #{scope[:schema]}"
|
241
|
+
|
242
|
+
if scope[:name]
|
243
|
+
sql << " AND table_name = #{scope[:name]}"
|
244
|
+
sql << " AND table_name IN (SELECT table_name FROM information_schema.tables WHERE table_schema = #{scope[:schema]})"
|
235
245
|
end
|
246
|
+
|
247
|
+
sql << " AND table_type = #{scope[:type]}" if scope[:type]
|
236
248
|
sql
|
237
249
|
end
|
238
250
|
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module Mysql2
|
6
|
+
module DatabaseStatements
|
7
|
+
# Returns an ActiveRecord::Result instance.
|
8
|
+
def select_all(*, **) # :nodoc:
|
9
|
+
if ExplainRegistry.collect? && prepared_statements
|
10
|
+
unprepared_statement { super }
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
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
|
+
private
|
49
|
+
def sync_timezone_changes(raw_connection)
|
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) }
|
55
|
+
combine_multi_statements(statements).each do |statement|
|
56
|
+
with_raw_connection do |conn|
|
57
|
+
raw_execute(statement, name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def last_inserted_id(result)
|
63
|
+
if supports_insert_returning?
|
64
|
+
super
|
65
|
+
else
|
66
|
+
@raw_connection&.last_id
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def multi_statements_enabled?
|
71
|
+
flags = @config[:flags]
|
72
|
+
|
73
|
+
if flags.is_a?(Array)
|
74
|
+
flags.include?("MULTI_STATEMENTS")
|
75
|
+
else
|
76
|
+
flags.anybits?(::Mysql2::Client::MULTI_STATEMENTS)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def with_multi_statements
|
81
|
+
if multi_statements_enabled?
|
82
|
+
return yield
|
83
|
+
end
|
84
|
+
|
85
|
+
with_raw_connection do |conn|
|
86
|
+
conn.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
|
87
|
+
|
88
|
+
yield
|
89
|
+
ensure
|
90
|
+
conn.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
95
|
+
log(sql, name, async: async) do |notification_payload|
|
96
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
97
|
+
sync_timezone_changes(conn)
|
98
|
+
result = conn.query(sql)
|
99
|
+
conn.abandon_results!
|
100
|
+
verified!
|
101
|
+
handle_warnings(sql)
|
102
|
+
notification_payload[:row_count] = result&.size || 0
|
103
|
+
result
|
104
|
+
end
|
105
|
+
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
|
+
|
112
|
+
mark_transaction_written_if_write(sql)
|
113
|
+
|
114
|
+
type_casted_binds = type_casted_binds(binds)
|
115
|
+
|
116
|
+
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
117
|
+
with_raw_connection do |conn|
|
118
|
+
sync_timezone_changes(conn)
|
119
|
+
|
120
|
+
if cache_stmt
|
121
|
+
stmt = @statements[sql] ||= conn.prepare(sql)
|
122
|
+
else
|
123
|
+
stmt = conn.prepare(sql)
|
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
|
140
|
+
|
141
|
+
ret = yield stmt, result
|
142
|
+
notification_payload[:row_count] = result&.size || 0
|
143
|
+
result.free if result
|
144
|
+
stmt.close unless cache_stmt
|
145
|
+
ret
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|