activerecord 4.2.11.1 → 5.2.4.5
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 +4 -4
- data/CHANGELOG.md +594 -1620
- data/MIT-LICENSE +2 -2
- data/README.rdoc +10 -11
- data/examples/performance.rb +32 -31
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +263 -249
- data/lib/active_record/association_relation.rb +11 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +77 -43
- data/lib/active_record/associations/association_scope.rb +106 -133
- data/lib/active_record/associations/belongs_to_association.rb +52 -41
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +29 -38
- data/lib/active_record/associations/builder/belongs_to.rb +77 -30
- data/lib/active_record/associations/builder/collection_association.rb +9 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
- data/lib/active_record/associations/builder/has_many.rb +6 -4
- data/lib/active_record/associations/builder/has_one.rb +13 -6
- data/lib/active_record/associations/builder/singular_association.rb +15 -11
- data/lib/active_record/associations/collection_association.rb +139 -280
- data/lib/active_record/associations/collection_proxy.rb +231 -133
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_many_association.rb +34 -89
- data/lib/active_record/associations/has_many_through_association.rb +49 -76
- data/lib/active_record/associations/has_one_association.rb +38 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -87
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/join_dependency.rb +133 -159
- data/lib/active_record/associations/preloader/association.rb +85 -120
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +81 -91
- data/lib/active_record/associations/singular_association.rb +27 -34
- data/lib/active_record/associations/through_association.rb +38 -18
- data/lib/active_record/associations.rb +1732 -1597
- data/lib/active_record/attribute_assignment.rb +58 -182
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
- data/lib/active_record/attribute_methods/dirty.rb +94 -135
- data/lib/active_record/attribute_methods/primary_key.rb +86 -71
- data/lib/active_record/attribute_methods/query.rb +4 -2
- data/lib/active_record/attribute_methods/read.rb +45 -63
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
- data/lib/active_record/attribute_methods/write.rb +30 -45
- data/lib/active_record/attribute_methods.rb +166 -109
- data/lib/active_record/attributes.rb +201 -82
- data/lib/active_record/autosave_association.rb +94 -36
- data/lib/active_record/base.rb +57 -44
- data/lib/active_record/callbacks.rb +97 -57
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
- data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
- data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
- data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -601
- data/lib/active_record/connection_adapters/column.rb +50 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
- 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 +41 -180
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
- 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 +269 -308
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +40 -27
- data/lib/active_record/core.rb +178 -198
- data/lib/active_record/counter_cache.rb +79 -36
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +135 -88
- data/lib/active_record/errors.rb +179 -52
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +188 -132
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +148 -112
- data/lib/active_record/integration.rb +70 -28
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +21 -3
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +88 -96
- data/lib/active_record/locking/pessimistic.rb +15 -3
- data/lib/active_record/log_subscriber.rb +95 -33
- data/lib/active_record/migration/command_recorder.rb +133 -90
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +581 -282
- data/lib/active_record/model_schema.rb +290 -111
- data/lib/active_record/nested_attributes.rb +264 -222
- data/lib/active_record/no_touching.rb +7 -1
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +347 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +94 -32
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +9 -3
- data/lib/active_record/railties/databases.rake +149 -156
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +414 -267
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +204 -55
- data/lib/active_record/relation/calculations.rb +256 -248
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +288 -239
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +86 -86
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
- 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 +7 -1
- data/lib/active_record/relation/predicate_builder.rb +116 -119
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +448 -393
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -13
- 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 +287 -340
- data/lib/active_record/result.rb +54 -36
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +155 -124
- data/lib/active_record/schema.rb +30 -24
- data/lib/active_record/schema_dumper.rb +91 -87
- data/lib/active_record/schema_migration.rb +19 -16
- data/lib/active_record/scoping/default.rb +102 -85
- data/lib/active_record/scoping/named.rb +81 -32
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +45 -35
- data/lib/active_record/store.rb +42 -36
- 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 +134 -96
- data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +199 -124
- 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 +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
- 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 +24 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type.rb +79 -23
- 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 +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +40 -41
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +34 -22
- 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 +43 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
- data/lib/rails/generators/active_record/migration.rb +18 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +72 -49
- 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_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- 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.rb +0 -163
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
@@ -1,209 +1,87 @@
|
|
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
|
-
include
|
8
|
-
|
9
|
-
class SchemaCreation < AbstractAdapter::SchemaCreation
|
10
|
-
def visit_AddColumn(o)
|
11
|
-
add_column_position!(super, column_options(o))
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def visit_DropForeignKey(name)
|
17
|
-
"DROP FOREIGN KEY #{name}"
|
18
|
-
end
|
19
|
-
|
20
|
-
def visit_TableDefinition(o)
|
21
|
-
name = o.name
|
22
|
-
create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(name)} "
|
23
|
-
|
24
|
-
statements = o.columns.map { |c| accept c }
|
25
|
-
statements.concat(o.indexes.map { |column_name, options| index_in_create(name, column_name, options) })
|
26
|
-
|
27
|
-
create_sql << "(#{statements.join(', ')}) " if statements.present?
|
28
|
-
create_sql << "#{o.options}"
|
29
|
-
create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
|
30
|
-
create_sql
|
31
|
-
end
|
32
|
-
|
33
|
-
def visit_ChangeColumnDefinition(o)
|
34
|
-
column = o.column
|
35
|
-
options = o.options
|
36
|
-
sql_type = type_to_sql(o.type, options[:limit], options[:precision], options[:scale])
|
37
|
-
change_column_sql = "CHANGE #{quote_column_name(column.name)} #{quote_column_name(options[:name])} #{sql_type}"
|
38
|
-
add_column_options!(change_column_sql, options.merge(column: column))
|
39
|
-
add_column_position!(change_column_sql, options)
|
40
|
-
end
|
41
|
-
|
42
|
-
def add_column_position!(sql, options)
|
43
|
-
if options[:first]
|
44
|
-
sql << " FIRST"
|
45
|
-
elsif options[:after]
|
46
|
-
sql << " AFTER #{quote_column_name(options[:after])}"
|
47
|
-
end
|
48
|
-
sql
|
49
|
-
end
|
50
|
-
|
51
|
-
def index_in_create(table_name, column_name, options)
|
52
|
-
index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.add_index_options(table_name, column_name, options)
|
53
|
-
"#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def schema_creation
|
58
|
-
SchemaCreation.new self
|
59
|
-
end
|
60
|
-
|
61
|
-
def prepare_column_options(column, types) # :nodoc:
|
62
|
-
spec = super
|
63
|
-
spec.delete(:limit) if :boolean === column.type
|
64
|
-
spec
|
65
|
-
end
|
66
|
-
|
67
|
-
class Column < ConnectionAdapters::Column # :nodoc:
|
68
|
-
attr_reader :collation, :strict, :extra
|
69
|
-
|
70
|
-
def initialize(name, default, cast_type, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
|
71
|
-
@strict = strict
|
72
|
-
@collation = collation
|
73
|
-
@extra = extra
|
74
|
-
super(name, default, cast_type, sql_type, null)
|
75
|
-
assert_valid_default(default)
|
76
|
-
extract_default
|
77
|
-
end
|
78
|
-
|
79
|
-
def extract_default
|
80
|
-
if blob_or_text_column?
|
81
|
-
@default = null || strict ? nil : ''
|
82
|
-
elsif missing_default_forged_as_empty_string?(@default)
|
83
|
-
@default = nil
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def has_default?
|
88
|
-
return false if blob_or_text_column? # MySQL forbids defaults on blob and text columns
|
89
|
-
super
|
90
|
-
end
|
91
|
-
|
92
|
-
def blob_or_text_column?
|
93
|
-
sql_type =~ /blob/i || type == :text
|
94
|
-
end
|
95
|
-
|
96
|
-
def case_sensitive?
|
97
|
-
collation && !collation.match(/_ci$/)
|
98
|
-
end
|
99
|
-
|
100
|
-
def ==(other)
|
101
|
-
super &&
|
102
|
-
collation == other.collation &&
|
103
|
-
strict == other.strict &&
|
104
|
-
extra == other.extra
|
105
|
-
end
|
106
|
-
|
107
|
-
private
|
108
|
-
|
109
|
-
# MySQL misreports NOT NULL column default when none is given.
|
110
|
-
# We can't detect this for columns which may have a legitimate ''
|
111
|
-
# default (string) but we can for others (integer, datetime, boolean,
|
112
|
-
# and the rest).
|
113
|
-
#
|
114
|
-
# Test whether the column has default '', is not null, and is not
|
115
|
-
# a type allowing default ''.
|
116
|
-
def missing_default_forged_as_empty_string?(default)
|
117
|
-
type != :string && !null && default == ''
|
118
|
-
end
|
119
|
-
|
120
|
-
def assert_valid_default(default)
|
121
|
-
if blob_or_text_column? && default.present?
|
122
|
-
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def attributes_for_hash
|
127
|
-
super + [collation, strict, extra]
|
128
|
-
end
|
129
|
-
end
|
19
|
+
include MySQL::Quoting
|
20
|
+
include MySQL::SchemaStatements
|
130
21
|
|
131
22
|
##
|
132
23
|
# :singleton-method:
|
133
|
-
# By default, the
|
134
|
-
# as boolean. If you wish to disable this emulation
|
135
|
-
# 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
|
136
26
|
# to your application.rb file:
|
137
27
|
#
|
138
|
-
# ActiveRecord::ConnectionAdapters::
|
139
|
-
class_attribute :emulate_booleans
|
140
|
-
self.emulate_booleans = true
|
141
|
-
|
142
|
-
LOST_CONNECTION_ERROR_MESSAGES = [
|
143
|
-
"Server shutdown in progress",
|
144
|
-
"Broken pipe",
|
145
|
-
"Lost connection to MySQL server during query",
|
146
|
-
"MySQL server has gone away" ]
|
147
|
-
|
148
|
-
QUOTED_TRUE, QUOTED_FALSE = '1', '0'
|
28
|
+
# ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans = false
|
29
|
+
class_attribute :emulate_booleans, default: true
|
149
30
|
|
150
31
|
NATIVE_DATABASE_TYPES = {
|
151
|
-
:
|
152
|
-
:
|
153
|
-
:
|
154
|
-
:
|
155
|
-
:
|
156
|
-
:
|
157
|
-
:
|
158
|
-
:
|
159
|
-
:
|
160
|
-
:
|
161
|
-
:
|
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" },
|
162
45
|
}
|
163
46
|
|
164
|
-
|
165
|
-
|
47
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
48
|
+
private def dealloc(stmt)
|
49
|
+
stmt[:stmt].close
|
50
|
+
end
|
51
|
+
end
|
166
52
|
|
167
|
-
# FIXME: Make the first parameter more similar for the two adapters
|
168
53
|
def initialize(connection, logger, connection_options, config)
|
169
|
-
super(connection, logger)
|
170
|
-
@connection_options, @config = connection_options, config
|
171
|
-
@quoted_column_names, @quoted_table_names = {}, {}
|
54
|
+
super(connection, logger, config)
|
172
55
|
|
173
|
-
@
|
56
|
+
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
174
57
|
|
175
|
-
if
|
176
|
-
|
177
|
-
else
|
178
|
-
@prepared_statements = false
|
58
|
+
if version < "5.1.10"
|
59
|
+
raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.1.10."
|
179
60
|
end
|
180
61
|
end
|
181
62
|
|
182
|
-
|
183
|
-
|
184
|
-
true
|
63
|
+
def version #:nodoc:
|
64
|
+
@version ||= Version.new(version_string)
|
185
65
|
end
|
186
66
|
|
187
|
-
def
|
188
|
-
|
67
|
+
def mariadb? # :nodoc:
|
68
|
+
/mariadb/i.match?(full_version)
|
189
69
|
end
|
190
70
|
|
191
71
|
def supports_bulk_alter? #:nodoc:
|
192
72
|
true
|
193
73
|
end
|
194
74
|
|
195
|
-
# Technically MySQL allows to create indexes with the sort order syntax
|
196
|
-
# but at the moment (5.5) it doesn't yet implement them
|
197
75
|
def supports_index_sort_order?
|
198
|
-
|
76
|
+
!mariadb? && version >= "8.0.1"
|
199
77
|
end
|
200
78
|
|
201
|
-
# MySQL 4 technically support transaction isolation, but it is affected by a bug
|
202
|
-
# where the transaction level gets persisted for the whole session:
|
203
|
-
#
|
204
|
-
# http://bugs.mysql.com/bug.php?id=39170
|
205
79
|
def supports_transaction_isolation?
|
206
|
-
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
def supports_explain?
|
84
|
+
true
|
207
85
|
end
|
208
86
|
|
209
87
|
def supports_indexes_in_create?
|
@@ -215,11 +93,35 @@ module ActiveRecord
|
|
215
93
|
end
|
216
94
|
|
217
95
|
def supports_views?
|
218
|
-
|
96
|
+
true
|
219
97
|
end
|
220
98
|
|
221
99
|
def supports_datetime_with_precision?
|
222
|
-
|
100
|
+
if mariadb?
|
101
|
+
version >= "5.3.0"
|
102
|
+
else
|
103
|
+
version >= "5.6.4"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def supports_virtual_columns?
|
108
|
+
if mariadb?
|
109
|
+
version >= "5.2.0"
|
110
|
+
else
|
111
|
+
version >= "5.7.5"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def supports_advisory_locks?
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
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
|
122
|
+
|
123
|
+
def release_advisory_lock(lock_name) # :nodoc:
|
124
|
+
query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
|
223
125
|
end
|
224
126
|
|
225
127
|
def native_database_types
|
@@ -227,7 +129,7 @@ module ActiveRecord
|
|
227
129
|
end
|
228
130
|
|
229
131
|
def index_algorithms
|
230
|
-
{ default:
|
132
|
+
{ default: "ALGORITHM = DEFAULT".dup, copy: "ALGORITHM = COPY".dup, inplace: "ALGORITHM = INPLACE".dup }
|
231
133
|
end
|
232
134
|
|
233
135
|
# HELPER METHODS ===========================================
|
@@ -238,62 +140,16 @@ module ActiveRecord
|
|
238
140
|
raise NotImplementedError
|
239
141
|
end
|
240
142
|
|
241
|
-
def new_column(field, default, cast_type, sql_type = nil, null = true, collation = "", extra = "") # :nodoc:
|
242
|
-
Column.new(field, default, cast_type, sql_type, null, collation, strict_mode?, extra)
|
243
|
-
end
|
244
|
-
|
245
143
|
# Must return the MySQL error number from the exception, if the exception has an
|
246
144
|
# error number.
|
247
145
|
def error_number(exception) # :nodoc:
|
248
146
|
raise NotImplementedError
|
249
147
|
end
|
250
148
|
|
251
|
-
# QUOTING ==================================================
|
252
|
-
|
253
|
-
def _quote(value) # :nodoc:
|
254
|
-
if value.is_a?(Type::Binary::Data)
|
255
|
-
"x'#{value.hex}'"
|
256
|
-
else
|
257
|
-
super
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
def quote_column_name(name) #:nodoc:
|
262
|
-
@quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
|
263
|
-
end
|
264
|
-
|
265
|
-
def quote_table_name(name) #:nodoc:
|
266
|
-
@quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
|
267
|
-
end
|
268
|
-
|
269
|
-
def quoted_true
|
270
|
-
QUOTED_TRUE
|
271
|
-
end
|
272
|
-
|
273
|
-
def unquoted_true
|
274
|
-
1
|
275
|
-
end
|
276
|
-
|
277
|
-
def quoted_false
|
278
|
-
QUOTED_FALSE
|
279
|
-
end
|
280
|
-
|
281
|
-
def unquoted_false
|
282
|
-
0
|
283
|
-
end
|
284
|
-
|
285
|
-
def quoted_date(value)
|
286
|
-
if supports_datetime_with_precision? && value.acts_like?(:time) && value.respond_to?(:usec)
|
287
|
-
"#{super}.#{sprintf("%06d", value.usec)}"
|
288
|
-
else
|
289
|
-
super
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
149
|
# REFERENTIAL INTEGRITY ====================================
|
294
150
|
|
295
151
|
def disable_referential_integrity #:nodoc:
|
296
|
-
old =
|
152
|
+
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
297
153
|
|
298
154
|
begin
|
299
155
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
@@ -303,32 +159,43 @@ module ActiveRecord
|
|
303
159
|
end
|
304
160
|
end
|
305
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
|
+
|
306
170
|
#--
|
307
171
|
# DATABASE STATEMENTS ======================================
|
308
172
|
#++
|
309
173
|
|
310
|
-
def
|
311
|
-
|
312
|
-
|
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)
|
313
181
|
end
|
314
182
|
|
315
183
|
# Executes the SQL statement in the context of this connection.
|
316
184
|
def execute(sql, name = nil)
|
317
|
-
log(sql, name)
|
185
|
+
log(sql, name) do
|
186
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
187
|
+
@connection.query(sql)
|
188
|
+
end
|
189
|
+
end
|
318
190
|
end
|
319
191
|
|
320
|
-
#
|
321
|
-
# stuff in an abstract way without concerning ourselves about whether it
|
322
|
-
# explicitly freed or not.
|
323
|
-
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:
|
324
196
|
yield execute(sql, name)
|
325
197
|
end
|
326
198
|
|
327
|
-
def update_sql(sql, name = nil) #:nodoc:
|
328
|
-
super
|
329
|
-
@connection.affected_rows
|
330
|
-
end
|
331
|
-
|
332
199
|
def begin_db_transaction
|
333
200
|
execute "BEGIN"
|
334
201
|
end
|
@@ -349,7 +216,7 @@ module ActiveRecord
|
|
349
216
|
# In the simple case, MySQL allows us to place JOINs directly into the UPDATE
|
350
217
|
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
351
218
|
# these, we must use a subquery.
|
352
|
-
def join_to_update(update, select)
|
219
|
+
def join_to_update(update, select, key) # :nodoc:
|
353
220
|
if select.limit || select.offset || select.orders.any?
|
354
221
|
super
|
355
222
|
else
|
@@ -382,9 +249,9 @@ module ActiveRecord
|
|
382
249
|
# create_database 'matt_development', charset: :big5
|
383
250
|
def create_database(name, options = {})
|
384
251
|
if options[:collation]
|
385
|
-
execute "CREATE DATABASE
|
252
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
|
386
253
|
else
|
387
|
-
execute "CREATE DATABASE
|
254
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')}"
|
388
255
|
end
|
389
256
|
end
|
390
257
|
|
@@ -393,99 +260,42 @@ module ActiveRecord
|
|
393
260
|
# Example:
|
394
261
|
# drop_database('sebastian_development')
|
395
262
|
def drop_database(name) #:nodoc:
|
396
|
-
execute "DROP DATABASE IF EXISTS
|
263
|
+
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
397
264
|
end
|
398
265
|
|
399
266
|
def current_database
|
400
|
-
|
267
|
+
query_value("SELECT database()", "SCHEMA")
|
401
268
|
end
|
402
269
|
|
403
270
|
# Returns the database character set.
|
404
271
|
def charset
|
405
|
-
show_variable
|
272
|
+
show_variable "character_set_database"
|
406
273
|
end
|
407
274
|
|
408
275
|
# Returns the database collation strategy.
|
409
276
|
def collation
|
410
|
-
show_variable
|
411
|
-
end
|
412
|
-
|
413
|
-
def tables(name = nil, database = nil, like = nil) #:nodoc:
|
414
|
-
sql = "SHOW TABLES "
|
415
|
-
sql << "IN #{quote_table_name(database)} " if database
|
416
|
-
sql << "LIKE #{quote(like)}" if like
|
417
|
-
|
418
|
-
execute_and_free(sql, 'SCHEMA') do |result|
|
419
|
-
result.collect { |field| field.first }
|
420
|
-
end
|
277
|
+
show_variable "collation_database"
|
421
278
|
end
|
422
|
-
alias data_sources tables
|
423
279
|
|
424
280
|
def truncate(table_name, name = nil)
|
425
281
|
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
426
282
|
end
|
427
283
|
|
428
|
-
def
|
429
|
-
|
430
|
-
return true if tables(nil, nil, name).any?
|
431
|
-
|
432
|
-
name = name.to_s
|
433
|
-
schema, table = name.split('.', 2)
|
434
|
-
|
435
|
-
unless table # A table was provided without a schema
|
436
|
-
table = schema
|
437
|
-
schema = nil
|
438
|
-
end
|
439
|
-
|
440
|
-
tables(nil, schema, table).any?
|
441
|
-
end
|
442
|
-
alias data_source_exists? table_exists?
|
443
|
-
|
444
|
-
# Returns an array of indexes for the given table.
|
445
|
-
def indexes(table_name, name = nil) #:nodoc:
|
446
|
-
indexes = []
|
447
|
-
current_index = nil
|
448
|
-
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
|
449
|
-
each_hash(result) do |row|
|
450
|
-
if current_index != row[:Key_name]
|
451
|
-
next if row[:Key_name] == 'PRIMARY' # skip the primary key
|
452
|
-
current_index = row[:Key_name]
|
453
|
-
|
454
|
-
mysql_index_type = row[:Index_type].downcase.to_sym
|
455
|
-
index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
|
456
|
-
index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
|
457
|
-
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using)
|
458
|
-
end
|
459
|
-
|
460
|
-
indexes.last.columns << row[:Column_name]
|
461
|
-
indexes.last.lengths << row[:Sub_part]
|
462
|
-
end
|
463
|
-
end
|
464
|
-
|
465
|
-
indexes
|
466
|
-
end
|
284
|
+
def table_comment(table_name) # :nodoc:
|
285
|
+
scope = quoted_scope(table_name)
|
467
286
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
sql_type = field[:Type]
|
475
|
-
cast_type = lookup_cast_type(sql_type)
|
476
|
-
new_column(field_name, field[:Default], cast_type, sql_type, field[:Null] == "YES", field[:Collation], field[:Extra])
|
477
|
-
end
|
478
|
-
end
|
479
|
-
end
|
480
|
-
|
481
|
-
def create_table(table_name, options = {}) #:nodoc:
|
482
|
-
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
|
483
293
|
end
|
484
294
|
|
485
295
|
def bulk_change_table(table_name, operations) #:nodoc:
|
486
296
|
sqls = operations.flat_map do |command, args|
|
487
297
|
table, arguments = args.shift, args
|
488
|
-
method = :"#{command}
|
298
|
+
method = :"#{command}_for_alter"
|
489
299
|
|
490
300
|
if respond_to?(method, true)
|
491
301
|
send(method, table, *arguments)
|
@@ -497,6 +307,11 @@ module ActiveRecord
|
|
497
307
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
498
308
|
end
|
499
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
|
+
|
500
315
|
# Renames a table.
|
501
316
|
#
|
502
317
|
# Example:
|
@@ -506,8 +321,23 @@ module ActiveRecord
|
|
506
321
|
rename_table_indexes(table_name, new_name)
|
507
322
|
end
|
508
323
|
|
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.
|
509
339
|
def drop_table(table_name, options = {})
|
510
|
-
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
340
|
+
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
511
341
|
end
|
512
342
|
|
513
343
|
def rename_index(table_name, old_name, new_name)
|
@@ -520,149 +350,158 @@ module ActiveRecord
|
|
520
350
|
end
|
521
351
|
end
|
522
352
|
|
523
|
-
def change_column_default(table_name, column_name,
|
524
|
-
|
525
|
-
change_column table_name, column_name,
|
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
|
526
356
|
end
|
527
357
|
|
528
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
529
|
-
column = column_for(table_name, column_name)
|
530
|
-
|
358
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
531
359
|
unless null || default.nil?
|
532
360
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
533
361
|
end
|
534
362
|
|
535
|
-
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
|
536
368
|
end
|
537
369
|
|
538
370
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
539
|
-
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)}")
|
540
372
|
end
|
541
373
|
|
542
374
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
543
|
-
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)}")
|
544
376
|
rename_column_indexes(table_name, column_name, new_column_name)
|
545
377
|
end
|
546
378
|
|
547
379
|
def add_index(table_name, column_name, options = {}) #:nodoc:
|
548
|
-
index_name, index_type, index_columns,
|
549
|
-
|
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
|
550
388
|
end
|
551
389
|
|
552
390
|
def foreign_keys(table_name)
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
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]}
|
562
410
|
SQL
|
563
411
|
|
564
|
-
create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
|
565
|
-
|
566
412
|
fk_info.map do |row|
|
567
413
|
options = {
|
568
|
-
column: row[
|
569
|
-
name: row[
|
570
|
-
primary_key: row[
|
414
|
+
column: row["column"],
|
415
|
+
name: row["name"],
|
416
|
+
primary_key: row["primary_key"]
|
571
417
|
}
|
572
418
|
|
573
|
-
options[:on_update] = extract_foreign_key_action(
|
574
|
-
options[:on_delete] = extract_foreign_key_action(
|
419
|
+
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
420
|
+
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
575
421
|
|
576
|
-
ForeignKeyDefinition.new(table_name, row[
|
422
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
577
423
|
end
|
578
424
|
end
|
579
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
|
445
|
+
end
|
446
|
+
|
580
447
|
# Maps logical Rails types to MySQL-specific data types.
|
581
|
-
def type_to_sql(type, limit
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
when
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
end
|
599
|
-
when 'text'
|
600
|
-
case limit
|
601
|
-
when 0..0xff; 'tinytext'
|
602
|
-
when nil, 0x100..0xffff; 'text'
|
603
|
-
when 0x10000..0xffffff; 'mediumtext'
|
604
|
-
when 0x1000000..0xffffffff; 'longtext'
|
605
|
-
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
|
606
465
|
end
|
607
|
-
when 'datetime'
|
608
|
-
return super unless precision
|
609
466
|
|
610
|
-
|
611
|
-
|
612
|
-
else raise(ActiveRecordError, "No datetime type has precision of #{precision}. The allowed range of precision is from 0 to 6.")
|
613
|
-
end
|
614
|
-
else
|
615
|
-
super
|
616
|
-
end
|
467
|
+
sql = "#{sql} unsigned" if unsigned && type != :primary_key
|
468
|
+
sql
|
617
469
|
end
|
618
470
|
|
619
471
|
# SHOW VARIABLES LIKE 'name'
|
620
472
|
def show_variable(name)
|
621
|
-
|
622
|
-
variables.first['Value'] unless variables.empty?
|
473
|
+
query_value("SELECT @@#{name}", "SCHEMA")
|
623
474
|
rescue ActiveRecord::StatementInvalid
|
624
475
|
nil
|
625
476
|
end
|
626
477
|
|
627
|
-
|
628
|
-
|
629
|
-
execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
|
630
|
-
create_table = each_hash(result).first[:"Create Table"]
|
631
|
-
if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
|
632
|
-
keys = $1.split(",").map { |key| key.delete('`"') }
|
633
|
-
keys.length == 1 ? [keys.first, nil] : nil
|
634
|
-
else
|
635
|
-
nil
|
636
|
-
end
|
637
|
-
end
|
638
|
-
end
|
478
|
+
def primary_keys(table_name) # :nodoc:
|
479
|
+
raise ArgumentError unless table_name.present?
|
639
480
|
|
640
|
-
|
641
|
-
def primary_key(table)
|
642
|
-
pk_and_sequence = pk_and_sequence_for(table)
|
643
|
-
pk_and_sequence && pk_and_sequence.first
|
644
|
-
end
|
481
|
+
scope = quoted_scope(table_name)
|
645
482
|
|
646
|
-
|
647
|
-
|
648
|
-
|
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
|
649
491
|
end
|
650
492
|
|
651
|
-
def case_sensitive_comparison(table, attribute, column, value)
|
652
|
-
if column.case_sensitive?
|
653
|
-
table[attribute].eq(value)
|
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))
|
654
496
|
else
|
655
497
|
super
|
656
498
|
end
|
657
499
|
end
|
658
500
|
|
659
|
-
def
|
660
|
-
|
661
|
-
super
|
662
|
-
else
|
663
|
-
table[attribute].eq(value)
|
664
|
-
end
|
501
|
+
def can_perform_case_insensitive_comparison_for?(column)
|
502
|
+
column.case_sensitive?
|
665
503
|
end
|
504
|
+
private :can_perform_case_insensitive_comparison_for?
|
666
505
|
|
667
506
|
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
|
668
507
|
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
|
@@ -673,274 +512,376 @@ module ActiveRecord
|
|
673
512
|
# Convert Arel node to string
|
674
513
|
s = s.to_sql unless s.is_a?(String)
|
675
514
|
# Remove any ASC/DESC modifiers
|
676
|
-
s.gsub(/\s+(?:ASC|DESC)\b/i,
|
515
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
677
516
|
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
678
517
|
|
679
|
-
|
518
|
+
(order_columns << super).join(", ")
|
680
519
|
end
|
681
520
|
|
682
521
|
def strict_mode?
|
683
522
|
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
684
523
|
end
|
685
524
|
|
686
|
-
def
|
687
|
-
|
525
|
+
def default_index_type?(index) # :nodoc:
|
526
|
+
index.using == :btree || super
|
688
527
|
end
|
689
528
|
|
690
|
-
|
529
|
+
def insert_fixtures_set(fixture_set, tables_to_delete = [])
|
530
|
+
with_multi_statements do
|
531
|
+
super { discard_remaining_results }
|
532
|
+
end
|
533
|
+
end
|
691
534
|
|
692
|
-
|
693
|
-
|
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
|
694
547
|
|
695
|
-
|
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
|
696
557
|
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
702
|
-
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
703
|
-
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
704
|
-
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
705
|
-
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
706
|
-
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
558
|
+
def max_allowed_packet
|
559
|
+
bytes_margin = 2
|
560
|
+
@max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
|
561
|
+
end
|
707
562
|
|
708
|
-
|
709
|
-
|
710
|
-
register_integer_type m, %r(^mediumint)i, limit: 3
|
711
|
-
register_integer_type m, %r(^smallint)i, limit: 2
|
712
|
-
register_integer_type m, %r(^tinyint)i, limit: 1
|
563
|
+
def initialize_type_map(m = type_map)
|
564
|
+
super
|
713
565
|
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
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
|
718
594
|
|
719
|
-
|
720
|
-
|
721
|
-
|
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
|
722
600
|
end
|
723
601
|
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
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
|
728
610
|
end
|
729
|
-
end
|
730
611
|
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
Type::UnsignedInteger.new(options)
|
612
|
+
def extract_precision(sql_type)
|
613
|
+
if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
|
614
|
+
super || 0
|
735
615
|
else
|
736
|
-
|
616
|
+
super
|
737
617
|
end
|
738
618
|
end
|
739
|
-
end
|
740
|
-
|
741
|
-
# MySQL is too stupid to create a temporary table for use subquery, so we have
|
742
|
-
# to give it some prompting in the form of a subsubquery. Ugh!
|
743
|
-
def subquery_for(key, select)
|
744
|
-
subsubselect = select.clone
|
745
|
-
subsubselect.projections = [key]
|
746
619
|
|
747
|
-
#
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
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
|
763
664
|
end
|
764
665
|
end
|
765
666
|
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
def quoted_columns_for_index(column_names, options = {})
|
770
|
-
option_strings = Hash[column_names.map {|name| [name, '']}]
|
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
|
771
670
|
|
772
|
-
|
773
|
-
|
671
|
+
unless options.key?(:default)
|
672
|
+
options[:default] = column.default
|
673
|
+
end
|
774
674
|
|
775
|
-
|
776
|
-
|
675
|
+
unless options.key?(:null)
|
676
|
+
options[:null] = column.null
|
677
|
+
end
|
777
678
|
|
778
|
-
|
779
|
-
|
679
|
+
unless options.key?(:comment)
|
680
|
+
options[:comment] = column.comment
|
681
|
+
end
|
780
682
|
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
RecordNotUnique.new(message, exception)
|
785
|
-
when 1452
|
786
|
-
InvalidForeignKey.new(message, exception)
|
787
|
-
else
|
788
|
-
super
|
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))
|
789
686
|
end
|
790
|
-
end
|
791
687
|
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
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
|
+
}
|
797
695
|
|
798
|
-
|
799
|
-
|
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))
|
700
|
+
end
|
800
701
|
|
801
|
-
|
802
|
-
|
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}"
|
803
706
|
end
|
804
707
|
|
805
|
-
|
806
|
-
|
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)}"
|
807
711
|
end
|
808
712
|
|
809
|
-
options
|
810
|
-
|
811
|
-
|
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)]
|
715
|
+
end
|
812
716
|
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
name: new_column_name,
|
817
|
-
default: column.default,
|
818
|
-
null: column.null,
|
819
|
-
auto_increment: column.extra == "auto_increment"
|
820
|
-
}
|
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)]
|
719
|
+
end
|
821
720
|
|
822
|
-
|
823
|
-
|
824
|
-
|
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]
|
825
726
|
|
826
|
-
|
827
|
-
|
828
|
-
|
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?
|
829
730
|
|
830
|
-
|
831
|
-
|
832
|
-
|
731
|
+
key_name = quote_column_name(key.name)
|
732
|
+
Arel::SelectManager.new(subselect.as("__active_record_temp")).project(Arel.sql(key_name))
|
733
|
+
end
|
833
734
|
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
end
|
735
|
+
def supports_rename_index?
|
736
|
+
mariadb? ? false : version >= "5.7.6"
|
737
|
+
end
|
838
738
|
|
839
|
-
|
840
|
-
|
841
|
-
"DROP INDEX #{index_name}"
|
842
|
-
end
|
739
|
+
def configure_connection
|
740
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
843
741
|
|
844
|
-
|
845
|
-
|
846
|
-
end
|
742
|
+
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
743
|
+
variables["sql_auto_is_null"] = 0
|
847
744
|
|
848
|
-
|
849
|
-
|
850
|
-
|
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
|
851
749
|
|
852
|
-
|
750
|
+
defaults = [":default", :default].to_set
|
853
751
|
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
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
|
865
777
|
|
866
|
-
|
867
|
-
|
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(", ")
|
868
787
|
|
869
|
-
|
870
|
-
|
871
|
-
|
788
|
+
# ...and send them all in one query
|
789
|
+
execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
|
790
|
+
end
|
872
791
|
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
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
|
796
|
+
end
|
877
797
|
|
878
|
-
|
879
|
-
|
880
|
-
# If the user has provided another value for sql_mode, don't replace it.
|
881
|
-
unless variables.has_key?('sql_mode')
|
882
|
-
variables['sql_mode'] = strict_mode? ? 'STRICT_ALL_TABLES' : ''
|
798
|
+
def create_table_info(table_name) # :nodoc:
|
799
|
+
exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
883
800
|
end
|
884
801
|
|
885
|
-
|
886
|
-
|
887
|
-
# (trailing comma because variable_assignments will always have content)
|
888
|
-
if @config[:encoding]
|
889
|
-
encoding = "NAMES #{@config[:encoding]}"
|
890
|
-
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
891
|
-
encoding << ", "
|
802
|
+
def arel_visitor
|
803
|
+
Arel::Visitors::MySQL.new(self)
|
892
804
|
end
|
893
805
|
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
end
|
901
|
-
# or else nil; compact to clear nils out
|
902
|
-
end.compact.join(', ')
|
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)
|
903
812
|
|
904
|
-
|
905
|
-
|
906
|
-
|
813
|
+
options = {
|
814
|
+
message: message,
|
815
|
+
}
|
907
816
|
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
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])
|
913
823
|
end
|
824
|
+
|
825
|
+
MismatchedForeignKey.new(options)
|
914
826
|
end
|
915
|
-
end
|
916
827
|
|
917
|
-
|
918
|
-
|
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
|
919
838
|
|
920
|
-
def
|
921
|
-
|
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
|
922
847
|
end
|
923
|
-
end
|
924
848
|
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
when
|
929
|
-
when
|
930
|
-
|
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}")
|
931
856
|
end
|
932
857
|
end
|
933
858
|
|
934
|
-
|
859
|
+
def version_string
|
860
|
+
full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
861
|
+
end
|
935
862
|
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
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
|
941
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
|
942
881
|
end
|
943
|
-
|
882
|
+
|
883
|
+
ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
|
884
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
944
885
|
end
|
945
886
|
end
|
946
887
|
end
|