mysql icon indicating copy to clipboard operation
mysql copied to clipboard

Set a nil date to NULL rather than 0000-00-00

Open Freeaqingme opened this issue 9 years ago • 31 comments

I've got a row defined: "date_disconnect" datetime DEFAULT NULL

I'm also running mysql in strictmode:

> select @@global.sql_mode\G
*************************** 1. row ***************************
@@global.sql_mode: REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

I set these modes because if I want to indicate the absence of a data, I like to use NULL, rather than set an "arbitrary" date 2015 years ago. In line with ANSI SQL.

The DSN used: root:@tcp(localhost:3306)/cluegetter?sql_notes=false&strict=true

Please let me know what you think of this PR. I'll get some tests if you agree on the chosen approach.

Freeaqingme avatar Jun 05 '15 00:06 Freeaqingme

@julienschmidt Do you have any considerations about this change?

Freeaqingme avatar Jun 21 '15 20:06 Freeaqingme

:+1:

AlekSi avatar Aug 23 '15 10:08 AlekSi

I'm not entirely convinced. There is not really a "nil date" since time.Time is a number. Therefore it's default value is 0, not nil. There is also a semantic difference between interface{}(nil) and interface{}(t) with t being an uninitialized time.Time value.

On the other hand, 0000-00-00 makes absolutely no sense in strict mode. Therefore this approach is feasible.

@arnehormann @xaprb Yea or Nay?

julienschmidt avatar Sep 08 '15 12:09 julienschmidt

There is not really a "nil date" since time.Time is a number.

*time.Time can be nil. Right now there is no way to store it in database.

AlekSi avatar Sep 08 '15 12:09 AlekSi

*time.Time can be nil. Right now there is no way to store it in database.

But *time.Time is not a valid database/sql Value. Is it converted to an uninitialized time.Time value?

julienschmidt avatar Sep 08 '15 12:09 julienschmidt

Nor *string, *bool, etc., but they work across most drivers. Probably due to different interpretations of

It is either nil or an instance of one of these types

Specifically, my internal code works with *time.Time, *string and *bool with github.com/mattn/go-sqlite3, github.com/lib/pq and mostly with this package – except *time.Time.

AlekSi avatar Sep 08 '15 12:09 AlekSi

database/sql converts all these values to the values the driver must be able to handle: http://golang.org/src/database/sql/convert.go#L35

We don't know which of the values the user originally passed. So we don't know when you passed *time.Time. Looking at http://golang.org/src/database/sql/driver/types.go#L234, I think it is converted to nil.

julienschmidt avatar Sep 08 '15 12:09 julienschmidt

I really dislike all the type handling magic. Especially when it comes in form of dsn parameters - and this could easily become yet another one. How about dropping that stuff alltogether? We could add a mapping function to sanitize inputs as a var - like the logger... per default doing what the driver does right now with variants for the current dsn-stuff.

arnehormann avatar Sep 08 '15 12:09 arnehormann

I really dislike all the type handling magic.

I especially hate the magic the database/sql package does

julienschmidt avatar Sep 08 '15 13:09 julienschmidt

I really dislike all the type handling magic.

I especially hate the magic the database/sql package does

Me too... I hate that it's needed at all.

arnehormann avatar Sep 08 '15 13:09 arnehormann

@julienschmidt This issue has been open for a while now, perhaps we can do something about it?

Freeaqingme avatar Mar 16 '16 19:03 Freeaqingme

Maybe fix the tests so it can be merged? I do think 0/nil → NULL makes a lot more sense than 0/nil → 0000-00-00. A mapping function (w/ default == current behavior) would be a good and flexible solution. This would add a lot of flexibility to it.

dveeden avatar Mar 16 '16 21:03 dveeden

I just spent 1-2 hours (longer than the time spent on the change itself) on trying to comprehend how the TestDateTime() stuff works. During that time I wasn't able to come up with a way that would fail without my change, and pass with my change. The TestDateTime tries to cover (imho) too many things at once, and it's taking me too long to come up with something sensible.

If you have any ideas how to test this sensibly I'd be happy to hear it. If not, I'll close this pull request.

Freeaqingme avatar Mar 26 '16 18:03 Freeaqingme

I am running into this exact issue with strict set on in mysql 5.7.10 using gorm with time.Time fields in structs. It seems very reasonable to me that a time.Time{} (uninitialized/zero value) for a NULLable database datetime column should be inserted as NULL (not '0000-00-00'). Is there any reason not to accept this PR? With strict=true the existing code simply doesn't work in 5.7 (zero dates are not allowed).

ghufftesla avatar Jul 28 '16 17:07 ghufftesla

0000-00-00 is not accepted by default from MySQL 5.7. So I'm +1 for NULL rather than 0000-00-00.

methane avatar Jan 10 '18 09:01 methane

This PR needs an update then

julienschmidt avatar Jan 10 '18 11:01 julienschmidt

+1 for using nil as well. Gorm with zero date 0000-00-00 causes issues with MySQL 5.7.

@Freeaqingme thoughts on updating?

tab1293 avatar Mar 07 '18 16:03 tab1293

@tab1293 Sorry, right now I've got different priorities. Though, please feel free to check out my branch and do whatever needs to be done.

Freeaqingme avatar Mar 08 '18 15:03 Freeaqingme

I tried but it's difficult than I thought. Currently, this driver support passing time.Time for TIME column. It means "0000-00-00 12:34:56" is valid for TIME column, but "0000-00-00 00:00:00" is not valid.

And strict mode is deprecated for now. How about closing this pull request for now and create new issue for discussion?

methane avatar Mar 09 '18 13:03 methane

This should be fixed soon, but the chances that we do something wrong that breaks things is high. Thus, I'd suggest to postpone any changes until directly after the v1.4.0 release. Then we have enough time before the next release to gather feedback.

julienschmidt avatar May 26 '18 09:05 julienschmidt

Related: #783

dolmen avatar Jun 15 '18 12:06 dolmen

Any update on this guys ?

wgpshashank avatar Jun 29 '18 07:06 wgpshashank

There are no valid solution. Use just nil, don't use zero value of time.Time.

methane avatar Jun 29 '18 08:06 methane

You can not assign null value to time.Time variable

wgpshashank avatar Jun 29 '18 14:06 wgpshashank

You can pass nil to DB.Exec() and DB.Query(). No need to assign nil to time.Time variable.

methane avatar Jun 29 '18 14:06 methane

I tried but it's difficult than I thought. Currently, this driver support passing time.Time for TIME column. It means "0000-00-00 12:34:56" is valid for TIME column, but "0000-00-00 00:00:00" is not valid.

Now I think we should drop support passing time.Time argument for TIME column. When scanning, we don't support scan TIME column into time.Time.

At some point, civil.Time may be supported.

methane avatar Sep 28 '18 11:09 methane

use gorm.Expr("NULL")

vacoo avatar May 24 '19 22:05 vacoo

What new about this problem?

kolkov avatar Aug 08 '19 20:08 kolkov

No plan to convert Zero Time to NULL. You can use NullTime instead.

I suggest closing this issue because the merit of this change is not large enough to break backward compatibility. "00:00:00" is a valid value for time column. And driver doesn't know the type of the column. So we can not use nil for date and datetime column but "00:00:00" for time column.

When we want to set NULL, we can use nil instead of zero Time.

methane avatar Sep 05 '19 03:09 methane

There is a chance to do this, but there are no timeline for it because of the upstream issue.

  1. Go adds more types for date and time: https://github.com/golang/go/issues/19700
  2. We support them, especially for TIME columns.
  3. Deprecate using Time value for TIME columns.
  4. Do the change.

It may take more than one year. Don't wait this issue is fixed. Use NullTime or nil instead.

methane avatar Sep 05 '19 03:09 methane