server/sources/native.py
changeset 9583 e337a9f658e0
parent 9574 2d4c4842bd04
child 9585 3f5b59527d31
equal deleted inserted replaced
9582:46ed25d38fe2 9583:e337a9f658e0
     1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     1 # copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     3 #
     3 #
     4 # This file is part of CubicWeb.
     4 # This file is part of CubicWeb.
     5 #
     5 #
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
   189      OR EXISTS(SELECT 1 FROM tx_relation_actions as TRA
   189      OR EXISTS(SELECT 1 FROM tx_relation_actions as TRA
   190                WHERE TRA.tx_uuid=T.tx_uuid AND (
   190                WHERE TRA.tx_uuid=T.tx_uuid AND (
   191                    TRA.eid_from=%(eid)s OR TRA.eid_to=%(eid)s))
   191                    TRA.eid_from=%(eid)s OR TRA.eid_to=%(eid)s))
   192      )''' % {'txuuid': session.transaction_data['undoing_uuid'],
   192      )''' % {'txuuid': session.transaction_data['undoing_uuid'],
   193              'eid': eid}).fetchone()
   193              'eid': eid}).fetchone()
       
   194 
       
   195 
       
   196 class DefaultEidGenerator(object):
       
   197     __slots__ = ('source', 'cnx', 'lock')
       
   198 
       
   199     def __init__(self, source):
       
   200         self.source = source
       
   201         self.cnx = None
       
   202         self.lock = Lock()
       
   203 
       
   204     def close(self):
       
   205         if self.cnx:
       
   206             self.cnx.close()
       
   207         self.cnx = None
       
   208 
       
   209     def create_eid(self, _session):
       
   210         # lock needed to prevent 'Connection is busy with results for another
       
   211         # command (0)' errors with SQLServer
       
   212         with self.lock:
       
   213             return self._create_eid() # pylint: disable=E1102
       
   214 
       
   215     def _create_eid(self): # pylint: disable=E0202
       
   216         # internal function doing the eid creation without locking.
       
   217         # needed for the recursive handling of disconnections (otherwise we
       
   218         # deadlock on self._eid_cnx_lock
       
   219         source = self.source
       
   220         if self.cnx is None:
       
   221             self.cnx = source.get_connection()
       
   222         cnx = self.cnx
       
   223         try:
       
   224             cursor = cnx.cursor()
       
   225             for sql in source.dbhelper.sqls_increment_sequence('entities_id_seq'):
       
   226                 cursor.execute(sql)
       
   227             eid = cursor.fetchone()[0]
       
   228         except (source.OperationalError, source.InterfaceError):
       
   229             # FIXME: better detection of deconnection pb
       
   230             source.warning("trying to reconnect create eid connection")
       
   231             self.cnx = None
       
   232             return self._create_eid() # pylint: disable=E1102
       
   233         except source.DbapiError as exc:
       
   234             # We get this one with pyodbc and SQL Server when connection was reset
       
   235             if exc.args[0] == '08S01':
       
   236                 source.warning("trying to reconnect create eid connection")
       
   237                 self.cnx = None
       
   238                 return self._create_eid() # pylint: disable=E1102
       
   239             else:
       
   240                 raise
       
   241         except Exception: # WTF?
       
   242             cnx.rollback()
       
   243             self.cnx = None
       
   244             source.exception('create eid failed in an unforeseen way on SQL statement %s', sql)
       
   245             raise
       
   246         else:
       
   247             cnx.commit()
       
   248             return eid
       
   249 
       
   250 
       
   251 class SQLITEEidGenerator(object):
       
   252     __slots__ = ('source', 'lock')
       
   253 
       
   254     def __init__(self, source):
       
   255         self.source = source
       
   256         self.lock = Lock()
       
   257 
       
   258     def close(self):
       
   259         pass
       
   260 
       
   261     def create_eid(self, session):
       
   262         source = self.source
       
   263         with self.lock:
       
   264             for sql in source.dbhelper.sqls_increment_sequence('entities_id_seq'):
       
   265                 cursor = source.doexec(session, sql)
       
   266             return cursor.fetchone()[0]
   194 
   267 
   195 
   268 
   196 class NativeSQLSource(SQLAdapterMixIn, AbstractSource):
   269 class NativeSQLSource(SQLAdapterMixIn, AbstractSource):
   197     """adapter for source using the native cubicweb schema (see below)
   270     """adapter for source using the native cubicweb schema (see below)
   198     """
   271     """
   261                                              ATTR_MAP.copy())
   334                                              ATTR_MAP.copy())
   262         # full text index helper
   335         # full text index helper
   263         self.do_fti = not repo.config['delay-full-text-indexation']
   336         self.do_fti = not repo.config['delay-full-text-indexation']
   264         # sql queries cache
   337         # sql queries cache
   265         self._cache = QueryCache(repo.config['rql-cache-size'])
   338         self._cache = QueryCache(repo.config['rql-cache-size'])
   266         # we need a lock to protect eid attribution function (XXX, really?
       
   267         # explain)
       
   268         self._eid_cnx_lock = Lock()
       
   269         self._eid_creation_cnx = None
       
   270         # (etype, attr) / storage mapping
   339         # (etype, attr) / storage mapping
   271         self._storages = {}
   340         self._storages = {}
       
   341         self.binary_to_str = self.dbhelper.dbapi_module.binary_to_str
   272         if self.dbdriver == 'sqlite':
   342         if self.dbdriver == 'sqlite':
   273             self._create_eid = None
   343             self.eid_generator = SQLITEEidGenerator(self)
   274             self.create_eid = self._create_eid_sqlite
   344         else:
   275         self.binary_to_str = self.dbhelper.dbapi_module.binary_to_str
   345             self.eid_generator = DefaultEidGenerator(self)
       
   346         self.create_eid = self.eid_generator.create_eid
   276 
   347 
   277     def check_config(self, source_entity):
   348     def check_config(self, source_entity):
   278         """check configuration of source entity"""
   349         """check configuration of source entity"""
   279         if source_entity.host_config:
   350         if source_entity.host_config:
   280             msg = source_entity._cw._('the system source has its configuration '
   351             msg = source_entity._cw._('the system source has its configuration '
   366             self.eid_type_source = self.eid_type_source_pre_131
   437             self.eid_type_source = self.eid_type_source_pre_131
   367         super(NativeSQLSource, self).init(activated, source_entity)
   438         super(NativeSQLSource, self).init(activated, source_entity)
   368         self.init_creating(source_entity._cw.cnxset)
   439         self.init_creating(source_entity._cw.cnxset)
   369 
   440 
   370     def shutdown(self):
   441     def shutdown(self):
   371         if self._eid_creation_cnx:
   442         self.eid_generator.close()
   372             self._eid_creation_cnx.close()
       
   373             self._eid_creation_cnx = None
       
   374 
   443 
   375     # XXX deprecates [un]map_attribute?
   444     # XXX deprecates [un]map_attribute?
   376     def map_attribute(self, etype, attr, cb, sourcedb=True):
   445     def map_attribute(self, etype, attr, cb, sourcedb=True):
   377         self._rql_sqlgen.attr_map['%s.%s' % (etype, attr)] = (cb, sourcedb)
   446         self._rql_sqlgen.attr_map['%s.%s' % (etype, attr)] = (cb, sourcedb)
   378 
   447 
   804             if result:
   873             if result:
   805                 return result[0]
   874                 return result[0]
   806         except Exception:
   875         except Exception:
   807             pass
   876             pass
   808         return None
   877         return None
   809 
       
   810     def _create_eid_sqlite(self, session):
       
   811         with self._eid_cnx_lock:
       
   812             for sql in self.dbhelper.sqls_increment_sequence('entities_id_seq'):
       
   813                 cursor = self.doexec(session, sql)
       
   814             return cursor.fetchone()[0]
       
   815 
       
   816     def create_eid(self, session): # pylint: disable=E0202
       
   817         # lock needed to prevent 'Connection is busy with results for another
       
   818         # command (0)' errors with SQLServer
       
   819         with self._eid_cnx_lock:
       
   820             return self._create_eid() # pylint: disable=E1102
       
   821 
       
   822     def _create_eid(self): # pylint: disable=E0202
       
   823         # internal function doing the eid creation without locking.
       
   824         # needed for the recursive handling of disconnections (otherwise we
       
   825         # deadlock on self._eid_cnx_lock
       
   826         if self._eid_creation_cnx is None:
       
   827             self._eid_creation_cnx = self.get_connection()
       
   828         cnx = self._eid_creation_cnx
       
   829         try:
       
   830             cursor = cnx.cursor()
       
   831             for sql in self.dbhelper.sqls_increment_sequence('entities_id_seq'):
       
   832                 cursor.execute(sql)
       
   833             eid = cursor.fetchone()[0]
       
   834         except (self.OperationalError, self.InterfaceError):
       
   835             # FIXME: better detection of deconnection pb
       
   836             self.warning("trying to reconnect create eid connection")
       
   837             self._eid_creation_cnx = None
       
   838             return self._create_eid() # pylint: disable=E1102
       
   839         except self.DbapiError as exc:
       
   840             # We get this one with pyodbc and SQL Server when connection was reset
       
   841             if exc.args[0] == '08S01':
       
   842                 self.warning("trying to reconnect create eid connection")
       
   843                 self._eid_creation_cnx = None
       
   844                 return self._create_eid() # pylint: disable=E1102
       
   845             else:
       
   846                 raise
       
   847         except Exception: # WTF?
       
   848             cnx.rollback()
       
   849             self._eid_creation_cnx = None
       
   850             self.exception('create eid failed in an unforeseen way on SQL statement %s', sql)
       
   851             raise
       
   852         else:
       
   853             cnx.commit()
       
   854             return eid
       
   855 
   878 
   856     def _handle_is_relation_sql(self, session, sql, attrs):
   879     def _handle_is_relation_sql(self, session, sql, attrs):
   857         """ Handler for specific is_relation sql that may be
   880         """ Handler for specific is_relation sql that may be
   858         overwritten in some stores"""
   881         overwritten in some stores"""
   859         self.doexec(session, sql % attrs)
   882         self.doexec(session, sql % attrs)