Skip to content
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

activerecord: Avoid mysql non-null text column defaulting to empty string #7780

Conversation

dylanahsmith
Copy link
Contributor

The mysql database adaptor assumes that non-null text columns behave as if the default value is an empty string. This is the default behaviour for mysql connections, but putting the connection in strict mode with SET SQL_MODE='STRICT_ALL_TABLES' makes like other databases, where the default value is NULL so the value must be specified.

The mysql adaptor defaults to using this strict mode, although it can be disabled with setting strict: false in the database configuration. However, the mysql adapter has a hack based on non-strict mode behaviour to detect an empty string default value. So even though strict mode is used, this hack is continuing non-strict mode behaviour within strict mode.

I propose just removing this hack to mimic the non-strict behaviour to make its behaviour consistent with other databases. This is what this pull request does.

…ring.

With the :strict config, which defaults to true, the non-null text
columns behave as expected. So the mysql adapter hack to make it behave as
if the default is an empty string is continuing non-strict mode behaviour
within strict mode.
@rafaelfranca
Copy link
Member

I agree with this patch.

@jonleighton this is related with your last change could you review?

@jonleighton
Copy link
Member

I will take a proper look after the weekend. Thanks!

@jeremy
Copy link
Member

jeremy commented Sep 28, 2012

This causes a regression if you don't have strict set, though.

@@ -14,7 +14,7 @@ def initialize(name, default, sql_type = nil, null = true, collation = nil)
def extract_default(default)
if sql_type =~ /blob/i || type == :text
if default.blank?
return null ? nil : ''
return nil

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return keyword isn't necessary here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed it

@dylanahsmith
Copy link
Contributor Author

This causes a regression if you don't have strict set, though.

Yes. If they explicitly set strict: false in their database.yml, then it will be a regression. However, by setting this config they likely expect quirky behaviour like this. Do we really want to support this hack to work around this quirk when that is the point strict mode? If we want to remove it, then the best time to do it is before a major release (e.g. 4.0).

Here is a commit that would fix the regression if we decide to continue supporting this hack for non-strict mode dylanahsmith@fd1c0af

@jonleighton
Copy link
Member

I am 👍 on this. I don't think the regression for non-strict is severe enough to justify the pain of having this workaround in our code base given that strict is the default. I agree that the 4.0 release is a good time to introduce this. However, please add a clear explanation to the CHANGELOG so that people have something to refer to when upgrading.

# MySQL 5 and higher is quirky with not null text/blob columns.
# With MySQL Text/blob columns cannot have defaults. If the column is not
# null MySQL will report that the column has a null default
# but it behaves as though the column had a default of ''
def test_mysql_text_not_null_defaults
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just completely rm this test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@dylanahsmith
Copy link
Contributor Author

However, please add a clear explanation to the CHANGELOG so that people have something to refer to when upgrading.

I looked into this and noticed that strict mode wasn't used in rails 3.2, and that :strict configuration value wasn't available. It was added afterwards. So no changelog is needed for this change.

@ghost ghost assigned jonleighton Oct 15, 2012
@jonleighton
Copy link
Member

I discussed with the @jeremy the other day and he was concerned that we would be pushing busy work onto users by merging this, because if a user has strict: false then the default is reported by mysql as null but it is in fact an empty string. (By which I mean insert into foo values () will place an empty string in the field, but show full fields from foo will report null.)

However, I think that in strict: true mode, if a user has chosen to make the field a not null, they don't actually want it to be magically populated with an empty string, as this then changes the expected behaviour of strict mode.

So my suggestion is that:

  • We keep the hack which makes Column#default report '', but only for non-strict mode
  • We get rid of the keys.concat thing in dirty.rb. If the user is in strict mode, they should get an error if they haven't set the field's value. If they're in non-strict mode, the database will set the field's value to '' automatically.

I think they allows users an opt-out from the busy work (by setting strict: false) but keeps the semantics of strict mode consistent and allows us to avoid a horrible hack in dirty.rb.

I will try to catch up with @jeremy about this again today.

@jonleighton
Copy link
Member

@dylanahsmith I talked to @jeremy and he was ok with my proposal so I've implemented that, see above commit. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants