1# -*- coding: utf-8 -*- 2from tarantool.connection import Connection as BaseConnection 3from tarantool.error import * 4 5 6paramstyle = 'named' 7apilevel = "2.0" 8threadsafety = 1 9 10 11class Cursor: 12 13 def __init__(self, conn): 14 self._c = conn 15 self._lastrowid = None 16 self._rowcount = None 17 self.arraysize = 1 18 self._rows = None 19 20 def callproc(self, procname, *params): 21 """ 22 Call a stored database procedure with the given name. The sequence of 23 parameters must contain one entry for each argument that the 24 procedure expects. The result of the call is returned as modified 25 copy of the input sequence. Input parameters are left untouched, 26 output and input/output parameters replaced with possibly new values. 27 """ 28 raise NotSupportedError("callproc() method is not supported") 29 30 @property 31 def rows(self): 32 return self._rows 33 34 @property 35 def description(self): 36 # FIXME Implement this method please 37 raise NotImplementedError("description() property is not implemented") 38 39 def close(self): 40 """ 41 Close the cursor now (rather than whenever __del__ is called). 42 The cursor will be unusable from this point forward; DatabaseError 43 exception will be raised if any operation is attempted with 44 the cursor. 45 """ 46 self._c = None 47 self._rows = None 48 self._lastrowid = None 49 self._rowcount = None 50 51 def _check_not_closed(self, error=None): 52 if self._c is None: 53 raise InterfaceError(error or "Can not operate on a closed cursor") 54 if self._c.is_closed(): 55 raise InterfaceError("The cursor can not be used " 56 "with a closed connection") 57 58 def execute(self, query, params=None): 59 """ 60 Prepare and execute a database operation (query or command). 61 """ 62 self._check_not_closed("Can not execute on closed cursor.") 63 64 response = self._c.execute(query, params) 65 66 self._rows = response.data 67 self._rowcount = response.affected_row_count or -1 68 if response.autoincrement_ids: 69 self._lastrowid = response.autoincrement_ids[-1] 70 else: 71 self._lastrowid = None 72 73 def executemany(self, query, param_sets): 74 self._check_not_closed("Can not execute on closed cursor.") 75 rowcount = 0 76 for params in param_sets: 77 self.execute(query, params) 78 if self.rowcount == -1: 79 rowcount = -1 80 if rowcount != -1: 81 rowcount += self.rowcount 82 self._rowcount = rowcount 83 84 @property 85 def lastrowid(self): 86 """ 87 This read-only attribute provides the rowid of the last modified row 88 (most databases return a rowid only when a single INSERT operation is 89 performed). 90 """ 91 return self._lastrowid 92 93 @property 94 def rowcount(self): 95 """ 96 This read-only attribute specifies the number of rows that the last 97 .execute*() produced (for DQL statements like SELECT) or affected ( 98 for DML statements like UPDATE or INSERT). 99 """ 100 return self._rowcount 101 102 def _check_result_set(self, error=None): 103 """ 104 Non-public method for raising an error when Cursor object does not have 105 any row to fetch. Useful for checking access after DQL requests. 106 """ 107 if self._rows is None: 108 raise InterfaceError(error or "No result set to fetch from") 109 110 def fetchone(self): 111 """ 112 Fetch the next row of a query result set, returning a single 113 sequence, or None when no more data is available. 114 """ 115 self._check_result_set() 116 return self.fetchmany(1)[0] if self._rows else None 117 118 def fetchmany(self, size=None): 119 """ 120 Fetch the next set of rows of a query result, returning a sequence of 121 sequences (e.g. a list of tuples). An empty sequence is returned when 122 no more rows are available. 123 """ 124 self._check_result_set() 125 126 size = size or self.arraysize 127 128 if len(self._rows) < size: 129 items = self._rows 130 self._rows = [] 131 else: 132 items, self._rows = self._rows[:size], self._rows[size:] 133 134 return items 135 136 def fetchall(self): 137 """Fetch all (remaining) rows of a query result, returning them as a 138 sequence of sequences (e.g. a list of tuples). Note that the cursor's 139 arraysize attribute can affect the performance of this operation. 140 """ 141 self._check_result_set() 142 143 items = self._rows 144 self._rows = [] 145 return items 146 147 def setinputsizes(self, sizes): 148 """PEP-249 allows to not implement this method and do nothing.""" 149 150 def setoutputsize(self, size, column=None): 151 """PEP-249 allows to not implement this method and do nothing.""" 152 153 154class Connection(BaseConnection): 155 156 def __init__(self, *args, **kwargs): 157 super(Connection, self).__init__(*args, **kwargs) 158 self._set_autocommit(kwargs.get('autocommit', True)) 159 160 def _set_autocommit(self, autocommit): 161 """Autocommit is True by default and the default will be changed 162 to False. Set the autocommit property explicitly to True or verify 163 it when lean on autocommit behaviour.""" 164 if not isinstance(autocommit, bool): 165 raise InterfaceError("autocommit parameter must be boolean, " 166 "not %s" % autocommit.__class__.__name__) 167 if autocommit is False: 168 raise NotSupportedError("The connector supports " 169 "only autocommit mode") 170 self._autocommit = autocommit 171 172 @property 173 def autocommit(self): 174 """Autocommit state""" 175 return self._autocommit 176 177 @autocommit.setter 178 def autocommit(self, autocommit): 179 """Set autocommit state""" 180 self._set_autocommit(autocommit) 181 182 def _check_not_closed(self, error=None): 183 """ 184 Checks if the connection is not closed and rises an error if it is. 185 """ 186 if self.is_closed(): 187 raise InterfaceError(error or "The connector is closed") 188 189 def close(self): 190 """ 191 Closes the connection 192 """ 193 self._check_not_closed("The closed connector can not be closed again.") 194 super(Connection, self).close() 195 196 def commit(self): 197 """ 198 Commit any pending transaction to the database. 199 """ 200 self._check_not_closed("Can not commit on the closed connection") 201 202 def rollback(self): 203 """ 204 Roll back pending transaction 205 """ 206 self._check_not_closed("Can not roll back on a closed connection") 207 raise NotSupportedError("Transactions are not supported in this" 208 "version of connector") 209 210 def cursor(self): 211 """ 212 Return a new Cursor Object using the connection. 213 """ 214 self._check_not_closed("Cursor creation is not allowed on a closed " 215 "connection") 216 return Cursor(self) 217 218 219def connect(dsn=None, host=None, port=None, 220 user=None, password=None, **kwargs): 221 """ 222 Constructor for creating a connection to the database. 223 224 :param str dsn: Data source name (Tarantool URI) 225 ([[[username[:password]@]host:]port) 226 :param str host: Server hostname or IP-address 227 :param int port: Server port 228 :param str user: Tarantool user 229 :param str password: User password 230 :rtype: Connection 231 """ 232 233 if dsn: 234 raise NotImplementedError("dsn param is not implemented in" 235 "this version of dbapi module") 236 params = {} 237 if host: 238 params["host"] = host 239 if port: 240 params["port"] = port 241 if user: 242 params["user"] = user 243 if password: 244 params["password"] = password 245 246 kwargs.update(params) 247 248 return Connection(**kwargs) 249