From 37214ba82c6a5bcc7fec7e9293a82a8ea9def773 Mon Sep 17 00:00:00 2001 From: xicilion Date: Mon, 18 Jan 2021 01:59:03 +0800 Subject: [PATCH] db, feat: support savepoint. --- fibjs/include/SQLite.h | 7 ++--- fibjs/include/ifs/DbConnection.h | 44 +++++++++++++++++++++----------- fibjs/include/mssql.h | 7 ++--- fibjs/include/mysql.h | 7 ++--- fibjs/src/db/sql/SQLite.cpp | 19 +++++++++----- fibjs/src/db/sql/db_api.h | 40 +++++++++++++++++++++-------- fibjs/src/db/sql/mssql.cpp | 19 +++++++++----- fibjs/src/db/sql/mysql.cpp | 19 +++++++++----- idl/zh-cn/DbConnection.idl | 33 +++++++++++++++++++----- test/db_test.js | 42 ++++++++++++++++++++++++++++++ 10 files changed, 175 insertions(+), 62 deletions(-) diff --git a/fibjs/include/SQLite.h b/fibjs/include/SQLite.h index 44445040d7..057b316de9 100644 --- a/fibjs/include/SQLite.h +++ b/fibjs/include/SQLite.h @@ -27,10 +27,11 @@ class SQLite : public SQLite_base { // DbConnection_base virtual result_t get_type(exlib::string& retVal); virtual result_t close(AsyncEvent* ac); - virtual result_t begin(AsyncEvent* ac); - virtual result_t commit(AsyncEvent* ac); - virtual result_t rollback(AsyncEvent* ac); + virtual result_t begin(exlib::string point, AsyncEvent* ac); + virtual result_t commit(exlib::string point, AsyncEvent* ac); + virtual result_t rollback(exlib::string point, AsyncEvent* ac); virtual result_t trans(v8::Local func, bool& retVal); + virtual result_t trans(exlib::string point, v8::Local func, bool& retVal); virtual result_t execute(exlib::string sql, OptArgs args, obj_ptr& retVal, AsyncEvent* ac); virtual result_t createTable(v8::Local opts, AsyncEvent* ac); virtual result_t dropTable(v8::Local opts, AsyncEvent* ac); diff --git a/fibjs/include/ifs/DbConnection.h b/fibjs/include/ifs/DbConnection.h index 5bf0272fa9..695d717c25 100644 --- a/fibjs/include/ifs/DbConnection.h +++ b/fibjs/include/ifs/DbConnection.h @@ -23,10 +23,11 @@ class DbConnection_base : public object_base { // DbConnection_base virtual result_t get_type(exlib::string& retVal) = 0; virtual result_t close(AsyncEvent* ac) = 0; - virtual result_t begin(AsyncEvent* ac) = 0; - virtual result_t commit(AsyncEvent* ac) = 0; - virtual result_t rollback(AsyncEvent* ac) = 0; + virtual result_t begin(exlib::string point, AsyncEvent* ac) = 0; + virtual result_t commit(exlib::string point, AsyncEvent* ac) = 0; + virtual result_t rollback(exlib::string point, AsyncEvent* ac) = 0; virtual result_t trans(v8::Local func, bool& retVal) = 0; + virtual result_t trans(exlib::string point, v8::Local func, bool& retVal) = 0; virtual result_t execute(exlib::string sql, OptArgs args, obj_ptr& retVal, AsyncEvent* ac) = 0; virtual result_t createTable(v8::Local opts, AsyncEvent* ac) = 0; virtual result_t dropTable(v8::Local opts, AsyncEvent* ac) = 0; @@ -72,9 +73,9 @@ class DbConnection_base : public object_base { public: ASYNC_MEMBER0(DbConnection_base, close); - ASYNC_MEMBER0(DbConnection_base, begin); - ASYNC_MEMBER0(DbConnection_base, commit); - ASYNC_MEMBER0(DbConnection_base, rollback); + ASYNC_MEMBER1(DbConnection_base, begin, exlib::string); + ASYNC_MEMBER1(DbConnection_base, commit, exlib::string); + ASYNC_MEMBER1(DbConnection_base, rollback, exlib::string); ASYNC_MEMBERVALUE3(DbConnection_base, execute, exlib::string, OptArgs, obj_ptr); ASYNC_MEMBER1(DbConnection_base, createTable, v8::Local); ASYNC_MEMBER1(DbConnection_base, dropTable, v8::Local); @@ -173,12 +174,14 @@ inline void DbConnection_base::s_begin(const v8::FunctionCallbackInfo METHOD_INSTANCE(DbConnection_base); METHOD_ENTER(); - ASYNC_METHOD_OVER(0, 0); + ASYNC_METHOD_OVER(1, 0); + + OPT_ARG(exlib::string, 0, ""); if (!cb.IsEmpty()) - hr = pInst->acb_begin(cb, args); + hr = pInst->acb_begin(v0, cb, args); else - hr = pInst->ac_begin(); + hr = pInst->ac_begin(v0); METHOD_VOID(); } @@ -189,12 +192,14 @@ inline void DbConnection_base::s_commit(const v8::FunctionCallbackInfoacb_commit(cb, args); + hr = pInst->acb_commit(v0, cb, args); else - hr = pInst->ac_commit(); + hr = pInst->ac_commit(v0); METHOD_VOID(); } @@ -205,12 +210,14 @@ inline void DbConnection_base::s_rollback(const v8::FunctionCallbackInfoacb_rollback(cb, args); + hr = pInst->acb_rollback(v0, cb, args); else - hr = pInst->ac_rollback(); + hr = pInst->ac_rollback(v0); METHOD_VOID(); } @@ -229,6 +236,13 @@ inline void DbConnection_base::s_trans(const v8::FunctionCallbackInfo hr = pInst->trans(v0, vr); + METHOD_OVER(2, 2); + + ARG(exlib::string, 0); + ARG(v8::Local, 1); + + hr = pInst->trans(v0, v1, vr); + METHOD_RETURN(); } diff --git a/fibjs/include/mssql.h b/fibjs/include/mssql.h index 172bc715c1..aeb040444e 100644 --- a/fibjs/include/mssql.h +++ b/fibjs/include/mssql.h @@ -29,10 +29,11 @@ class mssql : public MSSQL_base { // DbConnection_base virtual result_t get_type(exlib::string& retVal); virtual result_t close(AsyncEvent* ac); - virtual result_t begin(AsyncEvent* ac); - virtual result_t commit(AsyncEvent* ac); - virtual result_t rollback(AsyncEvent* ac); + virtual result_t begin(exlib::string point, AsyncEvent* ac); + virtual result_t commit(exlib::string point, AsyncEvent* ac); + virtual result_t rollback(exlib::string point, AsyncEvent* ac); virtual result_t trans(v8::Local func, bool& retVal); + virtual result_t trans(exlib::string point, v8::Local func, bool& retVal); virtual result_t execute(exlib::string sql, OptArgs args, obj_ptr& retVal, AsyncEvent* ac); virtual result_t createTable(v8::Local opts, AsyncEvent* ac); virtual result_t dropTable(v8::Local opts, AsyncEvent* ac); diff --git a/fibjs/include/mysql.h b/fibjs/include/mysql.h index 7dd44a6506..ffdd052c69 100644 --- a/fibjs/include/mysql.h +++ b/fibjs/include/mysql.h @@ -28,10 +28,11 @@ class mysql : public MySQL_base { // DbConnection_base virtual result_t get_type(exlib::string& retVal); virtual result_t close(AsyncEvent* ac); - virtual result_t begin(AsyncEvent* ac); - virtual result_t commit(AsyncEvent* ac); - virtual result_t rollback(AsyncEvent* ac); + virtual result_t begin(exlib::string point, AsyncEvent* ac); + virtual result_t commit(exlib::string point, AsyncEvent* ac); + virtual result_t rollback(exlib::string point, AsyncEvent* ac); virtual result_t trans(v8::Local func, bool& retVal); + virtual result_t trans(exlib::string point, v8::Local func, bool& retVal); virtual result_t execute(exlib::string sql, OptArgs args, obj_ptr& retVal, AsyncEvent* ac); virtual result_t createTable(v8::Local opts, AsyncEvent* ac); virtual result_t dropTable(v8::Local opts, AsyncEvent* ac); diff --git a/fibjs/src/db/sql/SQLite.cpp b/fibjs/src/db/sql/SQLite.cpp index d0c32b3f75..e9aa7fdacd 100644 --- a/fibjs/src/db/sql/SQLite.cpp +++ b/fibjs/src/db/sql/SQLite.cpp @@ -327,24 +327,29 @@ result_t SQLite::execute(const char* sql, int32_t sLen, obj_ptr& retVal) return 0; } -result_t SQLite::begin(AsyncEvent* ac) +result_t SQLite::begin(exlib::string point, AsyncEvent* ac) { - return db_begin(this, ac); + return db_begin(this, point, ac); } -result_t SQLite::commit(AsyncEvent* ac) +result_t SQLite::commit(exlib::string point, AsyncEvent* ac) { - return db_commit(this, ac); + return db_commit(this, point, ac); } -result_t SQLite::rollback(AsyncEvent* ac) +result_t SQLite::rollback(exlib::string point, AsyncEvent* ac) { - return db_rollback(this, ac); + return db_rollback(this, point, ac); } result_t SQLite::trans(v8::Local func, bool& retVal) { - return db_trans(this, func, retVal); + return trans("", func, retVal); +} + +result_t SQLite::trans(exlib::string point, v8::Local func, bool& retVal) +{ + return db_trans(this, point, func, retVal); } result_t SQLite::execute(exlib::string sql, OptArgs args, obj_ptr& retVal, diff --git a/fibjs/src/db/sql/db_api.h b/fibjs/src/db/sql/db_api.h index 34b7fa96e3..9b4f23f96f 100644 --- a/fibjs/src/db/sql/db_api.h +++ b/fibjs/src/db/sql/db_api.h @@ -147,7 +147,7 @@ result_t db_remove(T* pThis, v8::Local opts, int32_t& retVal, } template -result_t db_begin(T* pThis, AsyncEvent* ac) +result_t db_begin(T* pThis, exlib::string point, AsyncEvent* ac, bool mssql = false) { if (!pThis->m_conn) return CHECK_ERROR(CALL_E_INVALID_CALL); @@ -156,11 +156,17 @@ result_t db_begin(T* pThis, AsyncEvent* ac) return CHECK_ERROR(CALL_E_NOSYNC); obj_ptr retVal; - return pThis->execute("BEGIN", 5, retVal); + + if (point.empty()) + return pThis->execute("BEGIN", 5, retVal); + else { + exlib::string str((mssql ? "SAVE TRANSACTION " : "SAVEPOINT ") + point); + return pThis->execute(str.c_str(), str.length(), retVal); + } } template -result_t db_commit(T* pThis, AsyncEvent* ac) +result_t db_commit(T* pThis, exlib::string point, AsyncEvent* ac, bool mssql = false) { if (!pThis->m_conn) return CHECK_ERROR(CALL_E_INVALID_CALL); @@ -169,11 +175,17 @@ result_t db_commit(T* pThis, AsyncEvent* ac) return CHECK_ERROR(CALL_E_NOSYNC); obj_ptr retVal; - return pThis->execute("COMMIT", 6, retVal); + + if (point.empty()) + return pThis->execute("COMMIT", 6, retVal); + else { + exlib::string str((mssql ? "COMMIT TRANSACTION " : "RELEASE SAVEPOINT ") + point); + return pThis->execute(str.c_str(), str.length(), retVal); + } } template -result_t db_rollback(T* pThis, AsyncEvent* ac) +result_t db_rollback(T* pThis, exlib::string point, AsyncEvent* ac, bool mssql = false) { if (!pThis->m_conn) return CHECK_ERROR(CALL_E_INVALID_CALL); @@ -182,17 +194,23 @@ result_t db_rollback(T* pThis, AsyncEvent* ac) return CHECK_ERROR(CALL_E_NOSYNC); obj_ptr retVal; - return pThis->execute("ROLLBACK", 8, retVal); + + if (point.empty()) + return pThis->execute("ROLLBACK", 8, retVal); + else { + exlib::string str((mssql ? "ROLLBACK TRANSACTION " : "ROLLBACK TO ") + point); + return pThis->execute(str.c_str(), str.length(), retVal); + } } template -inline result_t db_trans(T* pThis, v8::Local func, bool& retVal) +inline result_t db_trans(T* pThis, exlib::string point, v8::Local func, bool& retVal) { v8::Local v = pThis->wrap(); result_t hr = 0; retVal = false; - hr = pThis->ac_begin(); + hr = pThis->ac_begin(point); if (hr < 0) return hr; @@ -201,15 +219,15 @@ inline result_t db_trans(T* pThis, v8::Local func, bool& retVal) pThis->enter(); if (result.IsEmpty()) { - pThis->ac_rollback(); + pThis->ac_rollback(point); return CALL_E_JAVASCRIPT; } if (result->IsFalse()) - return pThis->ac_rollback(); + return pThis->ac_rollback(point); else { retVal = true; - return pThis->ac_commit(); + return pThis->ac_commit(point); } } } diff --git a/fibjs/src/db/sql/mssql.cpp b/fibjs/src/db/sql/mssql.cpp index 9ab3e61003..d00e511073 100644 --- a/fibjs/src/db/sql/mssql.cpp +++ b/fibjs/src/db/sql/mssql.cpp @@ -290,24 +290,29 @@ result_t mssql::execute(const char* sql, int32_t sLen, return 0; } -result_t mssql::begin(AsyncEvent* ac) +result_t mssql::begin(exlib::string point, AsyncEvent* ac) { - return db_begin(this, ac); + return db_begin(this, point, ac, true); } -result_t mssql::commit(AsyncEvent* ac) +result_t mssql::commit(exlib::string point, AsyncEvent* ac) { - return db_commit(this, ac); + return db_commit(this, point, ac, true); } -result_t mssql::rollback(AsyncEvent* ac) +result_t mssql::rollback(exlib::string point, AsyncEvent* ac) { - return db_rollback(this, ac); + return db_rollback(this, point, ac, true); } result_t mssql::trans(v8::Local func, bool& retVal) { - return db_trans(this, func, retVal); + return trans("", func, retVal); +} + +result_t mssql::trans(exlib::string point, v8::Local func, bool& retVal) +{ + return db_trans(this, point, func, retVal); } result_t mssql::execute(exlib::string sql, OptArgs args, obj_ptr& retVal, diff --git a/fibjs/src/db/sql/mysql.cpp b/fibjs/src/db/sql/mysql.cpp index cda4916acc..83b59f0464 100644 --- a/fibjs/src/db/sql/mysql.cpp +++ b/fibjs/src/db/sql/mysql.cpp @@ -286,24 +286,29 @@ result_t mysql::execute(const char* sql, int32_t sLen, return 0; } -result_t mysql::begin(AsyncEvent* ac) +result_t mysql::begin(exlib::string point, AsyncEvent* ac) { - return db_begin(this, ac); + return db_begin(this, point, ac); } -result_t mysql::commit(AsyncEvent* ac) +result_t mysql::commit(exlib::string point, AsyncEvent* ac) { - return db_commit(this, ac); + return db_commit(this, point, ac); } -result_t mysql::rollback(AsyncEvent* ac) +result_t mysql::rollback(exlib::string point, AsyncEvent* ac) { - return db_rollback(this, ac); + return db_rollback(this, point, ac); } result_t mysql::trans(v8::Local func, bool& retVal) { - return db_trans(this, func, retVal); + return trans("", func, retVal); +} + +result_t mysql::trans(exlib::string point, v8::Local func, bool& retVal) +{ + return db_trans(this, point, func, retVal); } result_t mysql::execute(exlib::string sql, OptArgs args, obj_ptr& retVal, diff --git a/idl/zh-cn/DbConnection.idl b/idl/zh-cn/DbConnection.idl index 4f00100058..88f2792e2d 100644 --- a/idl/zh-cn/DbConnection.idl +++ b/idl/zh-cn/DbConnection.idl @@ -7,14 +7,23 @@ interface DbConnection : object /*! @brief 关闭当前数据库连接 */ close() async; - /*! @brief 在当前数据库连接上启动一个事务 */ - begin() async; + /*! @brief 在当前数据库连接上启动一个事务 + + @param point 指定事务的名称,缺省不指定 + */ + begin(String point = "") async; - /*! @brief 提交当前数据库连接上的事务 */ - commit() async; + /*! @brief 提交当前数据库连接上的事务 + + @param point 指定事务的名称,缺省不指定 + */ + commit(String point = "") async; - /*! @brief 回滚当前数据库连接上的事务 */ - rollback() async; + /*! @brief 回滚当前数据库连接上的事务 + + @param point 指定事务的名称,缺省不指定 + */ + rollback(String point = "") async; /*! @brief 进入事务执行一个函数,并根据函数执行情况提交或者回滚 func 执行有三种结果: @@ -27,6 +36,18 @@ interface DbConnection : object */ Boolean trans(Function func); + /*! @brief 进入事务执行一个函数,并根据函数执行情况提交或者回滚 + func 执行有三种结果: + * 函数正常返回,包括运行结束和主动 return,此时事务将自动提交 + * 函数返回 false,此时事务将回滚 + * 函数运行错误,事务自动回滚 + + @param point 指定事务的名称 + @param func 以事务方式执行的函数 + @return 返回事务是否提交,正常 commit 时返回 true, rollback 时返回 false,如果事务出错则抛出错误 + */ + Boolean trans(String point, Function func); + /*! @brief 执行一个 sql 命令,并返回执行结果,可根据参数格式化字符串 @param sql 格式化字符串,可选参数用 ? 指定。例如:'SELECT FROM TEST WHERE [id]=?' diff --git a/test/db_test.js b/test/db_test.js index 516c2d9f0f..d7834984ba 100644 --- a/test/db_test.js +++ b/test/db_test.js @@ -769,6 +769,34 @@ describe("db", () => { assert.equal(rs[0].t2, "test101.1"); }); + describe("savepoint", () => { + it("begin/commit", () => { + conn.begin(); + + conn.begin('p0'); + conn.execute("update test set t2='test101.100' where t1=101"); + conn.commit('p0'); + + var rs = conn.execute("select * from test where t1=101"); + assert.equal(rs[0].t2, "test101.100"); + + conn.rollback(); + }); + + it("begin/rollback", () => { + conn.begin(); + + conn.begin('p0'); + conn.execute("update test set t2='test101.100' where t1=101"); + conn.rollback('p0'); + + var rs = conn.execute("select * from test where t1=101"); + assert.equal(rs[0].t2, "test101.1"); + + conn.rollback(); + }); + }); + it("begin/rollback", () => { conn.begin(); conn.execute("update test set t2='test101.2' where t1=101"); @@ -828,6 +856,20 @@ describe("db", () => { var rs = conn.execute("select * from test where t1=101"); assert.equal(rs[0].t2, "test101.2.1"); }); + + it("trans savepoint", () => { + conn.trans(function (conn1) { + conn.trans("point", function (conn2) { + this.execute("update test set t2='test101.300' where t1=101"); + return false; + }); + + var rs = conn.execute("select * from test where t1=101"); + assert.equal(rs[0].t2, "test101.2.1"); + + return false; + }); + }); }); });