activerecord 4.2.0 → 5.2.8.1
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 +640 -928
- 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 +264 -247
- data/lib/active_record/association_relation.rb +24 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +87 -41
- data/lib/active_record/associations/association_scope.rb +106 -132
- data/lib/active_record/associations/belongs_to_association.rb +55 -36
- 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 +14 -23
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
- 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 +145 -266
- data/lib/active_record/associations/collection_proxy.rb +242 -138
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +35 -75
- data/lib/active_record/associations/has_many_through_association.rb +51 -69
- data/lib/active_record/associations/has_one_association.rb +39 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
- 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 +134 -154
- data/lib/active_record/associations/preloader/association.rb +85 -116
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +83 -93
- data/lib/active_record/associations/singular_association.rb +27 -40
- data/lib/active_record/associations/through_association.rb +48 -23
- data/lib/active_record/associations.rb +1732 -1596
- 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 +12 -5
- data/lib/active_record/attribute_methods/dirty.rb +94 -125
- 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 +62 -36
- data/lib/active_record/attribute_methods/write.rb +31 -46
- data/lib/active_record/attribute_methods.rb +170 -117
- data/lib/active_record/attributes.rb +201 -74
- data/lib/active_record/autosave_association.rb +118 -45
- data/lib/active_record/base.rb +60 -48
- 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 +37 -13
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
- data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
- 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 +42 -195
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
- 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 +5 -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 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -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 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
- 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 +466 -280
- 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 +439 -330
- 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 -324
- 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 +205 -202
- data/lib/active_record/counter_cache.rb +80 -37
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +136 -90
- data/lib/active_record/errors.rb +180 -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 +11 -6
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +193 -135
- 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 +48 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +92 -98
- 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 +594 -267
- data/lib/active_record/model_schema.rb +292 -111
- data/lib/active_record/nested_attributes.rb +266 -214
- data/lib/active_record/no_touching.rb +8 -2
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +350 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +117 -35
- 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 +160 -174
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +447 -288
- 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 +259 -244
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +290 -253
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +91 -68
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
- 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 +118 -92
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +446 -389
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -16
- 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 -339
- 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 -19
- data/lib/active_record/scoping/default.rb +102 -84
- 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 +136 -95
- data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
- 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 +208 -123
- 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 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
- 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 +30 -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 +41 -32
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +36 -21
- 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 -6
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
- 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.rb +7 -5
- metadata +77 -53
- 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 -149
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- 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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,203 +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
|
-
class Column < ConnectionAdapters::Column # :nodoc:
|
62
|
-
attr_reader :collation, :strict, :extra
|
63
|
-
|
64
|
-
def initialize(name, default, cast_type, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
|
65
|
-
@strict = strict
|
66
|
-
@collation = collation
|
67
|
-
@extra = extra
|
68
|
-
super(name, default, cast_type, sql_type, null)
|
69
|
-
assert_valid_default(default)
|
70
|
-
extract_default
|
71
|
-
end
|
72
|
-
|
73
|
-
def extract_default
|
74
|
-
if blob_or_text_column?
|
75
|
-
@default = null || strict ? nil : ''
|
76
|
-
elsif missing_default_forged_as_empty_string?(@default)
|
77
|
-
@default = nil
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def has_default?
|
82
|
-
return false if blob_or_text_column? # MySQL forbids defaults on blob and text columns
|
83
|
-
super
|
84
|
-
end
|
85
|
-
|
86
|
-
def blob_or_text_column?
|
87
|
-
sql_type =~ /blob/i || type == :text
|
88
|
-
end
|
89
|
-
|
90
|
-
def case_sensitive?
|
91
|
-
collation && !collation.match(/_ci$/)
|
92
|
-
end
|
93
|
-
|
94
|
-
def ==(other)
|
95
|
-
super &&
|
96
|
-
collation == other.collation &&
|
97
|
-
strict == other.strict &&
|
98
|
-
extra == other.extra
|
99
|
-
end
|
100
|
-
|
101
|
-
private
|
102
|
-
|
103
|
-
# MySQL misreports NOT NULL column default when none is given.
|
104
|
-
# We can't detect this for columns which may have a legitimate ''
|
105
|
-
# default (string) but we can for others (integer, datetime, boolean,
|
106
|
-
# and the rest).
|
107
|
-
#
|
108
|
-
# Test whether the column has default '', is not null, and is not
|
109
|
-
# a type allowing default ''.
|
110
|
-
def missing_default_forged_as_empty_string?(default)
|
111
|
-
type != :string && !null && default == ''
|
112
|
-
end
|
113
|
-
|
114
|
-
def assert_valid_default(default)
|
115
|
-
if blob_or_text_column? && default.present?
|
116
|
-
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def attributes_for_hash
|
121
|
-
super + [collation, strict, extra]
|
122
|
-
end
|
123
|
-
end
|
19
|
+
include MySQL::Quoting
|
20
|
+
include MySQL::SchemaStatements
|
124
21
|
|
125
22
|
##
|
126
23
|
# :singleton-method:
|
127
|
-
# By default, the
|
128
|
-
# as boolean. If you wish to disable this emulation
|
129
|
-
# 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
|
130
26
|
# to your application.rb file:
|
131
27
|
#
|
132
|
-
# ActiveRecord::ConnectionAdapters::
|
133
|
-
class_attribute :emulate_booleans
|
134
|
-
self.emulate_booleans = true
|
135
|
-
|
136
|
-
LOST_CONNECTION_ERROR_MESSAGES = [
|
137
|
-
"Server shutdown in progress",
|
138
|
-
"Broken pipe",
|
139
|
-
"Lost connection to MySQL server during query",
|
140
|
-
"MySQL server has gone away" ]
|
141
|
-
|
142
|
-
QUOTED_TRUE, QUOTED_FALSE = '1', '0'
|
28
|
+
# ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans = false
|
29
|
+
class_attribute :emulate_booleans, default: true
|
143
30
|
|
144
31
|
NATIVE_DATABASE_TYPES = {
|
145
|
-
:
|
146
|
-
:
|
147
|
-
:
|
148
|
-
:
|
149
|
-
:
|
150
|
-
:
|
151
|
-
:
|
152
|
-
:
|
153
|
-
:
|
154
|
-
:
|
155
|
-
:
|
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" },
|
156
45
|
}
|
157
46
|
|
158
|
-
|
159
|
-
|
47
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
48
|
+
private def dealloc(stmt)
|
49
|
+
stmt[:stmt].close
|
50
|
+
end
|
51
|
+
end
|
160
52
|
|
161
|
-
# FIXME: Make the first parameter more similar for the two adapters
|
162
53
|
def initialize(connection, logger, connection_options, config)
|
163
|
-
super(connection, logger)
|
164
|
-
@connection_options, @config = connection_options, config
|
165
|
-
@quoted_column_names, @quoted_table_names = {}, {}
|
54
|
+
super(connection, logger, config)
|
166
55
|
|
167
|
-
@
|
56
|
+
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
168
57
|
|
169
|
-
if
|
170
|
-
|
171
|
-
else
|
172
|
-
@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."
|
173
60
|
end
|
174
61
|
end
|
175
62
|
|
176
|
-
|
177
|
-
|
178
|
-
true
|
63
|
+
def version #:nodoc:
|
64
|
+
@version ||= Version.new(version_string)
|
179
65
|
end
|
180
66
|
|
181
|
-
def
|
182
|
-
|
67
|
+
def mariadb? # :nodoc:
|
68
|
+
/mariadb/i.match?(full_version)
|
183
69
|
end
|
184
70
|
|
185
71
|
def supports_bulk_alter? #:nodoc:
|
186
72
|
true
|
187
73
|
end
|
188
74
|
|
189
|
-
# Technically MySQL allows to create indexes with the sort order syntax
|
190
|
-
# but at the moment (5.5) it doesn't yet implement them
|
191
75
|
def supports_index_sort_order?
|
192
|
-
|
76
|
+
!mariadb? && version >= "8.0.1"
|
193
77
|
end
|
194
78
|
|
195
|
-
# MySQL 4 technically support transaction isolation, but it is affected by a bug
|
196
|
-
# where the transaction level gets persisted for the whole session:
|
197
|
-
#
|
198
|
-
# http://bugs.mysql.com/bug.php?id=39170
|
199
79
|
def supports_transaction_isolation?
|
200
|
-
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
def supports_explain?
|
84
|
+
true
|
201
85
|
end
|
202
86
|
|
203
87
|
def supports_indexes_in_create?
|
@@ -209,7 +93,35 @@ module ActiveRecord
|
|
209
93
|
end
|
210
94
|
|
211
95
|
def supports_views?
|
212
|
-
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
def supports_datetime_with_precision?
|
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
|
213
125
|
end
|
214
126
|
|
215
127
|
def native_database_types
|
@@ -217,7 +129,7 @@ module ActiveRecord
|
|
217
129
|
end
|
218
130
|
|
219
131
|
def index_algorithms
|
220
|
-
{ default:
|
132
|
+
{ default: "ALGORITHM = DEFAULT".dup, copy: "ALGORITHM = COPY".dup, inplace: "ALGORITHM = INPLACE".dup }
|
221
133
|
end
|
222
134
|
|
223
135
|
# HELPER METHODS ===========================================
|
@@ -228,54 +140,16 @@ module ActiveRecord
|
|
228
140
|
raise NotImplementedError
|
229
141
|
end
|
230
142
|
|
231
|
-
def new_column(field, default, cast_type, sql_type = nil, null = true, collation = "", extra = "") # :nodoc:
|
232
|
-
Column.new(field, default, cast_type, sql_type, null, collation, strict_mode?, extra)
|
233
|
-
end
|
234
|
-
|
235
143
|
# Must return the MySQL error number from the exception, if the exception has an
|
236
144
|
# error number.
|
237
145
|
def error_number(exception) # :nodoc:
|
238
146
|
raise NotImplementedError
|
239
147
|
end
|
240
148
|
|
241
|
-
# QUOTING ==================================================
|
242
|
-
|
243
|
-
def _quote(value) # :nodoc:
|
244
|
-
if value.is_a?(Type::Binary::Data)
|
245
|
-
"x'#{value.hex}'"
|
246
|
-
else
|
247
|
-
super
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
def quote_column_name(name) #:nodoc:
|
252
|
-
@quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
|
253
|
-
end
|
254
|
-
|
255
|
-
def quote_table_name(name) #:nodoc:
|
256
|
-
@quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
|
257
|
-
end
|
258
|
-
|
259
|
-
def quoted_true
|
260
|
-
QUOTED_TRUE
|
261
|
-
end
|
262
|
-
|
263
|
-
def unquoted_true
|
264
|
-
1
|
265
|
-
end
|
266
|
-
|
267
|
-
def quoted_false
|
268
|
-
QUOTED_FALSE
|
269
|
-
end
|
270
|
-
|
271
|
-
def unquoted_false
|
272
|
-
0
|
273
|
-
end
|
274
|
-
|
275
149
|
# REFERENTIAL INTEGRITY ====================================
|
276
150
|
|
277
151
|
def disable_referential_integrity #:nodoc:
|
278
|
-
old =
|
152
|
+
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
279
153
|
|
280
154
|
begin
|
281
155
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
@@ -285,32 +159,43 @@ module ActiveRecord
|
|
285
159
|
end
|
286
160
|
end
|
287
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
|
+
|
288
170
|
#--
|
289
171
|
# DATABASE STATEMENTS ======================================
|
290
172
|
#++
|
291
173
|
|
292
|
-
def
|
293
|
-
|
294
|
-
|
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)
|
295
181
|
end
|
296
182
|
|
297
183
|
# Executes the SQL statement in the context of this connection.
|
298
184
|
def execute(sql, name = nil)
|
299
|
-
log(sql, name)
|
185
|
+
log(sql, name) do
|
186
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
187
|
+
@connection.query(sql)
|
188
|
+
end
|
189
|
+
end
|
300
190
|
end
|
301
191
|
|
302
|
-
#
|
303
|
-
# stuff in an abstract way without concerning ourselves about whether it
|
304
|
-
# explicitly freed or not.
|
305
|
-
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:
|
306
196
|
yield execute(sql, name)
|
307
197
|
end
|
308
198
|
|
309
|
-
def update_sql(sql, name = nil) #:nodoc:
|
310
|
-
super
|
311
|
-
@connection.affected_rows
|
312
|
-
end
|
313
|
-
|
314
199
|
def begin_db_transaction
|
315
200
|
execute "BEGIN"
|
316
201
|
end
|
@@ -324,14 +209,14 @@ module ActiveRecord
|
|
324
209
|
execute "COMMIT"
|
325
210
|
end
|
326
211
|
|
327
|
-
def
|
212
|
+
def exec_rollback_db_transaction #:nodoc:
|
328
213
|
execute "ROLLBACK"
|
329
214
|
end
|
330
215
|
|
331
216
|
# In the simple case, MySQL allows us to place JOINs directly into the UPDATE
|
332
217
|
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
333
218
|
# these, we must use a subquery.
|
334
|
-
def join_to_update(update, select)
|
219
|
+
def join_to_update(update, select, key) # :nodoc:
|
335
220
|
if select.limit || select.offset || select.orders.any?
|
336
221
|
super
|
337
222
|
else
|
@@ -364,9 +249,9 @@ module ActiveRecord
|
|
364
249
|
# create_database 'matt_development', charset: :big5
|
365
250
|
def create_database(name, options = {})
|
366
251
|
if options[:collation]
|
367
|
-
execute "CREATE DATABASE
|
252
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
|
368
253
|
else
|
369
|
-
execute "CREATE DATABASE
|
254
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')}"
|
370
255
|
end
|
371
256
|
end
|
372
257
|
|
@@ -375,97 +260,42 @@ module ActiveRecord
|
|
375
260
|
# Example:
|
376
261
|
# drop_database('sebastian_development')
|
377
262
|
def drop_database(name) #:nodoc:
|
378
|
-
execute "DROP DATABASE IF EXISTS
|
263
|
+
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
379
264
|
end
|
380
265
|
|
381
266
|
def current_database
|
382
|
-
|
267
|
+
query_value("SELECT database()", "SCHEMA")
|
383
268
|
end
|
384
269
|
|
385
270
|
# Returns the database character set.
|
386
271
|
def charset
|
387
|
-
show_variable
|
272
|
+
show_variable "character_set_database"
|
388
273
|
end
|
389
274
|
|
390
275
|
# Returns the database collation strategy.
|
391
276
|
def collation
|
392
|
-
show_variable
|
393
|
-
end
|
394
|
-
|
395
|
-
def tables(name = nil, database = nil, like = nil) #:nodoc:
|
396
|
-
sql = "SHOW TABLES "
|
397
|
-
sql << "IN #{quote_table_name(database)} " if database
|
398
|
-
sql << "LIKE #{quote(like)}" if like
|
399
|
-
|
400
|
-
execute_and_free(sql, 'SCHEMA') do |result|
|
401
|
-
result.collect { |field| field.first }
|
402
|
-
end
|
277
|
+
show_variable "collation_database"
|
403
278
|
end
|
404
279
|
|
405
280
|
def truncate(table_name, name = nil)
|
406
281
|
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
407
282
|
end
|
408
283
|
|
409
|
-
def
|
410
|
-
|
411
|
-
return true if tables(nil, nil, name).any?
|
412
|
-
|
413
|
-
name = name.to_s
|
414
|
-
schema, table = name.split('.', 2)
|
415
|
-
|
416
|
-
unless table # A table was provided without a schema
|
417
|
-
table = schema
|
418
|
-
schema = nil
|
419
|
-
end
|
420
|
-
|
421
|
-
tables(nil, schema, table).any?
|
422
|
-
end
|
423
|
-
|
424
|
-
# Returns an array of indexes for the given table.
|
425
|
-
def indexes(table_name, name = nil) #:nodoc:
|
426
|
-
indexes = []
|
427
|
-
current_index = nil
|
428
|
-
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
|
429
|
-
each_hash(result) do |row|
|
430
|
-
if current_index != row[:Key_name]
|
431
|
-
next if row[:Key_name] == 'PRIMARY' # skip the primary key
|
432
|
-
current_index = row[:Key_name]
|
433
|
-
|
434
|
-
mysql_index_type = row[:Index_type].downcase.to_sym
|
435
|
-
index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
|
436
|
-
index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
|
437
|
-
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using)
|
438
|
-
end
|
439
|
-
|
440
|
-
indexes.last.columns << row[:Column_name]
|
441
|
-
indexes.last.lengths << row[:Sub_part]
|
442
|
-
end
|
443
|
-
end
|
444
|
-
|
445
|
-
indexes
|
446
|
-
end
|
447
|
-
|
448
|
-
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
449
|
-
def columns(table_name)#:nodoc:
|
450
|
-
sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
|
451
|
-
execute_and_free(sql, 'SCHEMA') do |result|
|
452
|
-
each_hash(result).map do |field|
|
453
|
-
field_name = set_field_encoding(field[:Field])
|
454
|
-
sql_type = field[:Type]
|
455
|
-
cast_type = lookup_cast_type(sql_type)
|
456
|
-
new_column(field_name, field[:Default], cast_type, sql_type, field[:Null] == "YES", field[:Collation], field[:Extra])
|
457
|
-
end
|
458
|
-
end
|
459
|
-
end
|
284
|
+
def table_comment(table_name) # :nodoc:
|
285
|
+
scope = quoted_scope(table_name)
|
460
286
|
|
461
|
-
|
462
|
-
|
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
|
463
293
|
end
|
464
294
|
|
465
295
|
def bulk_change_table(table_name, operations) #:nodoc:
|
466
296
|
sqls = operations.flat_map do |command, args|
|
467
297
|
table, arguments = args.shift, args
|
468
|
-
method = :"#{command}
|
298
|
+
method = :"#{command}_for_alter"
|
469
299
|
|
470
300
|
if respond_to?(method, true)
|
471
301
|
send(method, table, *arguments)
|
@@ -477,6 +307,11 @@ module ActiveRecord
|
|
477
307
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
478
308
|
end
|
479
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
|
+
|
480
315
|
# Renames a table.
|
481
316
|
#
|
482
317
|
# Example:
|
@@ -486,398 +321,567 @@ module ActiveRecord
|
|
486
321
|
rename_table_indexes(table_name, new_name)
|
487
322
|
end
|
488
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.
|
489
339
|
def drop_table(table_name, options = {})
|
490
|
-
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}"
|
491
341
|
end
|
492
342
|
|
493
343
|
def rename_index(table_name, old_name, new_name)
|
494
344
|
if supports_rename_index?
|
345
|
+
validate_index_length!(table_name, new_name)
|
346
|
+
|
495
347
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
|
496
348
|
else
|
497
349
|
super
|
498
350
|
end
|
499
351
|
end
|
500
352
|
|
501
|
-
def change_column_default(table_name, column_name,
|
502
|
-
|
503
|
-
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
|
504
356
|
end
|
505
357
|
|
506
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
507
|
-
column = column_for(table_name, column_name)
|
508
|
-
|
358
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
509
359
|
unless null || default.nil?
|
510
360
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
511
361
|
end
|
512
362
|
|
513
|
-
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
|
514
368
|
end
|
515
369
|
|
516
370
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
517
|
-
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)}")
|
518
372
|
end
|
519
373
|
|
520
374
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
521
|
-
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)}")
|
522
376
|
rename_column_indexes(table_name, column_name, new_column_name)
|
523
377
|
end
|
524
378
|
|
525
379
|
def add_index(table_name, column_name, options = {}) #:nodoc:
|
526
|
-
index_name, index_type, index_columns,
|
527
|
-
|
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
|
528
388
|
end
|
529
389
|
|
530
390
|
def foreign_keys(table_name)
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
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]}
|
540
410
|
SQL
|
541
411
|
|
542
|
-
create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
|
543
|
-
|
544
412
|
fk_info.map do |row|
|
545
413
|
options = {
|
546
|
-
column: row[
|
547
|
-
name: row[
|
548
|
-
primary_key: row[
|
414
|
+
column: row["column"],
|
415
|
+
name: row["name"],
|
416
|
+
primary_key: row["primary_key"]
|
549
417
|
}
|
550
418
|
|
551
|
-
options[:on_update] = extract_foreign_key_action(
|
552
|
-
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"])
|
553
421
|
|
554
|
-
ForeignKeyDefinition.new(table_name, row[
|
422
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
555
423
|
end
|
556
424
|
end
|
557
425
|
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
when 5..8; 'bigint'
|
575
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
576
|
-
end
|
577
|
-
when 'text'
|
578
|
-
case limit
|
579
|
-
when 0..0xff; 'tinytext'
|
580
|
-
when nil, 0x100..0xffff; 'text'
|
581
|
-
when 0x10000..0xffffff; 'mediumtext'
|
582
|
-
when 0x1000000..0xffffffff; 'longtext'
|
583
|
-
else raise(ActiveRecordError, "No text type has character length #{limit}")
|
584
|
-
end
|
585
|
-
else
|
586
|
-
super
|
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)
|
587
442
|
end
|
588
|
-
end
|
589
443
|
|
590
|
-
|
591
|
-
def show_variable(name)
|
592
|
-
variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
|
593
|
-
variables.first['Value'] unless variables.empty?
|
444
|
+
table_options
|
594
445
|
end
|
595
446
|
|
596
|
-
#
|
597
|
-
def
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
447
|
+
# Maps logical Rails types to MySQL-specific data types.
|
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
|
603
463
|
else
|
604
|
-
|
464
|
+
super
|
605
465
|
end
|
606
|
-
|
466
|
+
|
467
|
+
sql = "#{sql} unsigned" if unsigned && type != :primary_key
|
468
|
+
sql
|
607
469
|
end
|
608
470
|
|
609
|
-
#
|
610
|
-
def
|
611
|
-
|
612
|
-
|
471
|
+
# SHOW VARIABLES LIKE 'name'
|
472
|
+
def show_variable(name)
|
473
|
+
query_value("SELECT @@#{name}", "SCHEMA")
|
474
|
+
rescue ActiveRecord::StatementInvalid
|
475
|
+
nil
|
613
476
|
end
|
614
477
|
|
615
|
-
def
|
616
|
-
|
617
|
-
|
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
|
618
491
|
end
|
619
492
|
|
620
|
-
def case_sensitive_comparison(table, attribute, column, value)
|
621
|
-
if column.case_sensitive?
|
622
|
-
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))
|
623
496
|
else
|
624
497
|
super
|
625
498
|
end
|
626
499
|
end
|
627
500
|
|
628
|
-
def
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
501
|
+
def can_perform_case_insensitive_comparison_for?(column)
|
502
|
+
column.case_sensitive?
|
503
|
+
end
|
504
|
+
private :can_perform_case_insensitive_comparison_for?
|
505
|
+
|
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(", ")
|
634
519
|
end
|
635
520
|
|
636
521
|
def strict_mode?
|
637
522
|
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
638
523
|
end
|
639
524
|
|
640
|
-
def
|
641
|
-
|
525
|
+
def default_index_type?(index) # :nodoc:
|
526
|
+
index.using == :btree || super
|
642
527
|
end
|
643
528
|
|
644
|
-
|
529
|
+
def insert_fixtures_set(fixture_set, tables_to_delete = [])
|
530
|
+
with_multi_statements do
|
531
|
+
super { discard_remaining_results }
|
532
|
+
end
|
533
|
+
end
|
645
534
|
|
646
|
-
|
647
|
-
|
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
|
648
547
|
|
649
|
-
|
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
|
650
557
|
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
656
|
-
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
657
|
-
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
658
|
-
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
659
|
-
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
660
|
-
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
|
661
562
|
|
662
|
-
|
663
|
-
|
664
|
-
register_integer_type m, %r(^mediumint)i, limit: 3
|
665
|
-
register_integer_type m, %r(^smallint)i, limit: 2
|
666
|
-
register_integer_type m, %r(^tinyint)i, limit: 1
|
563
|
+
def initialize_type_map(m = type_map)
|
564
|
+
super
|
667
565
|
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
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
|
672
594
|
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
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
|
677
600
|
end
|
678
|
-
end
|
679
601
|
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
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
|
686
609
|
end
|
687
610
|
end
|
688
|
-
end
|
689
611
|
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
subselect.project Arel.sql(key.name)
|
698
|
-
subselect.from subsubselect.as('__active_record_temp')
|
699
|
-
end
|
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
|
700
619
|
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
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
|
708
664
|
end
|
709
665
|
end
|
710
666
|
|
711
|
-
|
712
|
-
|
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
|
713
670
|
|
714
|
-
|
715
|
-
|
671
|
+
unless options.key?(:default)
|
672
|
+
options[:default] = column.default
|
673
|
+
end
|
716
674
|
|
717
|
-
|
718
|
-
|
675
|
+
unless options.key?(:null)
|
676
|
+
options[:null] = column.null
|
677
|
+
end
|
719
678
|
|
720
|
-
|
721
|
-
|
679
|
+
unless options.key?(:comment)
|
680
|
+
options[:comment] = column.comment
|
681
|
+
end
|
722
682
|
|
723
|
-
|
724
|
-
|
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
|
725
687
|
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
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))
|
734
700
|
end
|
735
|
-
end
|
736
701
|
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
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
|
742
707
|
|
743
|
-
|
744
|
-
|
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
|
745
712
|
|
746
|
-
|
747
|
-
|
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)]
|
748
715
|
end
|
749
716
|
|
750
|
-
|
751
|
-
|
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)]
|
752
719
|
end
|
753
720
|
|
754
|
-
|
755
|
-
|
756
|
-
|
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]
|
757
726
|
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
name: new_column_name,
|
762
|
-
default: column.default,
|
763
|
-
null: column.null,
|
764
|
-
auto_increment: column.extra == "auto_increment"
|
765
|
-
}
|
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?
|
766
730
|
|
767
|
-
|
768
|
-
|
769
|
-
|
731
|
+
key_name = quote_column_name(key.name)
|
732
|
+
Arel::SelectManager.new(subselect.as("__active_record_temp")).project(Arel.sql(key_name))
|
733
|
+
end
|
770
734
|
|
771
|
-
|
772
|
-
|
773
|
-
|
735
|
+
def supports_rename_index?
|
736
|
+
mariadb? ? false : version >= "5.7.6"
|
737
|
+
end
|
774
738
|
|
775
|
-
|
776
|
-
|
777
|
-
end
|
739
|
+
def configure_connection
|
740
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
778
741
|
|
779
|
-
|
780
|
-
|
781
|
-
"ADD #{index_type} INDEX #{index_name} (#{index_columns})"
|
782
|
-
end
|
742
|
+
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
743
|
+
variables["sql_auto_is_null"] = 0
|
783
744
|
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
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
|
788
749
|
|
789
|
-
|
790
|
-
[add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
|
791
|
-
end
|
750
|
+
defaults = [":default", :default].to_set
|
792
751
|
|
793
|
-
|
794
|
-
|
795
|
-
|
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
|
796
777
|
|
797
|
-
|
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(", ")
|
798
787
|
|
799
|
-
|
800
|
-
|
801
|
-
|
788
|
+
# ...and send them all in one query
|
789
|
+
execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
|
790
|
+
end
|
802
791
|
|
803
|
-
|
804
|
-
|
805
|
-
|
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
|
806
797
|
|
807
|
-
|
808
|
-
|
809
|
-
|
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
|
810
801
|
|
811
|
-
|
812
|
-
|
802
|
+
def arel_visitor
|
803
|
+
Arel::Visitors::MySQL.new(self)
|
804
|
+
end
|
813
805
|
|
814
|
-
|
815
|
-
|
816
|
-
|
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)
|
817
812
|
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout)
|
813
|
+
options = {
|
814
|
+
message: message,
|
815
|
+
}
|
822
816
|
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
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
|
829
824
|
|
830
|
-
|
831
|
-
# http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
|
832
|
-
# (trailing comma because variable_assignments will always have content)
|
833
|
-
if @config[:encoding]
|
834
|
-
encoding = "NAMES #{@config[:encoding]}"
|
835
|
-
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
836
|
-
encoding << ", "
|
825
|
+
MismatchedForeignKey.new(options)
|
837
826
|
end
|
838
827
|
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
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.")
|
845
836
|
end
|
846
|
-
|
847
|
-
end.compact.join(', ')
|
848
|
-
|
849
|
-
# ...and send them all in one query
|
850
|
-
@connection.query "SET #{encoding} #{variable_assignments}"
|
851
|
-
end
|
837
|
+
end
|
852
838
|
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
when
|
857
|
-
when
|
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}")
|
858
846
|
end
|
859
847
|
end
|
860
|
-
end
|
861
848
|
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
when
|
866
|
-
when
|
867
|
-
|
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}")
|
868
856
|
end
|
869
857
|
end
|
870
858
|
|
871
|
-
|
859
|
+
def version_string
|
860
|
+
full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
861
|
+
end
|
872
862
|
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
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
|
878
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
|
879
881
|
end
|
880
|
-
|
882
|
+
|
883
|
+
ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
|
884
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
881
885
|
end
|
882
886
|
end
|
883
887
|
end
|