Skip to content

Commit 021cb17

Browse files
committed
minor changes and added dbapi2 descriptions
1 parent 5d93a80 commit 021cb17

File tree

1 file changed

+164
-20
lines changed

1 file changed

+164
-20
lines changed

tarantool/dbapi.py

Lines changed: 164 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
"""
2-
Supports python 3.6 and above
2+
https://www.python.org/dev/peps/pep-0249/
33
"""
44
import re
5-
from copy import deepcopy
65

76
from tarantool.error import InterfaceError
8-
97
from .connection import Connection as BaseConnection
108

9+
update_insert_pattern = re.compile(r'^UPDATE|INSERT')
10+
1111

1212
class Cursor:
1313
_lastrowid = 0
@@ -16,18 +16,31 @@ class Cursor:
1616
position = 0
1717
arraysize = 200
1818
autocommit = True
19-
ui_pattern = re.compile(r'^(UPDATE|INSERT)')
20-
u_pattern = re.compile(r'^INSERT')
2119

2220
def __init__(self, connection):
2321
self._c = connection
2422
self.rows = []
2523

26-
def callproc(self, procname, *params):
27-
pass
24+
def callproc(self, procname, *params): # TODO
25+
"""
26+
Call a stored database procedure with the given name. The sequence of
27+
parameters must contain one entry for each argument that the
28+
procedure expects. The result of the call is returned as modified
29+
copy of the input sequence. Input parameters are left untouched,
30+
output and input/output parameters replaced with possibly new values.
2831
29-
def close(self): # TODO: Find out how to implement closing connection correctly
30-
pass
32+
The procedure may also provide a result set as output. This must then
33+
be made available through the standard .fetch*() methods.
34+
"""
35+
36+
def close(self): # TODO
37+
"""
38+
Close the cursor now (rather than whenever __del__ is called).
39+
40+
The cursor will be unusable from this point forward; an Error (or
41+
subclass) exception will be raised if any operation is attempted with
42+
the cursor.
43+
"""
3144

3245
@staticmethod
3346
def _convert_param(p):
@@ -38,30 +51,58 @@ def _convert_param(p):
3851
return "'%s'" % p
3952

4053
@staticmethod
41-
def _extract_last_row_id(body): # Need to be checked
54+
def _extract_last_row_id(body): # TODO: Need to be checked
4255
try:
4356
val = tuple(tuple(body.items())[0][-1].items())[-1][-1][0]
4457
except TypeError:
4558
val = -1
4659
return val
4760

4861
def execute(self, query, params=None):
62+
"""
63+
Prepare and execute a database operation (query or command).
64+
65+
Parameters may be provided as sequence or mapping and will be bound
66+
to variables in the operation. Variables are specified in a
67+
database-specific notation (see the module's paramstyle attribute for
68+
details).
69+
70+
A reference to the operation will be retained by the cursor. If the
71+
same operation object is passed in again, then the cursor can
72+
optimize its behavior. This is most effective for algorithms where
73+
the same operation is used, but different parameters are bound to it
74+
(many times).
75+
76+
For maximum efficiency when reusing an operation, it is best to use
77+
the .setinputsizes() method to specify the parameter types and sizes
78+
ahead of time. It is legal for a parameter to not match the
79+
predefined information; the implementation should compensate,
80+
possibly with a loss of efficiency.
81+
82+
The parameters may also be specified as list of tuples to e.g. insert
83+
multiple rows in a single operation, but this kind of usage is
84+
deprecated: .executemany() should be used instead.
85+
86+
Return values are not defined.
87+
"""
4988
if params:
50-
query = query % tuple(self._convert_param(param) for param in params)
89+
query = query % tuple(
90+
self._convert_param(param) for param in params)
5191

5292
response = self._c.execute(query)
5393

54-
self.rows = tuple(response.body.values())[1] if len(response.body) > 1 else []
94+
self.rows = tuple(response.body.values())[1] if len(
95+
response.body) > 1 else []
5596

56-
if self.ui_pattern.match(query):
97+
if update_insert_pattern.match(query.upper()):
5798
try:
5899
self._rowcount = response.rowcount
59100
except InterfaceError:
60101
self._rowcount = 1
61102
else:
62103
self._rowcount = 1
63104

64-
if self.u_pattern.match(query):
105+
if query.upper().startswith('INSERT'):
65106
self._lastrowid = self._extract_last_row_id(response.body)
66107
return response
67108

@@ -70,16 +111,69 @@ def executemany(self, query, params):
70111

71112
@property
72113
def lastrowid(self):
114+
"""
115+
This read-only attribute provides the rowid of the last modified row
116+
(most databases return a rowid only when a single INSERT operation is
117+
performed). If the operation does not set a rowid or if the database
118+
does not support rowids, this attribute should be set to None.
119+
120+
The semantics of .lastrowid are undefined in case the last executed
121+
statement modified more than one row, e.g. when using INSERT with
122+
.executemany().
123+
124+
Warning Message: "DB-API extension cursor.lastrowid used"
125+
"""
73126
return self._lastrowid
74127

75128
@property
76129
def rowcount(self):
130+
"""
131+
This read-only attribute specifies the number of rows that the last
132+
.execute*() produced (for DQL statements like SELECT) or affected (
133+
for DML statements like UPDATE or INSERT).
134+
135+
The attribute is -1 in case no .execute*() has been performed on the
136+
cursor or the rowcount of the last operation is cannot be determined
137+
by the interface.
138+
139+
Note:
140+
Future versions of the DB API specification could redefine the latter
141+
case to have the object return None instead of -1.
142+
"""
77143
return self._rowcount
78144

79145
def fetchone(self):
80-
return self.rows[0] if len(self.rows) else None
146+
"""
147+
Fetch the next row of a query result set, returning a single
148+
sequence, or None when no more data is available.
149+
150+
An Error (or subclass) exception is raised if the previous call to
151+
.execute*() did not produce any result set or no call was issued yet.
152+
"""
153+
154+
return self.fetchmany(1)[0] if len(self.rows) else None
81155

82156
def fetchmany(self, size):
157+
"""
158+
Fetch the next set of rows of a query result, returning a sequence of
159+
sequences (e.g. a list of tuples). An empty sequence is returned when
160+
no more rows are available.
161+
162+
The number of rows to fetch per call is specified by the parameter.
163+
If it is not given, the cursor's arraysize determines the number of
164+
rows to be fetched. The method should try to fetch as many rows as
165+
indicated by the size parameter. If this is not possible due to the
166+
specified number of rows not being available, fewer rows may be
167+
returned.
168+
169+
An Error (or subclass) exception is raised if the previous call to
170+
.execute*() did not produce any result set or no call was issued yet.
171+
172+
Note there are performance considerations involved with the size
173+
parameter. For optimal performance, it is usually best to use the
174+
.arraysize attribute. If the size parameter is used, then it is best
175+
for it to retain the same value from one .fetchmany() call to the next.
176+
"""
83177
if len(self.rows) < size:
84178
items = self.rows
85179
self.rows = []
@@ -89,12 +183,32 @@ def fetchmany(self, size):
89183
return items if len(items) else []
90184

91185
def fetchall(self):
92-
items = deepcopy(self.rows)
186+
"""Fetch all (remaining) rows of a query result, returning them as a
187+
sequence of sequences (e.g. a list of tuples). Note that the cursor's
188+
arraysize attribute can affect the performance of this operation.
189+
190+
An Error (or subclass) exception is raised if the previous call to
191+
.execute*() did not produce any result set or no call was issued yet.
192+
"""
193+
items = self.rows[:]
93194
self.rows = []
94195
return items
95196

96197
def setinputsizes(self, sizes):
97-
pass
198+
"""This can be used before a call to .execute*() to predefine memory
199+
areas for the operation's parameters.
200+
201+
sizes is specified as a sequence — one item for each input parameter.
202+
The item should be a Type Object that corresponds to the input that
203+
will be used, or it should be an integer specifying the maximum
204+
length of a string parameter. If the item is None, then no predefined
205+
memory area will be reserved for that column (this is useful to avoid
206+
predefined areas for large inputs).
207+
208+
This method would be used before the .execute*() method is invoked.
209+
210+
Implementations are free to have this method do nothing and users are
211+
free to not use it."""
98212

99213
def setoutputsize(self, size, column=None):
100214
pass
@@ -105,13 +219,36 @@ class Connection(BaseConnection):
105219

106220
server_version = 2
107221

108-
def commit(self):
109-
pass
222+
def commit(self): # TODO
223+
"""
224+
Commit any pending transaction to the database.
225+
226+
Note that if the database supports an auto-commit feature, this must
227+
be initially off. An interface method may be provided to turn it back
228+
on.
229+
230+
Database modules that do not support transactions should implement
231+
this method with void functionality.
232+
"""
110233

111234
def rollback(self):
112-
pass
235+
"""
236+
In case a database does provide transactions this method causes the
237+
database to roll back to the start of any pending transaction.
238+
Closing a connection without committing the changes first will cause
239+
an implicit rollback to be performed.
240+
"""
113241

114242
def close(self):
243+
"""
244+
Close the connection now (rather than whenever .__del__() is called).
245+
246+
The connection will be unusable from this point forward; an Error (or
247+
subclass) exception will be raised if any operation is attempted with
248+
the connection. The same applies to all cursor objects trying to use
249+
the connection. Note that closing a connection without committing the
250+
changes first will cause an implicit rollback to be performed.
251+
"""
115252
if self._socket:
116253
self._socket.close()
117254
self._socket = None
@@ -121,4 +258,11 @@ def _set_cursor(self):
121258
return self._cursor
122259

123260
def cursor(self, params=None):
261+
"""
262+
Return a new Cursor Object using the connection.
263+
264+
If the database does not provide a direct cursor concept, the module
265+
will have to emulate cursors using other means to the extent needed
266+
by this specification.
267+
"""
124268
return self._cursor or self._set_cursor()

0 commit comments

Comments
 (0)