diff --git a/src/box/alter.cc b/src/box/alter.cc index ce7060c6c8f9f011b469440d53d2c0ed64af9e1d..e47ead8cd3b66e7381ab5e7682c5c409a79d81eb 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -63,13 +63,6 @@ /* {{{ Auxiliary functions and methods. */ -static void -box_schema_version_bump(void) -{ - ++schema_version; - box_broadcast_schema(); -} - int access_check_ddl(const char *name, uint32_t object_id, uint32_t owner_uid, enum box_schema_object_type type, @@ -1584,6 +1577,14 @@ TruncateIndex::~TruncateIndex() index_abort_create(new_index); } +static inline void +schema_version_bump(struct space *space) +{ + if (!space_is_temporary(space)) + box_bump_schema_version(); + stmt_cache_bump_schema_version(); +} + /** * UpdateSchemaVersion - increment schema_version. Used on * in alter_space_do(), i.e. when creating or dropping @@ -1600,8 +1601,7 @@ class UpdateSchemaVersion: public AlterSpaceOp void UpdateSchemaVersion::alter(struct alter_space *alter) { - (void)alter; - box_schema_version_bump(); + schema_version_bump(alter->old_space); } /** @@ -2185,7 +2185,7 @@ on_replace_dd_space(struct trigger * /* trigger */, void *event) * AlterSpaceOps are registered in case of space * create. */ - box_schema_version_bump(); + schema_version_bump(space); /* * So may happen that until the DDL change record * is written to the WAL, the space is used for @@ -2279,7 +2279,7 @@ on_replace_dd_space(struct trigger * /* trigger */, void *event) * deleting the space from the space_cache, since no * AlterSpaceOps are registered in case of space drop. */ - box_schema_version_bump(); + schema_version_bump(old_space); struct trigger *on_commit = txn_alter_trigger_new(on_drop_space_commit, old_space); if (on_commit == NULL) @@ -5002,7 +5002,12 @@ on_replace_dd_trigger(struct trigger * /* trigger */, void *event) txn_stmt_on_rollback(stmt, on_rollback); txn_stmt_on_commit(stmt, on_commit); - box_schema_version_bump(); + /* + * The triggers are not supported for temporary spaces, + * so we always bump box schema version. + */ + box_bump_schema_version(); + stmt_cache_bump_schema_version(); return 0; } diff --git a/src/box/execute.c b/src/box/execute.c index ff544132f0e710dc9e45277cd8942887b48d768b..9e221bc5986c44e548be38ab52d7f881a94dbd71 100644 --- a/src/box/execute.c +++ b/src/box/execute.c @@ -85,7 +85,7 @@ sql_row_to_port(struct sql_stmt *stmt, struct region *region, struct port *port) static bool sql_stmt_schema_version_is_valid(struct sql_stmt *stmt) { - return sql_stmt_schema_version(stmt) == box_schema_version(); + return sql_stmt_schema_version(stmt) == stmt_cache_schema_version(); } /** diff --git a/src/box/iproto.cc b/src/box/iproto.cc index c9c88da2f8883b2315aab2a415fcc9249dba575b..7a6cde0a382f94ecfefe65f59db8d04d1f9c4d76 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -58,7 +58,7 @@ #include "tuple_convert.h" #include "session.h" #include "xrow.h" -#include "schema.h" /* schema_version */ +#include "schema.h" #include "replication.h" /* instance_uuid */ #include "iproto_constants.h" #include "iproto_features.h" @@ -1277,7 +1277,7 @@ iproto_connection_resume(struct iproto_connection *con) if (iproto_enqueue_batch(con, con->p_ibuf) != 0) { struct error *e = box_error_last(); error_log(e); - iproto_write_error(&con->io, e, ::schema_version, 0); + iproto_write_error(&con->io, e, box_schema_version(), 0); iproto_connection_close(con); } } @@ -1366,7 +1366,7 @@ iproto_connection_on_input(ev_loop *loop, struct ev_io *watcher, } catch (Exception *e) { e->log(); /* Best effort at sending the error message to the client. */ - iproto_write_error(io, e, ::schema_version, 0); + iproto_write_error(io, e, box_schema_version(), 0); iproto_connection_close(con); } } @@ -2020,9 +2020,10 @@ static int tx_check_msg(struct iproto_msg *msg) { uint64_t new_schema_version = msg->header.schema_version; - if (new_schema_version != 0 && new_schema_version != schema_version) { + if (new_schema_version != 0 && + new_schema_version != box_schema_version()) { diag_set(ClientError, ER_WRONG_SCHEMA_VERSION, - new_schema_version, schema_version); + new_schema_version, box_schema_version()); return -1; } enum iproto_type type = (enum iproto_type)msg->header.type; @@ -2058,7 +2059,7 @@ tx_reply_error(struct iproto_msg *msg) { struct obuf *out = msg->connection->tx.p_obuf; iproto_reply_error(out, diag_last_error(&fiber()->diag), - msg->header.sync, ::schema_version); + msg->header.sync, box_schema_version()); iproto_wpos_create(&msg->wpos, out); } @@ -2073,7 +2074,7 @@ tx_reply_iproto_error(struct cmsg *m) struct obuf *out = msg->connection->tx.p_obuf; struct obuf_svp header = obuf_create_svp(out); iproto_reply_error(out, diag_last_error(&msg->diag), - msg->header.sync, ::schema_version); + msg->header.sync, box_schema_version()); iproto_wpos_create(&msg->wpos, out); tx_end_msg(msg, &header); } @@ -2117,7 +2118,7 @@ tx_process_begin(struct cmsg *m) } out = msg->connection->tx.p_obuf; header = obuf_create_svp(out); - iproto_reply_ok(out, msg->header.sync, ::schema_version); + iproto_reply_ok(out, msg->header.sync, box_schema_version()); iproto_wpos_create(&msg->wpos, out); tx_end_msg(msg, &header); return; @@ -2143,7 +2144,7 @@ tx_process_commit(struct cmsg *m) out = msg->connection->tx.p_obuf; header = obuf_create_svp(out); - iproto_reply_ok(out, msg->header.sync, ::schema_version); + iproto_reply_ok(out, msg->header.sync, box_schema_version()); iproto_wpos_create(&msg->wpos, out); tx_end_msg(msg, &header); return; @@ -2169,7 +2170,7 @@ tx_process_rollback(struct cmsg *m) out = msg->connection->tx.p_obuf; header = obuf_create_svp(out); - iproto_reply_ok(out, msg->header.sync, ::schema_version); + iproto_reply_ok(out, msg->header.sync, box_schema_version()); iproto_wpos_create(&msg->wpos, out); tx_end_msg(msg, &header); return; @@ -2198,7 +2199,7 @@ tx_process1(struct cmsg *m) goto error; if (tuple && tuple_to_obuf(tuple, out)) goto error; - iproto_reply_select(out, &svp, msg->header.sync, ::schema_version, + iproto_reply_select(out, &svp, msg->header.sync, box_schema_version(), tuple != 0); iproto_wpos_create(&msg->wpos, out); tx_end_msg(msg, &svp); @@ -2268,13 +2269,14 @@ tx_process_select(struct cmsg *m) assert(packed_pos != NULL); if (iproto_reply_select_with_position(out, &svp, msg->header.sync, - ::schema_version, count, + box_schema_version(), + count, packed_pos, packed_pos_end) != 0) goto discard; } else { iproto_reply_select(out, &svp, msg->header.sync, - ::schema_version, count); + box_schema_version(), count); } region_truncate(&fiber()->gc, region_svp); iproto_wpos_create(&msg->wpos, out); @@ -2390,7 +2392,7 @@ tx_process_call(struct cmsg *m) } iproto_reply_select(out, &svp, msg->header.sync, - ::schema_version, count); + box_schema_version(), count); iproto_wpos_create(&msg->wpos, out); tx_end_msg(msg, &svp); return; @@ -2432,26 +2434,26 @@ tx_process_misc(struct cmsg *m) box_process_auth(&msg->auth, con->salt, IPROTO_SALT_SIZE); iproto_reply_ok_xc(out, msg->header.sync, - ::schema_version); + box_schema_version()); break; case IPROTO_PING: iproto_reply_ok_xc(out, msg->header.sync, - ::schema_version); + box_schema_version()); break; case IPROTO_ID: tx_process_id(con, &msg->id); iproto_reply_id_xc(out, box_auth_type, msg->header.sync, - ::schema_version); + box_schema_version()); break; case IPROTO_VOTE_DEPRECATED: iproto_reply_vclock_xc(out, &replicaset.vclock, msg->header.sync, - ::schema_version); + box_schema_version()); break; case IPROTO_VOTE: box_process_vote(&ballot); iproto_reply_vote_xc(out, &ballot, msg->header.sync, - ::schema_version); + box_schema_version()); break; case IPROTO_WATCH: session_watch(con->session, msg->header.sync, @@ -2558,7 +2560,8 @@ tx_process_sql(struct cmsg *m) struct obuf_svp header_svp; if (is_unprepare) { header_svp = obuf_create_svp(out); - if (iproto_reply_ok(out, msg->header.sync, schema_version) != 0) + if (iproto_reply_ok(out, msg->header.sync, + box_schema_version()) != 0) goto error; iproto_wpos_create(&msg->wpos, out); tx_end_msg(msg, &header_svp); @@ -2575,7 +2578,8 @@ tx_process_sql(struct cmsg *m) goto error; } port_destroy(&port); - iproto_reply_sql(out, &header_svp, msg->header.sync, schema_version); + iproto_reply_sql(out, &header_svp, msg->header.sync, + box_schema_version()); iproto_wpos_create(&msg->wpos, out); tx_end_msg(msg, &header_svp); return; @@ -2631,7 +2635,8 @@ tx_process_replication(struct cmsg *m) * written row. Do not push it on top. */ } catch (Exception *e) { - iproto_write_error(io, e, ::schema_version, msg->header.sync); + iproto_write_error(io, e, box_schema_version(), + msg->header.sync); } struct obuf_svp empty = obuf_create_svp(msg->connection->tx.p_obuf); tx_end_msg(msg, &empty); @@ -3065,7 +3070,7 @@ iproto_session_push(struct session *session, struct port *port) return -1; } iproto_reply_chunk(con->tx.p_obuf, &svp, iproto_session_sync(session), - ::schema_version); + box_schema_version()); tx_push(con, &svp); return 0; } diff --git a/src/box/lua/info.c b/src/box/lua/info.c index 7bb9f46b0086154bbb307a8381e2c7bbe12c25fe..3b802fc3ffa989ff236655e7fd561253bc57edd9 100644 --- a/src/box/lua/info.c +++ b/src/box/lua/info.c @@ -694,6 +694,13 @@ lbox_schema_version(struct lua_State *L) return 1; } +static int +lbox_stmt_cache_version(struct lua_State *L) +{ + luaL_pushuint64(L, stmt_cache_schema_version()); + return 1; +} + static const struct luaL_Reg lbox_info_dynamic_meta[] = { {"id", lbox_info_id}, {"uuid", lbox_info_uuid}, @@ -716,6 +723,7 @@ static const struct luaL_Reg lbox_info_dynamic_meta[] = { {"election", lbox_info_election}, {"synchro", lbox_info_synchro}, {"schema_version", lbox_schema_version}, + {"statement_version", lbox_stmt_cache_version}, {NULL, NULL} }; diff --git a/src/box/schema.cc b/src/box/schema.cc index 0f57bab7d5372b80b66b289f0a18af01776af44f..68c28caa4e53f07564173a79953974d06ce67522 100644 --- a/src/box/schema.cc +++ b/src/box/schema.cc @@ -29,6 +29,7 @@ * SUCH DAMAGE. */ #include "schema.h" +#include "box.h" #include "sequence.h" #include "assoc.h" #include "alter.h" @@ -57,9 +58,18 @@ */ static struct mh_i32ptr_t *sequences; + +/** Schema version. */ +struct version { + /** Version of the schema used for iproto operations via netbox. */ + uint64_t box; + /** Version of the schema used for statement cache invalidation. */ + uint64_t stmt_cache; +}; + /** Public change counter. On its update clients need to fetch * new space data from the instance. */ -uint64_t schema_version = 0; +struct version schema_version = {0, 0}; /** Persistent version of the schema, stored in _schema["version"]. */ uint32_t dd_version_id = 0; @@ -77,7 +87,26 @@ struct entity_access entity_access; API_EXPORT uint64_t box_schema_version(void) { - return schema_version; + return schema_version.box; +} + +void +box_bump_schema_version(void) +{ + schema_version.box++; + box_broadcast_schema(); +} + +uint64_t +stmt_cache_schema_version(void) +{ + return schema_version.stmt_cache; +} + +void +stmt_cache_bump_schema_version(void) +{ + schema_version.stmt_cache++; } uint32_t diff --git a/src/box/schema.h b/src/box/schema.h index 64bf799ccfc41a8adab1056d2aa0a52922d8a7d9..0e5ebf8bde6b4782f26922194d5ef8dc680d2a96 100644 --- a/src/box/schema.h +++ b/src/box/schema.h @@ -46,7 +46,7 @@ struct func; /** * See `box_schema_version`. */ -extern uint64_t schema_version; +extern struct version schema_version; extern uint32_t dd_version_id; /** Triggers invoked after schema initialization. */ @@ -66,11 +66,33 @@ dd_check_is_disabled(void); /** * Returns the current version of the database schema, an unsigned number * that goes up when there is a major change in the schema, i.e., on DDL - * operations (\sa IPROTO_SCHEMA_VERSION). + * operations that should be propagated to the netbox clients via IPROTO + * (\sa IPROTO_SCHEMA_VERSION). */ API_EXPORT uint64_t box_schema_version(void); +/** + * Bump the schema version for the netbox clients. + */ +void +box_bump_schema_version(void); + +/** + * Returns the current version of the database schema, an unsigned number + * that goes up on every schema change. Current schema version is used to + * invalidate prepared statements in the statement cache if there was a + * schema change. + */ +uint64_t +stmt_cache_schema_version(void); + +/** + * Bump the schema version for the statement cache. + */ +void +stmt_cache_bump_schema_version(void); + /** \endcond public */ /** Return current persistent schema version. */ diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index a6561a3792425a436d275e3b80ee4c6ad776e6b8..746543f0c606c16f74f323cfd954381f323bdcc0 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -2379,7 +2379,7 @@ case OP_TTransaction: { */ case OP_IteratorOpen: { struct VdbeCursor *cur = p->apCsr[pOp->p1]; - if (box_schema_version() != p->schema_ver && + if (stmt_cache_schema_version() != p->schema_ver && (pOp->p5 & OPFLAG_SYSTEMSP) == 0) { p->expired = 1; diag_set(ClientError, ER_SQL_EXECUTE, "schema version has "\ diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c index ccc98ea038cccfd348c7a90bf7e4c0967c7a424b..1bb9576814a691410cd2c85ae189b1361d29b69c 100644 --- a/src/box/sql/vdbeaux.c +++ b/src/box/sql/vdbeaux.c @@ -67,7 +67,7 @@ sqlVdbeCreate(Parse * pParse) db->pVdbe = p; p->magic = VDBE_MAGIC_INIT; p->pParse = pParse; - p->schema_ver = box_schema_version(); + p->schema_ver = stmt_cache_schema_version(); assert(pParse->aLabel == 0); assert(pParse->nLabel == 0); assert(pParse->nOpAlloc == 0); diff --git a/test/box-luatest/fully-temporary_spaces_test.lua b/test/box-luatest/fully-temporary_spaces_test.lua index d1e1b1091d16d93763ea0eacd61526e9edef9a32..3da6cb9c276fd091e710da70be2d06abfbdf121b 100644 --- a/test/box-luatest/fully-temporary_spaces_test.lua +++ b/test/box-luatest/fully-temporary_spaces_test.lua @@ -155,6 +155,41 @@ g.test_temporary_create = function() end) end +-- check that temporary spaces don't update box schema version +-- but still update the statement one. +g.test_schema_version = function() + g.server:exec(function() + local box_version = box.info.schema_version + local stmt_cache_version = box.info.statement_version + local op = 0 + local s = box.schema.space.create('temp', { type = 'temporary' }) + op = op + 1 + t.assert_equals(box.info.schema_version, box_version) + t.assert_equals(box.info.statement_version, stmt_cache_version + op) + + s:format {{'a', 'unsigned'}} + op = op + 1 + t.assert_equals(box.info.schema_version, box_version) + t.assert_equals(box.info.statement_version, stmt_cache_version + op) + + s:create_index('pk') + op = op + 1 + t.assert_equals(box.info.schema_version, box_version) + t.assert_equals(box.info.statement_version, stmt_cache_version + op) + + s:truncate() + -- truncate doesn't update statement version + t.assert_equals(box.info.schema_version, box_version) + t.assert_equals(box.info.statement_version, stmt_cache_version + op) + + s:drop() + -- drop the space with its primary key index + op = op + 2 + t.assert_equals(box.info.schema_version, box_version) + t.assert_equals(box.info.statement_version, stmt_cache_version + op) + end) +end + -- check which features aren't supported for temporary spaces g.test_temporary_dont_support = function() g.server:exec(function() diff --git a/test/box/info.result b/test/box/info.result index a7444cad39d72f66c9f11a7ccc8e6adbd21e12d3..0c7ad5bf4a82884fc5f4d99851d8fbd22fde3f10 100644 --- a/test/box/info.result +++ b/test/box/info.result @@ -89,6 +89,7 @@ t - schema_version - signature - sql + - statement_version - status - synchro - uptime