[server] always monkeypatch QuerierHelper to handle dates and times on SQLite
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 18 Dec 2015 09:07:33 +0100
changeset 11030 c1fdd22232d1
parent 11029 c9d12d1d3081
child 11031 780939fc06da
[server] always monkeypatch QuerierHelper to handle dates and times on SQLite The patch prevents problems due to a SQLite bug causing some values to be returned as strings instead of properly typed objects. This should probably be improved to use SQLite connection hooks. But the major improvement is that the monkeypatch is now done all the time, and not just for tests.
devtools/__init__.py
server/sqlutils.py
--- a/devtools/__init__.py	Thu Dec 17 15:17:45 2015 +0100
+++ b/devtools/__init__.py	Fri Dec 18 09:07:33 2015 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -807,11 +807,6 @@
         #    traceback.print_stack(file=backup_stack_file)
         return backup_file
 
-    def _new_repo(self, config):
-        repo = super(SQLiteTestDataBaseHandler, self)._new_repo(config)
-        install_sqlite_patch(repo.querier)
-        return repo
-
     def _restore_database(self, backup_coordinates, _config):
         # remove database file if it exists ?
         dbfile = self.absolute_dbfile()
@@ -831,46 +826,6 @@
 atexit.register(SQLiteTestDataBaseHandler._cleanup_all_tmpdb)
 
 
-def install_sqlite_patch(querier):
-    """This patch hotfixes the following sqlite bug :
-       - http://www.sqlite.org/cvstrac/tktview?tn=1327,33
-       (some dates are returned as strings rather thant date objects)
-    """
-    if hasattr(querier.__class__, '_devtools_sqlite_patched'):
-        return # already monkey patched
-    def wrap_execute(base_execute):
-        def new_execute(*args, **kwargs):
-            rset = base_execute(*args, **kwargs)
-            if rset.description:
-                found_date = False
-                for row, rowdesc in zip(rset, rset.description):
-                    for cellindex, (value, vtype) in enumerate(zip(row, rowdesc)):
-                        if vtype in ('Date', 'Datetime') and isinstance(value, text_type):
-                            found_date = True
-                            value = value.rsplit('.', 1)[0]
-                            try:
-                                row[cellindex] = strptime(value, '%Y-%m-%d %H:%M:%S')
-                            except Exception:
-                                row[cellindex] = strptime(value, '%Y-%m-%d')
-                        if vtype == 'Time' and isinstance(value, text_type):
-                            found_date = True
-                            try:
-                                row[cellindex] = strptime(value, '%H:%M:%S')
-                            except Exception:
-                                # DateTime used as Time?
-                                row[cellindex] = strptime(value, '%Y-%m-%d %H:%M:%S')
-                        if vtype == 'Interval' and isinstance(value, int):
-                            found_date = True
-                            row[cellindex] = timedelta(0, value, 0) # XXX value is in number of seconds?
-                    if not found_date:
-                        break
-            return rset
-        return new_execute
-    querier.__class__.execute = wrap_execute(querier.__class__.execute)
-    querier.__class__._devtools_sqlite_patched = True
-
-
-
 HANDLERS = {}
 
 def register_handler(handlerkls, overwrite=False):
--- a/server/sqlutils.py	Thu Dec 17 15:17:45 2015 +0100
+++ b/server/sqlutils.py	Fri Dec 18 09:07:33 2015 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -25,7 +25,7 @@
 import subprocess
 from os.path import abspath
 from logging import getLogger
-from datetime import time, datetime
+from datetime import time, datetime, timedelta
 
 from six import string_types, text_type
 from six.moves import filter
@@ -34,7 +34,7 @@
 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.common.date import utctime, utcdatetime, strptime
 from logilab.database.sqlgen import SQLGenerator
 
 from cubicweb import Binary, ConfigurationError
@@ -479,7 +479,51 @@
 
 # connection initialization functions ##########################################
 
+def _install_sqlite_querier_patch():
+    """This monkey-patch hotfixes a bug sqlite causing some dates to be returned as strings rather than
+    date objects (http://www.sqlite.org/cvstrac/tktview?tn=1327,33)
+    """
+    from cubicweb.server.querier import QuerierHelper
+
+    if hasattr(QuerierHelper, '_sqlite_patched'):
+        return  # already monkey patched
+
+    def wrap_execute(base_execute):
+        def new_execute(*args, **kwargs):
+            rset = base_execute(*args, **kwargs)
+            if rset.description:
+                found_date = False
+                for row, rowdesc in zip(rset, rset.description):
+                    for cellindex, (value, vtype) in enumerate(zip(row, rowdesc)):
+                        if vtype in ('Date', 'Datetime') and isinstance(value, text_type):
+                            found_date = True
+                            value = value.rsplit('.', 1)[0]
+                            try:
+                                row[cellindex] = strptime(value, '%Y-%m-%d %H:%M:%S')
+                            except Exception:
+                                row[cellindex] = strptime(value, '%Y-%m-%d')
+                        if vtype == 'Time' and isinstance(value, text_type):
+                            found_date = True
+                            try:
+                                row[cellindex] = strptime(value, '%H:%M:%S')
+                            except Exception:
+                                # DateTime used as Time?
+                                row[cellindex] = strptime(value, '%Y-%m-%d %H:%M:%S')
+                        if vtype == 'Interval' and isinstance(value, int):
+                            found_date = True
+                            # XXX value is in number of seconds?
+                            row[cellindex] = timedelta(0, value, 0)
+                    if not found_date:
+                        break
+            return rset
+        return new_execute
+
+    QuerierHelper.execute = wrap_execute(QuerierHelper.execute)
+    QuerierHelper._sqlite_patched = True
+
+
 def init_sqlite_connexion(cnx):
+    _install_sqlite_querier_patch()
 
     class group_concat(object):
         def __init__(self):