cursors.py 18 KB

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