3
3
# See http://www.python.org/dev/peps/pep-0249/
4
4
#
5
5
# Many docstrings in this file are based on the PEP, which is in the public domain.
6
-
6
+ import decimal
7
7
import re
8
8
import uuid
9
- from datetime import datetime
10
- from databend_sqlalchemy .errors import ServerException , NotSupportedError
9
+ from datetime import datetime , date
10
+ from databend_sqlalchemy .errors import Error , ServerException , NotSupportedError
11
11
12
12
from databend_driver import BlockingDatabendClient
13
13
17
17
paramstyle = "pyformat" # Python extended format codes, e.g. ...WHERE name=%(name)s
18
18
19
19
20
- class Error (Exception ):
21
- """Exception that is the base class of all other error exceptions.
22
- You can use this to catch all errors with one single except statement.
23
- """
24
-
25
- pass
26
-
27
-
28
- class ParamEscaper (object ):
20
+ class ParamEscaper :
29
21
def escape_args (self , parameters ):
30
22
if isinstance (parameters , dict ):
31
23
return {k : self .escape_item (v ) for k , v in parameters .items ()}
@@ -45,15 +37,17 @@ def escape_string(self, item):
45
37
if isinstance (item , bytes ):
46
38
item = item .decode ("utf-8" )
47
39
return "'{}'" .format (
48
- item .replace ("\\ " , "\\ \\ " ).replace ("'" , "\\ '" ).replace ("$" , "$$" )
40
+ item .replace ("\\ " , "\\ \\ " ).replace ("'" , "\\ '" ).replace ("$" , "$$" ). replace ( "%" , "%%" )
49
41
)
50
42
51
43
def escape_item (self , item ):
52
44
if item is None :
53
45
return "NULL"
54
46
elif isinstance (item , (int , float )):
55
47
return self .escape_number (item )
56
- elif isinstance (item , datetime ):
48
+ elif isinstance (item , decimal .Decimal ):
49
+ return self .escape_number (item )
50
+ elif isinstance (item , (datetime , date )):
57
51
return self .escape_string (item .strftime ("%Y-%m-%d %H:%M:%S" ))
58
52
else :
59
53
return self .escape_string (item )
@@ -115,7 +109,7 @@ def rollback(self):
115
109
raise NotSupportedError ("Transactions are not supported" ) # pragma: no cover
116
110
117
111
118
- class Cursor ( object ) :
112
+ class Cursor :
119
113
"""These objects represent a database cursor, which is used to manage the context of a fetch
120
114
operation.
121
115
@@ -173,22 +167,39 @@ def close(self):
173
167
def execute (self , operation , parameters = None , is_response = True ):
174
168
"""Prepare and execute a database operation (query or command)."""
175
169
170
+ #ToDo - Fix this, which is preventing the execution of blank DDL sunch as CREATE INDEX statements which aren't suppoorted
171
+ # Seems hard to fix when statements are coming from metadata.create_all()
172
+ if operation == "" :
173
+ return
174
+
176
175
self ._reset_state ()
177
176
178
177
self ._state = self ._STATE_RUNNING
179
178
self ._uuid = uuid .uuid1 ()
180
179
181
- if is_response :
182
- rows = self ._db .query_iter (operation )
183
- schema = rows .schema ()
184
- columns = []
185
- for field in schema .fields ():
186
- columns .append ((field .name , field .data_type ))
187
- if self ._state != self ._STATE_RUNNING :
188
- raise Exception ("Should be running if processing response" )
189
- self ._rows = rows
190
- self ._columns = columns
191
- self ._state = self ._STATE_SUCCEEDED
180
+ try :
181
+ # operation = operation.replace('%%', '%')
182
+ if parameters :
183
+ query = operation % _escaper .escape_args (parameters )
184
+ else :
185
+ query = operation
186
+ query = query .replace ('%%' , '%' )
187
+ if is_response :
188
+ rows = self ._db .query_iter (query )
189
+ schema = rows .schema ()
190
+ columns = []
191
+ for field in schema .fields ():
192
+ columns .append ((field .name , field .data_type ))
193
+ if self ._state != self ._STATE_RUNNING :
194
+ raise Exception ("Should be running if processing response" )
195
+ self ._rows = rows
196
+ self ._columns = columns
197
+ self ._state = self ._STATE_SUCCEEDED
198
+ else :
199
+ self ._db .exec (query )
200
+ except Exception as e :
201
+ # We have to raise dbAPI error
202
+ raise Error (str (e )) from e
192
203
193
204
def executemany (self , operation , seq_of_parameters ):
194
205
"""Prepare a database operation (query or command) and then execute it against all parameter
@@ -208,23 +219,27 @@ def executemany(self, operation, seq_of_parameters):
208
219
209
220
m = RE_INSERT_VALUES .match (operation )
210
221
if m :
211
- q_prefix = m .group (1 ) % ()
212
- q_values = m .group (2 ).rstrip ()
213
-
214
- for parameters in seq_of_parameters [:- 1 ]:
215
- values_list .append (q_values % _escaper .escape_args (parameters ))
216
- query = "{} {};" .format (q_prefix , "," .join (values_list ))
217
- return self ._db .raw (query )
218
- for parameters in seq_of_parameters [:- 1 ]:
222
+ try :
223
+ q_prefix = m .group (1 ) #.replace('%%', '%') % ()
224
+ q_values = m .group (2 ).rstrip ()
225
+
226
+ for parameters in seq_of_parameters :
227
+ values_list .append (q_values % _escaper .escape_args (parameters ))
228
+ query = "{} {};" .format (q_prefix , "," .join (values_list ))
229
+ return self ._db .exec (query )
230
+ except Exception as e :
231
+ # We have to raise dbAPI error
232
+ raise Error (str (e )) from e
233
+ for parameters in seq_of_parameters :
219
234
self .execute (operation , parameters , is_response = False )
220
235
221
236
def fetchone (self ):
222
237
"""Fetch the next row of a query result set, returning a single sequence, or ``None`` when
223
238
no more data is available."""
224
239
if self ._state == self ._STATE_NONE :
225
- raise Exception ("No query yet" )
240
+ raise Error ("No query yet" )
226
241
if not self ._rows :
227
- raise Exception ("No rows yet" )
242
+ raise Error ("No rows yet" )
228
243
else :
229
244
self ._rownumber += 1
230
245
try :
@@ -243,7 +258,7 @@ def fetchmany(self, size=None):
243
258
specified number of rows not being available, fewer rows may be returned.
244
259
"""
245
260
if self ._state == self ._STATE_NONE :
246
- raise Exception ("No query yet" )
261
+ raise Error ("No query yet" )
247
262
248
263
if size is None :
249
264
size = 1
@@ -262,7 +277,7 @@ def fetchall(self):
262
277
(e.g. a list of tuples).
263
278
"""
264
279
if self ._state == self ._STATE_NONE :
265
- raise Exception ("No query yet" )
280
+ raise Error ("No query yet" )
266
281
267
282
data = []
268
283
if self ._rows :
0 commit comments