activerecord 3.2.22.5 → 5.2.8
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +657 -621
- data/MIT-LICENSE +2 -2
- data/README.rdoc +41 -46
- data/examples/performance.rb +55 -42
- data/examples/simple.rb +6 -5
- data/lib/active_record/aggregations.rb +264 -236
- data/lib/active_record/association_relation.rb +40 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -42
- data/lib/active_record/associations/association.rb +127 -75
- data/lib/active_record/associations/association_scope.rb +126 -92
- data/lib/active_record/associations/belongs_to_association.rb +78 -27
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
- data/lib/active_record/associations/builder/association.rb +117 -32
- data/lib/active_record/associations/builder/belongs_to.rb +135 -60
- data/lib/active_record/associations/builder/collection_association.rb +61 -54
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
- data/lib/active_record/associations/builder/has_many.rb +10 -64
- data/lib/active_record/associations/builder/has_one.rb +19 -51
- data/lib/active_record/associations/builder/singular_association.rb +28 -18
- data/lib/active_record/associations/collection_association.rb +226 -293
- data/lib/active_record/associations/collection_proxy.rb +1067 -69
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +83 -47
- data/lib/active_record/associations/has_many_through_association.rb +98 -65
- data/lib/active_record/associations/has_one_association.rb +57 -20
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
- data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
- data/lib/active_record/associations/join_dependency.rb +212 -164
- data/lib/active_record/associations/preloader/association.rb +95 -89
- data/lib/active_record/associations/preloader/through_association.rb +84 -44
- data/lib/active_record/associations/preloader.rb +123 -111
- data/lib/active_record/associations/singular_association.rb +33 -24
- data/lib/active_record/associations/through_association.rb +60 -26
- data/lib/active_record/associations.rb +1759 -1506
- data/lib/active_record/attribute_assignment.rb +60 -193
- data/lib/active_record/attribute_decorators.rb +90 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
- data/lib/active_record/attribute_methods/dirty.rb +113 -74
- data/lib/active_record/attribute_methods/primary_key.rb +106 -77
- data/lib/active_record/attribute_methods/query.rb +8 -5
- data/lib/active_record/attribute_methods/read.rb +63 -114
- data/lib/active_record/attribute_methods/serialization.rb +60 -90
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
- data/lib/active_record/attribute_methods/write.rb +43 -45
- data/lib/active_record/attribute_methods.rb +366 -149
- data/lib/active_record/attributes.rb +266 -0
- data/lib/active_record/autosave_association.rb +312 -225
- data/lib/active_record/base.rb +114 -505
- data/lib/active_record/callbacks.rb +145 -67
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +32 -23
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
- data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
- data/lib/active_record/connection_adapters/column.rb +50 -255
- data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -210
- data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
- data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +545 -27
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +145 -0
- data/lib/active_record/core.rb +559 -0
- data/lib/active_record/counter_cache.rb +200 -105
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +107 -69
- data/lib/active_record/enum.rb +244 -0
- data/lib/active_record/errors.rb +245 -60
- data/lib/active_record/explain.rb +35 -71
- data/lib/active_record/explain_registry.rb +32 -0
- data/lib/active_record/explain_subscriber.rb +18 -9
- data/lib/active_record/fixture_set/file.rb +82 -0
- data/lib/active_record/fixtures.rb +418 -275
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +209 -100
- data/lib/active_record/integration.rb +116 -21
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +107 -94
- data/lib/active_record/locking/pessimistic.rb +20 -8
- data/lib/active_record/log_subscriber.rb +99 -34
- data/lib/active_record/migration/command_recorder.rb +199 -64
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +17 -0
- data/lib/active_record/migration.rb +893 -296
- data/lib/active_record/model_schema.rb +328 -175
- data/lib/active_record/nested_attributes.rb +338 -242
- data/lib/active_record/no_touching.rb +58 -0
- data/lib/active_record/null_relation.rb +68 -0
- data/lib/active_record/persistence.rb +557 -170
- data/lib/active_record/query_cache.rb +14 -43
- data/lib/active_record/querying.rb +36 -24
- data/lib/active_record/railtie.rb +147 -52
- data/lib/active_record/railties/console_sandbox.rb +5 -4
- data/lib/active_record/railties/controller_runtime.rb +13 -6
- data/lib/active_record/railties/databases.rake +206 -488
- data/lib/active_record/readonly_attributes.rb +4 -6
- data/lib/active_record/reflection.rb +734 -228
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +249 -52
- data/lib/active_record/relation/calculations.rb +330 -284
- data/lib/active_record/relation/delegation.rb +135 -37
- data/lib/active_record/relation/finder_methods.rb +450 -287
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +193 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder.rb +132 -43
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +1037 -221
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +48 -151
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/relation.rb +451 -359
- data/lib/active_record/result.rb +129 -20
- data/lib/active_record/runtime_registry.rb +24 -0
- data/lib/active_record/sanitization.rb +164 -136
- data/lib/active_record/schema.rb +31 -19
- data/lib/active_record/schema_dumper.rb +154 -107
- data/lib/active_record/schema_migration.rb +56 -0
- data/lib/active_record/scoping/default.rb +108 -98
- data/lib/active_record/scoping/named.rb +125 -112
- data/lib/active_record/scoping.rb +77 -123
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +10 -6
- data/lib/active_record/statement_cache.rb +121 -0
- data/lib/active_record/store.rb +175 -16
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +337 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
- data/lib/active_record/timestamp.rb +80 -41
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +240 -119
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +71 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +21 -0
- data/lib/active_record/type/type_map.rb +62 -0
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type.rb +79 -0
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +35 -18
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +68 -0
- data/lib/active_record/validations/uniqueness.rb +133 -75
- data/lib/active_record/validations.rb +53 -43
- data/lib/active_record/version.rb +7 -7
- data/lib/active_record.rb +89 -57
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +61 -8
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
- data/lib/rails/generators/active_record/migration.rb +28 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
- data/lib/rails/generators/active_record.rb +10 -16
- metadata +141 -62
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,231 +1,155 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/connection_adapters/abstract_adapter"
|
4
|
+
require "active_record/connection_adapters/statement_pool"
|
5
|
+
require "active_record/connection_adapters/mysql/column"
|
6
|
+
require "active_record/connection_adapters/mysql/explain_pretty_printer"
|
7
|
+
require "active_record/connection_adapters/mysql/quoting"
|
8
|
+
require "active_record/connection_adapters/mysql/schema_creation"
|
9
|
+
require "active_record/connection_adapters/mysql/schema_definitions"
|
10
|
+
require "active_record/connection_adapters/mysql/schema_dumper"
|
11
|
+
require "active_record/connection_adapters/mysql/schema_statements"
|
12
|
+
require "active_record/connection_adapters/mysql/type_metadata"
|
13
|
+
|
14
|
+
require "active_support/core_ext/string/strip"
|
3
15
|
|
4
16
|
module ActiveRecord
|
5
17
|
module ConnectionAdapters
|
6
18
|
class AbstractMysqlAdapter < AbstractAdapter
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def initialize(name, default, sql_type = nil, null = true, collation = nil)
|
11
|
-
super(name, default, sql_type, null)
|
12
|
-
@collation = collation
|
13
|
-
end
|
14
|
-
|
15
|
-
def extract_default(default)
|
16
|
-
if sql_type =~ /blob/i || type == :text
|
17
|
-
if default.blank?
|
18
|
-
return null ? nil : ''
|
19
|
-
else
|
20
|
-
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
|
21
|
-
end
|
22
|
-
elsif missing_default_forged_as_empty_string?(default)
|
23
|
-
nil
|
24
|
-
else
|
25
|
-
super
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def has_default?
|
30
|
-
return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns
|
31
|
-
super
|
32
|
-
end
|
33
|
-
|
34
|
-
# Must return the relevant concrete adapter
|
35
|
-
def adapter
|
36
|
-
raise NotImplementedError
|
37
|
-
end
|
38
|
-
|
39
|
-
def case_sensitive?
|
40
|
-
collation && !collation.match(/_ci$/)
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def simplified_type(field_type)
|
46
|
-
return :boolean if adapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
|
47
|
-
|
48
|
-
case field_type
|
49
|
-
when /enum/i, /set/i then :string
|
50
|
-
when /year/i then :integer
|
51
|
-
when /bit/i then :binary
|
52
|
-
else
|
53
|
-
super
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def extract_limit(sql_type)
|
58
|
-
case sql_type
|
59
|
-
when /blob|text/i
|
60
|
-
case sql_type
|
61
|
-
when /tiny/i
|
62
|
-
255
|
63
|
-
when /medium/i
|
64
|
-
16777215
|
65
|
-
when /long/i
|
66
|
-
2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
|
67
|
-
else
|
68
|
-
super # we could return 65535 here, but we leave it undecorated by default
|
69
|
-
end
|
70
|
-
when /^bigint/i; 8
|
71
|
-
when /^int/i; 4
|
72
|
-
when /^mediumint/i; 3
|
73
|
-
when /^smallint/i; 2
|
74
|
-
when /^tinyint/i; 1
|
75
|
-
when /^enum\((.+)\)/i
|
76
|
-
$1.split(',').map{|enum| enum.strip.length - 2}.max
|
77
|
-
else
|
78
|
-
super
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# MySQL misreports NOT NULL column default when none is given.
|
83
|
-
# We can't detect this for columns which may have a legitimate ''
|
84
|
-
# default (string) but we can for others (integer, datetime, boolean,
|
85
|
-
# and the rest).
|
86
|
-
#
|
87
|
-
# Test whether the column has default '', is not null, and is not
|
88
|
-
# a type allowing default ''.
|
89
|
-
def missing_default_forged_as_empty_string?(default)
|
90
|
-
type != :string && !null && default == ''
|
91
|
-
end
|
92
|
-
end
|
19
|
+
include MySQL::Quoting
|
20
|
+
include MySQL::SchemaStatements
|
93
21
|
|
94
22
|
##
|
95
23
|
# :singleton-method:
|
96
|
-
# By default, the
|
97
|
-
# as boolean. If you wish to disable this emulation
|
98
|
-
# behavior in versions 0.13.1 and earlier) you can add the following line
|
24
|
+
# By default, the Mysql2Adapter will consider all columns of type <tt>tinyint(1)</tt>
|
25
|
+
# as boolean. If you wish to disable this emulation you can add the following line
|
99
26
|
# to your application.rb file:
|
100
27
|
#
|
101
|
-
# ActiveRecord::ConnectionAdapters::
|
102
|
-
class_attribute :emulate_booleans
|
103
|
-
self.emulate_booleans = true
|
104
|
-
|
105
|
-
LOST_CONNECTION_ERROR_MESSAGES = [
|
106
|
-
"Server shutdown in progress",
|
107
|
-
"Broken pipe",
|
108
|
-
"Lost connection to MySQL server during query",
|
109
|
-
"MySQL server has gone away" ]
|
110
|
-
|
111
|
-
QUOTED_TRUE, QUOTED_FALSE = '1', '0'
|
28
|
+
# ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans = false
|
29
|
+
class_attribute :emulate_booleans, default: true
|
112
30
|
|
113
31
|
NATIVE_DATABASE_TYPES = {
|
114
|
-
:
|
115
|
-
:
|
116
|
-
:
|
117
|
-
:
|
118
|
-
:
|
119
|
-
:
|
120
|
-
:
|
121
|
-
:
|
122
|
-
:
|
123
|
-
:
|
124
|
-
:
|
125
|
-
:
|
32
|
+
primary_key: "bigint auto_increment PRIMARY KEY",
|
33
|
+
string: { name: "varchar", limit: 255 },
|
34
|
+
text: { name: "text", limit: 65535 },
|
35
|
+
integer: { name: "int", limit: 4 },
|
36
|
+
float: { name: "float", limit: 24 },
|
37
|
+
decimal: { name: "decimal" },
|
38
|
+
datetime: { name: "datetime" },
|
39
|
+
timestamp: { name: "timestamp" },
|
40
|
+
time: { name: "time" },
|
41
|
+
date: { name: "date" },
|
42
|
+
binary: { name: "blob", limit: 65535 },
|
43
|
+
boolean: { name: "tinyint", limit: 1 },
|
44
|
+
json: { name: "json" },
|
126
45
|
}
|
127
46
|
|
128
|
-
class
|
129
|
-
|
47
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
48
|
+
private def dealloc(stmt)
|
49
|
+
stmt[:stmt].close
|
50
|
+
end
|
130
51
|
end
|
131
52
|
|
132
|
-
# FIXME: Make the first parameter more similar for the two adapters
|
133
53
|
def initialize(connection, logger, connection_options, config)
|
134
|
-
super(connection, logger)
|
135
|
-
@connection_options, @config = connection_options, config
|
136
|
-
@quoted_column_names, @quoted_table_names = {}, {}
|
54
|
+
super(connection, logger, config)
|
137
55
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
56
|
+
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
57
|
+
|
58
|
+
if version < "5.1.10"
|
59
|
+
raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.1.10."
|
142
60
|
end
|
143
61
|
end
|
144
62
|
|
145
|
-
def
|
146
|
-
|
63
|
+
def version #:nodoc:
|
64
|
+
@version ||= Version.new(version_string)
|
147
65
|
end
|
148
66
|
|
149
|
-
|
150
|
-
|
151
|
-
true
|
67
|
+
def mariadb? # :nodoc:
|
68
|
+
/mariadb/i.match?(full_version)
|
152
69
|
end
|
153
70
|
|
154
|
-
def
|
71
|
+
def supports_bulk_alter? #:nodoc:
|
155
72
|
true
|
156
73
|
end
|
157
74
|
|
158
|
-
|
159
|
-
|
75
|
+
def supports_index_sort_order?
|
76
|
+
!mariadb? && version >= "8.0.1"
|
77
|
+
end
|
78
|
+
|
79
|
+
def supports_transaction_isolation?
|
160
80
|
true
|
161
81
|
end
|
162
82
|
|
163
|
-
def
|
83
|
+
def supports_explain?
|
164
84
|
true
|
165
85
|
end
|
166
86
|
|
167
|
-
|
168
|
-
# but at the moment (5.5) it doesn't yet implement them
|
169
|
-
def supports_index_sort_order?
|
87
|
+
def supports_indexes_in_create?
|
170
88
|
true
|
171
89
|
end
|
172
90
|
|
173
|
-
def
|
174
|
-
|
91
|
+
def supports_foreign_keys?
|
92
|
+
true
|
175
93
|
end
|
176
94
|
|
177
|
-
|
95
|
+
def supports_views?
|
96
|
+
true
|
97
|
+
end
|
178
98
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
99
|
+
def supports_datetime_with_precision?
|
100
|
+
if mariadb?
|
101
|
+
version >= "5.3.0"
|
102
|
+
else
|
103
|
+
version >= "5.6.4"
|
104
|
+
end
|
183
105
|
end
|
184
106
|
|
185
|
-
|
186
|
-
|
187
|
-
|
107
|
+
def supports_virtual_columns?
|
108
|
+
if mariadb?
|
109
|
+
version >= "5.2.0"
|
110
|
+
else
|
111
|
+
version >= "5.7.5"
|
112
|
+
end
|
188
113
|
end
|
189
114
|
|
190
|
-
|
191
|
-
|
192
|
-
def error_number(exception) # :nodoc:
|
193
|
-
raise NotImplementedError
|
115
|
+
def supports_advisory_locks?
|
116
|
+
true
|
194
117
|
end
|
195
118
|
|
196
|
-
#
|
119
|
+
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
120
|
+
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
121
|
+
end
|
197
122
|
|
198
|
-
def
|
199
|
-
|
200
|
-
s = column.class.string_to_binary(value).unpack("H*")[0]
|
201
|
-
"x'#{s}'"
|
202
|
-
elsif value.kind_of?(BigDecimal)
|
203
|
-
value.to_s("F")
|
204
|
-
else
|
205
|
-
super
|
206
|
-
end
|
123
|
+
def release_advisory_lock(lock_name) # :nodoc:
|
124
|
+
query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
|
207
125
|
end
|
208
126
|
|
209
|
-
def
|
210
|
-
|
127
|
+
def native_database_types
|
128
|
+
NATIVE_DATABASE_TYPES
|
211
129
|
end
|
212
130
|
|
213
|
-
def
|
214
|
-
|
131
|
+
def index_algorithms
|
132
|
+
{ default: "ALGORITHM = DEFAULT".dup, copy: "ALGORITHM = COPY".dup, inplace: "ALGORITHM = INPLACE".dup }
|
215
133
|
end
|
216
134
|
|
217
|
-
|
218
|
-
|
135
|
+
# HELPER METHODS ===========================================
|
136
|
+
|
137
|
+
# The two drivers have slightly different ways of yielding hashes of results, so
|
138
|
+
# this method must be implemented to provide a uniform interface.
|
139
|
+
def each_hash(result) # :nodoc:
|
140
|
+
raise NotImplementedError
|
219
141
|
end
|
220
142
|
|
221
|
-
|
222
|
-
|
143
|
+
# Must return the MySQL error number from the exception, if the exception has an
|
144
|
+
# error number.
|
145
|
+
def error_number(exception) # :nodoc:
|
146
|
+
raise NotImplementedError
|
223
147
|
end
|
224
148
|
|
225
149
|
# REFERENTIAL INTEGRITY ====================================
|
226
150
|
|
227
|
-
def disable_referential_integrity
|
228
|
-
old =
|
151
|
+
def disable_referential_integrity #:nodoc:
|
152
|
+
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
229
153
|
|
230
154
|
begin
|
231
155
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
@@ -235,121 +159,99 @@ module ActiveRecord
|
|
235
159
|
end
|
236
160
|
end
|
237
161
|
|
162
|
+
# CONNECTION MANAGEMENT ====================================
|
163
|
+
|
164
|
+
# Clears the prepared statements cache.
|
165
|
+
def clear_cache!
|
166
|
+
reload_type_map
|
167
|
+
@statements.clear
|
168
|
+
end
|
169
|
+
|
170
|
+
#--
|
238
171
|
# DATABASE STATEMENTS ======================================
|
172
|
+
#++
|
173
|
+
|
174
|
+
def explain(arel, binds = [])
|
175
|
+
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
176
|
+
start = Time.now
|
177
|
+
result = exec_query(sql, "EXPLAIN", binds)
|
178
|
+
elapsed = Time.now - start
|
179
|
+
|
180
|
+
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
181
|
+
end
|
239
182
|
|
240
183
|
# Executes the SQL statement in the context of this connection.
|
241
184
|
def execute(sql, name = nil)
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
end
|
247
|
-
rescue ActiveRecord::StatementInvalid => exception
|
248
|
-
if exception.message.split(":").first =~ /Packets out of order/
|
249
|
-
raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
|
250
|
-
else
|
251
|
-
raise
|
185
|
+
log(sql, name) do
|
186
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
187
|
+
@connection.query(sql)
|
188
|
+
end
|
252
189
|
end
|
253
190
|
end
|
254
191
|
|
255
|
-
#
|
256
|
-
# stuff in
|
257
|
-
# explicitly freed or not.
|
258
|
-
def execute_and_free(sql, name = nil)
|
192
|
+
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
193
|
+
# to write stuff in an abstract way without concerning ourselves about whether it
|
194
|
+
# needs to be explicitly freed or not.
|
195
|
+
def execute_and_free(sql, name = nil) # :nodoc:
|
259
196
|
yield execute(sql, name)
|
260
197
|
end
|
261
198
|
|
262
|
-
def update_sql(sql, name = nil) #:nodoc:
|
263
|
-
super
|
264
|
-
@connection.affected_rows
|
265
|
-
end
|
266
|
-
|
267
199
|
def begin_db_transaction
|
268
200
|
execute "BEGIN"
|
269
|
-
|
270
|
-
|
201
|
+
end
|
202
|
+
|
203
|
+
def begin_isolated_db_transaction(isolation)
|
204
|
+
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
205
|
+
begin_db_transaction
|
271
206
|
end
|
272
207
|
|
273
208
|
def commit_db_transaction #:nodoc:
|
274
209
|
execute "COMMIT"
|
275
|
-
rescue Exception
|
276
|
-
# Transactions aren't supported
|
277
210
|
end
|
278
211
|
|
279
|
-
def
|
212
|
+
def exec_rollback_db_transaction #:nodoc:
|
280
213
|
execute "ROLLBACK"
|
281
|
-
rescue Exception
|
282
|
-
# Transactions aren't supported
|
283
|
-
end
|
284
|
-
|
285
|
-
def create_savepoint
|
286
|
-
execute("SAVEPOINT #{current_savepoint_name}")
|
287
|
-
end
|
288
|
-
|
289
|
-
def rollback_to_savepoint
|
290
|
-
execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
|
291
|
-
end
|
292
|
-
|
293
|
-
def release_savepoint
|
294
|
-
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
|
295
214
|
end
|
296
215
|
|
297
216
|
# In the simple case, MySQL allows us to place JOINs directly into the UPDATE
|
298
217
|
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
299
|
-
# these, we must use a subquery.
|
300
|
-
|
301
|
-
# in the form of a subsubquery. Ugh!
|
302
|
-
def join_to_update(update, select) #:nodoc:
|
218
|
+
# these, we must use a subquery.
|
219
|
+
def join_to_update(update, select, key) # :nodoc:
|
303
220
|
if select.limit || select.offset || select.orders.any?
|
304
|
-
|
305
|
-
subsubselect.projections = [update.key]
|
306
|
-
|
307
|
-
subselect = Arel::SelectManager.new(select.engine)
|
308
|
-
subselect.project Arel.sql(update.key.name)
|
309
|
-
subselect.from subsubselect.as('__active_record_temp')
|
310
|
-
|
311
|
-
update.where update.key.in(subselect)
|
221
|
+
super
|
312
222
|
else
|
313
223
|
update.table select.source
|
314
224
|
update.wheres = select.constraints
|
315
225
|
end
|
316
226
|
end
|
317
227
|
|
318
|
-
|
319
|
-
|
320
|
-
def structure_dump #:nodoc:
|
321
|
-
if supports_views?
|
322
|
-
sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
|
323
|
-
else
|
324
|
-
sql = "SHOW TABLES"
|
325
|
-
end
|
326
|
-
|
327
|
-
select_all(sql).map { |table|
|
328
|
-
table.delete('Table_type')
|
329
|
-
sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
|
330
|
-
exec_query(sql).first['Create Table'] + ";\n\n"
|
331
|
-
}.join
|
228
|
+
def empty_insert_statement_value
|
229
|
+
"VALUES ()"
|
332
230
|
end
|
333
231
|
|
232
|
+
# SCHEMA STATEMENTS ========================================
|
233
|
+
|
334
234
|
# Drops the database specified on the +name+ attribute
|
335
235
|
# and creates it again using the provided +options+.
|
336
236
|
def recreate_database(name, options = {})
|
337
237
|
drop_database(name)
|
338
|
-
create_database(name, options)
|
238
|
+
sql = create_database(name, options)
|
239
|
+
reconnect!
|
240
|
+
sql
|
339
241
|
end
|
340
242
|
|
341
243
|
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
342
244
|
# Charset defaults to utf8.
|
343
245
|
#
|
344
246
|
# Example:
|
345
|
-
# create_database 'charset_test', :
|
247
|
+
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
|
346
248
|
# create_database 'matt_development'
|
347
|
-
# create_database 'matt_development', :
|
249
|
+
# create_database 'matt_development', charset: :big5
|
348
250
|
def create_database(name, options = {})
|
349
251
|
if options[:collation]
|
350
|
-
execute "CREATE DATABASE
|
252
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
|
351
253
|
else
|
352
|
-
execute "CREATE DATABASE
|
254
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')}"
|
353
255
|
end
|
354
256
|
end
|
355
257
|
|
@@ -358,319 +260,628 @@ module ActiveRecord
|
|
358
260
|
# Example:
|
359
261
|
# drop_database('sebastian_development')
|
360
262
|
def drop_database(name) #:nodoc:
|
361
|
-
execute "DROP DATABASE IF EXISTS
|
263
|
+
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
362
264
|
end
|
363
265
|
|
364
266
|
def current_database
|
365
|
-
|
267
|
+
query_value("SELECT database()", "SCHEMA")
|
366
268
|
end
|
367
269
|
|
368
270
|
# Returns the database character set.
|
369
271
|
def charset
|
370
|
-
show_variable
|
272
|
+
show_variable "character_set_database"
|
371
273
|
end
|
372
274
|
|
373
275
|
# Returns the database collation strategy.
|
374
276
|
def collation
|
375
|
-
show_variable
|
277
|
+
show_variable "collation_database"
|
376
278
|
end
|
377
279
|
|
378
|
-
def
|
379
|
-
|
380
|
-
sql << "IN #{quote_table_name(database)} " if database
|
381
|
-
sql << "LIKE #{quote(like)}" if like
|
382
|
-
|
383
|
-
execute_and_free(sql, 'SCHEMA') do |result|
|
384
|
-
result.collect { |field| field.first }
|
385
|
-
end
|
280
|
+
def truncate(table_name, name = nil)
|
281
|
+
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
386
282
|
end
|
387
283
|
|
388
|
-
def
|
389
|
-
|
390
|
-
return true if tables(nil, nil, name).any?
|
391
|
-
|
392
|
-
name = name.to_s
|
393
|
-
schema, table = name.split('.', 2)
|
394
|
-
|
395
|
-
unless table # A table was provided without a schema
|
396
|
-
table = schema
|
397
|
-
schema = nil
|
398
|
-
end
|
284
|
+
def table_comment(table_name) # :nodoc:
|
285
|
+
scope = quoted_scope(table_name)
|
399
286
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
current_index = nil
|
407
|
-
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
|
408
|
-
each_hash(result) do |row|
|
409
|
-
if current_index != row[:Key_name]
|
410
|
-
next if row[:Key_name] == 'PRIMARY' # skip the primary key
|
411
|
-
current_index = row[:Key_name]
|
412
|
-
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [])
|
413
|
-
end
|
414
|
-
|
415
|
-
indexes.last.columns << row[:Column_name]
|
416
|
-
indexes.last.lengths << row[:Sub_part]
|
417
|
-
end
|
418
|
-
end
|
419
|
-
|
420
|
-
indexes
|
421
|
-
end
|
422
|
-
|
423
|
-
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
424
|
-
def columns(table_name, name = nil)#:nodoc:
|
425
|
-
sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
|
426
|
-
execute_and_free(sql, 'SCHEMA') do |result|
|
427
|
-
each_hash(result).map do |field|
|
428
|
-
new_column(field[:Field], field[:Default], field[:Type], field[:Null] == "YES", field[:Collation])
|
429
|
-
end
|
430
|
-
end
|
431
|
-
end
|
432
|
-
|
433
|
-
def create_table(table_name, options = {}) #:nodoc:
|
434
|
-
super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
|
287
|
+
query_value(<<-SQL.strip_heredoc, "SCHEMA").presence
|
288
|
+
SELECT table_comment
|
289
|
+
FROM information_schema.tables
|
290
|
+
WHERE table_schema = #{scope[:schema]}
|
291
|
+
AND table_name = #{scope[:name]}
|
292
|
+
SQL
|
435
293
|
end
|
436
294
|
|
437
295
|
def bulk_change_table(table_name, operations) #:nodoc:
|
438
|
-
sqls = operations.
|
296
|
+
sqls = operations.flat_map do |command, args|
|
439
297
|
table, arguments = args.shift, args
|
440
|
-
method = :"#{command}
|
298
|
+
method = :"#{command}_for_alter"
|
441
299
|
|
442
300
|
if respond_to?(method, true)
|
443
301
|
send(method, table, *arguments)
|
444
302
|
else
|
445
303
|
raise "Unknown method called : #{method}(#{arguments.inspect})"
|
446
304
|
end
|
447
|
-
end.
|
305
|
+
end.join(", ")
|
448
306
|
|
449
307
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
450
308
|
end
|
451
309
|
|
310
|
+
def change_table_comment(table_name, comment) #:nodoc:
|
311
|
+
comment = "" if comment.nil?
|
312
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
|
313
|
+
end
|
314
|
+
|
452
315
|
# Renames a table.
|
453
316
|
#
|
454
317
|
# Example:
|
455
318
|
# rename_table('octopuses', 'octopi')
|
456
319
|
def rename_table(table_name, new_name)
|
457
320
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
321
|
+
rename_table_indexes(table_name, new_name)
|
458
322
|
end
|
459
323
|
|
460
|
-
|
461
|
-
|
324
|
+
# Drops a table from the database.
|
325
|
+
#
|
326
|
+
# [<tt>:force</tt>]
|
327
|
+
# Set to +:cascade+ to drop dependent objects as well.
|
328
|
+
# Defaults to false.
|
329
|
+
# [<tt>:if_exists</tt>]
|
330
|
+
# Set to +true+ to only drop the table if it exists.
|
331
|
+
# Defaults to false.
|
332
|
+
# [<tt>:temporary</tt>]
|
333
|
+
# Set to +true+ to drop temporary table.
|
334
|
+
# Defaults to false.
|
335
|
+
#
|
336
|
+
# Although this command ignores most +options+ and the block if one is given,
|
337
|
+
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
338
|
+
# In that case, +options+ and the block will be used by create_table.
|
339
|
+
def drop_table(table_name, options = {})
|
340
|
+
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
462
341
|
end
|
463
342
|
|
464
|
-
def
|
465
|
-
|
466
|
-
|
343
|
+
def rename_index(table_name, old_name, new_name)
|
344
|
+
if supports_rename_index?
|
345
|
+
validate_index_length!(table_name, new_name)
|
346
|
+
|
347
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
|
348
|
+
else
|
349
|
+
super
|
350
|
+
end
|
467
351
|
end
|
468
352
|
|
469
|
-
def
|
470
|
-
|
353
|
+
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
|
354
|
+
default = extract_new_default_value(default_or_changes)
|
355
|
+
change_column table_name, column_name, nil, default: default
|
356
|
+
end
|
471
357
|
|
358
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
472
359
|
unless null || default.nil?
|
473
360
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
474
361
|
end
|
475
362
|
|
476
|
-
change_column table_name, column_name,
|
363
|
+
change_column table_name, column_name, nil, null: null
|
364
|
+
end
|
365
|
+
|
366
|
+
def change_column_comment(table_name, column_name, comment) #:nodoc:
|
367
|
+
change_column table_name, column_name, nil, comment: comment
|
477
368
|
end
|
478
369
|
|
479
370
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
480
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{
|
371
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}")
|
481
372
|
end
|
482
373
|
|
483
374
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
484
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{
|
375
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
376
|
+
rename_column_indexes(table_name, column_name, new_column_name)
|
377
|
+
end
|
378
|
+
|
379
|
+
def add_index(table_name, column_name, options = {}) #:nodoc:
|
380
|
+
index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
|
381
|
+
sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}".dup
|
382
|
+
execute add_sql_comment!(sql, comment)
|
383
|
+
end
|
384
|
+
|
385
|
+
def add_sql_comment!(sql, comment) # :nodoc:
|
386
|
+
sql << " COMMENT #{quote(comment)}" if comment.present?
|
387
|
+
sql
|
388
|
+
end
|
389
|
+
|
390
|
+
def foreign_keys(table_name)
|
391
|
+
raise ArgumentError unless table_name.present?
|
392
|
+
|
393
|
+
scope = quoted_scope(table_name)
|
394
|
+
|
395
|
+
fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
|
396
|
+
SELECT fk.referenced_table_name AS 'to_table',
|
397
|
+
fk.referenced_column_name AS 'primary_key',
|
398
|
+
fk.column_name AS 'column',
|
399
|
+
fk.constraint_name AS 'name',
|
400
|
+
rc.update_rule AS 'on_update',
|
401
|
+
rc.delete_rule AS 'on_delete'
|
402
|
+
FROM information_schema.referential_constraints rc
|
403
|
+
JOIN information_schema.key_column_usage fk
|
404
|
+
USING (constraint_schema, constraint_name)
|
405
|
+
WHERE fk.referenced_column_name IS NOT NULL
|
406
|
+
AND fk.table_schema = #{scope[:schema]}
|
407
|
+
AND fk.table_name = #{scope[:name]}
|
408
|
+
AND rc.constraint_schema = #{scope[:schema]}
|
409
|
+
AND rc.table_name = #{scope[:name]}
|
410
|
+
SQL
|
411
|
+
|
412
|
+
fk_info.map do |row|
|
413
|
+
options = {
|
414
|
+
column: row["column"],
|
415
|
+
name: row["name"],
|
416
|
+
primary_key: row["primary_key"]
|
417
|
+
}
|
418
|
+
|
419
|
+
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
420
|
+
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
421
|
+
|
422
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
def table_options(table_name) # :nodoc:
|
427
|
+
table_options = {}
|
428
|
+
|
429
|
+
create_table_info = create_table_info(table_name)
|
430
|
+
|
431
|
+
# strip create_definitions and partition_options
|
432
|
+
raw_table_options = create_table_info.sub(/\A.*\n\) /m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
|
433
|
+
|
434
|
+
# strip AUTO_INCREMENT
|
435
|
+
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
|
436
|
+
|
437
|
+
table_options[:options] = raw_table_options
|
438
|
+
|
439
|
+
# strip COMMENT
|
440
|
+
if raw_table_options.sub!(/ COMMENT='.+'/, "")
|
441
|
+
table_options[:comment] = table_comment(table_name)
|
442
|
+
end
|
443
|
+
|
444
|
+
table_options
|
485
445
|
end
|
486
446
|
|
487
447
|
# Maps logical Rails types to MySQL-specific data types.
|
488
|
-
def type_to_sql(type, limit
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
when
|
494
|
-
|
495
|
-
when
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
else raise(ActiveRecordError, "No text type has character length #{limit}")
|
448
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **) # :nodoc:
|
449
|
+
sql = \
|
450
|
+
case type.to_s
|
451
|
+
when "integer"
|
452
|
+
integer_to_sql(limit)
|
453
|
+
when "text"
|
454
|
+
text_to_sql(limit)
|
455
|
+
when "blob"
|
456
|
+
binary_to_sql(limit)
|
457
|
+
when "binary"
|
458
|
+
if (0..0xfff) === limit
|
459
|
+
"varbinary(#{limit})"
|
460
|
+
else
|
461
|
+
binary_to_sql(limit)
|
462
|
+
end
|
463
|
+
else
|
464
|
+
super
|
506
465
|
end
|
466
|
+
|
467
|
+
sql = "#{sql} unsigned" if unsigned && type != :primary_key
|
468
|
+
sql
|
469
|
+
end
|
470
|
+
|
471
|
+
# SHOW VARIABLES LIKE 'name'
|
472
|
+
def show_variable(name)
|
473
|
+
query_value("SELECT @@#{name}", "SCHEMA")
|
474
|
+
rescue ActiveRecord::StatementInvalid
|
475
|
+
nil
|
476
|
+
end
|
477
|
+
|
478
|
+
def primary_keys(table_name) # :nodoc:
|
479
|
+
raise ArgumentError unless table_name.present?
|
480
|
+
|
481
|
+
scope = quoted_scope(table_name)
|
482
|
+
|
483
|
+
query_values(<<-SQL.strip_heredoc, "SCHEMA")
|
484
|
+
SELECT column_name
|
485
|
+
FROM information_schema.key_column_usage
|
486
|
+
WHERE constraint_name = 'PRIMARY'
|
487
|
+
AND table_schema = #{scope[:schema]}
|
488
|
+
AND table_name = #{scope[:name]}
|
489
|
+
ORDER BY ordinal_position
|
490
|
+
SQL
|
491
|
+
end
|
492
|
+
|
493
|
+
def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
|
494
|
+
if column.collation && !column.case_sensitive?
|
495
|
+
table[attribute].eq(Arel::Nodes::Bin.new(value))
|
507
496
|
else
|
508
497
|
super
|
509
498
|
end
|
510
499
|
end
|
511
500
|
|
512
|
-
def
|
513
|
-
|
514
|
-
sql << " FIRST"
|
515
|
-
elsif options[:after]
|
516
|
-
sql << " AFTER #{quote_column_name(options[:after])}"
|
517
|
-
end
|
501
|
+
def can_perform_case_insensitive_comparison_for?(column)
|
502
|
+
column.case_sensitive?
|
518
503
|
end
|
504
|
+
private :can_perform_case_insensitive_comparison_for?
|
519
505
|
|
520
|
-
#
|
521
|
-
|
522
|
-
|
523
|
-
|
506
|
+
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
|
507
|
+
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
|
508
|
+
# distinct queries, and requires that the ORDER BY include the distinct column.
|
509
|
+
# See https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
|
510
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
511
|
+
order_columns = orders.reject(&:blank?).map { |s|
|
512
|
+
# Convert Arel node to string
|
513
|
+
s = s.to_sql unless s.is_a?(String)
|
514
|
+
# Remove any ASC/DESC modifiers
|
515
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
516
|
+
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
517
|
+
|
518
|
+
(order_columns << super).join(", ")
|
524
519
|
end
|
525
520
|
|
526
|
-
|
527
|
-
|
528
|
-
execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
|
529
|
-
create_table = each_hash(result).first[:"Create Table"]
|
530
|
-
if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
|
531
|
-
keys = $1.split(",").map { |key| key.gsub(/[`"]/, "") }
|
532
|
-
keys.length == 1 ? [keys.first, nil] : nil
|
533
|
-
else
|
534
|
-
nil
|
535
|
-
end
|
536
|
-
end
|
521
|
+
def strict_mode?
|
522
|
+
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
537
523
|
end
|
538
524
|
|
539
|
-
|
540
|
-
|
541
|
-
pk_and_sequence = pk_and_sequence_for(table)
|
542
|
-
pk_and_sequence && pk_and_sequence.first
|
525
|
+
def default_index_type?(index) # :nodoc:
|
526
|
+
index.using == :btree || super
|
543
527
|
end
|
544
528
|
|
545
|
-
def
|
546
|
-
|
529
|
+
def insert_fixtures_set(fixture_set, tables_to_delete = [])
|
530
|
+
with_multi_statements do
|
531
|
+
super { discard_remaining_results }
|
532
|
+
end
|
547
533
|
end
|
548
534
|
|
549
|
-
|
550
|
-
|
535
|
+
private
|
536
|
+
def combine_multi_statements(total_sql)
|
537
|
+
total_sql.each_with_object([]) do |sql, total_sql_chunks|
|
538
|
+
previous_packet = total_sql_chunks.last
|
539
|
+
sql << ";\n"
|
540
|
+
if max_allowed_packet_reached?(sql, previous_packet) || total_sql_chunks.empty?
|
541
|
+
total_sql_chunks << sql
|
542
|
+
else
|
543
|
+
previous_packet << sql
|
544
|
+
end
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
def max_allowed_packet_reached?(current_packet, previous_packet)
|
549
|
+
if current_packet.bytesize > max_allowed_packet
|
550
|
+
raise ActiveRecordError, "Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
|
551
|
+
elsif previous_packet.nil?
|
552
|
+
false
|
553
|
+
else
|
554
|
+
(current_packet.bytesize + previous_packet.bytesize) > max_allowed_packet
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
def max_allowed_packet
|
559
|
+
bytes_margin = 2
|
560
|
+
@max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
|
561
|
+
end
|
562
|
+
|
563
|
+
def initialize_type_map(m = type_map)
|
551
564
|
super
|
552
|
-
|
553
|
-
|
565
|
+
|
566
|
+
register_class_with_limit m, %r(char)i, MysqlString
|
567
|
+
|
568
|
+
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
569
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
570
|
+
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
571
|
+
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
572
|
+
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
573
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
574
|
+
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
575
|
+
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
576
|
+
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
577
|
+
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
578
|
+
|
579
|
+
register_integer_type m, %r(^bigint)i, limit: 8
|
580
|
+
register_integer_type m, %r(^int)i, limit: 4
|
581
|
+
register_integer_type m, %r(^mediumint)i, limit: 3
|
582
|
+
register_integer_type m, %r(^smallint)i, limit: 2
|
583
|
+
register_integer_type m, %r(^tinyint)i, limit: 1
|
584
|
+
|
585
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
|
586
|
+
m.alias_type %r(year)i, "integer"
|
587
|
+
m.alias_type %r(bit)i, "binary"
|
588
|
+
|
589
|
+
m.register_type(%r(enum)i) do |sql_type|
|
590
|
+
limit = sql_type[/^enum\((.+)\)/i, 1]
|
591
|
+
.split(",").map { |enum| enum.strip.length - 2 }.max
|
592
|
+
MysqlString.new(limit: limit)
|
593
|
+
end
|
594
|
+
|
595
|
+
m.register_type(%r(^set)i) do |sql_type|
|
596
|
+
limit = sql_type[/^set\((.+)\)/i, 1]
|
597
|
+
.split(",").map { |set| set.strip.length - 1 }.sum - 1
|
598
|
+
MysqlString.new(limit: limit)
|
599
|
+
end
|
554
600
|
end
|
555
|
-
end
|
556
601
|
|
557
|
-
|
558
|
-
|
559
|
-
|
602
|
+
def register_integer_type(mapping, key, options)
|
603
|
+
mapping.register_type(key) do |sql_type|
|
604
|
+
if /\bunsigned\b/.match?(sql_type)
|
605
|
+
Type::UnsignedInteger.new(options)
|
606
|
+
else
|
607
|
+
Type::Integer.new(options)
|
608
|
+
end
|
609
|
+
end
|
610
|
+
end
|
560
611
|
|
561
|
-
|
612
|
+
def extract_precision(sql_type)
|
613
|
+
if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
|
614
|
+
super || 0
|
615
|
+
else
|
616
|
+
super
|
617
|
+
end
|
618
|
+
end
|
562
619
|
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
620
|
+
# See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
|
621
|
+
ER_DUP_ENTRY = 1062
|
622
|
+
ER_NOT_NULL_VIOLATION = 1048
|
623
|
+
ER_DO_NOT_HAVE_DEFAULT = 1364
|
624
|
+
ER_NO_REFERENCED_ROW_2 = 1452
|
625
|
+
ER_DATA_TOO_LONG = 1406
|
626
|
+
ER_OUT_OF_RANGE = 1264
|
627
|
+
ER_LOCK_DEADLOCK = 1213
|
628
|
+
ER_CANNOT_ADD_FOREIGN = 1215
|
629
|
+
ER_CANNOT_CREATE_TABLE = 1005
|
630
|
+
ER_LOCK_WAIT_TIMEOUT = 1205
|
631
|
+
ER_QUERY_INTERRUPTED = 1317
|
632
|
+
ER_QUERY_TIMEOUT = 3024
|
633
|
+
|
634
|
+
def translate_exception(exception, message)
|
635
|
+
case error_number(exception)
|
636
|
+
when ER_DUP_ENTRY
|
637
|
+
RecordNotUnique.new(message)
|
638
|
+
when ER_NO_REFERENCED_ROW_2
|
639
|
+
InvalidForeignKey.new(message)
|
640
|
+
when ER_CANNOT_ADD_FOREIGN
|
641
|
+
mismatched_foreign_key(message)
|
642
|
+
when ER_CANNOT_CREATE_TABLE
|
643
|
+
if message.include?("errno: 150")
|
644
|
+
mismatched_foreign_key(message)
|
645
|
+
else
|
646
|
+
super
|
647
|
+
end
|
648
|
+
when ER_DATA_TOO_LONG
|
649
|
+
ValueTooLong.new(message)
|
650
|
+
when ER_OUT_OF_RANGE
|
651
|
+
RangeError.new(message)
|
652
|
+
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
653
|
+
NotNullViolation.new(message)
|
654
|
+
when ER_LOCK_DEADLOCK
|
655
|
+
Deadlocked.new(message)
|
656
|
+
when ER_LOCK_WAIT_TIMEOUT
|
657
|
+
LockWaitTimeout.new(message)
|
658
|
+
when ER_QUERY_TIMEOUT
|
659
|
+
StatementTimeout.new(message)
|
660
|
+
when ER_QUERY_INTERRUPTED
|
661
|
+
QueryCanceled.new(message)
|
662
|
+
else
|
663
|
+
super
|
570
664
|
end
|
571
665
|
end
|
572
666
|
|
573
|
-
|
574
|
-
|
667
|
+
def change_column_for_alter(table_name, column_name, type, options = {})
|
668
|
+
column = column_for(table_name, column_name)
|
669
|
+
type ||= column.sql_type
|
575
670
|
|
576
|
-
|
577
|
-
|
671
|
+
unless options.key?(:default)
|
672
|
+
options[:default] = column.default
|
673
|
+
end
|
578
674
|
|
579
|
-
|
580
|
-
|
675
|
+
unless options.key?(:null)
|
676
|
+
options[:null] = column.null
|
677
|
+
end
|
581
678
|
|
582
|
-
|
583
|
-
|
679
|
+
unless options.key?(:comment)
|
680
|
+
options[:comment] = column.comment
|
681
|
+
end
|
584
682
|
|
585
|
-
|
586
|
-
|
683
|
+
td = create_table_definition(table_name)
|
684
|
+
cd = td.new_column_definition(column.name, type, options)
|
685
|
+
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
686
|
+
end
|
587
687
|
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
688
|
+
def rename_column_for_alter(table_name, column_name, new_column_name)
|
689
|
+
column = column_for(table_name, column_name)
|
690
|
+
options = {
|
691
|
+
default: column.default,
|
692
|
+
null: column.null,
|
693
|
+
auto_increment: column.auto_increment?
|
694
|
+
}
|
695
|
+
|
696
|
+
current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
697
|
+
td = create_table_definition(table_name)
|
698
|
+
cd = td.new_column_definition(new_column_name, current_type, options)
|
699
|
+
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
596
700
|
end
|
597
|
-
end
|
598
701
|
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
end
|
702
|
+
def add_index_for_alter(table_name, column_name, options = {})
|
703
|
+
index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
|
704
|
+
index_algorithm[0, 0] = ", " if index_algorithm.present?
|
705
|
+
"ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
|
706
|
+
end
|
605
707
|
|
606
|
-
|
607
|
-
|
708
|
+
def remove_index_for_alter(table_name, options = {})
|
709
|
+
index_name = index_name_for_remove(table_name, options)
|
710
|
+
"DROP INDEX #{quote_column_name(index_name)}"
|
711
|
+
end
|
608
712
|
|
609
|
-
|
610
|
-
|
713
|
+
def add_timestamps_for_alter(table_name, options = {})
|
714
|
+
[add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
|
611
715
|
end
|
612
716
|
|
613
|
-
|
614
|
-
|
717
|
+
def remove_timestamps_for_alter(table_name, options = {})
|
718
|
+
[remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
|
615
719
|
end
|
616
720
|
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
721
|
+
# MySQL is too stupid to create a temporary table for use subquery, so we have
|
722
|
+
# to give it some prompting in the form of a subsubquery. Ugh!
|
723
|
+
def subquery_for(key, select)
|
724
|
+
subselect = select.clone
|
725
|
+
subselect.projections = [key]
|
622
726
|
|
623
|
-
|
624
|
-
|
727
|
+
# Materialize subquery by adding distinct
|
728
|
+
# to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
|
729
|
+
subselect.distinct unless select.limit || select.offset || select.orders.any?
|
625
730
|
|
626
|
-
|
627
|
-
|
628
|
-
options[:null] = column.null
|
629
|
-
else
|
630
|
-
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
731
|
+
key_name = quote_column_name(key.name)
|
732
|
+
Arel::SelectManager.new(subselect.as("__active_record_temp")).project(Arel.sql(key_name))
|
631
733
|
end
|
632
734
|
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
rename_column_sql
|
637
|
-
end
|
735
|
+
def supports_rename_index?
|
736
|
+
mariadb? ? false : version >= "5.7.6"
|
737
|
+
end
|
638
738
|
|
639
|
-
|
640
|
-
|
641
|
-
end
|
642
|
-
alias :remove_columns_sql :remove_column
|
739
|
+
def configure_connection
|
740
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
643
741
|
|
644
|
-
|
645
|
-
|
646
|
-
"ADD #{index_type} INDEX #{index_name} (#{index_columns})"
|
647
|
-
end
|
742
|
+
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
743
|
+
variables["sql_auto_is_null"] = 0
|
648
744
|
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
745
|
+
# Increase timeout so the server doesn't disconnect us.
|
746
|
+
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
747
|
+
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
748
|
+
variables["wait_timeout"] = wait_timeout
|
653
749
|
|
654
|
-
|
655
|
-
[add_column_sql(table_name, :created_at, :datetime), add_column_sql(table_name, :updated_at, :datetime)]
|
656
|
-
end
|
750
|
+
defaults = [":default", :default].to_set
|
657
751
|
|
658
|
-
|
659
|
-
|
660
|
-
|
752
|
+
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
753
|
+
# https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
|
754
|
+
# If the user has provided another value for sql_mode, don't replace it.
|
755
|
+
if sql_mode = variables.delete("sql_mode")
|
756
|
+
sql_mode = quote(sql_mode)
|
757
|
+
elsif !defaults.include?(strict_mode?)
|
758
|
+
if strict_mode?
|
759
|
+
sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
|
760
|
+
else
|
761
|
+
sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
|
762
|
+
sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
|
763
|
+
sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
|
764
|
+
end
|
765
|
+
sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
|
766
|
+
end
|
767
|
+
sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
|
768
|
+
|
769
|
+
# NAMES does not have an equals sign, see
|
770
|
+
# https://dev.mysql.com/doc/refman/5.7/en/set-names.html
|
771
|
+
# (trailing comma because variable_assignments will always have content)
|
772
|
+
if @config[:encoding]
|
773
|
+
encoding = "NAMES #{@config[:encoding]}".dup
|
774
|
+
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
775
|
+
encoding << ", "
|
776
|
+
end
|
661
777
|
|
662
|
-
|
778
|
+
# Gather up all of the SET variables...
|
779
|
+
variable_assignments = variables.map do |k, v|
|
780
|
+
if defaults.include?(v)
|
781
|
+
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
782
|
+
elsif !v.nil?
|
783
|
+
"@@SESSION.#{k} = #{quote(v)}"
|
784
|
+
end
|
785
|
+
# or else nil; compact to clear nils out
|
786
|
+
end.compact.join(", ")
|
663
787
|
|
664
|
-
|
665
|
-
|
666
|
-
|
788
|
+
# ...and send them all in one query
|
789
|
+
execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
|
790
|
+
end
|
667
791
|
|
668
|
-
|
669
|
-
|
670
|
-
|
792
|
+
def column_definitions(table_name) # :nodoc:
|
793
|
+
execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
|
794
|
+
each_hash(result)
|
795
|
+
end
|
671
796
|
end
|
672
|
-
|
673
|
-
|
797
|
+
|
798
|
+
def create_table_info(table_name) # :nodoc:
|
799
|
+
exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
800
|
+
end
|
801
|
+
|
802
|
+
def arel_visitor
|
803
|
+
Arel::Visitors::MySQL.new(self)
|
804
|
+
end
|
805
|
+
|
806
|
+
def mismatched_foreign_key(message)
|
807
|
+
match = %r/
|
808
|
+
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
809
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
|
810
|
+
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
811
|
+
/xmi.match(message)
|
812
|
+
|
813
|
+
options = {
|
814
|
+
message: message,
|
815
|
+
}
|
816
|
+
|
817
|
+
if match
|
818
|
+
options[:table] = match[:table]
|
819
|
+
options[:foreign_key] = match[:foreign_key]
|
820
|
+
options[:target_table] = match[:target_table]
|
821
|
+
options[:primary_key] = match[:primary_key]
|
822
|
+
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
823
|
+
end
|
824
|
+
|
825
|
+
MismatchedForeignKey.new(options)
|
826
|
+
end
|
827
|
+
|
828
|
+
def integer_to_sql(limit) # :nodoc:
|
829
|
+
case limit
|
830
|
+
when 1; "tinyint"
|
831
|
+
when 2; "smallint"
|
832
|
+
when 3; "mediumint"
|
833
|
+
when nil, 4; "int"
|
834
|
+
when 5..8; "bigint"
|
835
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead.")
|
836
|
+
end
|
837
|
+
end
|
838
|
+
|
839
|
+
def text_to_sql(limit) # :nodoc:
|
840
|
+
case limit
|
841
|
+
when 0..0xff; "tinytext"
|
842
|
+
when nil, 0x100..0xffff; "text"
|
843
|
+
when 0x10000..0xffffff; "mediumtext"
|
844
|
+
when 0x1000000..0xffffffff; "longtext"
|
845
|
+
else raise(ActiveRecordError, "No text type has byte length #{limit}")
|
846
|
+
end
|
847
|
+
end
|
848
|
+
|
849
|
+
def binary_to_sql(limit) # :nodoc:
|
850
|
+
case limit
|
851
|
+
when 0..0xff; "tinyblob"
|
852
|
+
when nil, 0x100..0xffff; "blob"
|
853
|
+
when 0x10000..0xffffff; "mediumblob"
|
854
|
+
when 0x1000000..0xffffffff; "longblob"
|
855
|
+
else raise(ActiveRecordError, "No binary type has byte length #{limit}")
|
856
|
+
end
|
857
|
+
end
|
858
|
+
|
859
|
+
def version_string
|
860
|
+
full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
861
|
+
end
|
862
|
+
|
863
|
+
class MysqlString < Type::String # :nodoc:
|
864
|
+
def serialize(value)
|
865
|
+
case value
|
866
|
+
when true then "1"
|
867
|
+
when false then "0"
|
868
|
+
else super
|
869
|
+
end
|
870
|
+
end
|
871
|
+
|
872
|
+
private
|
873
|
+
|
874
|
+
def cast_value(value)
|
875
|
+
case value
|
876
|
+
when true then "1"
|
877
|
+
when false then "0"
|
878
|
+
else super
|
879
|
+
end
|
880
|
+
end
|
881
|
+
end
|
882
|
+
|
883
|
+
ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
|
884
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
674
885
|
end
|
675
886
|
end
|
676
887
|
end
|