Permalink
Browse files

MySQL 5.6 and later supports microsecond precision in datetime.

You might want to branch it to include this only for 5.6, but
passing these values to < 5.6 doesn't cause issues either.
  • Loading branch information...
miyagawa authored and arthurnn committed Nov 12, 2012
1 parent 7e21be3 commit df5a38fc6aeb8dfaa816fcbe0efb3fe4de169833
Showing with 8 additions and 0 deletions.
  1. +8 −0 activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -83,6 +83,14 @@ def quote_string(string)
@connection.escape(string)
end
def quoted_date(value)
if value.acts_like?(:time) && value.respond_to?(:usec)
"#{super}.#{sprintf("%06d", value.usec)}"
else
super
end
end
# CONNECTION MANAGEMENT ====================================
def active?

1 comment on commit df5a38f

@dre-hh

This comment has been minimized.

Show comment
Hide comment
@dre-hh

dre-hh Mar 5, 2015

passing these values to < 5.6 doesn't cause issues either.

Unfortunately it does. Let's consider following usecase. you insert some records, which recieve a created at.

 INSERT INTO `recent_contact_requests` (`requester_id`, `requestee_id`, `created_at`, `updated_at`) VALUES (2, 3, '2014-11-23 13:41:38.394797', '2014-11-22 13:41:38.394797')
INSERT INTO `recent_contact_requests` (`requester_id`, `requestee_id`, `created_at`, `updated_at`) VALUES (1, 3, '2014-11-24 13:41:38.394797', '2014-11-23 13:41:38.394797')
INSERT INTO `recent_contact_requests` (`requester_id`, `requestee_id`, `created_at`, `updated_at`) VALUES (1, 2, '2014-11-22 13:41:38.394797', '2014-11-24 13:41:38.394797')
RecentContactRequest.where('created_at < ?', 100.days.ago)

which will generate the query

SELECT `recent_contact_requests`.* FROM `recent_contact_requests` WHERE (created_at < '2014-11-24 13:41:38.394797')

So you would expect it to return the first 2 records but not the last one, because the it's created is exactly2014-11-24 13:41:38.39479 but not less than it.

However this is not what will happen on mysql below 5.6. Below 5.6 all the records will be stored without the microsecond timestamp. So the last record will be stored with created_at: "2014-11-22 13:41:38".

And for the comparison of dates mysql, will just do a lexical comparison. So 2014-11-22 13:41:38 is actually less than 2014-11-24 13:41:38.394797 and the query will return the last record also.

You might want to branch it to include this only for 5.6

I.d.k why, but we experience this behavior on 5.6 either. The timestamps have been truncated on insert.

This implementation of microseconds also ignores the fact, that a a precision was set manually.

You can define the precision of timestamps for db queries by setting Time::DATE_FORMATS[:db]. If you then pass a datetime like object to an AR query it will automatically respect this setting.

So

# set date format to have 3 places for microseconds
Time::DATE_FORMATS[:db] = '%Y-%m-%d %H:%M:%S.%3N'
#fire up a query
RecentContactRequest.where('created_at < ?', 100.days.ago)

will result in

SELECT `recent_contact_requests`.* FROM `recent_contact_requests` WHERE (created_at < '2014-11-24 13:41:38.394.394797')

See issue #19223

dre-hh commented on df5a38f Mar 5, 2015

passing these values to < 5.6 doesn't cause issues either.

Unfortunately it does. Let's consider following usecase. you insert some records, which recieve a created at.

 INSERT INTO `recent_contact_requests` (`requester_id`, `requestee_id`, `created_at`, `updated_at`) VALUES (2, 3, '2014-11-23 13:41:38.394797', '2014-11-22 13:41:38.394797')
INSERT INTO `recent_contact_requests` (`requester_id`, `requestee_id`, `created_at`, `updated_at`) VALUES (1, 3, '2014-11-24 13:41:38.394797', '2014-11-23 13:41:38.394797')
INSERT INTO `recent_contact_requests` (`requester_id`, `requestee_id`, `created_at`, `updated_at`) VALUES (1, 2, '2014-11-22 13:41:38.394797', '2014-11-24 13:41:38.394797')
RecentContactRequest.where('created_at < ?', 100.days.ago)

which will generate the query

SELECT `recent_contact_requests`.* FROM `recent_contact_requests` WHERE (created_at < '2014-11-24 13:41:38.394797')

So you would expect it to return the first 2 records but not the last one, because the it's created is exactly2014-11-24 13:41:38.39479 but not less than it.

However this is not what will happen on mysql below 5.6. Below 5.6 all the records will be stored without the microsecond timestamp. So the last record will be stored with created_at: "2014-11-22 13:41:38".

And for the comparison of dates mysql, will just do a lexical comparison. So 2014-11-22 13:41:38 is actually less than 2014-11-24 13:41:38.394797 and the query will return the last record also.

You might want to branch it to include this only for 5.6

I.d.k why, but we experience this behavior on 5.6 either. The timestamps have been truncated on insert.

This implementation of microseconds also ignores the fact, that a a precision was set manually.

You can define the precision of timestamps for db queries by setting Time::DATE_FORMATS[:db]. If you then pass a datetime like object to an AR query it will automatically respect this setting.

So

# set date format to have 3 places for microseconds
Time::DATE_FORMATS[:db] = '%Y-%m-%d %H:%M:%S.%3N'
#fire up a query
RecentContactRequest.where('created_at < ?', 100.days.ago)

will result in

SELECT `recent_contact_requests`.* FROM `recent_contact_requests` WHERE (created_at < '2014-11-24 13:41:38.394.394797')

See issue #19223

Please sign in to comment.