fix bad-caching of datetime with tz info at sql generation time
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 22 Sep 2015 14:20:02 +0200
changeset 10643 cfded6d0da11
parent 10628 8f32cdc3f4ec
child 10644 c43e5dc41f8b
fix bad-caching of datetime with tz info at sql generation time There is a special handling for datetime with tzinfo, where value was stored in the query cache. The implementation of merge_args was simply overwriting parameters of the query with those in the query cache, expecting no collision. To fix this: * handle replacement of tzinfo in merge_args, not at sql generation time * add an assertion to ensure we've actually no collision Closes #6978316
server/sources/rql2sql.py
server/sqlutils.py
server/test/unittest_querier.py
--- a/server/sources/rql2sql.py	Tue Sep 29 14:41:28 2015 +0200
+++ b/server/sources/rql2sql.py	Tue Sep 22 14:20:02 2015 +0200
@@ -50,9 +50,7 @@
 __docformat__ = "restructuredtext en"
 
 import threading
-from datetime import datetime, time
 
-from logilab.common.date import utcdatetime, utctime
 from logilab.database import FunctionDescr, SQL_FUNCTIONS_REGISTRY
 
 from rql import BadRQLQuery, CoercionError
@@ -1516,14 +1514,6 @@
                 _id = value
                 if isinstance(_id, unicode):
                     _id = _id.encode()
-                # convert timestamp to utc.
-                # expect SET TiME ZONE to UTC at connection opening time.
-                # This shouldn't change anything for datetime without TZ.
-                value = self._args[_id]
-                if isinstance(value, datetime) and value.tzinfo is not None:
-                    self._query_attrs[_id] = utcdatetime(value)
-                elif isinstance(value, time) and value.tzinfo is not None:
-                    self._query_attrs[_id] = utctime(value)
         else:
             _id = str(id(constant)).replace('-', '', 1)
             self._query_attrs[_id] = value
--- a/server/sqlutils.py	Tue Sep 29 14:41:28 2015 +0200
+++ b/server/sqlutils.py	Tue Sep 22 14:20:02 2015 +0200
@@ -20,17 +20,18 @@
 __docformat__ = "restructuredtext en"
 
 import sys
-import os
 import re
 import subprocess
 from os.path import abspath
 from itertools import ifilter
 from logging import getLogger
+from datetime import time, datetime
 
 from logilab import database as db, common as lgc
 from logilab.common.shellutils import ProgressBar, DummyProgressBar
 from logilab.common.deprecation import deprecated
 from logilab.common.logging_ext import set_log_methods
+from logilab.common.date import utctime, utcdatetime
 from logilab.database.sqlgen import SQLGenerator
 
 from cubicweb import Binary, ConfigurationError
@@ -373,8 +374,17 @@
                 # convert cubicweb binary into db binary
                 if isinstance(val, Binary):
                     val = self._binary(val.getvalue())
+                # convert timestamp to utc.
+                # expect SET TiME ZONE to UTC at connection opening time.
+                # This shouldn't change anything for datetime without TZ.
+                elif isinstance(val, datetime) and val.tzinfo is not None:
+                    val = utcdatetime(val)
+                elif isinstance(val, time) and val.tzinfo is not None:
+                    val = utctime(val)
                 newargs[key] = val
             # should not collide
+            assert not (frozenset(newargs) & frozenset(query_args)), \
+                'unexpected collision: %s' % (frozenset(newargs) & frozenset(query_args))
             newargs.update(query_args)
             return newargs
         return query_args
--- a/server/test/unittest_querier.py	Tue Sep 29 14:41:28 2015 +0200
+++ b/server/test/unittest_querier.py	Tue Sep 22 14:20:02 2015 +0200
@@ -1397,6 +1397,13 @@
         self.assertEqual(datenaiss.tzinfo, None)
         self.assertEqual(datenaiss.utctimetuple()[:5], (1977, 6, 7, 1, 0))
 
+    def test_tz_datetime_cache_nonregr(self):
+        datenaiss = datetime(1977, 6, 7, 2, 0, tzinfo=FixedOffset(1))
+        self.qexecute("INSERT Personne X: X nom 'bob', X tzdatenaiss %(date)s",
+                     {'date': datenaiss})
+        self.assertTrue(self.qexecute("Any X WHERE X tzdatenaiss %(d)s", {'d': datenaiss}))
+        self.assertFalse(self.qexecute("Any X WHERE X tzdatenaiss %(d)s", {'d': datenaiss - timedelta(1)}))
+
     # non regression tests #####################################################
 
     def test_nonregr_1(self):