aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorRyuta Kamizono <kamipo@gmail.com>2015-10-11 21:45:55 +0900
committerRyuta Kamizono <kamipo@gmail.com>2017-02-23 12:51:49 +0900
commit505526335b1be76c74068d5d0735d175dd9e4193 (patch)
tree553b1444ffb29d18bfb18eb0712544076c988fe0 /activerecord
parent45468b87f1adeca8d6f7d11b4f09b4ecaf624ccf (diff)
downloadrails-505526335b1be76c74068d5d0735d175dd9e4193.tar.gz
rails-505526335b1be76c74068d5d0735d175dd9e4193.tar.bz2
rails-505526335b1be76c74068d5d0735d175dd9e4193.zip
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) ```
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md33
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb7
-rw-r--r--activerecord/test/cases/defaults_test.rb12
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb2
-rw-r--r--activerecord/test/cases/primary_keys_test.rb8
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb5
10 files changed, 80 insertions, 5 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index b27c03d935..25bc4e4e1f 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,36 @@
+* 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.
*Matthew Draper*
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 3686ad8b54..c43a2d1508 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -1071,7 +1071,7 @@ module ActiveRecord
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
end
- elsif [:datetime, :time, :interval].include?(type) && precision ||= native[:precision]
+ elsif [:datetime, :timestamp, :time, :interval].include?(type) && precision ||= native[:precision]
if (0..6) === precision
column_type_sql << "(#{precision})"
else
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 14269b4570..12dce89306 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -46,6 +46,7 @@ module ActiveRecord
float: { name: "float" },
decimal: { name: "decimal" },
datetime: { name: "datetime" },
+ timestamp: { name: "timestamp" },
time: { name: "time" },
date: { name: "date" },
binary: { name: "blob", limit: 65535 },
@@ -708,7 +709,7 @@ module ActiveRecord
end
def extract_precision(sql_type)
- if /time/.match?(sql_type)
+ if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
super || 0
else
super
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
index e8358271ab..083cd6340f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb
@@ -25,6 +25,14 @@ module ActiveRecord
end
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]
sql << " CHARACTER SET #{charset}"
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
index 773bbcef4e..6d88c14d50 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
@@ -75,6 +75,11 @@ module ActiveRecord
super
end
+
+ private
+ def aliased_types(name, fallback)
+ fallback
+ end
end
class Table < ActiveRecord::ConnectionAdapters::Table
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
index ad4a069d73..3e0afd9761 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
@@ -30,7 +30,10 @@ module ActiveRecord
end
def schema_type(column)
- if column.sql_type == "tinyblob"
+ case column.sql_type
+ when /\Atimestamp\b/
+ :timestamp
+ when "tinyblob"
:blob
else
super
@@ -38,7 +41,7 @@ module ActiveRecord
end
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
def schema_collation(column)
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index 6532efcf22..a6297673c9 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -100,11 +100,21 @@ if current_adapter?(:Mysql2Adapter)
include SchemaDumpingHelper
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")
assert_match %r/t\.datetime\s+"modified_datetime",\s+default: -> { "CURRENT_TIMESTAMP" }/, output
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
class DefaultsTestWithoutTransactionalFixtures < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index 48cfe89882..1d305fa11f 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -269,6 +269,8 @@ module ActiveRecord
if current_adapter?(:PostgreSQLAdapter)
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
assert_equal klass.connection.type_to_sql("datetime"), klass.columns_hash["foo"].sql_type
end
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 03c8644229..12386635f6 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -291,6 +291,14 @@ class PrimaryKeyAnyTypeTest < ActiveRecord::TestCase
schema = dump_table_schema "barcodes"
assert_match %r{create_table "barcodes", primary_key: "code", id: :string, limit: 42}, schema
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
class CompositePrimaryKeyTest < ActiveRecord::TestCase
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index 9a203a7293..90a314c83c 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -6,6 +6,11 @@ ActiveRecord::Schema.define do
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|
t.binary :var_binary, limit: 255
t.binary :var_binary_large, limit: 4095