1
1
"""
2
- Supports python 3.6 and above
2
+ https://www. python.org/dev/peps/pep-0249/
3
3
"""
4
4
import re
5
- from copy import deepcopy
6
5
7
6
from tarantool .error import InterfaceError
8
-
9
7
from .connection import Connection as BaseConnection
10
8
9
+ update_insert_pattern = re .compile (r'^UPDATE|INSERT' )
10
+
11
11
12
12
class Cursor :
13
13
_lastrowid = 0
@@ -16,18 +16,31 @@ class Cursor:
16
16
position = 0
17
17
arraysize = 200
18
18
autocommit = True
19
- ui_pattern = re .compile (r'^(UPDATE|INSERT)' )
20
- u_pattern = re .compile (r'^INSERT' )
21
19
22
20
def __init__ (self , connection ):
23
21
self ._c = connection
24
22
self .rows = []
25
23
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.
28
31
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
+ """
31
44
32
45
@staticmethod
33
46
def _convert_param (p ):
@@ -38,30 +51,58 @@ def _convert_param(p):
38
51
return "'%s'" % p
39
52
40
53
@staticmethod
41
- def _extract_last_row_id (body ): # Need to be checked
54
+ def _extract_last_row_id (body ): # TODO: Need to be checked
42
55
try :
43
56
val = tuple (tuple (body .items ())[0 ][- 1 ].items ())[- 1 ][- 1 ][0 ]
44
57
except TypeError :
45
58
val = - 1
46
59
return val
47
60
48
61
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
+ """
49
88
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 )
51
91
52
92
response = self ._c .execute (query )
53
93
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 []
55
96
56
- if self . ui_pattern . match (query ):
97
+ if update_insert_pattern . match (query . upper () ):
57
98
try :
58
99
self ._rowcount = response .rowcount
59
100
except InterfaceError :
60
101
self ._rowcount = 1
61
102
else :
62
103
self ._rowcount = 1
63
104
64
- if self . u_pattern . match ( query ):
105
+ if query . upper (). startswith ( 'INSERT' ):
65
106
self ._lastrowid = self ._extract_last_row_id (response .body )
66
107
return response
67
108
@@ -70,16 +111,69 @@ def executemany(self, query, params):
70
111
71
112
@property
72
113
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
+ """
73
126
return self ._lastrowid
74
127
75
128
@property
76
129
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
+ """
77
143
return self ._rowcount
78
144
79
145
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
81
155
82
156
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
+ """
83
177
if len (self .rows ) < size :
84
178
items = self .rows
85
179
self .rows = []
@@ -89,12 +183,32 @@ def fetchmany(self, size):
89
183
return items if len (items ) else []
90
184
91
185
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 [:]
93
194
self .rows = []
94
195
return items
95
196
96
197
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."""
98
212
99
213
def setoutputsize (self , size , column = None ):
100
214
pass
@@ -105,13 +219,36 @@ class Connection(BaseConnection):
105
219
106
220
server_version = 2
107
221
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
+ """
110
233
111
234
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
+ """
113
241
114
242
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
+ """
115
252
if self ._socket :
116
253
self ._socket .close ()
117
254
self ._socket = None
@@ -121,4 +258,11 @@ def _set_cursor(self):
121
258
return self ._cursor
122
259
123
260
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
+ """
124
268
return self ._cursor or self ._set_cursor ()
0 commit comments