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) |