Skip to content

Commit

Permalink
Improvements on the sending update/upsert statements
Browse files Browse the repository at this point in the history
**New features:**
* Implemented ability to send update/upsert requests with field names when schema is disabled (`fetch_schema=False`) and when fields are not found in the schema (good example of this case is using json path like `data.inner1.inner2.key1` as a key)

**Bug fixes:**
* Fixed issue with not being able to send Decimals in update statements. Now there are no extra checks - any payload is sent directly to Tarantool (fixes #34)

**Other changes**
* Fixed tests failing on modern Tarantool in the SQL queries.
  • Loading branch information
igorcoding committed May 5, 2024
1 parent 2277115 commit 6d049ed
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 69 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## v2.2.0
**New features:**
* Implemented ability to send update/upsert requests with field names when schema is disabled (`fetch_schema=False`) and when fields are not found in the schema (good example of this case is using json path like `data.inner1.inner2.key1` as a key)

**Bug fixes:**
* Fixed issue with not being able to send Decimals in update statements. Now there are no extra checks - any payload is sent directly to Tarantool (fixes [#34]())

**Other changes**
* Fixed tests failing on modern Tarantool in the SQL queries.

## v2.1.0
**Breaking changes:**
* Dropped support for Python 3.6
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ mypy:
$(PYTHON) -m mypy --enable-error-code ignore-without-code .

ruff:
$(PYTHON) -m ruff .
$(PYTHON) -m ruff check .

style-check:
$(PYTHON) -m black --check --diff .
Expand Down
2 changes: 1 addition & 1 deletion asynctnt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
TarantoolTuple,
)

__version__ = "2.1.0"
__version__ = "2.2.0"
61 changes: 31 additions & 30 deletions asynctnt/iproto/requests/update.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@ cdef char *encode_update_ops(WriteBuffer buffer,

uint32_t extra_length

bint field_encode_as_str
uint64_t field_no
char *field_str_c
ssize_t field_str_len
object field_no_obj

uint32_t splice_position, splice_offset

field_encode_as_str = 0
field_str_c = NULL

begin = NULL

if operations is not None:
Expand Down Expand Up @@ -60,22 +66,27 @@ cdef char *encode_update_ops(WriteBuffer buffer,
raise TypeError(
'Operation type must of a str or bytes type')

cpython.bytes.PyBytes_AsStringAndSize(str_temp, &op_str_c,
&op_str_len)

field_no_obj = operation[1]
if isinstance(field_no_obj, int):
field_no = <int> field_no_obj
elif isinstance(field_no_obj, str):
if space.metadata is not None:
field_no = <int> space.metadata.id_by_name(field_no_obj)
field_no = <int> space.metadata.id_by_name_safe(field_no_obj)
if field_no == -1:
field_encode_as_str = 1
else:
raise TypeError(
'Operation field_no must be int as there is '
'no format declaration in space {}'.format(space.sid))
field_encode_as_str = 1

if field_encode_as_str:
str_temp = encode_unicode_string(field_no_obj, buffer._encoding)
cpython.bytes.PyBytes_AsStringAndSize(str_temp, &field_str_c, &field_str_len)
else:
raise TypeError(
'Operation field_no must be of either int or str type')

cpython.bytes.PyBytes_AsStringAndSize(str_temp, &op_str_c,
&op_str_len)
op = <char> 0
if op_str_len == 1:
op = op_str_c[0]
Expand All @@ -85,28 +96,9 @@ cdef char *encode_update_ops(WriteBuffer buffer,
or op == tarantool.IPROTO_OP_AND \
or op == tarantool.IPROTO_OP_XOR \
or op == tarantool.IPROTO_OP_OR \
or op == tarantool.IPROTO_OP_DELETE:
op_argument = operation[2]
if not isinstance(op_argument, int):
raise TypeError(
'int argument required for '
'Arithmetic and Delete operations'
)
# mp_sizeof_array(3)
# + mp_sizeof_str(1)
# + mp_sizeof_uint(field_no)
extra_length = 1 + 2 + mp_sizeof_uint(field_no)
p = begin = buffer._ensure_allocated(p, extra_length)

p = mp_encode_array(p, 3)
p = mp_encode_str(p, op_str_c, 1)
p = mp_encode_uint(p, field_no)
buffer._length += (p - begin)
p = buffer.mp_encode_obj(p, op_argument)
elif op == tarantool.IPROTO_OP_INSERT \
or op == tarantool.IPROTO_OP_DELETE \
or op == tarantool.IPROTO_OP_INSERT \
or op == tarantool.IPROTO_OP_ASSIGN:
op_argument = operation[2]

# mp_sizeof_array(3)
# + mp_sizeof_str(1)
# + mp_sizeof_uint(field_no)
Expand All @@ -115,13 +107,19 @@ cdef char *encode_update_ops(WriteBuffer buffer,

p = mp_encode_array(p, 3)
p = mp_encode_str(p, op_str_c, 1)
p = mp_encode_uint(p, field_no)
if field_str_c == NULL:
p = mp_encode_uint(p, field_no)
else:
p = mp_encode_str(p, field_str_c, field_str_len)

buffer._length += (p - begin)

op_argument = operation[2]
p = buffer.mp_encode_obj(p, op_argument)

elif op == tarantool.IPROTO_OP_SPLICE:
if op_len < 5:
raise IndexError(
raise ValueError(
'Splice operation must have length of 5, '
'but got: {}'.format(op_len)
)
Expand All @@ -146,7 +144,10 @@ cdef char *encode_update_ops(WriteBuffer buffer,

p = mp_encode_array(p, 5)
p = mp_encode_str(p, op_str_c, 1)
p = mp_encode_uint(p, field_no)
if field_str_c == NULL:
p = mp_encode_uint(p, field_no)
else:
p = mp_encode_str(p, field_str_c, field_str_len)
p = mp_encode_uint(p, splice_position)
p = mp_encode_uint(p, splice_offset)
buffer._length += (p - begin)
Expand Down
1 change: 1 addition & 0 deletions asynctnt/iproto/schema.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ cdef public class Metadata [object C_Metadata, type C_Metadata_Type]:
cdef inline void add(self, int id, Field field)
cdef inline str name_by_id(self, int i)
cdef inline int id_by_name(self, str name) except *
cdef inline int id_by_name_safe(self, str name) except*


cdef class SchemaIndex:
Expand Down
9 changes: 9 additions & 0 deletions asynctnt/iproto/schema.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ cdef class Metadata:
raise KeyError('Field \'{}\' not found'.format(name))
return <int> <object> fld

cdef inline int id_by_name_safe(self, str name) except *:
cdef:
PyObject *fld

fld = cpython.dict.PyDict_GetItem(self.name_id_map, name)
if fld == NULL:
return -1
return <int> <object> fld

cdef inline int len(self):
return <int> cpython.list.PyList_GET_SIZE(self.fields)

Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,15 @@ skip_glob = [


[tool.ruff]
select = [
lint.select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
# "I", # isort
"C", # flake8-comprehensions
"B", # flake8-bugbear
]
ignore = [
lint.ignore = [
"E501", # line too long, handled by black
"B008", # do not perform function calls in argument defaults
"C901", # too complex
Expand Down
6 changes: 3 additions & 3 deletions tests/files/app.lua
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,15 @@ function truncate()
_truncate(box.space.tester)
_truncate(box.space.no_schema_space)

if box.space.SQL_SPACE ~= nil then
if box.space.sql_space ~= nil then
box.execute('DELETE FROM sql_space')
end

if box.space.SQL_SPACE_AUTOINCREMENT ~= nil then
if box.space.sql_space_autoincrement ~= nil then
box.execute('DELETE FROM sql_space_autoincrement')
end

if box.space.SQL_SPACE_AUTOINCREMENT_MULTIPLE ~= nil then
if box.space.sql_space_autoincrement_multiple ~= nil then
box.execute('DELETE FROM sql_space_autoincrement_multiple')
end

Expand Down
4 changes: 3 additions & 1 deletion tests/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ async def test__parse_numeric_map_keys(self):
async def test__read_buffer_reallocate_ok(self):
await self.tnt_reconnect(initial_read_buffer_size=1)

p, cmp = get_complex_param(encoding=self.conn.encoding)
p, cmp = get_complex_param(
encoding=self.conn.encoding, replace_bin=self.conn.version < (3, 0)
)
try:
res = await self.conn.call("func_param", [p])
except Exception as e:
Expand Down
16 changes: 12 additions & 4 deletions tests/test_op_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,16 @@ async def test__call_args_tuple(self):
self.fail(e)

async def test__call_complex_param(self):
p, cmp = get_complex_param(encoding=self.conn.encoding)
p, cmp = get_complex_param(
encoding=self.conn.encoding, replace_bin=self.conn.version < (3, 0)
)
res = await self.conn.call("func_param", [p])
self.assertDictEqual(res[0][0], cmp, "Body ok")

async def test__call_complex_param_bare(self):
p, cmp = get_complex_param(encoding=self.conn.encoding)
p, cmp = get_complex_param(
encoding=self.conn.encoding, replace_bin=self.conn.version < (3, 0)
)
cmp = [cmp]
res = await self.conn.call("func_param_bare", [p])
if not self.has_new_call():
Expand Down Expand Up @@ -177,12 +181,16 @@ async def test__call16_args_tuple(self):
self.fail(e)

async def test__call16_complex_param(self):
p, cmp = get_complex_param(encoding=self.conn.encoding)
p, cmp = get_complex_param(
encoding=self.conn.encoding, replace_bin=self.conn.version < (3, 0)
)
res = await self.conn.call("func_param", [p])
self.assertDictEqual(res[0][0], cmp, "Body ok")

async def test__call16_complex_param_bare(self):
p, cmp = get_complex_param(encoding=self.conn.encoding)
p, cmp = get_complex_param(
encoding=self.conn.encoding, replace_bin=self.conn.version < (3, 0)
)
res = await self.conn.call16("func_param_bare", [p])
self.assertDictEqual(res[0][0], cmp, "Body ok")

Expand Down
4 changes: 3 additions & 1 deletion tests/test_op_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ async def test__eval_args_tuple(self):
self.fail(e)

async def test__eval_complex_param(self):
p, cmp = get_complex_param(encoding=self.conn.encoding)
p, cmp = get_complex_param(
encoding=self.conn.encoding, replace_bin=self.conn.version < (3, 0)
)
res = await self.conn.eval("return {...}", [p])
self.assertDictEqual(res[0][0], cmp, "Body ok")

Expand Down
Loading

0 comments on commit 6d049ed

Please sign in to comment.