activerecord 5.0.7 → 5.1.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +657 -2080
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +28 -28
- data/examples/simple.rb +3 -3
- data/lib/active_record/aggregations.rb +244 -244
- data/lib/active_record/association_relation.rb +5 -5
- data/lib/active_record/associations/alias_tracker.rb +10 -11
- data/lib/active_record/associations/association.rb +23 -5
- data/lib/active_record/associations/association_scope.rb +95 -81
- data/lib/active_record/associations/belongs_to_association.rb +7 -4
- data/lib/active_record/associations/builder/belongs_to.rb +30 -16
- data/lib/active_record/associations/builder/collection_association.rb +1 -2
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
- data/lib/active_record/associations/collection_association.rb +36 -205
- data/lib/active_record/associations/collection_proxy.rb +132 -63
- data/lib/active_record/associations/has_many_association.rb +10 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -4
- data/lib/active_record/associations/has_one_association.rb +24 -28
- data/lib/active_record/associations/has_one_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
- data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +121 -118
- data/lib/active_record/associations/preloader/association.rb +64 -64
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
- data/lib/active_record/associations/preloader/collection_association.rb +6 -6
- data/lib/active_record/associations/preloader/has_many.rb +0 -2
- data/lib/active_record/associations/preloader/singular_association.rb +6 -8
- data/lib/active_record/associations/preloader/through_association.rb +41 -41
- data/lib/active_record/associations/preloader.rb +94 -94
- data/lib/active_record/associations/singular_association.rb +8 -25
- data/lib/active_record/associations/through_association.rb +2 -5
- data/lib/active_record/associations.rb +1591 -1562
- data/lib/active_record/attribute/user_provided_default.rb +4 -2
- data/lib/active_record/attribute.rb +98 -71
- data/lib/active_record/attribute_assignment.rb +61 -61
- data/lib/active_record/attribute_decorators.rb +35 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
- data/lib/active_record/attribute_methods/dirty.rb +229 -46
- data/lib/active_record/attribute_methods/primary_key.rb +74 -73
- data/lib/active_record/attribute_methods/read.rb +39 -35
- data/lib/active_record/attribute_methods/serialization.rb +7 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
- data/lib/active_record/attribute_methods/write.rb +30 -33
- data/lib/active_record/attribute_methods.rb +56 -65
- data/lib/active_record/attribute_mutation_tracker.rb +63 -11
- data/lib/active_record/attribute_set/builder.rb +27 -33
- data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
- data/lib/active_record/attribute_set.rb +9 -6
- data/lib/active_record/attributes.rb +22 -22
- data/lib/active_record/autosave_association.rb +18 -13
- data/lib/active_record/base.rb +24 -22
- data/lib/active_record/callbacks.rb +56 -14
- data/lib/active_record/coders/yaml_column.rb +9 -11
- data/lib/active_record/collection_cache_key.rb +3 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
- data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
- data/lib/active_record/connection_adapters/column.rb +26 -4
- data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
- data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
- data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
- data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
- data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +182 -222
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
- data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
- data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -19
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
- data/lib/active_record/connection_handling.rb +14 -26
- data/lib/active_record/core.rb +109 -93
- data/lib/active_record/counter_cache.rb +60 -13
- data/lib/active_record/define_callbacks.rb +20 -0
- data/lib/active_record/dynamic_matchers.rb +80 -79
- data/lib/active_record/enum.rb +8 -6
- data/lib/active_record/errors.rb +64 -15
- data/lib/active_record/explain.rb +1 -2
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +7 -4
- data/lib/active_record/fixture_set/file.rb +11 -8
- data/lib/active_record/fixtures.rb +66 -53
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +93 -79
- data/lib/active_record/integration.rb +7 -7
- data/lib/active_record/internal_metadata.rb +3 -16
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +69 -74
- data/lib/active_record/locking/pessimistic.rb +10 -1
- data/lib/active_record/log_subscriber.rb +23 -28
- data/lib/active_record/migration/command_recorder.rb +94 -94
- data/lib/active_record/migration/compatibility.rb +100 -47
- data/lib/active_record/migration/join_table.rb +6 -6
- data/lib/active_record/migration.rb +153 -155
- data/lib/active_record/model_schema.rb +94 -107
- data/lib/active_record/nested_attributes.rb +200 -199
- data/lib/active_record/null_relation.rb +11 -34
- data/lib/active_record/persistence.rb +65 -50
- data/lib/active_record/query_cache.rb +2 -6
- data/lib/active_record/querying.rb +3 -4
- data/lib/active_record/railtie.rb +16 -17
- data/lib/active_record/railties/controller_runtime.rb +6 -2
- data/lib/active_record/railties/databases.rake +105 -133
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -2
- data/lib/active_record/reflection.rb +154 -108
- data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
- data/lib/active_record/relation/batches.rb +80 -51
- data/lib/active_record/relation/calculations.rb +169 -162
- data/lib/active_record/relation/delegation.rb +32 -31
- data/lib/active_record/relation/finder_methods.rb +197 -231
- data/lib/active_record/relation/merger.rb +58 -62
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
- data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder.rb +92 -89
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +255 -293
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +4 -5
- data/lib/active_record/relation/where_clause.rb +80 -65
- data/lib/active_record/relation/where_clause_factory.rb +47 -8
- data/lib/active_record/relation.rb +93 -119
- data/lib/active_record/result.rb +41 -32
- data/lib/active_record/runtime_registry.rb +3 -3
- data/lib/active_record/sanitization.rb +176 -192
- data/lib/active_record/schema.rb +3 -3
- data/lib/active_record/schema_dumper.rb +15 -38
- data/lib/active_record/schema_migration.rb +8 -4
- data/lib/active_record/scoping/default.rb +90 -90
- data/lib/active_record/scoping/named.rb +11 -11
- data/lib/active_record/scoping.rb +6 -6
- data/lib/active_record/secure_token.rb +2 -2
- data/lib/active_record/statement_cache.rb +13 -15
- data/lib/active_record/store.rb +31 -32
- data/lib/active_record/suppressor.rb +2 -1
- data/lib/active_record/table_metadata.rb +9 -5
- data/lib/active_record/tasks/database_tasks.rb +65 -55
- data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
- data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
- data/lib/active_record/timestamp.rb +46 -25
- data/lib/active_record/touch_later.rb +1 -2
- data/lib/active_record/transactions.rb +97 -109
- data/lib/active_record/type/adapter_specific_registry.rb +46 -42
- data/lib/active_record/type/decimal_without_scale.rb +13 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
- data/lib/active_record/type/internal/abstract_json.rb +4 -0
- data/lib/active_record/type/serialized.rb +14 -8
- data/lib/active_record/type/text.rb +9 -0
- data/lib/active_record/type/time.rb +0 -1
- data/lib/active_record/type/type_map.rb +11 -15
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type.rb +17 -13
- data/lib/active_record/type_caster/connection.rb +8 -6
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.rb +2 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +8 -39
- data/lib/active_record/validations.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +20 -20
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
- data/lib/rails/generators/active_record/migration.rb +1 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
- data/lib/rails/generators/active_record.rb +4 -4
- metadata +24 -13
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -1,20 +1,22 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
|
11
|
-
|
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/schema_statements"
|
10
|
+
require "active_record/connection_adapters/mysql/type_metadata"
|
11
|
+
|
12
|
+
require "active_support/core_ext/string/strip"
|
12
13
|
|
13
14
|
module ActiveRecord
|
14
15
|
module ConnectionAdapters
|
15
16
|
class AbstractMysqlAdapter < AbstractAdapter
|
16
17
|
include MySQL::Quoting
|
17
18
|
include MySQL::ColumnDumper
|
19
|
+
include MySQL::SchemaStatements
|
18
20
|
|
19
21
|
def update_table_definition(table_name, base) # :nodoc:
|
20
22
|
MySQL::Table.new(table_name, base)
|
@@ -39,16 +41,17 @@ module ActiveRecord
|
|
39
41
|
self.emulate_booleans = true
|
40
42
|
|
41
43
|
NATIVE_DATABASE_TYPES = {
|
42
|
-
primary_key: "
|
44
|
+
primary_key: "bigint auto_increment PRIMARY KEY",
|
43
45
|
string: { name: "varchar", limit: 255 },
|
44
|
-
text: { name: "text" },
|
46
|
+
text: { name: "text", limit: 65535 },
|
45
47
|
integer: { name: "int", limit: 4 },
|
46
48
|
float: { name: "float" },
|
47
49
|
decimal: { name: "decimal" },
|
48
50
|
datetime: { name: "datetime" },
|
51
|
+
timestamp: { name: "timestamp" },
|
49
52
|
time: { name: "time" },
|
50
53
|
date: { name: "date" },
|
51
|
-
binary: { name: "blob" },
|
54
|
+
binary: { name: "blob", limit: 65535 },
|
52
55
|
boolean: { name: "tinyint", limit: 1 },
|
53
56
|
json: { name: "json" },
|
54
57
|
}
|
@@ -67,34 +70,17 @@ module ActiveRecord
|
|
67
70
|
|
68
71
|
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
69
72
|
|
70
|
-
if version <
|
71
|
-
raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.
|
73
|
+
if version < "5.1.10"
|
74
|
+
raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.1.10."
|
72
75
|
end
|
73
76
|
end
|
74
77
|
|
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
78
|
def version #:nodoc:
|
84
79
|
@version ||= Version.new(version_string)
|
85
80
|
end
|
86
81
|
|
87
82
|
def mariadb? # :nodoc:
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
# Returns true, since this connection adapter supports migrations.
|
92
|
-
def supports_migrations?
|
93
|
-
true
|
94
|
-
end
|
95
|
-
|
96
|
-
def supports_primary_key?
|
97
|
-
true
|
83
|
+
/mariadb/i.match?(full_version)
|
98
84
|
end
|
99
85
|
|
100
86
|
def supports_bulk_alter? #:nodoc:
|
@@ -135,9 +121,17 @@ module ActiveRecord
|
|
135
121
|
|
136
122
|
def supports_datetime_with_precision?
|
137
123
|
if mariadb?
|
138
|
-
version >=
|
124
|
+
version >= "5.3.0"
|
139
125
|
else
|
140
|
-
version >=
|
126
|
+
version >= "5.6.4"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def supports_virtual_columns?
|
131
|
+
if mariadb?
|
132
|
+
version >= "5.2.0"
|
133
|
+
else
|
134
|
+
version >= "5.7.5"
|
141
135
|
end
|
142
136
|
end
|
143
137
|
|
@@ -146,11 +140,11 @@ module ActiveRecord
|
|
146
140
|
end
|
147
141
|
|
148
142
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
149
|
-
|
143
|
+
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
150
144
|
end
|
151
145
|
|
152
146
|
def release_advisory_lock(lock_name) # :nodoc:
|
153
|
-
|
147
|
+
query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
|
154
148
|
end
|
155
149
|
|
156
150
|
def native_database_types
|
@@ -158,7 +152,7 @@ module ActiveRecord
|
|
158
152
|
end
|
159
153
|
|
160
154
|
def index_algorithms
|
161
|
-
{ default:
|
155
|
+
{ default: "ALGORITHM = DEFAULT", copy: "ALGORITHM = COPY", inplace: "ALGORITHM = INPLACE" }
|
162
156
|
end
|
163
157
|
|
164
158
|
# HELPER METHODS ===========================================
|
@@ -182,7 +176,7 @@ module ActiveRecord
|
|
182
176
|
# REFERENTIAL INTEGRITY ====================================
|
183
177
|
|
184
178
|
def disable_referential_integrity #:nodoc:
|
185
|
-
old =
|
179
|
+
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
186
180
|
|
187
181
|
begin
|
188
182
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
@@ -207,7 +201,7 @@ module ActiveRecord
|
|
207
201
|
def explain(arel, binds = [])
|
208
202
|
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
209
203
|
start = Time.now
|
210
|
-
result = exec_query(sql,
|
204
|
+
result = exec_query(sql, "EXPLAIN", binds)
|
211
205
|
elapsed = Time.now - start
|
212
206
|
|
213
207
|
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
@@ -215,7 +209,11 @@ module ActiveRecord
|
|
215
209
|
|
216
210
|
# Executes the SQL statement in the context of this connection.
|
217
211
|
def execute(sql, name = nil)
|
218
|
-
log(sql, name)
|
212
|
+
log(sql, name) do
|
213
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
214
|
+
@connection.query(sql)
|
215
|
+
end
|
216
|
+
end
|
219
217
|
end
|
220
218
|
|
221
219
|
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
@@ -293,91 +291,37 @@ module ActiveRecord
|
|
293
291
|
end
|
294
292
|
|
295
293
|
def current_database
|
296
|
-
|
294
|
+
query_value("SELECT database()", "SCHEMA")
|
297
295
|
end
|
298
296
|
|
299
297
|
# Returns the database character set.
|
300
298
|
def charset
|
301
|
-
show_variable
|
299
|
+
show_variable "character_set_database"
|
302
300
|
end
|
303
301
|
|
304
302
|
# Returns the database collation strategy.
|
305
303
|
def collation
|
306
|
-
show_variable
|
307
|
-
end
|
308
|
-
|
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
|
315
|
-
|
316
|
-
if name
|
317
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
318
|
-
Passing arguments to #tables is deprecated without replacement.
|
319
|
-
MSG
|
320
|
-
end
|
321
|
-
|
322
|
-
data_sources
|
323
|
-
end
|
324
|
-
|
325
|
-
def data_sources
|
326
|
-
sql = "SELECT table_name FROM information_schema.tables "
|
327
|
-
sql << "WHERE table_schema = DATABASE()"
|
328
|
-
|
329
|
-
select_values(sql, 'SCHEMA')
|
304
|
+
show_variable "collation_database"
|
330
305
|
end
|
331
306
|
|
332
307
|
def truncate(table_name, name = nil)
|
333
308
|
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
334
309
|
end
|
335
310
|
|
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
|
343
|
-
|
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 = #{schema} AND table_name = #{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 = #{schema} AND table_name = #{name}"
|
369
|
-
|
370
|
-
select_values(sql, 'SCHEMA').any?
|
371
|
-
end
|
372
|
-
|
373
311
|
# Returns an array of indexes for the given table.
|
374
312
|
def indexes(table_name, name = nil) #:nodoc:
|
313
|
+
if name
|
314
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
315
|
+
Passing name to #indexes is deprecated without replacement.
|
316
|
+
MSG
|
317
|
+
end
|
318
|
+
|
375
319
|
indexes = []
|
376
320
|
current_index = nil
|
377
|
-
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}",
|
321
|
+
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
|
378
322
|
each_hash(result) do |row|
|
379
323
|
if current_index != row[:Key_name]
|
380
|
-
next if row[:Key_name] ==
|
324
|
+
next if row[:Key_name] == "PRIMARY" # skip the primary key
|
381
325
|
current_index = row[:Key_name]
|
382
326
|
|
383
327
|
mysql_index_type = row[:Index_type].downcase.to_sym
|
@@ -394,33 +338,29 @@ module ActiveRecord
|
|
394
338
|
indexes
|
395
339
|
end
|
396
340
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
default, default_function = nil, "CURRENT_TIMESTAMP"
|
404
|
-
else
|
405
|
-
default, default_function = field[:Default], nil
|
406
|
-
end
|
407
|
-
new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation], comment: field[:Comment].presence)
|
341
|
+
def new_column_from_field(table_name, field) # :nodoc:
|
342
|
+
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
|
343
|
+
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\(\))?\z/i.match?(field[:Default])
|
344
|
+
default, default_function = nil, "CURRENT_TIMESTAMP"
|
345
|
+
else
|
346
|
+
default, default_function = field[:Default], nil
|
408
347
|
end
|
348
|
+
new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation], comment: field[:Comment].presence)
|
409
349
|
end
|
410
350
|
|
411
351
|
def table_comment(table_name) # :nodoc:
|
412
|
-
|
352
|
+
scope = quoted_scope(table_name)
|
413
353
|
|
414
|
-
|
354
|
+
query_value(<<-SQL.strip_heredoc, "SCHEMA")
|
415
355
|
SELECT table_comment
|
416
356
|
FROM information_schema.tables
|
417
|
-
WHERE table_schema = #{schema}
|
418
|
-
AND table_name = #{name}
|
357
|
+
WHERE table_schema = #{scope[:schema]}
|
358
|
+
AND table_name = #{scope[:name]}
|
419
359
|
SQL
|
420
360
|
end
|
421
361
|
|
422
362
|
def create_table(table_name, **options) #:nodoc:
|
423
|
-
super(table_name, options:
|
363
|
+
super(table_name, options: "ENGINE=InnoDB", **options)
|
424
364
|
end
|
425
365
|
|
426
366
|
def bulk_change_table(table_name, operations) #:nodoc:
|
@@ -479,7 +419,7 @@ module ActiveRecord
|
|
479
419
|
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
|
480
420
|
default = extract_new_default_value(default_or_changes)
|
481
421
|
column = column_for(table_name, column_name)
|
482
|
-
change_column table_name, column_name, column.sql_type, :
|
422
|
+
change_column table_name, column_name, column.sql_type, default: default
|
483
423
|
end
|
484
424
|
|
485
425
|
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
@@ -489,7 +429,7 @@ module ActiveRecord
|
|
489
429
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
490
430
|
end
|
491
431
|
|
492
|
-
change_column table_name, column_name, column.sql_type, :
|
432
|
+
change_column table_name, column_name, column.sql_type, null: null
|
493
433
|
end
|
494
434
|
|
495
435
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
@@ -515,35 +455,36 @@ module ActiveRecord
|
|
515
455
|
def foreign_keys(table_name)
|
516
456
|
raise ArgumentError unless table_name.present?
|
517
457
|
|
518
|
-
|
458
|
+
scope = quoted_scope(table_name)
|
519
459
|
|
520
|
-
fk_info =
|
460
|
+
fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
|
521
461
|
SELECT fk.referenced_table_name AS 'to_table',
|
522
462
|
fk.referenced_column_name AS 'primary_key',
|
523
463
|
fk.column_name AS 'column',
|
524
464
|
fk.constraint_name AS 'name',
|
525
465
|
rc.update_rule AS 'on_update',
|
526
466
|
rc.delete_rule AS 'on_delete'
|
527
|
-
FROM information_schema.
|
528
|
-
JOIN information_schema.
|
467
|
+
FROM information_schema.referential_constraints rc
|
468
|
+
JOIN information_schema.key_column_usage fk
|
529
469
|
USING (constraint_schema, constraint_name)
|
530
470
|
WHERE fk.referenced_column_name IS NOT NULL
|
531
|
-
AND fk.table_schema = #{schema}
|
532
|
-
AND fk.table_name = #{name}
|
533
|
-
AND rc.
|
471
|
+
AND fk.table_schema = #{scope[:schema]}
|
472
|
+
AND fk.table_name = #{scope[:name]}
|
473
|
+
AND rc.constraint_schema = #{scope[:schema]}
|
474
|
+
AND rc.table_name = #{scope[:name]}
|
534
475
|
SQL
|
535
476
|
|
536
477
|
fk_info.map do |row|
|
537
478
|
options = {
|
538
|
-
column: row[
|
539
|
-
name: row[
|
540
|
-
primary_key: row[
|
479
|
+
column: row["column"],
|
480
|
+
name: row["name"],
|
481
|
+
primary_key: row["primary_key"]
|
541
482
|
}
|
542
483
|
|
543
|
-
options[:on_update] = extract_foreign_key_action(row[
|
544
|
-
options[:on_delete] = extract_foreign_key_action(row[
|
484
|
+
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
485
|
+
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
545
486
|
|
546
|
-
ForeignKeyDefinition.new(table_name, row[
|
487
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
547
488
|
end
|
548
489
|
end
|
549
490
|
|
@@ -553,7 +494,7 @@ module ActiveRecord
|
|
553
494
|
create_table_info = create_table_info(table_name)
|
554
495
|
|
555
496
|
# strip create_definitions and partition_options
|
556
|
-
raw_table_options = create_table_info.sub(/\A.*\n\) /m,
|
497
|
+
raw_table_options = create_table_info.sub(/\A.*\n\) /m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
|
557
498
|
|
558
499
|
# strip AUTO_INCREMENT
|
559
500
|
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
|
@@ -561,7 +502,7 @@ module ActiveRecord
|
|
561
502
|
table_options[:options] = raw_table_options
|
562
503
|
|
563
504
|
# strip COMMENT
|
564
|
-
if raw_table_options.sub!(/ COMMENT='.+'/,
|
505
|
+
if raw_table_options.sub!(/ COMMENT='.+'/, "")
|
565
506
|
table_options[:comment] = table_comment(table_name)
|
566
507
|
end
|
567
508
|
|
@@ -569,31 +510,32 @@ module ActiveRecord
|
|
569
510
|
end
|
570
511
|
|
571
512
|
# Maps logical Rails types to MySQL-specific data types.
|
572
|
-
def type_to_sql(type, limit
|
573
|
-
sql =
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
when 'binary'
|
581
|
-
if (0..0xfff) === limit
|
582
|
-
"varbinary(#{limit})"
|
583
|
-
else
|
513
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **) # :nodoc:
|
514
|
+
sql = \
|
515
|
+
case type.to_s
|
516
|
+
when "integer"
|
517
|
+
integer_to_sql(limit)
|
518
|
+
when "text"
|
519
|
+
text_to_sql(limit)
|
520
|
+
when "blob"
|
584
521
|
binary_to_sql(limit)
|
522
|
+
when "binary"
|
523
|
+
if (0..0xfff) === limit
|
524
|
+
"varbinary(#{limit})"
|
525
|
+
else
|
526
|
+
binary_to_sql(limit)
|
527
|
+
end
|
528
|
+
else
|
529
|
+
super
|
585
530
|
end
|
586
|
-
else
|
587
|
-
super(type, limit, precision, scale)
|
588
|
-
end
|
589
531
|
|
590
|
-
sql <<
|
532
|
+
sql << " unsigned" if unsigned && type != :primary_key
|
591
533
|
sql
|
592
534
|
end
|
593
535
|
|
594
536
|
# SHOW VARIABLES LIKE 'name'
|
595
537
|
def show_variable(name)
|
596
|
-
|
538
|
+
query_value("SELECT @@#{name}", "SCHEMA")
|
597
539
|
rescue ActiveRecord::StatementInvalid
|
598
540
|
nil
|
599
541
|
end
|
@@ -601,21 +543,21 @@ module ActiveRecord
|
|
601
543
|
def primary_keys(table_name) # :nodoc:
|
602
544
|
raise ArgumentError unless table_name.present?
|
603
545
|
|
604
|
-
|
546
|
+
scope = quoted_scope(table_name)
|
605
547
|
|
606
|
-
|
548
|
+
query_values(<<-SQL.strip_heredoc, "SCHEMA")
|
607
549
|
SELECT column_name
|
608
550
|
FROM information_schema.key_column_usage
|
609
551
|
WHERE constraint_name = 'PRIMARY'
|
610
|
-
AND table_schema = #{schema}
|
611
|
-
AND table_name = #{name}
|
552
|
+
AND table_schema = #{scope[:schema]}
|
553
|
+
AND table_name = #{scope[:name]}
|
612
554
|
ORDER BY ordinal_position
|
613
555
|
SQL
|
614
556
|
end
|
615
557
|
|
616
|
-
def case_sensitive_comparison(table, attribute, column, value)
|
617
|
-
if
|
618
|
-
table[attribute].eq(Arel::Nodes::Bin.new(
|
558
|
+
def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
|
559
|
+
if column.collation && !column.case_sensitive?
|
560
|
+
table[attribute].eq(Arel::Nodes::Bin.new(value))
|
619
561
|
else
|
620
562
|
super
|
621
563
|
end
|
@@ -635,346 +577,384 @@ module ActiveRecord
|
|
635
577
|
# Convert Arel node to string
|
636
578
|
s = s.to_sql unless s.is_a?(String)
|
637
579
|
# Remove any ASC/DESC modifiers
|
638
|
-
s.gsub(/\s+(?:ASC|DESC)\b/i,
|
580
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
639
581
|
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
640
582
|
|
641
|
-
[super, *order_columns].join(
|
583
|
+
[super, *order_columns].join(", ")
|
642
584
|
end
|
643
585
|
|
644
586
|
def strict_mode?
|
645
587
|
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
646
588
|
end
|
647
589
|
|
648
|
-
def
|
649
|
-
|
590
|
+
def default_index_type?(index) # :nodoc:
|
591
|
+
index.using == :btree || super
|
650
592
|
end
|
651
593
|
|
652
|
-
|
653
|
-
|
654
|
-
def initialize_type_map(m) # :nodoc:
|
655
|
-
super
|
656
|
-
|
657
|
-
register_class_with_limit m, %r(char)i, MysqlString
|
658
|
-
|
659
|
-
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
660
|
-
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
661
|
-
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
662
|
-
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
663
|
-
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
664
|
-
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
665
|
-
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
666
|
-
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
667
|
-
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
668
|
-
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
669
|
-
m.register_type %r(^json)i, MysqlJson.new
|
594
|
+
private
|
670
595
|
|
671
|
-
|
672
|
-
|
673
|
-
register_integer_type m, %r(^mediumint)i, limit: 3
|
674
|
-
register_integer_type m, %r(^smallint)i, limit: 2
|
675
|
-
register_integer_type m, %r(^tinyint)i, limit: 1
|
596
|
+
def initialize_type_map(m)
|
597
|
+
super
|
676
598
|
|
677
|
-
|
678
|
-
|
679
|
-
|
599
|
+
register_class_with_limit m, %r(char)i, MysqlString
|
600
|
+
|
601
|
+
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
602
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
603
|
+
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
604
|
+
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
605
|
+
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
606
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
607
|
+
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
608
|
+
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
609
|
+
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
610
|
+
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
611
|
+
m.register_type %r(^json)i, MysqlJson.new
|
612
|
+
|
613
|
+
register_integer_type m, %r(^bigint)i, limit: 8
|
614
|
+
register_integer_type m, %r(^int)i, limit: 4
|
615
|
+
register_integer_type m, %r(^mediumint)i, limit: 3
|
616
|
+
register_integer_type m, %r(^smallint)i, limit: 2
|
617
|
+
register_integer_type m, %r(^tinyint)i, limit: 1
|
618
|
+
|
619
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
|
620
|
+
m.alias_type %r(year)i, "integer"
|
621
|
+
m.alias_type %r(bit)i, "binary"
|
622
|
+
|
623
|
+
m.register_type(%r(enum)i) do |sql_type|
|
624
|
+
limit = sql_type[/^enum\((.+)\)/i, 1]
|
625
|
+
.split(",").map { |enum| enum.strip.length - 2 }.max
|
626
|
+
MysqlString.new(limit: limit)
|
627
|
+
end
|
680
628
|
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
629
|
+
m.register_type(%r(^set)i) do |sql_type|
|
630
|
+
limit = sql_type[/^set\((.+)\)/i, 1]
|
631
|
+
.split(",").map { |set| set.strip.length - 1 }.sum - 1
|
632
|
+
MysqlString.new(limit: limit)
|
633
|
+
end
|
685
634
|
end
|
686
635
|
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
636
|
+
def register_integer_type(mapping, key, options)
|
637
|
+
mapping.register_type(key) do |sql_type|
|
638
|
+
if /\bunsigned\b/.match?(sql_type)
|
639
|
+
Type::UnsignedInteger.new(options)
|
640
|
+
else
|
641
|
+
Type::Integer.new(options)
|
642
|
+
end
|
643
|
+
end
|
691
644
|
end
|
692
|
-
end
|
693
645
|
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
Type::UnsignedInteger.new(options)
|
646
|
+
def extract_precision(sql_type)
|
647
|
+
if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
|
648
|
+
super || 0
|
698
649
|
else
|
699
|
-
|
650
|
+
super
|
700
651
|
end
|
701
652
|
end
|
702
|
-
end
|
703
653
|
|
704
|
-
|
705
|
-
|
706
|
-
super || 0
|
707
|
-
else
|
708
|
-
super
|
654
|
+
def fetch_type_metadata(sql_type, extra = "")
|
655
|
+
MySQL::TypeMetadata.new(super(sql_type), extra: extra)
|
709
656
|
end
|
710
|
-
end
|
711
657
|
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? }
|
722
|
-
when Integer
|
723
|
-
quoted_columns.each { |name, column| column << "(#{length})" }
|
658
|
+
def add_index_length(quoted_columns, **options)
|
659
|
+
if length = options[:length]
|
660
|
+
case length
|
661
|
+
when Hash
|
662
|
+
length = length.symbolize_keys
|
663
|
+
quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? }
|
664
|
+
when Integer
|
665
|
+
quoted_columns.each { |name, column| column << "(#{length})" }
|
666
|
+
end
|
724
667
|
end
|
668
|
+
|
669
|
+
quoted_columns
|
725
670
|
end
|
726
671
|
|
727
|
-
quoted_columns
|
728
|
-
|
672
|
+
def add_options_for_index_columns(quoted_columns, **options)
|
673
|
+
quoted_columns = add_index_length(quoted_columns, options)
|
674
|
+
super
|
675
|
+
end
|
729
676
|
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
677
|
+
# See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
|
678
|
+
ER_DUP_ENTRY = 1062
|
679
|
+
ER_NOT_NULL_VIOLATION = 1048
|
680
|
+
ER_DO_NOT_HAVE_DEFAULT = 1364
|
681
|
+
ER_NO_REFERENCED_ROW_2 = 1452
|
682
|
+
ER_DATA_TOO_LONG = 1406
|
683
|
+
ER_OUT_OF_RANGE = 1264
|
684
|
+
ER_LOCK_DEADLOCK = 1213
|
685
|
+
ER_CANNOT_ADD_FOREIGN = 1215
|
686
|
+
ER_CANNOT_CREATE_TABLE = 1005
|
687
|
+
|
688
|
+
def translate_exception(exception, message)
|
689
|
+
case error_number(exception)
|
690
|
+
when ER_DUP_ENTRY
|
691
|
+
RecordNotUnique.new(message)
|
692
|
+
when ER_NO_REFERENCED_ROW_2
|
693
|
+
InvalidForeignKey.new(message)
|
694
|
+
when ER_CANNOT_ADD_FOREIGN
|
695
|
+
mismatched_foreign_key(message)
|
696
|
+
when ER_CANNOT_CREATE_TABLE
|
697
|
+
if message.include?("errno: 150")
|
698
|
+
mismatched_foreign_key(message)
|
699
|
+
else
|
700
|
+
super
|
701
|
+
end
|
702
|
+
when ER_DATA_TOO_LONG
|
703
|
+
ValueTooLong.new(message)
|
704
|
+
when ER_OUT_OF_RANGE
|
705
|
+
RangeError.new(message)
|
706
|
+
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
707
|
+
NotNullViolation.new(message)
|
708
|
+
when ER_LOCK_DEADLOCK
|
709
|
+
Deadlocked.new(message)
|
710
|
+
else
|
711
|
+
super
|
712
|
+
end
|
713
|
+
end
|
734
714
|
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
when 1452
|
740
|
-
InvalidForeignKey.new(message)
|
741
|
-
when 1406
|
742
|
-
ValueTooLong.new(message)
|
743
|
-
else
|
744
|
-
super
|
715
|
+
def add_column_sql(table_name, column_name, type, options = {})
|
716
|
+
td = create_table_definition(table_name)
|
717
|
+
cd = td.new_column_definition(column_name, type, options)
|
718
|
+
schema_creation.accept(AddColumnDefinition.new(cd))
|
745
719
|
end
|
746
|
-
end
|
747
720
|
|
748
|
-
|
749
|
-
|
750
|
-
cd = td.new_column_definition(column_name, type, options)
|
751
|
-
schema_creation.accept(AddColumnDefinition.new(cd))
|
752
|
-
end
|
721
|
+
def change_column_sql(table_name, column_name, type, options = {})
|
722
|
+
column = column_for(table_name, column_name)
|
753
723
|
|
754
|
-
|
755
|
-
|
724
|
+
unless options.key?(:default)
|
725
|
+
options[:default] = column.default
|
726
|
+
end
|
756
727
|
|
757
|
-
|
758
|
-
|
759
|
-
|
728
|
+
unless options.key?(:null)
|
729
|
+
options[:null] = column.null
|
730
|
+
end
|
760
731
|
|
761
|
-
|
762
|
-
|
732
|
+
unless options.key?(:comment)
|
733
|
+
options[:comment] = column.comment
|
734
|
+
end
|
735
|
+
|
736
|
+
td = create_table_definition(table_name)
|
737
|
+
cd = td.new_column_definition(column.name, type, options)
|
738
|
+
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
763
739
|
end
|
764
740
|
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
741
|
+
def rename_column_sql(table_name, column_name, new_column_name)
|
742
|
+
column = column_for(table_name, column_name)
|
743
|
+
options = {
|
744
|
+
default: column.default,
|
745
|
+
null: column.null,
|
746
|
+
auto_increment: column.auto_increment?
|
747
|
+
}
|
769
748
|
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
auto_increment: column.auto_increment?
|
776
|
-
}
|
749
|
+
current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
750
|
+
td = create_table_definition(table_name)
|
751
|
+
cd = td.new_column_definition(new_column_name, current_type, options)
|
752
|
+
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
753
|
+
end
|
777
754
|
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
782
|
-
end
|
755
|
+
def remove_column_sql(table_name, column_name, type = nil, options = {})
|
756
|
+
"DROP #{quote_column_name(column_name)}"
|
757
|
+
end
|
783
758
|
|
784
|
-
|
785
|
-
|
786
|
-
|
759
|
+
def remove_columns_sql(table_name, *column_names)
|
760
|
+
column_names.map { |column_name| remove_column_sql(table_name, column_name) }
|
761
|
+
end
|
787
762
|
|
788
|
-
|
789
|
-
|
790
|
-
|
763
|
+
def add_index_sql(table_name, column_name, options = {})
|
764
|
+
index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
|
765
|
+
index_algorithm[0, 0] = ", " if index_algorithm.present?
|
766
|
+
"ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
|
767
|
+
end
|
791
768
|
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
end
|
769
|
+
def remove_index_sql(table_name, options = {})
|
770
|
+
index_name = index_name_for_remove(table_name, options)
|
771
|
+
"DROP INDEX #{index_name}"
|
772
|
+
end
|
797
773
|
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
end
|
774
|
+
def add_timestamps_sql(table_name, options = {})
|
775
|
+
[add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
|
776
|
+
end
|
802
777
|
|
803
|
-
|
804
|
-
|
805
|
-
|
778
|
+
def remove_timestamps_sql(table_name, options = {})
|
779
|
+
[remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
|
780
|
+
end
|
806
781
|
|
807
|
-
|
808
|
-
|
809
|
-
|
782
|
+
# MySQL is too stupid to create a temporary table for use subquery, so we have
|
783
|
+
# to give it some prompting in the form of a subsubquery. Ugh!
|
784
|
+
def subquery_for(key, select)
|
785
|
+
subsubselect = select.clone
|
786
|
+
subsubselect.projections = [key]
|
810
787
|
|
811
|
-
|
788
|
+
# Materialize subquery by adding distinct
|
789
|
+
# to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
|
790
|
+
subsubselect.distinct unless select.limit || select.offset || select.orders.any?
|
812
791
|
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
subsubselect.projections = [key]
|
792
|
+
subselect = Arel::SelectManager.new(select.engine)
|
793
|
+
subselect.project Arel.sql(key.name)
|
794
|
+
subselect.from subsubselect.as("__active_record_temp")
|
795
|
+
end
|
818
796
|
|
819
|
-
|
820
|
-
|
821
|
-
|
797
|
+
def supports_rename_index?
|
798
|
+
mariadb? ? false : version >= "5.7.6"
|
799
|
+
end
|
822
800
|
|
823
|
-
|
824
|
-
|
825
|
-
subselect.from subsubselect.as('__active_record_temp')
|
826
|
-
end
|
801
|
+
def configure_connection
|
802
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
827
803
|
|
828
|
-
|
829
|
-
|
830
|
-
end
|
804
|
+
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
805
|
+
variables["sql_auto_is_null"] = 0
|
831
806
|
|
832
|
-
|
833
|
-
|
807
|
+
# Increase timeout so the server doesn't disconnect us.
|
808
|
+
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
809
|
+
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
810
|
+
variables["wait_timeout"] = wait_timeout
|
834
811
|
|
835
|
-
|
836
|
-
variables['sql_auto_is_null'] = 0
|
812
|
+
defaults = [":default", :default].to_set
|
837
813
|
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
814
|
+
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
815
|
+
# http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
|
816
|
+
# If the user has provided another value for sql_mode, don't replace it.
|
817
|
+
if sql_mode = variables.delete("sql_mode")
|
818
|
+
sql_mode = quote(sql_mode)
|
819
|
+
elsif !defaults.include?(strict_mode?)
|
820
|
+
if strict_mode?
|
821
|
+
sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
|
822
|
+
else
|
823
|
+
sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
|
824
|
+
sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
|
825
|
+
sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
|
826
|
+
end
|
827
|
+
sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
|
828
|
+
end
|
829
|
+
sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
|
830
|
+
|
831
|
+
# NAMES does not have an equals sign, see
|
832
|
+
# http://dev.mysql.com/doc/refman/5.7/en/set-statement.html#id944430
|
833
|
+
# (trailing comma because variable_assignments will always have content)
|
834
|
+
if @config[:encoding]
|
835
|
+
encoding = "NAMES #{@config[:encoding]}"
|
836
|
+
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
837
|
+
encoding << ", "
|
838
|
+
end
|
842
839
|
|
843
|
-
|
840
|
+
# Gather up all of the SET variables...
|
841
|
+
variable_assignments = variables.map do |k, v|
|
842
|
+
if defaults.include?(v)
|
843
|
+
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
844
|
+
elsif !v.nil?
|
845
|
+
"@@SESSION.#{k} = #{quote(v)}"
|
846
|
+
end
|
847
|
+
# or else nil; compact to clear nils out
|
848
|
+
end.compact.join(", ")
|
844
849
|
|
845
|
-
|
846
|
-
|
847
|
-
# If the user has provided another value for sql_mode, don't replace it.
|
848
|
-
if sql_mode = variables.delete('sql_mode')
|
849
|
-
sql_mode = quote(sql_mode)
|
850
|
-
elsif !defaults.include?(strict_mode?)
|
851
|
-
if strict_mode?
|
852
|
-
sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
|
853
|
-
else
|
854
|
-
sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
|
855
|
-
sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
|
856
|
-
sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
|
857
|
-
end
|
858
|
-
sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
|
850
|
+
# ...and send them all in one query
|
851
|
+
execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
|
859
852
|
end
|
860
|
-
sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
|
861
853
|
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
encoding = "NAMES #{@config[:encoding]}"
|
867
|
-
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
868
|
-
encoding << ", "
|
854
|
+
def column_definitions(table_name) # :nodoc:
|
855
|
+
execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
|
856
|
+
each_hash(result)
|
857
|
+
end
|
869
858
|
end
|
870
859
|
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
elsif !v.nil?
|
876
|
-
"@@SESSION.#{k} = #{quote(v)}"
|
860
|
+
def extract_foreign_key_action(specifier) # :nodoc:
|
861
|
+
case specifier
|
862
|
+
when "CASCADE"; :cascade
|
863
|
+
when "SET NULL"; :nullify
|
877
864
|
end
|
878
|
-
|
879
|
-
end.compact.join(', ')
|
880
|
-
|
881
|
-
# ...and send them all in one query
|
882
|
-
@connection.query "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
|
883
|
-
end
|
865
|
+
end
|
884
866
|
|
885
|
-
|
886
|
-
|
887
|
-
each_hash(result)
|
867
|
+
def create_table_info(table_name) # :nodoc:
|
868
|
+
exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
888
869
|
end
|
889
|
-
end
|
890
870
|
|
891
|
-
|
892
|
-
|
893
|
-
when 'CASCADE'; :cascade
|
894
|
-
when 'SET NULL'; :nullify
|
871
|
+
def create_table_definition(*args) # :nodoc:
|
872
|
+
MySQL::TableDefinition.new(*args)
|
895
873
|
end
|
896
|
-
end
|
897
874
|
|
898
|
-
|
899
|
-
|
900
|
-
|
875
|
+
def mismatched_foreign_key(message)
|
876
|
+
match = %r/
|
877
|
+
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
878
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
|
879
|
+
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
880
|
+
/xmi.match(message)
|
901
881
|
|
902
|
-
|
903
|
-
|
904
|
-
|
882
|
+
options = {
|
883
|
+
message: message,
|
884
|
+
}
|
905
885
|
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
886
|
+
if match
|
887
|
+
options[:table] = match[:table]
|
888
|
+
options[:foreign_key] = match[:foreign_key]
|
889
|
+
options[:target_table] = match[:target_table]
|
890
|
+
options[:primary_key] = match[:primary_key]
|
891
|
+
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
892
|
+
end
|
911
893
|
|
912
|
-
|
913
|
-
case limit
|
914
|
-
when 1; 'tinyint'
|
915
|
-
when 2; 'smallint'
|
916
|
-
when 3; 'mediumint'
|
917
|
-
when nil, 4; 'int'
|
918
|
-
when 5..8; 'bigint'
|
919
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
894
|
+
MismatchedForeignKey.new(options)
|
920
895
|
end
|
921
|
-
end
|
922
896
|
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
897
|
+
def integer_to_sql(limit) # :nodoc:
|
898
|
+
case limit
|
899
|
+
when 1; "tinyint"
|
900
|
+
when 2; "smallint"
|
901
|
+
when 3; "mediumint"
|
902
|
+
when nil, 4; "int"
|
903
|
+
when 5..8; "bigint"
|
904
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead.")
|
905
|
+
end
|
930
906
|
end
|
931
|
-
end
|
932
907
|
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
908
|
+
def text_to_sql(limit) # :nodoc:
|
909
|
+
case limit
|
910
|
+
when 0..0xff; "tinytext"
|
911
|
+
when nil, 0x100..0xffff; "text"
|
912
|
+
when 0x10000..0xffffff; "mediumtext"
|
913
|
+
when 0x1000000..0xffffffff; "longtext"
|
914
|
+
else raise(ActiveRecordError, "No text type has byte length #{limit}")
|
915
|
+
end
|
940
916
|
end
|
941
|
-
end
|
942
|
-
|
943
|
-
def version_string
|
944
|
-
full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
945
|
-
end
|
946
917
|
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
918
|
+
def binary_to_sql(limit) # :nodoc:
|
919
|
+
case limit
|
920
|
+
when 0..0xff; "tinyblob"
|
921
|
+
when nil, 0x100..0xffff; "blob"
|
922
|
+
when 0x10000..0xffffff; "mediumblob"
|
923
|
+
when 0x1000000..0xffffffff; "longblob"
|
924
|
+
else raise(ActiveRecordError, "No binary type has byte length #{limit}")
|
925
|
+
end
|
952
926
|
end
|
953
|
-
end
|
954
927
|
|
955
|
-
|
956
|
-
|
957
|
-
case value
|
958
|
-
when true then MySQL::Quoting::QUOTED_TRUE
|
959
|
-
when false then MySQL::Quoting::QUOTED_FALSE
|
960
|
-
else super
|
961
|
-
end
|
928
|
+
def version_string
|
929
|
+
full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
962
930
|
end
|
963
931
|
|
964
|
-
|
932
|
+
class MysqlJson < Type::Internal::AbstractJson # :nodoc:
|
933
|
+
end
|
965
934
|
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
935
|
+
class MysqlString < Type::String # :nodoc:
|
936
|
+
def serialize(value)
|
937
|
+
case value
|
938
|
+
when true then MySQL::Quoting::QUOTED_TRUE
|
939
|
+
when false then MySQL::Quoting::QUOTED_FALSE
|
940
|
+
else super
|
941
|
+
end
|
971
942
|
end
|
943
|
+
|
944
|
+
private
|
945
|
+
|
946
|
+
def cast_value(value)
|
947
|
+
case value
|
948
|
+
when true then MySQL::Quoting::QUOTED_TRUE
|
949
|
+
when false then MySQL::Quoting::QUOTED_FALSE
|
950
|
+
else super
|
951
|
+
end
|
952
|
+
end
|
972
953
|
end
|
973
|
-
end
|
974
954
|
|
975
|
-
|
976
|
-
|
977
|
-
|
955
|
+
ActiveRecord::Type.register(:json, MysqlJson, adapter: :mysql2)
|
956
|
+
ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
|
957
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
978
958
|
end
|
979
959
|
end
|
980
960
|
end
|