PageRenderTime 27ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/MySQLdb-0.9.2c3/MySQLdb/MySQLdb/cursors.py

https://github.com/twleung/mac-mysql-python
Python | 454 lines | 378 code | 23 blank | 53 comment | 20 complexity | 39be3ef6405afc37197206b1eadf1940 MD5 | raw file
  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. insert_values = re.compile(r'\svalues\s*(\(.+\))', re.IGNORECASE)
  7. from _mysql_exceptions import Warning, Error, InterfaceError, DataError, \
  8. DatabaseError, OperationalError, IntegrityError, InternalError, \
  9. NotSupportedError, ProgrammingError
  10. class BaseCursor:
  11. """A base for Cursor classes. Useful attributes:
  12. description -- DB API 7-tuple describing columns in last query
  13. arraysize -- default number of rows fetchmany() will fetch
  14. See the MySQL docs for more information."""
  15. from _mysql_exceptions import MySQLError, Warning, Error, InterfaceError, \
  16. DatabaseError, DataError, OperationalError, IntegrityError, \
  17. InternalError, ProgrammingError, NotSupportedError
  18. def __init__(self, connection):
  19. self.connection = connection
  20. self.description = None
  21. self.rowcount = -1
  22. self.arraysize = 100
  23. self._executed = None
  24. self.lastrowid = None
  25. self.messages = []
  26. self.errorhandler = connection.errorhandler
  27. self._result = None
  28. def __del__(self):
  29. self.close()
  30. def close(self):
  31. """Close the cursor. No further queries will be possible."""
  32. if not self.connection: return
  33. del self.messages[:]
  34. self.nextset()
  35. self.connection = None
  36. self.errorhandler = None
  37. self._result = None
  38. def _check_executed(self):
  39. if not self._executed:
  40. self.errorhandler(self, ProgrammingError, "execute() first")
  41. def nextset(self):
  42. """Advance to the next result set.
  43. Returns None if there are no more result sets.
  44. Note that MySQL does not support multiple result sets at this
  45. time.
  46. """
  47. del self.messages[:]
  48. if self._executed:
  49. self.fetchall()
  50. return None
  51. def setinputsizes(self, *args):
  52. """Does nothing, required by DB API."""
  53. def setoutputsizes(self, *args):
  54. """Does nothing, required by DB API."""
  55. def _get_db(self):
  56. if not self.connection:
  57. self.errorhandler(self, ProgrammingError, "cursor closed")
  58. return self.connection
  59. def execute(self, query, args=()):
  60. """Execute a query.
  61. query -- string, query to execute on server
  62. args -- optional sequence or mapping, parameters to use with query.
  63. Note: If args is a sequence, then %s must be used as the
  64. parameter placeholder in the query. If a mapping is used,
  65. %(key)s must be used as the placeholder.
  66. Returns long integer rows affected, if any
  67. """
  68. del self.messages[:]
  69. return self._execute(query, args)
  70. def _execute(self, query, args):
  71. from types import ListType, TupleType
  72. from sys import exc_info
  73. try:
  74. r = self._query(query % self.connection.literal(args))
  75. except TypeError, m:
  76. if m.args[0] in ("not enough arguments for format string",
  77. "not all arguments converted"):
  78. self.errorhandler(self, ProgrammingError, m.args[0])
  79. else:
  80. self.errorhandler(self, TypeError, m)
  81. except:
  82. exc, value, tb = exc_info()
  83. del tb
  84. self.errorhandler(self, exc, value)
  85. self._executed = query
  86. return r
  87. def executemany(self, query, args):
  88. """Execute a multi-row query.
  89. query -- string, query to execute on server
  90. args
  91. Sequence of sequences or mappings, parameters to use with
  92. query.
  93. Returns long integer rows affected, if any.
  94. This method improves performance on multiple-row INSERT and
  95. REPLACE. Otherwise it is equivalent to looping over args with
  96. execute().
  97. """
  98. from string import join
  99. from sys import exc_info
  100. del self.messages[:]
  101. if not args: return
  102. m = insert_values.search(query)
  103. if not m:
  104. r = 0
  105. for a in args:
  106. r = r + self._execute(query, a)
  107. return r
  108. p = m.start(1)
  109. qv = query[p:]
  110. qargs = self.connection.literal(args)
  111. try:
  112. q = [ query % qargs[0] ]
  113. for a in qargs[1:]: q.append( qv % a )
  114. except TypeError, msg:
  115. if msg.args[0] in ("not enough arguments for format string",
  116. "not all arguments converted"):
  117. self.errorhandler(self, ProgrammingError, msg.args[0])
  118. else:
  119. self.errorhandler(self, TypeError, msg)
  120. except:
  121. exc, value, tb = exc_info()
  122. del tb
  123. self.errorhandler(self, exc, value)
  124. r = self._query(join(q,',\n'))
  125. self._executed = query
  126. return r
  127. def __do_query(self, q):
  128. from string import split, atoi
  129. db = self._get_db()
  130. db.query(q)
  131. self._result = self._get_result()
  132. self.rowcount = db.affected_rows()
  133. self.rownumber = 0
  134. self.description = self._result and self._result.describe() or None
  135. self.lastrowid = db.insert_id()
  136. self._check_for_warnings()
  137. return self.rowcount
  138. def _check_for_warnings(self): pass
  139. _query = __do_query
  140. def info(self):
  141. """Return some information about the last query (db.info())
  142. DEPRECATED: Use messages attribute"""
  143. self._check_executed()
  144. if self.messages:
  145. return self.messages[-1]
  146. else:
  147. return ''
  148. def insert_id(self):
  149. """Return the last inserted ID on an AUTO_INCREMENT columns.
  150. DEPRECATED: use lastrowid attribute"""
  151. self._check_executed()
  152. return self.lastrowid
  153. def _fetch_row(self, size=1):
  154. if not self._result:
  155. return ()
  156. return self._result.fetch_row(size, self._fetch_type)
  157. def __iter__(self):
  158. return iter(self.fetchone, None)
  159. Warning = Warning
  160. Error = Error
  161. InterfaceError = InterfaceError
  162. DatabaseError = DatabaseError
  163. DataError = DataError
  164. OperationalError = OperationalError
  165. IntegrityError = IntegrityError
  166. InternalError = InternalError
  167. ProgrammingError = ProgrammingError
  168. NotSupportedError = NotSupportedError
  169. class CursorWarningMixIn:
  170. """This is a MixIn class that provides the capability of raising
  171. the Warning exception when something went slightly wrong with your
  172. query."""
  173. def _check_for_warnings(self):
  174. from string import atoi, split
  175. info = self._get_db().info()
  176. if info is None:
  177. return
  178. warnings = atoi(split(info)[-1])
  179. if warnings:
  180. raise Warning, info
  181. class CursorStoreResultMixIn:
  182. """This is a MixIn class which causes the entire result set to be
  183. stored on the client side, i.e. it uses mysql_store_result(). If the
  184. result set can be very large, consider adding a LIMIT clause to your
  185. query, or using CursorUseResultMixIn instead."""
  186. def _get_result(self): return self._get_db().store_result()
  187. def close(self):
  188. """Close the cursor. Further queries will not be possible."""
  189. self._rows = ()
  190. BaseCursor.close(self)
  191. def _query(self, q):
  192. rowcount = self._BaseCursor__do_query(q)
  193. self._rows = self._fetch_row(0)
  194. self._result = None
  195. return rowcount
  196. def fetchone(self):
  197. """Fetches a single row from the cursor. None indicates that
  198. no more rows are available."""
  199. self._check_executed()
  200. if self.rownumber >= len(self._rows): return None
  201. result = self._rows[self.rownumber]
  202. self.rownumber = self.rownumber+1
  203. return result
  204. def fetchmany(self, size=None):
  205. """Fetch up to size rows from the cursor. Result set may be smaller
  206. than size. If size is not defined, cursor.arraysize is used."""
  207. self._check_executed()
  208. end = self.rownumber + (size or self.arraysize)
  209. result = self._rows[self.rownumber:end]
  210. self.rownumber = min(end, len(self._rows))
  211. return result
  212. def fetchall(self):
  213. """Fetchs all available rows from the cursor."""
  214. self._check_executed()
  215. result = self.rownumber and self._rows[self.rownumber:] or self._rows
  216. self.rownumber = len(self._rows)
  217. return result
  218. def seek(self, row, whence=0):
  219. """seek to a given row of the result set analogously to file.seek().
  220. This is non-standard extension. DEPRECATED: Use scroll method"""
  221. self._check_executed()
  222. if whence == 0:
  223. self.rownumber = row
  224. elif whence == 1:
  225. self.rownumber = self.rownumber + row
  226. elif whence == 2:
  227. self.rownumber = len(self._rows) + row
  228. def tell(self):
  229. """Return the current position in the result set analogously to
  230. file.tell(). This is a non-standard extension. DEPRECATED:
  231. use rownumber attribute"""
  232. self._check_executed()
  233. return self.rownumber
  234. def scroll(self, value, mode='relative'):
  235. """Scroll the cursor in the result set to a new position according
  236. to mode.
  237. If mode is 'relative' (default), value is taken as offset to
  238. the current position in the result set, if set to 'absolute',
  239. value states an absolute target position."""
  240. self._check_executed()
  241. if mode == 'relative':
  242. r = self.rownumber + value
  243. elif mode == 'absolute':
  244. r = value
  245. else:
  246. self.errorhandler(self, ProgrammingError,
  247. "unknown scroll mode %s" % `mode`)
  248. if r < 0 or r >= len(self._rows):
  249. self.errorhandler(self, IndexError, "out of range")
  250. self.rownumber = r
  251. def __iter__(self):
  252. self._check_executed()
  253. result = self.rownumber and self._rows[self.rownumber:] or self._rows
  254. return iter(result)
  255. class CursorUseResultMixIn:
  256. """This is a MixIn class which causes the result set to be stored
  257. in the server and sent row-by-row to client side, i.e. it uses
  258. mysql_use_result(). You MUST retrieve the entire result set and
  259. close() the cursor before additional queries can be peformed on
  260. the connection."""
  261. def close(self):
  262. """Close the cursor. No further queries can be executed."""
  263. del self.messages[:]
  264. self.nextset()
  265. self._result = None
  266. BaseCursor.close(self)
  267. def _get_result(self): return self._get_db().use_result()
  268. def fetchone(self):
  269. """Fetches a single row from the cursor."""
  270. self._check_executed()
  271. r = self._fetch_row(1)
  272. if not r: return None
  273. self.rownumber = self.rownumber + 1
  274. return r[0]
  275. def fetchmany(self, size=None):
  276. """Fetch up to size rows from the cursor. Result set may be smaller
  277. than size. If size is not defined, cursor.arraysize is used."""
  278. self._check_executed()
  279. r = self._fetch_row(size or self.arraysize)
  280. self.rownumber = self.rownumber + len(r)
  281. return r
  282. def fetchall(self):
  283. """Fetchs all available rows from the cursor."""
  284. self._check_executed()
  285. r = self._fetch_row(0)
  286. self.rownumber = self.rownumber + len(r)
  287. return r
  288. class CursorTupleRowsMixIn:
  289. """This is a MixIn class that causes all rows to be returned as tuples,
  290. which is the standard form required by DB API."""
  291. _fetch_type = 0
  292. class CursorDictRowsMixIn:
  293. """This is a MixIn class that causes all rows to be returned as
  294. dictionaries. This is a non-standard feature."""
  295. _fetch_type = 1
  296. def fetchoneDict(self):
  297. """Fetch a single row as a dictionary. Deprecated:
  298. Use fetchone() instead."""
  299. return self.fetchone()
  300. def fetchmanyDict(self, size=None):
  301. """Fetch several rows as a list of dictionaries. Deprecated:
  302. Use fetchmany() instead."""
  303. return self.fetchmany(size)
  304. def fetchallDict(self):
  305. """Fetch all available rows as a list of dictionaries. Deprecated:
  306. Use fetchall() instead."""
  307. return self.fetchall()
  308. class CursorOldDictRowsMixIn(CursorDictRowsMixIn):
  309. """This is a MixIn class that returns rows as dictionaries with
  310. the same key convention as the old Mysqldb (MySQLmodule). Don't
  311. use this."""
  312. _fetch_type = 2
  313. class CursorNW(CursorStoreResultMixIn, CursorTupleRowsMixIn,
  314. BaseCursor):
  315. """This is a basic Cursor class that returns rows as tuples and
  316. stores the result set in the client. Warnings are not raised."""
  317. class Cursor(CursorWarningMixIn, CursorNW):
  318. """This is the standard Cursor class that returns rows as tuples
  319. and stores the result set in the client. Warnings are raised as
  320. necessary."""
  321. class DictCursorNW(CursorStoreResultMixIn, CursorDictRowsMixIn,
  322. BaseCursor):
  323. """This is a Cursor class that returns rows as dictionaries and
  324. stores the result set in the client. Warnings are not raised."""
  325. class DictCursor(CursorWarningMixIn, DictCursorNW):
  326. """This is a Cursor class that returns rows as dictionaries and
  327. stores the result set in the client. Warnings are raised as
  328. necessary."""
  329. class SSCursorNW(CursorUseResultMixIn, CursorTupleRowsMixIn,
  330. BaseCursor):
  331. """This is a basic Cursor class that returns rows as tuples and
  332. stores the result set in the server. Warnings are not raised."""
  333. class SSCursor(CursorWarningMixIn, SSCursorNW):
  334. """This is a Cursor class that returns rows as tuples and stores
  335. the result set in the server. Warnings are raised as necessary."""
  336. class SSDictCursorNW(CursorUseResultMixIn, CursorDictRowsMixIn,
  337. BaseCursor):
  338. """This is a Cursor class that returns rows as dictionaries and
  339. stores the result set in the server. Warnings are not raised."""
  340. class SSDictCursor(CursorWarningMixIn, SSDictCursorNW):
  341. """This is a Cursor class that returns rows as dictionaries and
  342. stores the result set in the server. Warnings are raised as
  343. necessary."""