14 from time import time, clock |
14 from time import time, clock |
15 |
15 |
16 from cubicweb import ConnectionError, RequestSessionMixIn, set_log_methods |
16 from cubicweb import ConnectionError, RequestSessionMixIn, set_log_methods |
17 from cubicweb.cwvreg import CubicWebRegistry, MulCnxCubicWebRegistry |
17 from cubicweb.cwvreg import CubicWebRegistry, MulCnxCubicWebRegistry |
18 from cubicweb.cwconfig import CubicWebNoAppConfiguration |
18 from cubicweb.cwconfig import CubicWebNoAppConfiguration |
19 |
19 |
20 _MARKER = object() |
20 _MARKER = object() |
21 |
21 |
22 class ConnectionProperties(object): |
22 class ConnectionProperties(object): |
23 def __init__(self, cnxtype=None, lang=None, close=True, log=False): |
23 def __init__(self, cnxtype=None, lang=None, close=True, log=False): |
24 self.cnxtype = cnxtype or 'pyro' |
24 self.cnxtype = cnxtype or 'pyro' |
58 raise ConnectionError('Could not get repository for %s ' |
58 raise ConnectionError('Could not get repository for %s ' |
59 '(not registered in Pyro), ' |
59 '(not registered in Pyro), ' |
60 'you may have to restart your server-side ' |
60 'you may have to restart your server-side ' |
61 'application' % nsid) |
61 'application' % nsid) |
62 return core.getProxyForURI(uri) |
62 return core.getProxyForURI(uri) |
63 |
63 |
64 def repo_connect(repo, user, password, cnxprops=None): |
64 def repo_connect(repo, user, password, cnxprops=None): |
65 """Constructor to create a new connection to the CubicWeb repository. |
65 """Constructor to create a new connection to the CubicWeb repository. |
66 |
66 |
67 Returns a Connection instance. |
67 Returns a Connection instance. |
68 """ |
68 """ |
69 cnxprops = cnxprops or ConnectionProperties('inmemory') |
69 cnxprops = cnxprops or ConnectionProperties('inmemory') |
70 cnxid = repo.connect(unicode(user), password, cnxprops=cnxprops) |
70 cnxid = repo.connect(unicode(user), password, cnxprops=cnxprops) |
71 cnx = Connection(repo, cnxid, cnxprops) |
71 cnx = Connection(repo, cnxid, cnxprops) |
72 if cnxprops.cnxtype == 'inmemory': |
72 if cnxprops.cnxtype == 'inmemory': |
73 cnx.vreg = repo.vreg |
73 cnx.vreg = repo.vreg |
74 return cnx |
74 return cnx |
75 |
75 |
76 def connect(database=None, user=None, password=None, host=None, |
76 def connect(database=None, user=None, password=None, host=None, |
77 group=None, cnxprops=None, port=None, setvreg=True, mulcnx=True, |
77 group=None, cnxprops=None, port=None, setvreg=True, mulcnx=True, |
78 initlog=True): |
78 initlog=True): |
79 """Constructor for creating a connection to the CubicWeb repository. |
79 """Constructor for creating a connection to the CubicWeb repository. |
80 Returns a Connection object. |
80 Returns a Connection object. |
144 if cnx is not None: |
144 if cnx is not None: |
145 self.set_connection(cnx) |
145 self.set_connection(cnx) |
146 |
146 |
147 def base_url(self): |
147 def base_url(self): |
148 return self.vreg.config['base-url'] |
148 return self.vreg.config['base-url'] |
149 |
149 |
150 def from_controller(self): |
150 def from_controller(self): |
151 return 'view' |
151 return 'view' |
152 |
152 |
153 def set_connection(self, cnx, user=None): |
153 def set_connection(self, cnx, user=None): |
154 """method called by the session handler when the user is authenticated |
154 """method called by the session handler when the user is authenticated |
155 or an anonymous connection is open |
155 or an anonymous connection is open |
156 """ |
156 """ |
157 self.cnx = cnx |
157 self.cnx = cnx |
158 self.cursor = cnx.cursor(self) |
158 self.cursor = cnx.cursor(self) |
159 self.set_user(user) |
159 self.set_user(user) |
160 |
160 |
161 def set_default_language(self, vreg): |
161 def set_default_language(self, vreg): |
162 try: |
162 try: |
163 self.lang = vreg.property_value('ui.language') |
163 self.lang = vreg.property_value('ui.language') |
164 except: # property may not be registered |
164 except: # property may not be registered |
165 self.lang = 'en' |
165 self.lang = 'en' |
173 |
173 |
174 def decorate_rset(self, rset): |
174 def decorate_rset(self, rset): |
175 rset.vreg = self.vreg |
175 rset.vreg = self.vreg |
176 rset.req = self |
176 rset.req = self |
177 return rset |
177 return rset |
178 |
178 |
179 def describe(self, eid): |
179 def describe(self, eid): |
180 """return a tuple (type, sourceuri, extid) for the entity with id <eid>""" |
180 """return a tuple (type, sourceuri, extid) for the entity with id <eid>""" |
181 return self.cnx.describe(eid) |
181 return self.cnx.describe(eid) |
182 |
182 |
183 def source_defs(self): |
183 def source_defs(self): |
184 """return the definition of sources used by the repository.""" |
184 """return the definition of sources used by the repository.""" |
185 return self.cnx.source_defs() |
185 return self.cnx.source_defs() |
186 |
186 |
187 # entities cache management ############################################### |
187 # entities cache management ############################################### |
188 |
188 |
189 def entity_cache(self, eid): |
189 def entity_cache(self, eid): |
190 return self._eid_cache[eid] |
190 return self._eid_cache[eid] |
191 |
191 |
192 def set_entity_cache(self, entity): |
192 def set_entity_cache(self, entity): |
193 self._eid_cache[entity.eid] = entity |
193 self._eid_cache[entity.eid] = entity |
194 |
194 |
195 def cached_entities(self): |
195 def cached_entities(self): |
196 return self._eid_cache.values() |
196 return self._eid_cache.values() |
197 |
197 |
198 def drop_entity_cache(self, eid=None): |
198 def drop_entity_cache(self, eid=None): |
199 if eid is None: |
199 if eid is None: |
200 self._eid_cache = {} |
200 self._eid_cache = {} |
201 else: |
201 else: |
202 del self._eid_cache[eid] |
202 del self._eid_cache[eid] |
208 return self.cnx.session_data() |
208 return self.cnx.session_data() |
209 |
209 |
210 def get_session_data(self, key, default=None, pop=False): |
210 def get_session_data(self, key, default=None, pop=False): |
211 """return value associated to `key` in session data""" |
211 """return value associated to `key` in session data""" |
212 return self.cnx.get_session_data(key, default, pop) |
212 return self.cnx.get_session_data(key, default, pop) |
213 |
213 |
214 def set_session_data(self, key, value): |
214 def set_session_data(self, key, value): |
215 """set value associated to `key` in session data""" |
215 """set value associated to `key` in session data""" |
216 return self.cnx.set_session_data(key, value) |
216 return self.cnx.set_session_data(key, value) |
217 |
217 |
218 def del_session_data(self, key): |
218 def del_session_data(self, key): |
219 """remove value associated to `key` in session data""" |
219 """remove value associated to `key` in session data""" |
220 return self.cnx.del_session_data(key) |
220 return self.cnx.del_session_data(key) |
221 |
221 |
222 def get_shared_data(self, key, default=None, pop=False): |
222 def get_shared_data(self, key, default=None, pop=False): |
223 """return value associated to `key` in shared data""" |
223 """return value associated to `key` in shared data""" |
224 return self.cnx.get_shared_data(key, default, pop) |
224 return self.cnx.get_shared_data(key, default, pop) |
225 |
225 |
226 def set_shared_data(self, key, value, querydata=False): |
226 def set_shared_data(self, key, value, querydata=False): |
227 """set value associated to `key` in shared data |
227 """set value associated to `key` in shared data |
228 |
228 |
229 if `querydata` is true, the value will be added to the repository |
229 if `querydata` is true, the value will be added to the repository |
230 session's query data which are cleared on commit/rollback of the current |
230 session's query data which are cleared on commit/rollback of the current |
243 |
243 |
244 def set_user(self, user): |
244 def set_user(self, user): |
245 self._user = user |
245 self._user = user |
246 if user: |
246 if user: |
247 self.set_entity_cache(user) |
247 self.set_entity_cache(user) |
248 |
248 |
249 def execute(self, *args, **kwargs): |
249 def execute(self, *args, **kwargs): |
250 """Session interface compatibility""" |
250 """Session interface compatibility""" |
251 return self.cursor.execute(*args, **kwargs) |
251 return self.cursor.execute(*args, **kwargs) |
252 |
252 |
253 set_log_methods(DBAPIRequest, getLogger('cubicweb.dbapi')) |
253 set_log_methods(DBAPIRequest, getLogger('cubicweb.dbapi')) |
254 |
254 |
255 |
255 |
256 # exceptions ################################################################## |
256 # exceptions ################################################################## |
257 |
257 |
258 class ProgrammingError(Exception): #DatabaseError): |
258 class ProgrammingError(Exception): #DatabaseError): |
259 """Exception raised for errors that are related to the database's operation |
259 """Exception raised for errors that are related to the database's operation |
260 and not necessarily under the control of the programmer, e.g. an unexpected |
260 and not necessarily under the control of the programmer, e.g. an unexpected |
286 threadsafety = 1 |
286 threadsafety = 1 |
287 |
287 |
288 """String constant stating the type of parameter marker formatting expected by |
288 """String constant stating the type of parameter marker formatting expected by |
289 the interface. Possible values are : |
289 the interface. Possible values are : |
290 |
290 |
291 'qmark' Question mark style, |
291 'qmark' Question mark style, |
292 e.g. '...WHERE name=?' |
292 e.g. '...WHERE name=?' |
293 'numeric' Numeric, positional style, |
293 'numeric' Numeric, positional style, |
294 e.g. '...WHERE name=:1' |
294 e.g. '...WHERE name=:1' |
295 'named' Named style, |
295 'named' Named style, |
296 e.g. '...WHERE name=:name' |
296 e.g. '...WHERE name=:name' |
297 'format' ANSI C printf format codes, |
297 'format' ANSI C printf format codes, |
298 e.g. '...WHERE name=%s' |
298 e.g. '...WHERE name=%s' |
299 'pyformat' Python extended format codes, |
299 'pyformat' Python extended format codes, |
300 e.g. '...WHERE name=%(name)s' |
300 e.g. '...WHERE name=%(name)s' |
301 """ |
301 """ |
302 paramstyle = 'pyformat' |
302 paramstyle = 'pyformat' |
303 |
303 |
304 |
304 |
331 return '<Connection %s (anonymous)>' % self.sessionid |
331 return '<Connection %s (anonymous)>' % self.sessionid |
332 return '<Connection %s>' % self.sessionid |
332 return '<Connection %s>' % self.sessionid |
333 |
333 |
334 def request(self): |
334 def request(self): |
335 return DBAPIRequest(self.vreg, self) |
335 return DBAPIRequest(self.vreg, self) |
336 |
336 |
337 def session_data(self): |
337 def session_data(self): |
338 """return a dictionnary containing session data""" |
338 """return a dictionnary containing session data""" |
339 return self.data |
339 return self.data |
340 |
340 |
341 def get_session_data(self, key, default=None, pop=False): |
341 def get_session_data(self, key, default=None, pop=False): |
342 """return value associated to `key` in session data""" |
342 """return value associated to `key` in session data""" |
343 if pop: |
343 if pop: |
344 return self.data.pop(key, default) |
344 return self.data.pop(key, default) |
345 else: |
345 else: |
346 return self.data.get(key, default) |
346 return self.data.get(key, default) |
347 |
347 |
348 def set_session_data(self, key, value): |
348 def set_session_data(self, key, value): |
349 """set value associated to `key` in session data""" |
349 """set value associated to `key` in session data""" |
350 self.data[key] = value |
350 self.data[key] = value |
351 |
351 |
352 def del_session_data(self, key): |
352 def del_session_data(self, key): |
353 """remove value associated to `key` in session data""" |
353 """remove value associated to `key` in session data""" |
354 try: |
354 try: |
355 del self.data[key] |
355 del self.data[key] |
356 except KeyError: |
356 except KeyError: |
357 pass |
357 pass |
358 |
358 |
359 def check(self): |
359 def check(self): |
360 """raise `BadSessionId` if the connection is no more valid""" |
360 """raise `BadSessionId` if the connection is no more valid""" |
361 self._repo.check_session(self.sessionid) |
361 self._repo.check_session(self.sessionid) |
362 |
362 |
363 def get_shared_data(self, key, default=None, pop=False): |
363 def get_shared_data(self, key, default=None, pop=False): |
364 """return value associated to `key` in shared data""" |
364 """return value associated to `key` in shared data""" |
365 return self._repo.get_shared_data(self.sessionid, key, default, pop) |
365 return self._repo.get_shared_data(self.sessionid, key, default, pop) |
366 |
366 |
367 def set_shared_data(self, key, value, querydata=False): |
367 def set_shared_data(self, key, value, querydata=False): |
368 """set value associated to `key` in shared data |
368 """set value associated to `key` in shared data |
369 |
369 |
370 if `querydata` is true, the value will be added to the repository |
370 if `querydata` is true, the value will be added to the repository |
371 session's query data which are cleared on commit/rollback of the current |
371 session's query data which are cleared on commit/rollback of the current |
372 transaction, and won't be available through the connexion, only on the |
372 transaction, and won't be available through the connexion, only on the |
373 repository side. |
373 repository side. |
374 """ |
374 """ |
375 return self._repo.set_shared_data(self.sessionid, key, value, querydata) |
375 return self._repo.set_shared_data(self.sessionid, key, value, querydata) |
376 |
376 |
377 def get_schema(self): |
377 def get_schema(self): |
378 """Return the schema currently used by the repository. |
378 """Return the schema currently used by the repository. |
379 |
379 |
380 This is NOT part of the DB-API. |
380 This is NOT part of the DB-API. |
381 """ |
381 """ |
382 if self._closed is not None: |
382 if self._closed is not None: |
383 raise ProgrammingError('Closed connection') |
383 raise ProgrammingError('Closed connection') |
384 return self._repo.get_schema() |
384 return self._repo.get_schema() |
412 hm.set_schema(hm.schema) # reset structure |
412 hm.set_schema(hm.schema) # reset structure |
413 hm.register_system_hooks(config) |
413 hm.register_system_hooks(config) |
414 # application specific hooks |
414 # application specific hooks |
415 if self._repo.config.application_hooks: |
415 if self._repo.config.application_hooks: |
416 hm.register_hooks(config.load_hooks(self.vreg)) |
416 hm.register_hooks(config.load_hooks(self.vreg)) |
417 |
417 |
418 def source_defs(self): |
418 def source_defs(self): |
419 """Return the definition of sources used by the repository. |
419 """Return the definition of sources used by the repository. |
420 |
420 |
421 This is NOT part of the DB-API. |
421 This is NOT part of the DB-API. |
422 """ |
422 """ |
423 if self._closed is not None: |
423 if self._closed is not None: |
424 raise ProgrammingError('Closed connection') |
424 raise ProgrammingError('Closed connection') |
425 return self._repo.source_defs() |
425 return self._repo.source_defs() |
441 if self._closed is None and self._close_on_del: |
441 if self._closed is None and self._close_on_del: |
442 try: |
442 try: |
443 self.close() |
443 self.close() |
444 except: |
444 except: |
445 pass |
445 pass |
446 |
446 |
447 def describe(self, eid): |
447 def describe(self, eid): |
448 return self._repo.describe(self.sessionid, eid) |
448 return self._repo.describe(self.sessionid, eid) |
449 |
449 |
450 def close(self): |
450 def close(self): |
451 """Close the connection now (rather than whenever __del__ is called). |
451 """Close the connection now (rather than whenever __del__ is called). |
452 |
452 |
453 The connection will be unusable from this point forward; an Error (or |
453 The connection will be unusable from this point forward; an Error (or |
454 subclass) exception will be raised if any operation is attempted with |
454 subclass) exception will be raised if any operation is attempted with |
455 the connection. The same applies to all cursor objects trying to use the |
455 the connection. The same applies to all cursor objects trying to use the |
456 connection. Note that closing a connection without committing the |
456 connection. Note that closing a connection without committing the |
457 changes first will cause an implicit rollback to be performed. |
457 changes first will cause an implicit rollback to be performed. |
463 |
463 |
464 def commit(self): |
464 def commit(self): |
465 """Commit any pending transaction to the database. Note that if the |
465 """Commit any pending transaction to the database. Note that if the |
466 database supports an auto-commit feature, this must be initially off. An |
466 database supports an auto-commit feature, this must be initially off. An |
467 interface method may be provided to turn it back on. |
467 interface method may be provided to turn it back on. |
468 |
468 |
469 Database modules that do not support transactions should implement this |
469 Database modules that do not support transactions should implement this |
470 method with void functionality. |
470 method with void functionality. |
471 """ |
471 """ |
472 if not self._closed is None: |
472 if not self._closed is None: |
473 raise ProgrammingError('Connection is already closed') |
473 raise ProgrammingError('Connection is already closed') |
474 self._repo.commit(self.sessionid) |
474 self._repo.commit(self.sessionid) |
475 |
475 |
476 def rollback(self): |
476 def rollback(self): |
477 """This method is optional since not all databases provide transaction |
477 """This method is optional since not all databases provide transaction |
478 support. |
478 support. |
479 |
479 |
480 In case a database does provide transactions this method causes the the |
480 In case a database does provide transactions this method causes the the |
481 database to roll back to the start of any pending transaction. Closing |
481 database to roll back to the start of any pending transaction. Closing |
482 a connection without committing the changes first will cause an implicit |
482 a connection without committing the changes first will cause an implicit |
483 rollback to be performed. |
483 rollback to be performed. |
484 """ |
484 """ |
533 self._sessid = connection.sessionid |
533 self._sessid = connection.sessionid |
534 self._res = None |
534 self._res = None |
535 self._closed = None |
535 self._closed = None |
536 self._index = 0 |
536 self._index = 0 |
537 |
537 |
538 |
538 |
539 def close(self): |
539 def close(self): |
540 """Close the cursor now (rather than whenever __del__ is called). The |
540 """Close the cursor now (rather than whenever __del__ is called). The |
541 cursor will be unusable from this point forward; an Error (or subclass) |
541 cursor will be unusable from this point forward; an Error (or subclass) |
542 exception will be raised if any operation is attempted with the cursor. |
542 exception will be raised if any operation is attempted with the cursor. |
543 """ |
543 """ |
544 self._closed = True |
544 self._closed = True |
545 |
545 |
546 |
546 |
547 def execute(self, operation, parameters=None, eid_key=None, build_descr=True): |
547 def execute(self, operation, parameters=None, eid_key=None, build_descr=True): |
548 """Prepare and execute a database operation (query or command). |
548 """Prepare and execute a database operation (query or command). |
549 Parameters may be provided as sequence or mapping and will be bound to |
549 Parameters may be provided as sequence or mapping and will be bound to |
550 variables in the operation. Variables are specified in a |
550 variables in the operation. Variables are specified in a |
551 database-specific notation (see the module's paramstyle attribute for |
551 database-specific notation (see the module's paramstyle attribute for |
552 details). |
552 details). |
553 |
553 |
554 A reference to the operation will be retained by the cursor. If the |
554 A reference to the operation will be retained by the cursor. If the |
555 same operation object is passed in again, then the cursor can optimize |
555 same operation object is passed in again, then the cursor can optimize |
556 its behavior. This is most effective for algorithms where the same |
556 its behavior. This is most effective for algorithms where the same |
557 operation is used, but different parameters are bound to it (many |
557 operation is used, but different parameters are bound to it (many |
558 times). |
558 times). |
559 |
559 |
560 For maximum efficiency when reusing an operation, it is best to use the |
560 For maximum efficiency when reusing an operation, it is best to use the |
561 setinputsizes() method to specify the parameter types and sizes ahead |
561 setinputsizes() method to specify the parameter types and sizes ahead |
562 of time. It is legal for a parameter to not match the predefined |
562 of time. It is legal for a parameter to not match the predefined |
563 information; the implementation should compensate, possibly with a loss |
563 information; the implementation should compensate, possibly with a loss |
564 of efficiency. |
564 of efficiency. |
565 |
565 |
566 The parameters may also be specified as list of tuples to e.g. insert |
566 The parameters may also be specified as list of tuples to e.g. insert |
567 multiple rows in a single operation, but this kind of usage is |
567 multiple rows in a single operation, but this kind of usage is |
568 depreciated: executemany() should be used instead. |
568 depreciated: executemany() should be used instead. |
569 |
569 |
570 Return values are not defined by the DB-API, but this here it returns a |
570 Return values are not defined by the DB-API, but this here it returns a |
571 ResultSet object. |
571 ResultSet object. |
572 """ |
572 """ |
573 self._res = res = self._repo.execute(self._sessid, operation, |
573 self._res = res = self._repo.execute(self._sessid, operation, |
574 parameters, eid_key, build_descr) |
574 parameters, eid_key, build_descr) |
575 self.req.decorate_rset(res) |
575 self.req.decorate_rset(res) |
576 self._index = 0 |
576 self._index = 0 |
577 return res |
577 return res |
578 |
578 |
579 |
579 |
580 def executemany(self, operation, seq_of_parameters): |
580 def executemany(self, operation, seq_of_parameters): |
581 """Prepare a database operation (query or command) and then execute it |
581 """Prepare a database operation (query or command) and then execute it |
582 against all parameter sequences or mappings found in the sequence |
582 against all parameter sequences or mappings found in the sequence |
583 seq_of_parameters. |
583 seq_of_parameters. |
584 |
584 |
585 Modules are free to implement this method using multiple calls to the |
585 Modules are free to implement this method using multiple calls to the |
586 execute() method or by using array operations to have the database |
586 execute() method or by using array operations to have the database |
587 process the sequence as a whole in one call. |
587 process the sequence as a whole in one call. |
588 |
588 |
589 Use of this method for an operation which produces one or more result |
589 Use of this method for an operation which produces one or more result |
590 sets constitutes undefined behavior, and the implementation is |
590 sets constitutes undefined behavior, and the implementation is |
591 permitted (but not required) to raise an exception when it detects that |
591 permitted (but not required) to raise an exception when it detects that |
592 a result set has been created by an invocation of the operation. |
592 a result set has been created by an invocation of the operation. |
593 |
593 |
594 The same comments as for execute() also apply accordingly to this |
594 The same comments as for execute() also apply accordingly to this |
595 method. |
595 method. |
596 |
596 |
597 Return values are not defined. |
597 Return values are not defined. |
598 """ |
598 """ |
599 for parameters in seq_of_parameters: |
599 for parameters in seq_of_parameters: |
600 self.execute(operation, parameters) |
600 self.execute(operation, parameters) |
601 if self._res.rows is not None: |
601 if self._res.rows is not None: |
604 |
604 |
605 |
605 |
606 def fetchone(self): |
606 def fetchone(self): |
607 """Fetch the next row of a query result set, returning a single |
607 """Fetch the next row of a query result set, returning a single |
608 sequence, or None when no more data is available. |
608 sequence, or None when no more data is available. |
609 |
609 |
610 An Error (or subclass) exception is raised if the previous call to |
610 An Error (or subclass) exception is raised if the previous call to |
611 execute*() did not produce any result set or no call was issued yet. |
611 execute*() did not produce any result set or no call was issued yet. |
612 """ |
612 """ |
613 if self._res is None: |
613 if self._res is None: |
614 raise ProgrammingError('No result set') |
614 raise ProgrammingError('No result set') |
615 row = self._res.rows[self._index] |
615 row = self._res.rows[self._index] |
616 self._index += 1 |
616 self._index += 1 |
617 return row |
617 return row |
618 |
618 |
619 |
619 |
620 def fetchmany(self, size=None): |
620 def fetchmany(self, size=None): |
621 """Fetch the next set of rows of a query result, returning a sequence |
621 """Fetch the next set of rows of a query result, returning a sequence |
622 of sequences (e.g. a list of tuples). An empty sequence is returned |
622 of sequences (e.g. a list of tuples). An empty sequence is returned |
623 when no more rows are available. |
623 when no more rows are available. |
624 |
624 |
625 The number of rows to fetch per call is specified by the parameter. If |
625 The number of rows to fetch per call is specified by the parameter. If |
626 it is not given, the cursor's arraysize determines the number of rows |
626 it is not given, the cursor's arraysize determines the number of rows |
627 to be fetched. The method should try to fetch as many rows as indicated |
627 to be fetched. The method should try to fetch as many rows as indicated |
628 by the size parameter. If this is not possible due to the specified |
628 by the size parameter. If this is not possible due to the specified |
629 number of rows not being available, fewer rows may be returned. |
629 number of rows not being available, fewer rows may be returned. |
630 |
630 |
631 An Error (or subclass) exception is raised if the previous call to |
631 An Error (or subclass) exception is raised if the previous call to |
632 execute*() did not produce any result set or no call was issued yet. |
632 execute*() did not produce any result set or no call was issued yet. |
633 |
633 |
634 Note there are performance considerations involved with the size |
634 Note there are performance considerations involved with the size |
635 parameter. For optimal performance, it is usually best to use the |
635 parameter. For optimal performance, it is usually best to use the |
636 arraysize attribute. If the size parameter is used, then it is best |
636 arraysize attribute. If the size parameter is used, then it is best |
637 for it to retain the same value from one fetchmany() call to the next. |
637 for it to retain the same value from one fetchmany() call to the next. |
638 """ |
638 """ |
663 |
663 |
664 |
664 |
665 def setinputsizes(self, sizes): |
665 def setinputsizes(self, sizes): |
666 """This can be used before a call to execute*() to predefine memory |
666 """This can be used before a call to execute*() to predefine memory |
667 areas for the operation's parameters. |
667 areas for the operation's parameters. |
668 |
668 |
669 sizes is specified as a sequence -- one item for each input parameter. |
669 sizes is specified as a sequence -- one item for each input parameter. |
670 The item should be a Type Object that corresponds to the input that |
670 The item should be a Type Object that corresponds to the input that |
671 will be used, or it should be an integer specifying the maximum length |
671 will be used, or it should be an integer specifying the maximum length |
672 of a string parameter. If the item is None, then no predefined memory |
672 of a string parameter. If the item is None, then no predefined memory |
673 area will be reserved for that column (this is useful to avoid |
673 area will be reserved for that column (this is useful to avoid |
674 predefined areas for large inputs). |
674 predefined areas for large inputs). |
675 |
675 |
676 This method would be used before the execute*() method is invoked. |
676 This method would be used before the execute*() method is invoked. |
677 |
677 |
678 Implementations are free to have this method do nothing and users are |
678 Implementations are free to have this method do nothing and users are |
679 free to not use it. |
679 free to not use it. |
680 """ |
680 """ |
681 pass |
681 pass |
682 |
682 |
683 |
683 |
684 def setoutputsize(self, size, column=None): |
684 def setoutputsize(self, size, column=None): |
685 """Set a column buffer size for fetches of large columns (e.g. LONGs, |
685 """Set a column buffer size for fetches of large columns (e.g. LONGs, |
686 BLOBs, etc.). The column is specified as an index into the result |
686 BLOBs, etc.). The column is specified as an index into the result |
687 sequence. Not specifying the column will set the default size for all |
687 sequence. Not specifying the column will set the default size for all |
688 large columns in the cursor. |
688 large columns in the cursor. |
689 |
689 |
690 This method would be used before the execute*() method is invoked. |
690 This method would be used before the execute*() method is invoked. |
691 |
691 |
692 Implementations are free to have this method do nothing and users are |
692 Implementations are free to have this method do nothing and users are |
693 free to not use it. |
693 free to not use it. |
694 """ |
694 """ |
695 pass |
695 pass |
696 |
696 |
697 |
697 |
698 class LogCursor(Cursor): |
698 class LogCursor(Cursor): |
699 """override the standard cursor to log executed queries""" |
699 """override the standard cursor to log executed queries""" |
700 |
700 |
701 def execute(self, operation, parameters=None, eid_key=None, build_descr=True): |
701 def execute(self, operation, parameters=None, eid_key=None, build_descr=True): |
702 """override the standard cursor to log executed queries""" |
702 """override the standard cursor to log executed queries""" |
703 tstart, cstart = time(), clock() |
703 tstart, cstart = time(), clock() |
704 rset = Cursor.execute(self, operation, parameters, eid_key, build_descr) |
704 rset = Cursor.execute(self, operation, parameters, eid_key, build_descr) |
705 self.connection.executed_queries.append((operation, parameters, |
705 self.connection.executed_queries.append((operation, parameters, |