You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Enso-Bot/venv/Lib/site-packages/mysql/connector/cursor_cext.py

1011 lines
32 KiB
Python

# Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2.0, as
# published by the Free Software Foundation.
#
# This program is also distributed with certain software (including
# but not limited to OpenSSL) that is licensed under separate terms,
# as designated in a particular file or component or in included license
# documentation. The authors of MySQL hereby grant you an
# additional permission to link the program and your derivative works
# with the separately licensed software that they have included with
# MySQL.
#
# Without limiting anything contained in the foregoing, this file,
# which is part of MySQL Connector/Python, is also subject to the
# Universal FOSS Exception, version 1.0, a copy of which can be found at
# http://oss.oracle.com/licenses/universal-foss-exception.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License, version 2.0, for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""Cursor classes using the C Extension
"""
from collections import namedtuple
import re
import weakref
from _mysql_connector import MySQLInterfaceError # pylint: disable=F0401,E0611
from .abstracts import (MySQLConnectionAbstract, MySQLCursorAbstract,
NAMED_TUPLE_CACHE)
from .catch23 import PY2, isunicode
from . import errors
from .errorcode import CR_NO_RESULT_SET
from .cursor import (
RE_PY_PARAM, RE_SQL_INSERT_STMT,
RE_SQL_ON_DUPLICATE, RE_SQL_COMMENT, RE_SQL_INSERT_VALUES,
RE_SQL_SPLIT_STMTS, RE_SQL_FIND_PARAM
)
class _ParamSubstitutor(object):
"""
Substitutes parameters into SQL statement.
"""
def __init__(self, params):
self.params = params
self.index = 0
def __call__(self, matchobj):
index = self.index
self.index += 1
try:
return self.params[index]
except IndexError:
raise errors.ProgrammingError(
"Not enough parameters for the SQL statement")
@property
def remaining(self):
"""Returns number of parameters remaining to be substituted"""
return len(self.params) - self.index
class CMySQLCursor(MySQLCursorAbstract):
"""Default cursor for interacting with MySQL using C Extension"""
_raw = False
_buffered = False
_raw_as_string = False
def __init__(self, connection):
"""Initialize"""
MySQLCursorAbstract.__init__(self)
self._insert_id = 0
self._warning_count = 0
self._warnings = None
self._affected_rows = -1
self._rowcount = -1
self._nextrow = (None, None)
self._executed = None
self._executed_list = []
self._stored_results = []
if not isinstance(connection, MySQLConnectionAbstract):
raise errors.InterfaceError(errno=2048)
self._cnx = weakref.proxy(connection)
def reset(self, free=True):
"""Reset the cursor
When free is True (default) the result will be freed.
"""
self._rowcount = -1
self._nextrow = None
self._affected_rows = -1
self._insert_id = 0
self._warning_count = 0
self._warnings = None
self._warnings = None
self._warning_count = 0
self._description = None
self._executed = None
self._executed_list = []
if free and self._cnx:
self._cnx.free_result()
super(CMySQLCursor, self).reset()
def _fetch_warnings(self):
"""Fetch warnings
Fetch warnings doing a SHOW WARNINGS. Can be called after getting
the result.
Returns a result set or None when there were no warnings.
Raises errors.Error (or subclass) on errors.
Returns list of tuples or None.
"""
warnings = []
try:
# force freeing result
self._cnx.consume_results()
_ = self._cnx.cmd_query("SHOW WARNINGS")
warnings = self._cnx.get_rows()[0]
self._cnx.consume_results()
except MySQLInterfaceError as exc:
raise errors.get_mysql_exception(msg=exc.msg, errno=exc.errno,
sqlstate=exc.sqlstate)
except Exception as err:
raise errors.InterfaceError(
"Failed getting warnings; {0}".format(str(err)))
if warnings:
return warnings
return None
def _handle_warnings(self):
"""Handle possible warnings after all results are consumed"""
if self._cnx.get_warnings is True and self._warning_count:
self._warnings = self._fetch_warnings()
def _handle_result(self, result):
"""Handles the result after statement execution"""
if 'columns' in result:
self._description = result['columns']
self._rowcount = 0
self._handle_resultset()
else:
self._insert_id = result['insert_id']
self._warning_count = result['warning_count']
self._affected_rows = result['affected_rows']
self._rowcount = -1
self._handle_warnings()
if self._cnx.raise_on_warnings is True and self._warnings:
raise errors.get_mysql_exception(*self._warnings[0][1:3])
def _handle_resultset(self):
"""Handle a result set"""
pass
def _handle_eof(self):
"""Handle end of reading the result
Raises an errors.Error on errors.
"""
self._warning_count = self._cnx.warning_count
self._handle_warnings()
if self._cnx.raise_on_warnings is True and self._warnings:
raise errors.get_mysql_exception(*self._warnings[0][1:3])
if not self._cnx.more_results:
self._cnx.free_result()
def _execute_iter(self):
"""Generator returns MySQLCursor objects for multiple statements
Deprecated: use nextset() method directly.
This method is only used when multiple statements are executed
by the execute() method. It uses zip() to make an iterator from the
given query_iter (result of MySQLConnection.cmd_query_iter()) and
the list of statements that were executed.
"""
executed_list = RE_SQL_SPLIT_STMTS.split(self._executed)
i = 0
self._executed = executed_list[i]
yield self
while True:
try:
if not self.nextset():
raise StopIteration
except errors.InterfaceError as exc:
# Result without result set
if exc.errno != CR_NO_RESULT_SET:
raise
except StopIteration:
return
i += 1
try:
self._executed = executed_list[i].strip()
except IndexError:
self._executed = executed_list[0]
yield self
return
def execute(self, operation, params=(), multi=False):
"""Execute given statement using given parameters
Deprecated: The multi argument is not needed and nextset() should
be used to handle multiple result sets.
"""
if not operation:
return None
if not self._cnx:
raise errors.ProgrammingError("Cursor is not connected")
self._cnx.handle_unread_result()
stmt = ''
self.reset()
try:
if isunicode(operation):
stmt = operation.encode(self._cnx.python_charset)
else:
stmt = operation
except (UnicodeDecodeError, UnicodeEncodeError) as err:
raise errors.ProgrammingError(str(err))
if params:
prepared = self._cnx.prepare_for_mysql(params)
if isinstance(prepared, dict):
for key, value in prepared.items():
if PY2:
stmt = stmt.replace("%({0})s".format(key), value)
else:
stmt = stmt.replace("%({0})s".format(key).encode(),
value)
elif isinstance(prepared, (list, tuple)):
psub = _ParamSubstitutor(prepared)
stmt = RE_PY_PARAM.sub(psub, stmt)
if psub.remaining != 0:
raise errors.ProgrammingError(
"Not all parameters were used in the SQL statement")
try:
result = self._cnx.cmd_query(stmt, raw=self._raw,
buffered=self._buffered,
raw_as_string=self._raw_as_string)
except MySQLInterfaceError as exc:
raise errors.get_mysql_exception(msg=exc.msg, errno=exc.errno,
sqlstate=exc.sqlstate)
self._executed = stmt
self._handle_result(result)
if multi:
return self._execute_iter()
return None
def _batch_insert(self, operation, seq_params):
"""Implements multi row insert"""
def remove_comments(match):
"""Remove comments from INSERT statements.
This function is used while removing comments from INSERT
statements. If the matched string is a comment not enclosed
by quotes, it returns an empty string, else the string itself.
"""
if match.group(1):
return ""
return match.group(2)
tmp = re.sub(RE_SQL_ON_DUPLICATE, '',
re.sub(RE_SQL_COMMENT, remove_comments, operation))
matches = re.search(RE_SQL_INSERT_VALUES, tmp)
if not matches:
raise errors.InterfaceError(
"Failed rewriting statement for multi-row INSERT. "
"Check SQL syntax."
)
fmt = matches.group(1).encode(self._cnx.python_charset)
values = []
try:
stmt = operation.encode(self._cnx.python_charset)
for params in seq_params:
tmp = fmt
prepared = self._cnx.prepare_for_mysql(params)
if isinstance(prepared, dict):
for key, value in prepared.items():
tmp = tmp.replace("%({0})s".format(key).encode(), value)
elif isinstance(prepared, (list, tuple)):
psub = _ParamSubstitutor(prepared)
tmp = RE_PY_PARAM.sub(psub, tmp)
if psub.remaining != 0:
raise errors.ProgrammingError(
"Not all parameters were used in the SQL statement")
values.append(tmp)
if fmt in stmt:
stmt = stmt.replace(fmt, b','.join(values), 1)
self._executed = stmt
return stmt
return None
except (UnicodeDecodeError, UnicodeEncodeError) as err:
raise errors.ProgrammingError(str(err))
except Exception as err:
raise errors.InterfaceError(
"Failed executing the operation; %s" % err)
def executemany(self, operation, seq_params):
"""Execute the given operation multiple times"""
if not operation or not seq_params:
return None
if not self._cnx:
raise errors.ProgrammingError("Cursor is not connected")
self._cnx.handle_unread_result()
if not isinstance(seq_params, (list, tuple)):
raise errors.ProgrammingError(
"Parameters for query must be list or tuple.")
# Optimize INSERTs by batching them
if re.match(RE_SQL_INSERT_STMT, operation):
if not seq_params:
self._rowcount = 0
return None
stmt = self._batch_insert(operation, seq_params)
if stmt is not None:
return self.execute(stmt)
rowcnt = 0
try:
for params in seq_params:
self.execute(operation, params)
try:
while True:
if self._description:
rowcnt += len(self._cnx.get_rows()[0])
else:
rowcnt += self._affected_rows
if not self.nextset():
break
except StopIteration:
# No more results
pass
except (ValueError, TypeError) as err:
raise errors.ProgrammingError(
"Failed executing the operation; {0}".format(err))
self._rowcount = rowcnt
return None
@property
def description(self):
"""Returns description of columns in a result"""
return self._description
@property
def rowcount(self):
"""Returns the number of rows produced or affected"""
if self._rowcount == -1:
return self._affected_rows
return self._rowcount
@property
def lastrowid(self):
"""Returns the value generated for an AUTO_INCREMENT column"""
return self._insert_id
def close(self):
"""Close the cursor
The result will be freed.
"""
if not self._cnx:
return False
self._cnx.handle_unread_result()
self._warnings = None
self._cnx = None
return True
def callproc(self, procname, args=()):
"""Calls a stored procedure with the given arguments"""
if not procname or not isinstance(procname, str):
raise ValueError("procname must be a string")
if not isinstance(args, (tuple, list)):
raise ValueError("args must be a sequence")
argfmt = "@_{name}_arg{index}"
self._stored_results = []
try:
argnames = []
argtypes = []
if args:
for idx, arg in enumerate(args):
argname = argfmt.format(name=procname, index=idx + 1)
argnames.append(argname)
if isinstance(arg, tuple):
argtypes.append(" CAST({0} AS {1})".format(argname,
arg[1]))
self.execute("SET {0}=%s".format(argname), (arg[0],))
else:
argtypes.append(argname)
self.execute("SET {0}=%s".format(argname), (arg,))
call = "CALL {0}({1})".format(procname, ','.join(argnames))
result = self._cnx.cmd_query(call, raw=self._raw,
raw_as_string=self._raw_as_string)
results = []
while self._cnx.result_set_available:
result = self._cnx.fetch_eof_columns()
# pylint: disable=W0212
if self._raw:
cur = CMySQLCursorBufferedRaw(self._cnx._get_self())
else:
cur = CMySQLCursorBuffered(self._cnx._get_self())
cur._executed = "(a result of {0})".format(call)
cur._handle_result(result)
# pylint: enable=W0212
results.append(cur)
self._cnx.next_result()
self._stored_results = results
self._handle_eof()
if argnames:
self.reset()
select = "SELECT {0}".format(','.join(argtypes))
self.execute(select)
return self.fetchone()
return tuple()
except errors.Error:
raise
except Exception as err:
raise errors.InterfaceError(
"Failed calling stored routine; {0}".format(err))
def nextset(self):
"""Skip to the next available result set"""
if not self._cnx.next_result():
self.reset(free=True)
return None
self.reset(free=False)
if not self._cnx.result_set_available:
eof = self._cnx.fetch_eof_status()
self._handle_result(eof)
raise errors.InterfaceError(errno=CR_NO_RESULT_SET)
self._handle_result(self._cnx.fetch_eof_columns())
return True
def fetchall(self):
"""Returns all rows of a query result set
Returns a list of tuples.
"""
if not self._cnx.unread_result:
raise errors.InterfaceError("No result set to fetch from.")
rows = self._cnx.get_rows()
if self._nextrow and self._nextrow[0]:
rows[0].insert(0, self._nextrow[0])
if not rows[0]:
self._handle_eof()
return []
self._rowcount += len(rows[0])
self._handle_eof()
#self._cnx.handle_unread_result()
return rows[0]
def fetchmany(self, size=1):
"""Returns the next set of rows of a result set"""
if self._nextrow and self._nextrow[0]:
rows = [self._nextrow[0]]
size -= 1
else:
rows = []
if size and self._cnx.unread_result:
rows.extend(self._cnx.get_rows(size)[0])
if size:
if self._cnx.unread_result:
self._nextrow = self._cnx.get_row()
if self._nextrow and not self._nextrow[0] and \
not self._cnx.more_results:
self._cnx.free_result()
else:
self._nextrow = (None, None)
if not rows:
self._handle_eof()
return []
self._rowcount += len(rows)
return rows
def fetchone(self):
"""Returns next row of a query result set"""
row = self._nextrow
if not row and self._cnx.unread_result:
row = self._cnx.get_row()
if row and row[0]:
self._nextrow = self._cnx.get_row()
if not self._nextrow[0] and not self._cnx.more_results:
self._cnx.free_result()
else:
self._handle_eof()
return None
self._rowcount += 1
return row[0]
def __iter__(self):
"""Iteration over the result set
Iteration over the result set which calls self.fetchone()
and returns the next row.
"""
return iter(self.fetchone, None)
def stored_results(self):
"""Returns an iterator for stored results
This method returns an iterator over results which are stored when
callproc() is called. The iterator will provide MySQLCursorBuffered
instances.
Returns a iterator.
"""
for i in range(len(self._stored_results)):
yield self._stored_results[i]
self._stored_results = []
if PY2:
def next(self):
"""Used for iterating over the result set."""
return self.__next__()
def __next__(self):
"""Iteration over the result set
Used for iterating over the result set. Calls self.fetchone()
to get the next row.
Raises StopIteration when no more rows are available.
"""
try:
row = self.fetchone()
except errors.InterfaceError:
raise StopIteration
if not row:
raise StopIteration
return row
@property
def column_names(self):
"""Returns column names
This property returns the columns names as a tuple.
Returns a tuple.
"""
if not self.description:
return ()
return tuple([d[0] for d in self.description])
@property
def statement(self):
"""Returns the executed statement
This property returns the executed statement. When multiple
statements were executed, the current statement in the iterator
will be returned.
"""
try:
return self._executed.strip().decode('utf8')
except AttributeError:
return self._executed.strip()
@property
def with_rows(self):
"""Returns whether the cursor could have rows returned
This property returns True when column descriptions are available
and possibly also rows, which will need to be fetched.
Returns True or False.
"""
if self.description:
return True
return False
def __str__(self):
fmt = "{class_name}: {stmt}"
if self._executed:
try:
executed = self._executed.decode('utf-8')
except AttributeError:
executed = self._executed
if len(executed) > 40:
executed = executed[:40] + '..'
else:
executed = '(Nothing executed yet)'
return fmt.format(class_name=self.__class__.__name__, stmt=executed)
class CMySQLCursorBuffered(CMySQLCursor):
"""Cursor using C Extension buffering results"""
def __init__(self, connection):
"""Initialize"""
super(CMySQLCursorBuffered, self).__init__(connection)
self._rows = None
self._next_row = 0
def _handle_resultset(self):
"""Handle a result set"""
self._rows = self._cnx.get_rows()[0]
self._next_row = 0
self._rowcount = len(self._rows)
self._handle_eof()
def reset(self, free=True):
"""Reset the cursor to default"""
self._rows = None
self._next_row = 0
super(CMySQLCursorBuffered, self).reset(free=free)
def _fetch_row(self):
"""Returns the next row in the result set
Returns a tuple or None.
"""
row = None
try:
row = self._rows[self._next_row]
except IndexError:
return None
else:
self._next_row += 1
return row
def fetchall(self):
if self._rows is None:
raise errors.InterfaceError("No result set to fetch from.")
res = self._rows[self._next_row:]
self._next_row = len(self._rows)
return res
def fetchmany(self, size=1):
res = []
cnt = size or self.arraysize
while cnt > 0:
cnt -= 1
row = self._fetch_row()
if row:
res.append(row)
else:
break
return res
def fetchone(self):
return self._fetch_row()
class CMySQLCursorRaw(CMySQLCursor):
"""Cursor using C Extension return raw results"""
_raw = True
class CMySQLCursorBufferedRaw(CMySQLCursorBuffered):
"""Cursor using C Extension buffering raw results"""
_raw = True
class CMySQLCursorDict(CMySQLCursor):
"""Cursor using C Extension returning rows as dictionaries"""
_raw = False
def fetchone(self):
"""Returns all rows of a query result set
"""
row = super(CMySQLCursorDict, self).fetchone()
if row:
return dict(zip(self.column_names, row))
return None
def fetchmany(self, size=1):
"""Returns next set of rows as list of dictionaries"""
res = super(CMySQLCursorDict, self).fetchmany(size=size)
return [dict(zip(self.column_names, row)) for row in res]
def fetchall(self):
"""Returns all rows of a query result set as list of dictionaries"""
res = super(CMySQLCursorDict, self).fetchall()
return [dict(zip(self.column_names, row)) for row in res]
class CMySQLCursorBufferedDict(CMySQLCursorBuffered):
"""Cursor using C Extension buffering and returning rows as dictionaries"""
_raw = False
def _fetch_row(self):
row = super(CMySQLCursorBufferedDict, self)._fetch_row()
if row:
return dict(zip(self.column_names, row))
return None
def fetchall(self):
res = super(CMySQLCursorBufferedDict, self).fetchall()
return [dict(zip(self.column_names, row)) for row in res]
class CMySQLCursorNamedTuple(CMySQLCursor):
"""Cursor using C Extension returning rows as named tuples"""
def _handle_resultset(self):
"""Handle a result set"""
super(CMySQLCursorNamedTuple, self)._handle_resultset()
# pylint: disable=W0201
columns = tuple(self.column_names)
try:
self.named_tuple = NAMED_TUPLE_CACHE[columns]
except KeyError:
self.named_tuple = namedtuple('Row', columns)
NAMED_TUPLE_CACHE[columns] = self.named_tuple
# pylint: enable=W0201
def fetchone(self):
"""Returns all rows of a query result set
"""
row = super(CMySQLCursorNamedTuple, self).fetchone()
if row:
return self.named_tuple(*row)
return None
def fetchmany(self, size=1):
"""Returns next set of rows as list of named tuples"""
res = super(CMySQLCursorNamedTuple, self).fetchmany(size=size)
return [self.named_tuple(*res[0])]
def fetchall(self):
"""Returns all rows of a query result set as list of named tuples"""
res = super(CMySQLCursorNamedTuple, self).fetchall()
return [self.named_tuple(*row) for row in res]
class CMySQLCursorBufferedNamedTuple(CMySQLCursorBuffered):
"""Cursor using C Extension buffering and returning rows as named tuples"""
def _handle_resultset(self):
super(CMySQLCursorBufferedNamedTuple, self)._handle_resultset()
# pylint: disable=W0201
self.named_tuple = namedtuple('Row', self.column_names)
# pylint: enable=W0201
def _fetch_row(self):
row = super(CMySQLCursorBufferedNamedTuple, self)._fetch_row()
if row:
return self.named_tuple(*row)
return None
def fetchall(self):
res = super(CMySQLCursorBufferedNamedTuple, self).fetchall()
return [self.named_tuple(*row) for row in res]
class CMySQLCursorPrepared(CMySQLCursor):
"""Cursor using MySQL Prepared Statements"""
def __init__(self, connection):
super(CMySQLCursorPrepared, self).__init__(connection)
self._rows = None
self._rowcount = 0
self._next_row = 0
self._binary = True
self._stmt = None
def _handle_eof(self):
"""Handle EOF packet"""
self._nextrow = (None, None)
self._handle_warnings()
if self._cnx.raise_on_warnings is True and self._warnings:
raise errors.get_mysql_exception(
self._warnings[0][1], self._warnings[0][2])
def _fetch_row(self, raw=False):
"""Returns the next row in the result set
Returns a tuple or None.
"""
if not self._stmt or not self._stmt.have_result_set:
return None
row = None
if self._nextrow == (None, None):
(row, eof) = self._cnx.get_row(
binary=self._binary, columns=self.description, raw=raw,
prep_stmt=self._stmt)
else:
(row, eof) = self._nextrow
if row:
self._nextrow = self._cnx.get_row(
binary=self._binary, columns=self.description, raw=raw,
prep_stmt=self._stmt)
eof = self._nextrow[1]
if eof is not None:
self._warning_count = eof["warning_count"]
self._handle_eof()
if self._rowcount == -1:
self._rowcount = 1
else:
self._rowcount += 1
if eof:
self._warning_count = eof["warning_count"]
self._handle_eof()
return row
def callproc(self, procname, args=None):
"""Calls a stored procedue
Not supported with CMySQLCursorPrepared.
"""
raise errors.NotSupportedError()
def close(self):
"""Close the cursor
This method will try to deallocate the prepared statement and close
the cursor.
"""
if self._stmt:
self.reset()
self._cnx.cmd_stmt_close(self._stmt)
self._stmt = None
super(CMySQLCursorPrepared, self).close()
def reset(self, free=True):
"""Resets the prepared statement."""
if self._stmt:
self._cnx.cmd_stmt_reset(self._stmt)
super(CMySQLCursorPrepared, self).reset(free=free)
def execute(self, operation, params=(), multi=False): # multi is unused
"""Prepare and execute a MySQL Prepared Statement
This method will preare the given operation and execute it using
the given parameters.
If the cursor instance already had a prepared statement, it is
first closed.
"""
if not operation:
return
if not self._cnx:
raise errors.ProgrammingError("Cursor is not connected")
self._cnx.handle_unread_result(prepared=True)
if operation is not self._executed:
if self._stmt:
self._cnx.cmd_stmt_close(self._stmt)
self._executed = operation
try:
if not isinstance(operation, bytes):
charset = self._cnx.charset
if charset == "utf8mb4":
charset = "utf8"
operation = operation.encode(charset)
except (UnicodeDecodeError, UnicodeEncodeError) as err:
raise errors.ProgrammingError(str(err))
# need to convert %s to ? before sending it to MySQL
if b"%s" in operation:
operation = re.sub(RE_SQL_FIND_PARAM, b"?", operation)
try:
self._stmt = self._cnx.cmd_stmt_prepare(operation)
except (errors.Error, errors.ProgrammingError):
self._stmt = None
raise
self._cnx.cmd_stmt_reset(self._stmt)
if params and self._stmt.param_count != len(params):
raise errors.ProgrammingError(
errno=1210,
msg="Incorrect number of arguments executing prepared "
"statement")
res = self._cnx.cmd_stmt_execute(self._stmt, *params)
if res:
self._handle_result(res)
def executemany(self, operation, seq_params):
"""Prepare and execute a MySQL Prepared Statement many times
This method will prepare the given operation and execute with each
tuple found the list seq_params.
If the cursor instance already had a prepared statement, it is
first closed.
"""
rowcnt = 0
try:
for params in seq_params:
self.execute(operation, params)
if self.with_rows:
self.fetchall()
rowcnt += self._rowcount
except (ValueError, TypeError) as err:
raise errors.InterfaceError(
"Failed executing the operation; {error}".format(error=err))
except:
# Raise whatever execute() raises
raise
self._rowcount = rowcnt
def fetchone(self):
"""Returns next row of a query result set
Returns a tuple or None.
"""
return self._fetch_row() or None
def fetchmany(self, size=None):
"""Returns the next set of rows of a result set
Returns a list of tuples.
"""
res = []
cnt = size or self.arraysize
while cnt > 0 and self._stmt.have_result_set:
cnt -= 1
row = self._fetch_row()
if row:
res.append(row)
return res
def fetchall(self):
"""Returns all rows of a query result set
Returns a list of tuples.
"""
if not self._stmt.have_result_set:
raise errors.InterfaceError("No result set to fetch from.")
rows = self._cnx.get_rows(prep_stmt=self._stmt)
if self._nextrow and self._nextrow[0]:
rows[0].insert(0, self._nextrow[0])
if not rows[0]:
self._handle_eof()
return []
self._rowcount += len(rows[0])
self._handle_eof()
return rows[0]