Mercurial > p > mysql-python > mysqldb-2
comparison MySQLdb/cursors.py @ 0:e48810735f11 MySQLdb
Copying 1.2.1 to be the new trunk
author | adustman |
---|---|
date | Sun, 02 Apr 2006 18:20:53 +0000 |
parents | |
children | b5a377255eea |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:e48810735f11 |
---|---|
1 """MySQLdb Cursors | |
2 | |
3 This module implements Cursors of various types for MySQLdb. By | |
4 default, MySQLdb uses the Cursor class. | |
5 | |
6 """ | |
7 | |
8 import re | |
9 insert_values = re.compile(r'\svalues\s*(\(.+\))', re.IGNORECASE) | |
10 from _mysql_exceptions import Warning, Error, InterfaceError, DataError, \ | |
11 DatabaseError, OperationalError, IntegrityError, InternalError, \ | |
12 NotSupportedError, ProgrammingError | |
13 | |
14 | |
15 class BaseCursor(object): | |
16 | |
17 """A base for Cursor classes. Useful attributes: | |
18 | |
19 description | |
20 A tuple of DB API 7-tuples describing the columns in | |
21 the last executed query; see PEP-249 for details. | |
22 | |
23 description_flags | |
24 Tuple of column flags for last query, one entry per column | |
25 in the result set. Values correspond to those in | |
26 MySQLdb.constants.FLAG. See MySQL documentation (C API) | |
27 for more information. Non-standard extension. | |
28 | |
29 arraysize | |
30 default number of rows fetchmany() will fetch | |
31 | |
32 """ | |
33 | |
34 from _mysql_exceptions import MySQLError, Warning, Error, InterfaceError, \ | |
35 DatabaseError, DataError, OperationalError, IntegrityError, \ | |
36 InternalError, ProgrammingError, NotSupportedError | |
37 | |
38 def __init__(self, connection): | |
39 from weakref import proxy | |
40 | |
41 self.connection = proxy(connection) | |
42 self.description = None | |
43 self.description_flags = None | |
44 self.rowcount = -1 | |
45 self.arraysize = 1 | |
46 self._executed = None | |
47 self.lastrowid = None | |
48 self.messages = [] | |
49 self.errorhandler = connection.errorhandler | |
50 self._result = None | |
51 self._warnings = 0 | |
52 self._info = None | |
53 self.rownumber = None | |
54 | |
55 def __del__(self): | |
56 self.close() | |
57 self.errorhandler = None | |
58 self._result = None | |
59 | |
60 def close(self): | |
61 """Close the cursor. No further queries will be possible.""" | |
62 if not self.connection: return | |
63 while self.nextset(): pass | |
64 self.connection = None | |
65 | |
66 def _check_executed(self): | |
67 if not self._executed: | |
68 self.errorhandler(self, ProgrammingError, "execute() first") | |
69 | |
70 def _warning_check(self): | |
71 from warnings import warn | |
72 if self._warnings: | |
73 warnings = self._get_db().show_warnings() | |
74 if warnings: | |
75 # This is done in two loops in case | |
76 # Warnings are set to raise exceptions. | |
77 for w in warnings: | |
78 self.messages.append((self.Warning, w)) | |
79 for w in warnings: | |
80 warn(w[-1], self.Warning, 3) | |
81 elif self._info: | |
82 self.messages.append((self.Warning, self._info)) | |
83 warn(self._info, self.Warning, 3) | |
84 | |
85 def nextset(self): | |
86 """Advance to the next result set. | |
87 | |
88 Returns None if there are no more result sets. | |
89 """ | |
90 if self._executed: | |
91 self.fetchall() | |
92 del self.messages[:] | |
93 | |
94 db = self._get_db() | |
95 nr = db.next_result() | |
96 if nr == -1: | |
97 return None | |
98 self._do_get_result() | |
99 self._post_get_result() | |
100 self._warning_check() | |
101 return 1 | |
102 | |
103 def _post_get_result(self): pass | |
104 | |
105 def _do_get_result(self): | |
106 db = self._get_db() | |
107 self._result = self._get_result() | |
108 self.rowcount = db.affected_rows() | |
109 self.rownumber = 0 | |
110 self.description = self._result and self._result.describe() or None | |
111 self.description_flags = self._result and self._result.field_flags() or None | |
112 self.lastrowid = db.insert_id() | |
113 self._warnings = db.warning_count() | |
114 self._info = db.info() | |
115 | |
116 def setinputsizes(self, *args): | |
117 """Does nothing, required by DB API.""" | |
118 | |
119 def setoutputsizes(self, *args): | |
120 """Does nothing, required by DB API.""" | |
121 | |
122 def _get_db(self): | |
123 if not self.connection: | |
124 self.errorhandler(self, ProgrammingError, "cursor closed") | |
125 return self.connection | |
126 | |
127 def execute(self, query, args=None): | |
128 | |
129 """Execute a query. | |
130 | |
131 query -- string, query to execute on server | |
132 args -- optional sequence or mapping, parameters to use with query. | |
133 | |
134 Note: If args is a sequence, then %s must be used as the | |
135 parameter placeholder in the query. If a mapping is used, | |
136 %(key)s must be used as the placeholder. | |
137 | |
138 Returns long integer rows affected, if any | |
139 | |
140 """ | |
141 from types import ListType, TupleType | |
142 from sys import exc_info | |
143 del self.messages[:] | |
144 db = self._get_db() | |
145 charset = db.character_set_name() | |
146 query = query.encode(charset) | |
147 if args is not None: | |
148 query = query % db.literal(args) | |
149 try: | |
150 r = self._query(query) | |
151 except TypeError, m: | |
152 if m.args[0] in ("not enough arguments for format string", | |
153 "not all arguments converted"): | |
154 self.messages.append((ProgrammingError, m.args[0])) | |
155 self.errorhandler(self, ProgrammingError, m.args[0]) | |
156 else: | |
157 self.messages.append((TypeError, m)) | |
158 self.errorhandler(self, TypeError, m) | |
159 except: | |
160 exc, value, tb = exc_info() | |
161 del tb | |
162 self.messages.append((exc, value)) | |
163 self.errorhandler(self, exc, value) | |
164 self._executed = query | |
165 self._warning_check() | |
166 return r | |
167 | |
168 def executemany(self, query, args): | |
169 | |
170 """Execute a multi-row query. | |
171 | |
172 query -- string, query to execute on server | |
173 | |
174 args | |
175 | |
176 Sequence of sequences or mappings, parameters to use with | |
177 query. | |
178 | |
179 Returns long integer rows affected, if any. | |
180 | |
181 This method improves performance on multiple-row INSERT and | |
182 REPLACE. Otherwise it is equivalent to looping over args with | |
183 execute(). | |
184 | |
185 """ | |
186 del self.messages[:] | |
187 db = self._get_db() | |
188 if not args: return | |
189 m = insert_values.search(query) | |
190 if not m: | |
191 r = 0 | |
192 for a in args: | |
193 r = r + self.execute(query, a) | |
194 return r | |
195 p = m.start(1) | |
196 charset = db.character_set_name() | |
197 query = query.encode(charset) | |
198 qv = query[p:] | |
199 qargs = db.literal(args) | |
200 try: | |
201 q = [ query % qargs[0] ] | |
202 q.extend([ qv % a for a in qargs[1:] ]) | |
203 except TypeError, msg: | |
204 if msg.args[0] in ("not enough arguments for format string", | |
205 "not all arguments converted"): | |
206 self.messages.append((ProgrammingError, msg.args[0])) | |
207 self.errorhandler(self, ProgrammingError, msg.args[0]) | |
208 else: | |
209 self.messages.append((TypeError, msg)) | |
210 self.errorhandler(self, TypeError, msg) | |
211 except: | |
212 from sys import exc_info | |
213 exc, value, tb = exc_info() | |
214 del tb | |
215 self.errorhandler(self, exc, value) | |
216 r = self._query(',\n'.join(q)) | |
217 self._warning_check() | |
218 return r | |
219 | |
220 def callproc(self, procname, args=()): | |
221 | |
222 """Execute stored procedure procname with args | |
223 | |
224 procname -- string, name of procedure to execute on server | |
225 | |
226 args -- Sequence of parameters to use with procedure | |
227 | |
228 Returns the original args. | |
229 | |
230 Compatibility warning: PEP-249 specifies that any modified | |
231 parameters must be returned. This is currently impossible | |
232 as they are only available by storing them in a server | |
233 variable and then retrieved by a query. Since stored | |
234 procedures return zero or more result sets, there is no | |
235 reliable way to get at OUT or INOUT parameters via callproc. | |
236 The server variables are named @_procname_n, where procname | |
237 is the parameter above and n is the position of the parameter | |
238 (from zero). Once all result sets generated by the procedure | |
239 have been fetched, you can issue a SELECT @_procname_0, ... | |
240 query using .execute() to get any OUT or INOUT values. | |
241 | |
242 Compatibility warning: The act of calling a stored procedure | |
243 itself creates an empty result set. This appears after any | |
244 result sets generated by the procedure. This is non-standard | |
245 behavior with respect to the DB-API. Be sure to use nextset() | |
246 to advance through all result sets; otherwise you may get | |
247 disconnected. | |
248 """ | |
249 | |
250 from types import UnicodeType | |
251 db = self._get_db() | |
252 charset = db.character_set_name() | |
253 for index, arg in enumerate(args): | |
254 q = "SET @_%s_%d=%s" % (procname, index, | |
255 db.literal(arg)) | |
256 if type(q) is UnicodeType: | |
257 q = q.encode(charset) | |
258 self._query(q) | |
259 self.nextset() | |
260 | |
261 q = "CALL %s(%s)" % (procname, | |
262 ','.join(['@_%s_%d' % (procname, i) | |
263 for i in range(len(args))])) | |
264 if type(q) is UnicodeType: | |
265 q = q.encode(charset) | |
266 self._query(q) | |
267 self._warning_check() | |
268 return args | |
269 | |
270 def _do_query(self, q): | |
271 db = self._get_db() | |
272 self._last_executed = q | |
273 db.query(q) | |
274 self._do_get_result() | |
275 return self.rowcount | |
276 | |
277 def _query(self, q): return self._do_query(q) | |
278 | |
279 def _fetch_row(self, size=1): | |
280 if not self._result: | |
281 return () | |
282 return self._result.fetch_row(size, self._fetch_type) | |
283 | |
284 def __iter__(self): | |
285 return iter(self.fetchone, None) | |
286 | |
287 Warning = Warning | |
288 Error = Error | |
289 InterfaceError = InterfaceError | |
290 DatabaseError = DatabaseError | |
291 DataError = DataError | |
292 OperationalError = OperationalError | |
293 IntegrityError = IntegrityError | |
294 InternalError = InternalError | |
295 ProgrammingError = ProgrammingError | |
296 NotSupportedError = NotSupportedError | |
297 | |
298 | |
299 class CursorStoreResultMixIn(object): | |
300 | |
301 """This is a MixIn class which causes the entire result set to be | |
302 stored on the client side, i.e. it uses mysql_store_result(). If the | |
303 result set can be very large, consider adding a LIMIT clause to your | |
304 query, or using CursorUseResultMixIn instead.""" | |
305 | |
306 def _get_result(self): return self._get_db().store_result() | |
307 | |
308 def _query(self, q): | |
309 rowcount = self._do_query(q) | |
310 self._post_get_result() | |
311 return rowcount | |
312 | |
313 def _post_get_result(self): | |
314 self._rows = self._fetch_row(0) | |
315 self._result = None | |
316 | |
317 def fetchone(self): | |
318 """Fetches a single row from the cursor. None indicates that | |
319 no more rows are available.""" | |
320 self._check_executed() | |
321 if self.rownumber >= len(self._rows): return None | |
322 result = self._rows[self.rownumber] | |
323 self.rownumber = self.rownumber+1 | |
324 return result | |
325 | |
326 def fetchmany(self, size=None): | |
327 """Fetch up to size rows from the cursor. Result set may be smaller | |
328 than size. If size is not defined, cursor.arraysize is used.""" | |
329 self._check_executed() | |
330 end = self.rownumber + (size or self.arraysize) | |
331 result = self._rows[self.rownumber:end] | |
332 self.rownumber = min(end, len(self._rows)) | |
333 return result | |
334 | |
335 def fetchall(self): | |
336 """Fetchs all available rows from the cursor.""" | |
337 self._check_executed() | |
338 if self.rownumber: | |
339 result = self._rows[self.rownumber:] | |
340 else: | |
341 result = self._rows | |
342 self.rownumber = len(self._rows) | |
343 return result | |
344 | |
345 def scroll(self, value, mode='relative'): | |
346 """Scroll the cursor in the result set to a new position according | |
347 to mode. | |
348 | |
349 If mode is 'relative' (default), value is taken as offset to | |
350 the current position in the result set, if set to 'absolute', | |
351 value states an absolute target position.""" | |
352 self._check_executed() | |
353 if mode == 'relative': | |
354 r = self.rownumber + value | |
355 elif mode == 'absolute': | |
356 r = value | |
357 else: | |
358 self.errorhandler(self, ProgrammingError, | |
359 "unknown scroll mode %s" % `mode`) | |
360 if r < 0 or r >= len(self._rows): | |
361 self.errorhandler(self, IndexError, "out of range") | |
362 self.rownumber = r | |
363 | |
364 def __iter__(self): | |
365 self._check_executed() | |
366 result = self.rownumber and self._rows[self.rownumber:] or self._rows | |
367 return iter(result) | |
368 | |
369 | |
370 class CursorUseResultMixIn(object): | |
371 | |
372 """This is a MixIn class which causes the result set to be stored | |
373 in the server and sent row-by-row to client side, i.e. it uses | |
374 mysql_use_result(). You MUST retrieve the entire result set and | |
375 close() the cursor before additional queries can be peformed on | |
376 the connection.""" | |
377 | |
378 def _get_result(self): return self._get_db().use_result() | |
379 | |
380 def fetchone(self): | |
381 """Fetches a single row from the cursor.""" | |
382 self._check_executed() | |
383 r = self._fetch_row(1) | |
384 if not r: return None | |
385 self.rownumber = self.rownumber + 1 | |
386 return r[0] | |
387 | |
388 def fetchmany(self, size=None): | |
389 """Fetch up to size rows from the cursor. Result set may be smaller | |
390 than size. If size is not defined, cursor.arraysize is used.""" | |
391 self._check_executed() | |
392 r = self._fetch_row(size or self.arraysize) | |
393 self.rownumber = self.rownumber + len(r) | |
394 return r | |
395 | |
396 def fetchall(self): | |
397 """Fetchs all available rows from the cursor.""" | |
398 self._check_executed() | |
399 r = self._fetch_row(0) | |
400 self.rownumber = self.rownumber + len(r) | |
401 return r | |
402 | |
403 def __iter__(self): | |
404 return self | |
405 | |
406 def next(self): | |
407 row = self.fetchone() | |
408 if row is None: | |
409 raise StopIteration | |
410 return row | |
411 | |
412 | |
413 class CursorTupleRowsMixIn(object): | |
414 | |
415 """This is a MixIn class that causes all rows to be returned as tuples, | |
416 which is the standard form required by DB API.""" | |
417 | |
418 _fetch_type = 0 | |
419 | |
420 | |
421 class CursorDictRowsMixIn(object): | |
422 | |
423 """This is a MixIn class that causes all rows to be returned as | |
424 dictionaries. This is a non-standard feature.""" | |
425 | |
426 _fetch_type = 1 | |
427 | |
428 def fetchoneDict(self): | |
429 """Fetch a single row as a dictionary. Deprecated: | |
430 Use fetchone() instead. Will be removed in 1.3.""" | |
431 from warnings import warn | |
432 warn("fetchoneDict() is non-standard and will be removed in 1.3", | |
433 DeprecationWarning, 2) | |
434 return self.fetchone() | |
435 | |
436 def fetchmanyDict(self, size=None): | |
437 """Fetch several rows as a list of dictionaries. Deprecated: | |
438 Use fetchmany() instead. Will be removed in 1.3.""" | |
439 from warnings import warn | |
440 warn("fetchmanyDict() is non-standard and will be removed in 1.3", | |
441 DeprecationWarning, 2) | |
442 return self.fetchmany(size) | |
443 | |
444 def fetchallDict(self): | |
445 """Fetch all available rows as a list of dictionaries. Deprecated: | |
446 Use fetchall() instead. Will be removed in 1.3.""" | |
447 from warnings import warn | |
448 warn("fetchallDict() is non-standard and will be removed in 1.3", | |
449 DeprecationWarning, 2) | |
450 return self.fetchall() | |
451 | |
452 | |
453 class CursorOldDictRowsMixIn(CursorDictRowsMixIn): | |
454 | |
455 """This is a MixIn class that returns rows as dictionaries with | |
456 the same key convention as the old Mysqldb (MySQLmodule). Don't | |
457 use this.""" | |
458 | |
459 _fetch_type = 2 | |
460 | |
461 | |
462 class Cursor(CursorStoreResultMixIn, CursorTupleRowsMixIn, | |
463 BaseCursor): | |
464 | |
465 """This is the standard Cursor class that returns rows as tuples | |
466 and stores the result set in the client.""" | |
467 | |
468 | |
469 class DictCursor(CursorStoreResultMixIn, CursorDictRowsMixIn, | |
470 BaseCursor): | |
471 | |
472 """This is a Cursor class that returns rows as dictionaries and | |
473 stores the result set in the client.""" | |
474 | |
475 | |
476 class SSCursor(CursorUseResultMixIn, CursorTupleRowsMixIn, | |
477 BaseCursor): | |
478 | |
479 """This is a Cursor class that returns rows as tuples and stores | |
480 the result set in the server.""" | |
481 | |
482 | |
483 class SSDictCursor(CursorUseResultMixIn, CursorDictRowsMixIn, | |
484 BaseCursor): | |
485 | |
486 """This is a Cursor class that returns rows as dictionaries and | |
487 stores the result set in the server.""" | |
488 | |
489 |