Skip to content

Commit f5d0838

Browse files
committed
Add sql execute to connection
Closes #159 Co-authored-by Denis Ignatenko <[email protected]>
1 parent 3b0a3fa commit f5d0838

File tree

4 files changed

+90
-5
lines changed

4 files changed

+90
-5
lines changed

tarantool/connection.py

+24-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
RequestSubscribe,
3535
RequestUpdate,
3636
RequestUpsert,
37-
RequestAuthenticate
37+
RequestAuthenticate,
38+
RequestExecute
3839
)
3940
from tarantool.space import Space
4041
from tarantool.const import (
@@ -254,13 +255,14 @@ def _send_request_wo_reconnect(self, request):
254255
255256
:raise: NetworkError
256257
'''
257-
assert isinstance(request, Request)
258+
if not issubclass(type(request), Request):
259+
raise NetworkError
258260

259261
response = None
260262
while True:
261263
try:
262264
self._socket.sendall(bytes(request))
263-
response = Response(self, self._read_response())
265+
response = request.response_class(self, self._read_response())
264266
break
265267
except SchemaReloadException as e:
266268
self.update_schema(e.schema_version)
@@ -785,3 +787,22 @@ def generate_sync(self):
785787
Need override for async io connection
786788
'''
787789
return 0
790+
791+
def execute(self, query, params=None):
792+
'''
793+
Execute SQL request.
794+
795+
:param query: SQL syntax query
796+
:type query: str
797+
798+
:param params: Bind values to use in query
799+
:type params: list, dict
800+
801+
:return: query result data
802+
:rtype: list
803+
'''
804+
if not params:
805+
params = []
806+
request = RequestExecute(self, query, params)
807+
response = self._send_request(request)
808+
return response

tarantool/const.py

+8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@
2929
#
3030
IPROTO_DATA = 0x30
3131
IPROTO_ERROR = 0x31
32+
#
33+
IPROTO_METADATA = 0x32
34+
IPROTO_SQL_TEXT = 0x40
35+
IPROTO_SQL_BIND = 0x41
36+
IPROTO_SQL_INFO = 0x42
37+
IPROTO_SQL_INFO_ROW_COUNT = 0x00
38+
IPROTO_SQL_INFO_AUTOINCREMENT_IDS = 0x01
3239

3340
IPROTO_GREETING_SIZE = 128
3441
IPROTO_BODY_MAX_LEN = 2147483648
@@ -44,6 +51,7 @@
4451
REQUEST_TYPE_EVAL = 8
4552
REQUEST_TYPE_UPSERT = 9
4653
REQUEST_TYPE_CALL = 10
54+
REQUEST_TYPE_EXECUTE = 11
4755
REQUEST_TYPE_PING = 64
4856
REQUEST_TYPE_JOIN = 65
4957
REQUEST_TYPE_SUBSCRIBE = 66

tarantool/request.py

+27-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import msgpack
88
import hashlib
99

10-
10+
from tarantool.error import DatabaseError
1111
from tarantool.const import (
1212
IPROTO_CODE,
1313
IPROTO_SYNC,
@@ -27,6 +27,8 @@
2727
IPROTO_OPS,
2828
# IPROTO_INDEX_BASE,
2929
IPROTO_SCHEMA_ID,
30+
IPROTO_SQL_TEXT,
31+
IPROTO_SQL_BIND,
3032
REQUEST_TYPE_OK,
3133
REQUEST_TYPE_PING,
3234
REQUEST_TYPE_SELECT,
@@ -37,11 +39,13 @@
3739
REQUEST_TYPE_UPSERT,
3840
REQUEST_TYPE_CALL16,
3941
REQUEST_TYPE_CALL,
42+
REQUEST_TYPE_EXECUTE,
4043
REQUEST_TYPE_EVAL,
4144
REQUEST_TYPE_AUTHENTICATE,
4245
REQUEST_TYPE_JOIN,
4346
REQUEST_TYPE_SUBSCRIBE
4447
)
48+
from tarantool.response import Response, ResponseExecute
4549
from tarantool.utils import (
4650
strxor,
4751
binary_types
@@ -64,6 +68,7 @@ def __init__(self, conn):
6468
self.conn = conn
6569
self._sync = None
6670
self._body = ''
71+
self.response_class = Response
6772

6873
def __bytes__(self):
6974
return self.header(len(self._body)) + self._body
@@ -332,3 +337,24 @@ def __init__(self, conn, sync):
332337
request_body = msgpack.dumps({IPROTO_CODE: self.request_type,
333338
IPROTO_SYNC: sync})
334339
self._body = request_body
340+
341+
342+
class RequestExecute(Request):
343+
'''
344+
Represents EXECUTE request
345+
'''
346+
request_type = REQUEST_TYPE_EXECUTE
347+
348+
# pylint: disable=W0231
349+
def __init__(self, conn, sql, args):
350+
super(RequestExecute, self).__init__(conn)
351+
if isinstance(args, dict):
352+
args = [{":%s" % name: value} for name, value in args.items()]
353+
try:
354+
request_body = msgpack.dumps({IPROTO_SQL_TEXT: sql,
355+
IPROTO_SQL_BIND: args})
356+
except ValueError as e:
357+
raise DatabaseError("Value error: %s" % e)
358+
359+
self._body = request_body
360+
self.response_class = ResponseExecute

tarantool/response.py

+31-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
IPROTO_ERROR,
1818
IPROTO_SYNC,
1919
IPROTO_SCHEMA_ID,
20-
REQUEST_TYPE_ERROR
20+
REQUEST_TYPE_ERROR,
21+
IPROTO_SQL_INFO,
22+
IPROTO_SQL_INFO_ROW_COUNT,
23+
IPROTO_SQL_INFO_AUTOINCREMENT_IDS
2124
)
2225
from tarantool.error import (
2326
DatabaseError,
@@ -245,3 +248,30 @@ def __str__(self):
245248
return ''.join(output)
246249

247250
__repr__ = __str__
251+
252+
253+
class ResponseExecute(Response):
254+
@property
255+
def lastrowid(self):
256+
if self.body is None:
257+
raise InterfaceError("Trying to access data, when there's no data")
258+
info = self.body.get(IPROTO_SQL_INFO)
259+
260+
if info is None:
261+
return None
262+
263+
lastrowids = info.get(IPROTO_SQL_INFO_AUTOINCREMENT_IDS)
264+
265+
return lastrowids[-1] if lastrowids else None
266+
267+
@property
268+
def rowcount(self):
269+
if self._body is None:
270+
raise InterfaceError("Trying to access data, when there's no data")
271+
272+
info = self._body.get(IPROTO_SQL_INFO)
273+
274+
if info is None:
275+
return -1
276+
277+
return info.get(IPROTO_SQL_INFO_ROW_COUNT, -1)

0 commit comments

Comments
 (0)