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