Source code for etcdb.cursor

from pprint import pprint

from pyetcd import EtcdKeyNotFound, EtcdRaftInternal

from etcdb import ProgrammingError, InternalError
from etcdb.execute.ddl.create import create_database, create_table
from etcdb.execute.ddl.drop import drop_database, drop_table
from etcdb.execute.dml.delete import execute_delete
from etcdb.execute.dml.insert import insert
from etcdb.execute.dml.select import execute_select
from etcdb.execute.dml.show import show_databases, show_tables, desc_table
from etcdb.execute.dml.update import execute_update
from etcdb.execute.dml.use import use_database
from etcdb.execute.dml.wait import execute_wait
from etcdb.log import LOG
from etcdb.sqlparser.parser import SQLParser, SQLParserError


[docs]class ColInfo(object): def __init__(self, name='', width=None): self.name = name if width and width > len(name): self.width = width else: self.width = len(self.name) def __repr__(self): return "ColInfo: name={name}, width={width}".format(name=self.name, width=self.width)
[docs]class Cursor(object): """These objects represent a database cursor, which is used to manage the context of a fetch operation. Cursors created from the same connection are not isolated, i.e. , any changes done to the database by a cursor are immediately visible by the other cursors. Cursors created from different connections can or can not be isolated, depending on how the transaction support is implemented (see also the connection's .rollback () and .commit () methods). """ description = None """This read-only attribute is a sequence of 7-item sequences. Each of these sequences contains information describing one result column: name type_code display_size internal_size precision scale null_ok The first two items ( name and type_code ) are mandatory, the other five are optional and are set to None if no meaningful values can be provided. """ @property def rowcount(self): return self._rowcount """This read-only attribute specifies the number of rows that the last .execute*() produced (for DQL statements like SELECT ) or affected (for DML statements like UPDATE or INSERT ).""" arraysize = 1 """This read/write attribute specifies the number of rows to fetch at a time with .fetchmany(). It defaults to 1 meaning to fetch a single row at a time. """ connection = None """Etcd connection object""" # ColInfo = ColInfo _result_set = None def __init__(self, connection): self.connection = connection self._sql_parser = SQLParser() self._db = connection.db self._timeout = connection.timeout self.lastrowid = None self._rowcount = 0 @property def n_cols(self): return self._result_set.n_cols @property def n_rows(self): try: return self._result_set.n_rows except AttributeError: return 0 @property def result_set(self): return self._result_set
[docs] @staticmethod def close(): """Close the cursor now (rather than whenever __del__ is called). """ pass
[docs] @staticmethod def morgify(query, args): """Prepare query string that will be sent to parser :param query: Query text :param args: Tuple with query arguments :return: Query text :rtype: str """ if args: query %= tuple(["'%s'" % a for a in args]) return query
[docs] def execute(self, query, args=None): """Prepare and execute a database operation (query or command). :param query: Query text. :type query: str :param args: Optional query arguments. :type args: tuple :raise ProgrammingError: if query can't be parsed. :raise InternalError: If etcd is not ready to serve request """ query = self.morgify(query, args) LOG.debug('Executing: %s', query) try: tree = self._sql_parser.parse(query) except SQLParserError as err: raise ProgrammingError('Error while parsing query: %s: %s' % (query, err)) if not self._db: self._db = tree.db self._result_set = None self._rowcount = 0 try: if tree.query_type == "SHOW_DATABASES": self._result_set = show_databases(self.connection.client) elif tree.query_type == "CREATE_DATABASE": create_database(self.connection.client, tree) elif tree.query_type == "DROP_DATABASE": drop_database(self.connection.client, tree) elif tree.query_type == "USE_DATABASE": self._db = use_database(self.connection.client, tree) elif tree.query_type == "CREATE_TABLE": create_table(self.connection.client, tree, db=self._db) elif tree.query_type == "DROP_TABLE": drop_table(self.connection.client, tree, db=self._db) elif tree.query_type == "SHOW_TABLES": self._result_set = show_tables(self.connection.client, tree, db=self._db) elif tree.query_type == "DESC_TABLE": self._result_set = desc_table(self.connection.client, tree, db=self._db) elif tree.query_type == "INSERT": self._rowcount = insert(self.connection.client, tree, db=self._db) elif tree.query_type == 'SELECT': self._result_set = execute_select(self.connection.client, tree, db=self._db) elif tree.query_type == 'WAIT': self._result_set = execute_wait(self.connection.client, tree, db=self._db) elif tree.query_type == "UPDATE": self._rowcount = execute_update(self.connection.client, tree, db=self._db) elif tree.query_type == "DELETE": self._rowcount = execute_delete(self.connection.client, tree, db=self._db) except EtcdRaftInternal as err: raise InternalError(err) if self._result_set is not None: self._rowcount = self._result_set.n_rows
[docs] @staticmethod def executemany(operation, **kwargs): """Prepare a database operation (query or command) and then execute it against all parameter sequences or mappings found in the sequence seq_of_parameters . """ pass
[docs] def fetchone(self): """Fetch the next row of a query result set, returning a single sequence, or None when no more data is available.""" try: return tuple(self._result_set.next()) except (StopIteration, AttributeError): return None
[docs] def fetchmany(self, n): """Fetch the next set of rows of a query result, returning a sequence of sequences (e.g. a list of tuples). An empty sequence is returned when no more rows are available. """ rows = () for i in xrange(n): row = self.fetchone() if row: rows += (row,) return rows
[docs] def fetchall(self): """Fetch all (remaining) rows of a query result, returning them as a sequence of sequences (e.g. a list of tuples). Note that the cursor's arraysize attribute can affect the performance of this operation.""" result = () if len(self._result_set) > 0: for row in self._result_set: result += (tuple(row), ) self._result_set.rows = [] return result
[docs] @staticmethod def setinputsizes(sizes): """This can be used before a call to .execute*() to predefine memory areas for the operation's parameters. """ pass
[docs] @staticmethod def setoutputsize(size): """Set a column buffer size for fetches of large columns (e.g. LONG s, BLOB s, etc.). The column is specified as an index into the result sequence. Not specifying the column will set the default size for all large columns in the cursor. """ pass
def _get_next_auto_inc(self, db, tbl): key = '/{db}/{tbl}/_auto_inc'.format( db=db, tbl=tbl, ) try: etcd_result = self.connection.client.read(key) return int(etcd_result.node['value']) except EtcdKeyNotFound: return 1 def _set_next_auto_inc(self, db, tbl): n = self._get_next_auto_inc(db, tbl) key = '/{db}/{tbl}/_auto_inc'.format( db=db, tbl=tbl, ) self.connection.client.write(key, n + 1)