提交 594c7dc2 编写于 作者: R Rafael França 提交者: GitHub

Merge pull request #23553 from kamipo/correctly_dump_native_timestamp_types_for_mysql

Correctly dump native timestamp types for MySQL
* Correctly dump native timestamp types for MySQL.
The native timestamp type in MySQL is different from datetime type.
Internal representation of the timestamp type is UNIX time, This means
that timestamp columns are affected by time zone.
> SET time_zone = '+00:00';
Query OK, 0 rows affected (0.00 sec)
> INSERT INTO time_with_zone(ts,dt) VALUES (NOW(),NOW());
Query OK, 1 row affected (0.02 sec)
> SELECT * FROM time_with_zone;
+---------------------+---------------------+
| ts | dt |
+---------------------+---------------------+
| 2016-02-07 22:11:44 | 2016-02-07 22:11:44 |
+---------------------+---------------------+
1 row in set (0.00 sec)
> SET time_zone = '-08:00';
Query OK, 0 rows affected (0.00 sec)
> SELECT * FROM time_with_zone;
+---------------------+---------------------+
| ts | dt |
+---------------------+---------------------+
| 2016-02-07 14:11:44 | 2016-02-07 22:11:44 |
+---------------------+---------------------+
1 row in set (0.00 sec)
*Ryuta Kamizono*
* All integer-like PKs are autoincrement unless they have an explicit default. * All integer-like PKs are autoincrement unless they have an explicit default.
*Matthew Draper* *Matthew Draper*
......
...@@ -1071,7 +1071,7 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc: ...@@ -1071,7 +1071,7 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified" raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
end end
elsif [:datetime, :time, :interval].include?(type) && precision ||= native[:precision] elsif [:datetime, :timestamp, :time, :interval].include?(type) && precision ||= native[:precision]
if (0..6) === precision if (0..6) === precision
column_type_sql << "(#{precision})" column_type_sql << "(#{precision})"
else else
......
...@@ -46,6 +46,7 @@ def arel_visitor # :nodoc: ...@@ -46,6 +46,7 @@ def arel_visitor # :nodoc:
float: { name: "float" }, float: { name: "float" },
decimal: { name: "decimal" }, decimal: { name: "decimal" },
datetime: { name: "datetime" }, datetime: { name: "datetime" },
timestamp: { name: "timestamp" },
time: { name: "time" }, time: { name: "time" },
date: { name: "date" }, date: { name: "date" },
binary: { name: "blob", limit: 65535 }, binary: { name: "blob", limit: 65535 },
...@@ -708,7 +709,7 @@ def register_integer_type(mapping, key, options) ...@@ -708,7 +709,7 @@ def register_integer_type(mapping, key, options)
end end
def extract_precision(sql_type) def extract_precision(sql_type)
if /time/.match?(sql_type) if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
super || 0 super || 0
else else
super super
......
...@@ -25,6 +25,14 @@ def add_table_options!(create_sql, options) ...@@ -25,6 +25,14 @@ def add_table_options!(create_sql, options)
end end
def add_column_options!(sql, options) def add_column_options!(sql, options)
# By default, TIMESTAMP columns are NOT NULL, cannot contain NULL values,
# and assigning NULL assigns the current timestamp. To permit a TIMESTAMP
# column to contain NULL, explicitly declare it with the NULL attribute.
# See http://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html
if /\Atimestamp\b/.match?(options[:column].sql_type) && !options[:primary_key]
sql << " NULL" unless options[:null] == false || options_include_default?(options)
end
if charset = options[:charset] if charset = options[:charset]
sql << " CHARACTER SET #{charset}" sql << " CHARACTER SET #{charset}"
end end
......
...@@ -75,6 +75,11 @@ def new_column_definition(name, type, **options) # :nodoc: ...@@ -75,6 +75,11 @@ def new_column_definition(name, type, **options) # :nodoc:
super super
end end
private
def aliased_types(name, fallback)
fallback
end
end end
class Table < ActiveRecord::ConnectionAdapters::Table class Table < ActiveRecord::ConnectionAdapters::Table
......
...@@ -30,7 +30,10 @@ def explicit_primary_key_default?(column) ...@@ -30,7 +30,10 @@ def explicit_primary_key_default?(column)
end end
def schema_type(column) def schema_type(column)
if column.sql_type == "tinyblob" case column.sql_type
when /\Atimestamp\b/
:timestamp
when "tinyblob"
:blob :blob
else else
super super
...@@ -38,7 +41,7 @@ def schema_type(column) ...@@ -38,7 +41,7 @@ def schema_type(column)
end end
def schema_precision(column) def schema_precision(column)
super unless /time/.match?(column.sql_type) && column.precision == 0 super unless /\A(?:date)?time(?:stamp)?\b/.match?(column.sql_type) && column.precision == 0
end end
def schema_collation(column) def schema_collation(column)
......
...@@ -100,11 +100,21 @@ class MysqlDefaultExpressionTest < ActiveRecord::TestCase ...@@ -100,11 +100,21 @@ class MysqlDefaultExpressionTest < ActiveRecord::TestCase
include SchemaDumpingHelper include SchemaDumpingHelper
if ActiveRecord::Base.connection.version >= "5.6.0" if ActiveRecord::Base.connection.version >= "5.6.0"
test "schema dump includes default expression" do test "schema dump datetime includes default expression" do
output = dump_table_schema("datetime_defaults") output = dump_table_schema("datetime_defaults")
assert_match %r/t\.datetime\s+"modified_datetime",\s+default: -> { "CURRENT_TIMESTAMP" }/, output assert_match %r/t\.datetime\s+"modified_datetime",\s+default: -> { "CURRENT_TIMESTAMP" }/, output
end end
end end
test "schema dump timestamp includes default expression" do
output = dump_table_schema("timestamp_defaults")
assert_match %r/t\.timestamp\s+"modified_timestamp",\s+default: -> { "CURRENT_TIMESTAMP" }/, output
end
test "schema dump timestamp without default expression" do
output = dump_table_schema("timestamp_defaults")
assert_match %r/t\.timestamp\s+"nullable_timestamp"$/, output
end
end end
class DefaultsTestWithoutTransactionalFixtures < ActiveRecord::TestCase class DefaultsTestWithoutTransactionalFixtures < ActiveRecord::TestCase
......
...@@ -269,6 +269,8 @@ def test_add_column_with_timestamp_type ...@@ -269,6 +269,8 @@ def test_add_column_with_timestamp_type
if current_adapter?(:PostgreSQLAdapter) if current_adapter?(:PostgreSQLAdapter)
assert_equal "timestamp without time zone", klass.columns_hash["foo"].sql_type assert_equal "timestamp without time zone", klass.columns_hash["foo"].sql_type
elsif current_adapter?(:Mysql2Adapter)
assert_equal "timestamp", klass.columns_hash["foo"].sql_type
else else
assert_equal klass.connection.type_to_sql("datetime"), klass.columns_hash["foo"].sql_type assert_equal klass.connection.type_to_sql("datetime"), klass.columns_hash["foo"].sql_type
end end
......
...@@ -291,6 +291,14 @@ def test_any_type_primary_key ...@@ -291,6 +291,14 @@ def test_any_type_primary_key
schema = dump_table_schema "barcodes" schema = dump_table_schema "barcodes"
assert_match %r{create_table "barcodes", primary_key: "code", id: :string, limit: 42}, schema assert_match %r{create_table "barcodes", primary_key: "code", id: :string, limit: 42}, schema
end end
if current_adapter?(:Mysql2Adapter) && subsecond_precision_supported?
test "schema typed primary key column" do
@connection.create_table(:scheduled_logs, id: :timestamp, precision: 6, force: true)
schema = dump_table_schema("scheduled_logs")
assert_match %r/create_table "scheduled_logs", id: :timestamp, precision: 6/, schema
end
end
end end
class CompositePrimaryKeyTest < ActiveRecord::TestCase class CompositePrimaryKeyTest < ActiveRecord::TestCase
......
...@@ -6,6 +6,11 @@ ...@@ -6,6 +6,11 @@
end end
end end
create_table :timestamp_defaults, force: true do |t|
t.timestamp :nullable_timestamp
t.timestamp :modified_timestamp, default: -> { "CURRENT_TIMESTAMP" }
end
create_table :binary_fields, force: true do |t| create_table :binary_fields, force: true do |t|
t.binary :var_binary, limit: 255 t.binary :var_binary, limit: 255
t.binary :var_binary_large, limit: 4095 t.binary :var_binary_large, limit: 4095
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册