# HG changeset patch # User Sylvain Thénault # Date 1450426053 -3600 # Node ID c1fdd22232d11f13f8f2d2eb24e7128465369b00 # Parent c9d12d1d3081bef8030174e848644adc728c0f18 [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. diff -r c9d12d1d3081 -r c1fdd22232d1 devtools/__init__.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): diff -r c9d12d1d3081 -r c1fdd22232d1 server/sqlutils.py --- 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):