1
- # -*- coding: utf-8 -*-
1
+ """
2
+ https://www.python.org/dev/peps/pep-0249/
3
+ """
4
+ import re
2
5
6
+ from tarantool .error import InterfaceError
7
+ from .connection import Connection as BaseConnection
3
8
4
- class Cursor ( object ):
9
+ update_insert_pattern = re . compile ( r'^UPDATE|INSERT' )
5
10
11
+
12
+ class Cursor :
13
+ _lastrowid = 0
14
+ _rowcount = 0
6
15
description = None
7
- rowcount = None
8
- arraysize = None
16
+ position = 0
17
+ arraysize = 200
18
+ autocommit = True
9
19
10
- def __init__ (self ):
11
- pass
20
+ def __init__ (self , connection ):
21
+ self ._c = connection
22
+ self .rows = []
23
+
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.
31
+
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
+ """
44
+
45
+ @staticmethod
46
+ def _convert_param (p ):
47
+ if p is None :
48
+ return "NULL"
49
+ if isinstance (p , bool ):
50
+ return str (p )
51
+ return "'%s'" % p
52
+
53
+ @staticmethod
54
+ def _extract_last_row_id (body ): # TODO: Need to be checked
55
+ try :
56
+ val = tuple (tuple (body .items ())[0 ][- 1 ].items ())[- 1 ][- 1 ][0 ]
57
+ except TypeError :
58
+ val = - 1
59
+ return val
60
+
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
+ """
88
+ if params :
89
+ query = query % tuple (
90
+ self ._convert_param (param ) for param in params )
91
+
92
+ response = self ._c .execute (query )
93
+
94
+ self .rows = tuple (response .body .values ())[1 ] if len (
95
+ response .body ) > 1 else []
96
+
97
+ if update_insert_pattern .match (query .upper ()):
98
+ try :
99
+ self ._rowcount = response .rowcount
100
+ except InterfaceError :
101
+ self ._rowcount = 1
102
+ else :
103
+ self ._rowcount = 1
104
+
105
+ if query .upper ().startswith ('INSERT' ):
106
+ self ._lastrowid = self ._extract_last_row_id (response .body )
107
+ return response
12
108
13
- def callproc (self , procname , * params ):
14
- pass
15
-
16
- def close (self ):
17
- pass
18
-
19
- def execute (self , query , params ):
20
- pass
21
-
22
109
def executemany (self , query , params ):
23
- pass
24
-
110
+ return self .execute (query , params )
111
+
112
+ @property
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
+ """
126
+ return self ._lastrowid
127
+
128
+ @property
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
+ """
143
+ return self ._rowcount
144
+
25
145
def fetchone (self ):
26
- pass
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
155
+
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
+ """
177
+ if len (self .rows ) < size :
178
+ items = self .rows
179
+ self .rows = []
180
+ else :
181
+ items , self .rows = self .rows [:size ], self .rows [size :]
182
+
183
+ return items if len (items ) else []
27
184
28
- def fetchmany (self ):
29
- pass
30
-
31
185
def fetchall (self ):
32
- pass
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 [:]
194
+ self .rows = []
195
+ return items
33
196
34
197
def setinputsizes (self , sizes ):
35
- pass
36
-
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."""
212
+
37
213
def setoutputsize (self , size , column = None ):
38
214
pass
39
215
40
- class Connection (object ):
41
216
42
- def __init__ (self ):
43
- pass
44
-
45
- def close (self ):
46
- pass
47
-
48
- def commit (self ):
49
- pass
217
+ class Connection (BaseConnection ):
218
+ _cursor = None
219
+
220
+ server_version = 2
221
+
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
+ """
50
233
51
234
def rollback (self ):
52
- pass
53
-
54
- def cursor (self ):
55
- 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
+ """
241
+
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
+ """
252
+ if self ._socket :
253
+ self ._socket .close ()
254
+ self ._socket = None
255
+
256
+ def _set_cursor (self ):
257
+ self ._cursor = Cursor (self )
258
+ return self ._cursor
259
+
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
+ """
268
+ return self ._cursor or self ._set_cursor ()
0 commit comments