From 5a38c5c90072e15e03f9578c0933537c74721334 Mon Sep 17 00:00:00 2001 From: Mergen Imeev <imeevma@tarantool.org> Date: Tue, 18 Oct 2022 11:51:33 +0300 Subject: [PATCH] sql: fix another cursor invalidation This patch fixes the issue described in issue #5310 when the tuple format has more fields than the space format. This solution is more general than the solution in 89057a21b. Follow-up #5310 Closes #4666 NO_DOC=bugfix --- .../gh-5310-fix-cursor-invalidation.md | 2 +- src/box/ck_constraint.c | 1 + src/box/sql.c | 29 ++++++++++--------- src/box/sql.h | 14 ++++++--- src/box/sql/func.c | 1 + src/box/sql/vdbe.c | 1 + .../gh_5310_cursor_invalidation_test.lua | 19 +++++++++++- 7 files changed, 47 insertions(+), 20 deletions(-) diff --git a/changelogs/unreleased/gh-5310-fix-cursor-invalidation.md b/changelogs/unreleased/gh-5310-fix-cursor-invalidation.md index e4878e2b31..1aa93a77e9 100644 --- a/changelogs/unreleased/gh-5310-fix-cursor-invalidation.md +++ b/changelogs/unreleased/gh-5310-fix-cursor-invalidation.md @@ -1,4 +1,4 @@ ## bugfix/sql * Fixed a crash that could occur when tuples longer than specified in - the format were selected (gh-5310). + the space format were selected (gh-5310, gh-4666). diff --git a/src/box/ck_constraint.c b/src/box/ck_constraint.c index af26da313a..9648ea2a8a 100644 --- a/src/box/ck_constraint.c +++ b/src/box/ck_constraint.c @@ -200,6 +200,7 @@ ck_constraint_on_replace_trigger(struct trigger *trigger, void *event) "field_ref"); return -1; } + vdbe_field_ref_create(field_ref, space->def->field_count); vdbe_field_ref_prepare_tuple(field_ref, new_tuple); struct ck_constraint *ck_constraint; diff --git a/src/box/sql.c b/src/box/sql.c index da9ecea80a..7202c25875 100644 --- a/src/box/sql.c +++ b/src/box/sql.c @@ -1361,16 +1361,16 @@ sql_template_space_new(Parse *parser, const char *name) } /** - * Initialize a new vdbe_field_ref instance with given tuple - * data. + * Fill vdbe_field_ref instance with given tuple data. + * * @param field_ref The vdbe_field_ref instance to initialize. * @param tuple The tuple object pointer or NULL when undefined. * @param data The tuple data (is always defined). * @param data_sz The size of tuple data (is always defined). */ static void -vdbe_field_ref_create(struct vdbe_field_ref *field_ref, struct tuple *tuple, - const char *data, uint32_t data_sz) +vdbe_field_ref_fill(struct vdbe_field_ref *field_ref, struct tuple *tuple, + const char *data, uint32_t data_sz) { field_ref->tuple = tuple; field_ref->data = data; @@ -1379,13 +1379,7 @@ vdbe_field_ref_create(struct vdbe_field_ref *field_ref, struct tuple *tuple, const char *field0 = data; field_ref->format = NULL; uint32_t mp_count = mp_decode_array(&field0); - if (tuple != NULL) { - assert(tuple_format(tuple) != NULL); - uint32_t field_count = tuple_format(tuple)->total_field_count; - field_ref->field_count = MIN(field_count, mp_count); - } else { - field_ref->field_count = mp_count; - } + field_ref->field_count = MIN(field_ref->field_capacity, mp_count); field_ref->slots[0] = (uint32_t)(field0 - data); memset(&field_ref->slots[1], 0, field_ref->field_count * sizeof(field_ref->slots[0])); @@ -1397,15 +1391,22 @@ void vdbe_field_ref_prepare_data(struct vdbe_field_ref *field_ref, const char *data, uint32_t data_sz) { - vdbe_field_ref_create(field_ref, NULL, data, data_sz); + vdbe_field_ref_fill(field_ref, NULL, data, data_sz); } void vdbe_field_ref_prepare_tuple(struct vdbe_field_ref *field_ref, struct tuple *tuple) { - vdbe_field_ref_create(field_ref, tuple, tuple_data(tuple), - tuple_bsize(tuple)); + vdbe_field_ref_fill(field_ref, tuple, tuple_data(tuple), + tuple_bsize(tuple)); +} + +void +vdbe_field_ref_create(struct vdbe_field_ref *ref, uint32_t capacity) +{ + memset(ref, 0, sizeof(*ref) + capacity * sizeof(ref->slots[0])); + ref->field_capacity = capacity; } ssize_t diff --git a/src/box/sql.h b/src/box/sql.h index dc40c32e36..18611c52e2 100644 --- a/src/box/sql.h +++ b/src/box/sql.h @@ -376,6 +376,8 @@ struct vdbe_field_ref { uint32_t data_sz; /** Count of fields in tuple. */ uint32_t field_count; + /** Number of allocated slots. */ + uint32_t field_capacity; /** Format that match data in field data. */ struct tuple_format *format; /** @@ -394,8 +396,8 @@ struct vdbe_field_ref { }; /** - * Initialize a new vdbe_field_ref instance with given tuple - * data. + * Fill vdbe_field_ref instance with given tuple data. + * * @param field_ref The vdbe_field_ref instance to initialize. * @param data The tuple data. * @param data_sz The size of tuple data. @@ -405,8 +407,8 @@ vdbe_field_ref_prepare_data(struct vdbe_field_ref *field_ref, const char *data, uint32_t data_sz); /** - * Initialize a new vdbe_field_ref instance with given tuple - * data. + * Fill vdbe_field_ref instance with given tuple data. + * * @param field_ref The vdbe_field_ref instance to initialize. * @param tuple The tuple object pointer. */ @@ -414,6 +416,10 @@ void vdbe_field_ref_prepare_tuple(struct vdbe_field_ref *field_ref, struct tuple *tuple); +/** Initialize a new vdbe_field_ref instance. */ +void +vdbe_field_ref_create(struct vdbe_field_ref *ref, uint32_t capacity); + #if defined(__cplusplus) } /* extern "C" { */ #endif diff --git a/src/box/sql/func.c b/src/box/sql/func.c index a6a0148793..e7079fafb9 100644 --- a/src/box/sql/func.c +++ b/src/box/sql/func.c @@ -2443,6 +2443,7 @@ func_sql_expr_call(struct func *func, struct port *args, struct port *ret) struct vdbe_field_ref *ref; size_t size = sizeof(ref->slots[0]) * count + sizeof(*ref); ref = region_aligned_alloc(region, size, alignof(*ref)); + vdbe_field_ref_create(ref, count); vdbe_field_ref_prepare_data(ref, data, mp_size); ref->format = format; if (sql_bind_ptr(stmt, 1, ref) != 0) diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 29a8be7834..15ed3acce7 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -206,6 +206,7 @@ allocateCursor( memset(pCx, 0, offsetof(VdbeCursor,uc)); pCx->eCurType = eCurType; pCx->nField = nField; + vdbe_field_ref_create(&pCx->field_ref, nField); if (eCurType==CURTYPE_TARANTOOL) { pCx->uc.pCursor = (BtCursor*)&pMem->z[bt_offset]; sqlCursorZero(pCx->uc.pCursor); diff --git a/test/sql-luatest/gh_5310_cursor_invalidation_test.lua b/test/sql-luatest/gh_5310_cursor_invalidation_test.lua index 508249ad94..496f05b920 100644 --- a/test/sql-luatest/gh_5310_cursor_invalidation_test.lua +++ b/test/sql-luatest/gh_5310_cursor_invalidation_test.lua @@ -11,7 +11,8 @@ g.after_all(function() g.server:stop() end) -g.test_cursor_invalidation = function() +-- Make sure that tuples with undescribed fields do not cause an error. +g.test_cursor_invalidation_1 = function() g.server:exec(function() local t = require('luatest') local s = box.schema.space.create('T', {format = {'A'}}) @@ -22,3 +23,19 @@ g.test_cursor_invalidation = function() s:drop() end) end + +-- +-- Make sure that tuples with fields described in tuple format but not described +-- in space format do not cause an error. +-- +g.test_cursor_invalidation_2 = function() + g.server:exec(function() + local t = require('luatest') + local s = box.schema.space.create('T', {format = {{'A', 'integer'}}}) + s:create_index('ii', {parts = {{1, 'integer'}, {2, 'integer'}, + {3, 'integer'}, {4, 'integer'}}}) + s:insert({1,2,3,4}) + t.assert_equals(box.execute([[SELECT * FROM t;]]).rows, {{1}}) + s:drop() + end) +end -- GitLab