diff --git a/src/box/txn.c b/src/box/txn.c index 1002c2136878f13becc5f56019d3c236e56faac5..38b1b595f03d9624b56b43dd7f66af645c21002b 100644 --- a/src/box/txn.c +++ b/src/box/txn.c @@ -750,10 +750,20 @@ txn_savepoint_new(struct txn *txn, const char *name) svp->stmt = stailq_last(&txn->stmts); svp->in_sub_stmt = txn->in_sub_stmt; svp->fk_deferred_count = txn->fk_deferred_count; - if (name != NULL) + if (name != NULL) { + /* + * If savepoint with given name already exists, + * erase it from the list. This has to be done + * in accordance with ANSI SQL compliance. + */ + struct txn_savepoint *old_svp = + txn_savepoint_by_name(txn, name); + if (old_svp != NULL) + rlist_del(&old_svp->link); memcpy(svp->name, name, name_len + 1); - else + } else { svp->name[0] = 0; + } rlist_add_entry(&txn->savepoints, svp, link); return svp; } diff --git a/test/sql/savepoints.result b/test/sql/savepoints.result index b5a0b7f4640f9642fe15f8477e2fbefd633ddf15..e48db302a8349e9064ceed6f83d7f2685560a20c 100644 --- a/test/sql/savepoints.result +++ b/test/sql/savepoints.result @@ -95,3 +95,27 @@ release_sv_fail(); box.commit(); --- ... +-- Make sure that if the current transaction has a savepoint +-- with the same name, the old savepoint is deleted and +-- a new one is set. Note that no error should be raised. +-- +collision_sv_2 = function() + box.begin() + box.execute('SAVEPOINT t1;') + box.execute('SAVEPOINT t2;') + local _,err = box.execute('SAVEPOINT t1;') + assert(err == nil) + box.execute('RELEASE SAVEPOINT t1;') + local _,err = box.execute('RELEASE SAVEPOINT t1;') + assert(err ~= nil) + local _, err = box.execute('ROLLBACK TO t2;') + assert(err == nil) +end; +--- +... +collision_sv_2(); +--- +... +box.commit(); +--- +... diff --git a/test/sql/savepoints.test.lua b/test/sql/savepoints.test.lua index a4312c114cdabeb0b4f7ee37217629a4885a0893..99622a4db01f9581793987a1eaf7de6ed761a154 100644 --- a/test/sql/savepoints.test.lua +++ b/test/sql/savepoints.test.lua @@ -56,3 +56,22 @@ release_sv_fail = function() end; release_sv_fail(); box.commit(); + +-- Make sure that if the current transaction has a savepoint +-- with the same name, the old savepoint is deleted and +-- a new one is set. Note that no error should be raised. +-- +collision_sv_2 = function() + box.begin() + box.execute('SAVEPOINT t1;') + box.execute('SAVEPOINT t2;') + local _,err = box.execute('SAVEPOINT t1;') + assert(err == nil) + box.execute('RELEASE SAVEPOINT t1;') + local _,err = box.execute('RELEASE SAVEPOINT t1;') + assert(err ~= nil) + local _, err = box.execute('ROLLBACK TO t2;') + assert(err == nil) +end; +collision_sv_2(); +box.commit();