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