From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtpng3.m.smailru.net (smtpng3.m.smailru.net [94.100.177.149]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id CA87B469719 for ; Fri, 13 Mar 2020 17:06:22 +0300 (MSK) From: olegrok@tarantool.org Date: Fri, 13 Mar 2020 17:06:12 +0300 Message-Id: <20200313140612.29775-1-olegrok@tarantool.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH v3] box: allow to retrieve the last generated value of sequence List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: tarantool-patches@dev.tarantool.org, v.shpilevoy@tarantool.org, korablev@tarantool.org Cc: Oleg Babin From: Oleg Babin This patch introduces "current" function for sequences. It returns the last retrieved value of specified sequence or throws an error if no value has been generated yet. This patch partially reverts 3ff1f1e36e14381c0ebb5862943d4da281254767 (box: remove sequence_get) here similar function "get" was removed to avoid possible misleading with "currval" function of PosgreSQL that returns the last obtained value of the sequence in the scope of current session. In contrast "current" returns the last globally retrieved value of the sequence. Closes #4752 @TarantoolBot document Title: sequence:current() This patch introduces "current" function for sequences. It returns the last retrieved value of specified sequence or throws an error if no value has been generated yet ("next" has not been called yet or right after "reset" is called). Example: ```lua sq = box.schema.sequence.create('test') --- ... sq:current() --- - error: Sequence 'test' is not started ... sq:next() --- - 1 ... sq:current() --- - 1 ... sq:set(42) --- ... sq:current() --- - 42 ... sq:reset() --- ... sq:current() -- error --- - error: Sequence 'test' is not started ... ``` --- Issue: https://github.com/tarantool/tarantool/issues/4752 Branch: https://github.com/tarantool/tarantool/tree/olegrok/4752-sequence-current @ChangeLog - sequence:current() - a function to get sequence's current value, without changing it (gh-4752). v1: https://lists.tarantool.org/pipermail/tarantool-patches/2020-March/014683.html Changes: - Use ClientError instead of SequenceError v2: https://lists.tarantool.org/pipermail/tarantool-patches/2020-March/014699.html Changes: - Clarify description - Use FFI instead of Lua C API for box_sequence_current call extra/exports | 1 + src/box/box.cc | 14 ++++++++ src/box/box.h | 12 +++++++ src/box/errcode.h | 1 + src/box/lua/schema.lua | 13 +++++++ src/box/sequence.c | 12 ++++--- src/box/sequence.h | 7 ++-- src/box/sql/vdbe.c | 5 ++- test/box/misc.result | 1 + test/box/sequence.result | 73 ++++++++++++++++++++++++++++++++++++++ test/box/sequence.test.lua | 24 +++++++++++++ 11 files changed, 155 insertions(+), 8 deletions(-) diff --git a/extra/exports b/extra/exports index 3a0637317..cbb5adcf4 100644 --- a/extra/exports +++ b/extra/exports @@ -215,6 +215,7 @@ box_update box_upsert box_truncate box_sequence_next +box_sequence_current box_sequence_set box_sequence_reset box_index_iterator diff --git a/src/box/box.cc b/src/box/box.cc index 09dd67ab4..a5052dba4 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -1414,6 +1414,20 @@ box_sequence_next(uint32_t seq_id, int64_t *result) *result = value; return 0; } + +int +box_sequence_current(uint32_t seq_id, int64_t *result) +{ + struct sequence *seq = sequence_cache_find(seq_id); + if (seq == NULL) + return -1; + if (access_check_sequence(seq) != 0) + return -1; + if (sequence_get_value(seq, result) != 0) + return -1; + return 0; +} + int box_sequence_set(uint32_t seq_id, int64_t value) { diff --git a/src/box/box.h b/src/box/box.h index f37a945eb..044d929d4 100644 --- a/src/box/box.h +++ b/src/box/box.h @@ -423,6 +423,18 @@ box_truncate(uint32_t space_id); API_EXPORT int box_sequence_next(uint32_t seq_id, int64_t *result); +/** + * Get the last value returned by a sequence. + * + * \param seq_id sequence identifier + * \param[out] result pointer to a variable where the current sequence + * value will be stored on success + * \retval -1 on error (check box_error_last()) + * \retval 0 on success + */ +API_EXPORT int +box_sequence_current(uint32_t seq_id, int64_t *result); + /** * Set a sequence value. * diff --git a/src/box/errcode.h b/src/box/errcode.h index d7ec97e8c..444171778 100644 --- a/src/box/errcode.h +++ b/src/box/errcode.h @@ -264,6 +264,7 @@ struct errcode_record { /*209 */_(ER_SESSION_SETTING_INVALID_VALUE, "Session setting %s expected a value of type %s") \ /*210 */_(ER_SQL_PREPARE, "Failed to prepare SQL statement: %s") \ /*211 */_(ER_WRONG_QUERY_ID, "Prepared statement with id %u does not exist") \ + /*212 */_(ER_SEQUENCE_NOT_STARTED, "Sequence '%s' is not started") \ /* * !IMPORTANT! Please follow instructions at start of the file diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index aba439ffb..55efb9896 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -72,6 +72,10 @@ ffi.cdef[[ int box_txn_begin(); /** \endcond public */ + /** \cond public */ + int + box_sequence_current(uint32_t seq_id, int64_t *result); + /** \endcond public */ typedef struct txn_savepoint box_txn_savepoint_t; box_txn_savepoint_t * @@ -1812,6 +1816,15 @@ sequence_mt.next = function(self) return internal.sequence.next(self.id) end +local sequence_value = ffi.new('int64_t[1]') +sequence_mt.current = function(self) + local rc = builtin.box_sequence_current(self.id, sequence_value) + if rc < 0 then + box.error(box.error.last()) + end + return sequence_value[0] +end + sequence_mt.set = function(self, value) return internal.sequence.set(self.id, value) end diff --git a/src/box/sequence.c b/src/box/sequence.c index 5ebfa2747..4afbc2666 100644 --- a/src/box/sequence.c +++ b/src/box/sequence.c @@ -367,14 +367,18 @@ sequence_data_iterator_create(void) return &iter->base; } -int64_t -sequence_get_value(struct sequence *seq) +int +sequence_get_value(struct sequence *seq, int64_t *result) { uint32_t key = seq->def->id; uint32_t hash = sequence_hash(key); uint32_t pos = light_sequence_find_key(&sequence_data_index, hash, key); - assert(pos != light_sequence_end); + if (pos == light_sequence_end) { + diag_set(ClientError, ER_SEQUENCE_NOT_STARTED, seq->def->name); + return -1; + } struct sequence_data data = light_sequence_get(&sequence_data_index, pos); - return data.value; + *result = data.value; + return 0; } diff --git a/src/box/sequence.h b/src/box/sequence.h index a164da9af..8c442872a 100644 --- a/src/box/sequence.h +++ b/src/box/sequence.h @@ -171,10 +171,11 @@ sequence_data_iterator_create(void); * Get last element of given sequence. * * @param seq sequence to get value from. - * @retval last element of sequence. + * @result value of sequence. + * Return 0 on success, -1 if sequence is not initialized. */ -int64_t -sequence_get_value(struct sequence *seq); +int +sequence_get_value(struct sequence *seq, int64_t *result); #if defined(__cplusplus) } /* extern "C" */ diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 912a10153..0de07ca1d 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -4328,7 +4328,10 @@ case OP_IdxInsert: { p->nChange++; if (pOp->p3 > 0 && ((aMem[pOp->p3].flags) & MEM_Null) != 0) { assert(space->sequence != NULL); - int64_t value = sequence_get_value(space->sequence); + int64_t value; + if (sequence_get_value(space->sequence, &value) != 0) { + goto abort_due_to_error; + } if (vdbe_add_new_autoinc_id(p, value) != 0) goto abort_due_to_error; } diff --git a/test/box/misc.result b/test/box/misc.result index 5ac5e0f26..047591b60 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -636,6 +636,7 @@ t; 209: box.error.SESSION_SETTING_INVALID_VALUE 210: box.error.SQL_PREPARE 211: box.error.WRONG_QUERY_ID + 212: box.error.SEQUENCE_NOT_STARTED ... test_run:cmd("setopt delimiter ''"); --- diff --git a/test/box/sequence.result b/test/box/sequence.result index 0a6cfee2c..e1ac30fd1 100644 --- a/test/box/sequence.result +++ b/test/box/sequence.result @@ -188,10 +188,18 @@ sq.step, sq.min, sq.max, sq.start, sq.cycle - 1 - false ... +sq:current() -- error +--- +- error: Sequence 'test' is not started +... sq:next() -- 1 --- - 1 ... +sq:current() -- 1 +--- +- 1 +... sq:next() -- 2 --- - 2 @@ -199,6 +207,10 @@ sq:next() -- 2 sq:set(100) --- ... +sq:current() -- 100 +--- +- 100 +... sq:next() -- 101 --- - 101 @@ -210,6 +222,10 @@ sq:next() -- 102 sq:reset() --- ... +sq:current() -- error +--- +- error: Sequence 'test' is not started +... sq:next() -- 1 --- - 1 @@ -233,10 +249,18 @@ sq.step, sq.min, sq.max, sq.start, sq.cycle - -1 - false ... +sq:current() -- error +--- +- error: Sequence 'test' is not started +... sq:next() -- -1 --- - -1 ... +sq:current() -- -1 +--- +- -1 +... sq:next() -- -2 --- - -2 @@ -244,6 +268,10 @@ sq:next() -- -2 sq:set(-100) --- ... +sq:current() -- -100 +--- +- -100 +... sq:next() -- -101 --- - -101 @@ -255,6 +283,10 @@ sq:next() -- -102 sq:reset() --- ... +sq:current() -- error +--- +- error: Sequence 'test' is not started +... sq:next() -- -1 --- - -1 @@ -2289,3 +2321,44 @@ box.space._space_sequence:update({s.id}, {{'=', 2, t[2]}}) s:drop() --- ... +-- +-- gh-4752: introduce sequence:current() method which +-- fetches current sequence value but doesn't modify +-- sequence itself. +-- +sq = box.schema.sequence.create('test') +--- +... +sq:current() +--- +- error: Sequence 'test' is not started +... +sq:next() +--- +- 1 +... +sq:current() +--- +- 1 +... +sq:set(42) +--- +... +sq:current() +--- +- 42 +... +sq:current() +--- +- 42 +... +sq:reset() +--- +... +sq:current() +--- +- error: Sequence 'test' is not started +... +sq:drop() +--- +... diff --git a/test/box/sequence.test.lua b/test/box/sequence.test.lua index 8e00571e5..6b8e847b6 100644 --- a/test/box/sequence.test.lua +++ b/test/box/sequence.test.lua @@ -58,12 +58,16 @@ box.sequence.test == nil -- Default ascending sequence. sq = box.schema.sequence.create('test') sq.step, sq.min, sq.max, sq.start, sq.cycle +sq:current() -- error sq:next() -- 1 +sq:current() -- 1 sq:next() -- 2 sq:set(100) +sq:current() -- 100 sq:next() -- 101 sq:next() -- 102 sq:reset() +sq:current() -- error sq:next() -- 1 sq:next() -- 2 sq:drop() @@ -71,12 +75,16 @@ sq:drop() -- Default descending sequence. sq = box.schema.sequence.create('test', {step = -1}) sq.step, sq.min, sq.max, sq.start, sq.cycle +sq:current() -- error sq:next() -- -1 +sq:current() -- -1 sq:next() -- -2 sq:set(-100) +sq:current() -- -100 sq:next() -- -101 sq:next() -- -102 sq:reset() +sq:current() -- error sq:next() -- -1 sq:next() -- -2 sq:drop() @@ -780,3 +788,19 @@ pk = s:create_index('pk', {sequence = true}) t = box.space._space_sequence:get({s.id}) box.space._space_sequence:update({s.id}, {{'=', 2, t[2]}}) s:drop() + +-- +-- gh-4752: introduce sequence:current() method which +-- fetches current sequence value but doesn't modify +-- sequence itself. +-- +sq = box.schema.sequence.create('test') +sq:current() +sq:next() +sq:current() +sq:set(42) +sq:current() +sq:current() +sq:reset() +sq:current() +sq:drop() -- 2.23.0