New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix erroneous nil default precision on virtual datetime columns #46110
Fix erroneous nil default precision on virtual datetime columns #46110
Conversation
@@ -286,7 +286,7 @@ def test_add_column_with_timestamp_type | |||
elsif current_adapter?(:OracleAdapter) | |||
assert_equal "TIMESTAMP(6)", column.sql_type | |||
else | |||
assert_equal connection.type_to_sql("datetime"), column.sql_type | |||
assert_equal connection.type_to_sql("datetime(6)"), column.sql_type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about this change, but it gets CI passing again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is concerning. Do you know you needed this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. I made the change to see if it would fail any other tests, but it didn't. I'm not familiar with these tests, and haven't had a chance to deep dive and figure out why it was required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the SQL generated on SQLite3 for the t.column :foo, :timestamp
now includes the precision of 6, when before it didn't. We need to investigate why this happened before merging it. I think this is the right behavior, but it is a behavior change, so we need to make sure the previous behavior was indeed a bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. This is indeed the right behavior.
Now you moved the code to add new default precision to after aliased_types
is called. So :timestamp
becomes :datetime
and the new default is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For posterity, here is a link to the failing build.
I've figured out why it was failing, and it's a similar reason to why this bug existed:
The incorrect virtual datetime precision was because the default assignment happened before we finalized the type from virtual
to datetime
. Similarly, alias resolution happened after default precision assignment, so timestamp
type columns implemented as datetime
were using nil
precision as their default instead of 6
.
So
- if that behavior is not expected, then this PR introduces a bug, but
- if that behavior is expected, then this PR fixes both bugs.
Based on the description of #42297, I believe this is intended:
Set precision 6 by default for datetime columns, this is the standard behaviour for timestamps #34970 it would good to extend this to all datetimes by default.
In fact, I think we have an opportunity to refactor and remove duplicate code in the t.timestamps
path, since we apply the default precision to all datetime
columns.
Edit: I must not have refreshed and missed your replies. Glad we both independently confirmed the expected behavior. 👍
Active Record erroneously uses a precision of `nil` instead of the default precision, when adding a virtual datetime column. This tests that behavior ahead of fixing it.
Prior to this change t.virtual :column_name, type: :datetime would erroneously produce the same result as t.virtual :column_name, type: :datetime, precision: nil This is because the code path for `virtual` skipped the default lookup: - `t.datetime` is delegated to the `column` method - `column` sees `type == :datetime` and sets a default `precision` is none was given - `column` calls `new_column_definition` to add the result to `@columns_hash` - `t.virtual` is delegated to the `column` method - `column` sees `type == :virtual`, not `:datetime`, so skips the default lookup - `column` calls `new_column_definition` to add the result to `@columns_hash` - `new_column_definition` sees `type == :virtual`, so sets `type = options[:type]` By moving the default lookup, we get consistent code paths: - `t.datetime` is delegated to the `column` method - `column` calls `new_column_definition` to add the result to `@columns_hash` - `new_column_definition` sees `type == :datetime` and sets a default `precision` is none was given - `t.virtual` is delegated to the `column` method - `column` calls `new_column_definition` to add the result to `@columns_hash` - `new_column_definition` sees `type == :virtual`, so sets `type = options[:type]` - `new_column_definition` sees `type == :datetime` and sets a default `precision` is none was given
e889868
to
b85ee91
Compare
…cision Fix erroneous nil default precision on virtual datetime columns
Since Rails 7.0.5, `Timestamp` columns use precision 6 by default. We need to specify the same precision to work `CURRENT_TIMESTAMP` correctly. Ref: rails/rails#46110
Motivation / Background
Prior to this change, virtual datetime columns did not have the same default precision as regular datetime columns, resulting in the following being erroneously equivalent:
This change fixes the default precision lookup, so virtual and regular datetime column default precisions match.
Detail
The existing code paths look roughly like:
t.datetime
is delegated to thecolumn
methodcolumn
seestype == :datetime
and sets a defaultprecision
is none was givencolumn
callsnew_column_definition
to add the result to@columns_hash
t.virtual
is delegated to thecolumn
methodcolumn
seestype == :virtual
, not:datetime
, so skips the default lookupcolumn
callsnew_column_definition
to add the result to@columns_hash
new_column_definition
seestype == :virtual
, so setstype = options[:type]
This PR moves the location of the default lookup, resulting in the following code paths:
t.datetime
is delegated to thecolumn
methodcolumn
callsnew_column_definition
to add the result to@columns_hash
new_column_definition
seestype == :datetime
and sets a defaultprecision
is none was givent.virtual
is delegated to thecolumn
methodcolumn
callsnew_column_definition
to add the result to@columns_hash
new_column_definition
seestype == :virtual
, so setstype = options[:type]
new_column_definition
seestype == :datetime
and sets a defaultprecision
is none was givenAdditional information
In our application, we use LHM to make schema changes to existing tables. In LHM, migrations describe columns in SQL:
The above LHM results in the following schema entry, as expected:
However, if a developer runs a command that loads the schema from
db/schema.rb
(e.g.rails db:schema:load
orrails db:reset
), the column is silently created with the incorrect precision. If they later run a command that dumps the schema (e.g.rails db:schema:dump
, orrails db:migrate
), the file is updated to reflect the corrupted precision:We only noticed this bug because migrations started causing unrelated changes to the schema file. It is possible that others have run into this bug with regular migrations, but may not have noticed the precision mismatch.
Checklist
Before submitting the PR make sure the following are checked:
[Fix #issue-number]
main
(if not - rebase it).