From: dan Date: Fri, 12 Sep 2025 15:02:47 +0000 (+0000) Subject: Add experimental API sqlite3_set_errmsg(). Use this in sqlite3changeset_apply() to... X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d96852538216b79b7d03a5e1ce2a9a2343e1aca2;p=thirdparty%2Fsqlite.git Add experimental API sqlite3_set_errmsg(). Use this in sqlite3changeset_apply() to return any error code and error message via the SQLite handle. FossilOrigin-Name: 4d5b60a1e57448f03af2a657fe7cdabb04ebaf9688d5cc700dd8f9892a5cba15 --- diff --git a/ext/session/session9.test b/ext/session/session9.test index 5c406c344e..6207aae427 100644 --- a/ext/session/session9.test +++ b/ext/session/session9.test @@ -290,4 +290,39 @@ do_execsql_test 5.4 { SELECT * FROM X1; } {1 1} +#-------------------------------------------------------------------- +reset_db +do_execsql_test 6.0 { + CREATE TABLE p1(a INTEGER PRIMARY KEY, b); + CREATE TABLE c1(x INTEGER PRIMARY KEY, y REFERENCES p1); +} + +set ::cc [changeset_from_sql { + INSERT INTO c1 VALUES(10, 20); +}] + +forcedelete test.db2 +sqlite3 db2 test.db2 +do_execsql_test -db db2 6.1 { + CREATE TABLE c1(x INTEGER PRIMARY KEY, y REFERENCES p1); + PRAGMA foreign_keys = ON; + PRAGMA foreign_keys; +} {1} + +proc conflict_handler {args} { + puts $args + return "OMIT" +} + +do_test 6.2 { + list [catch {sqlite3changeset_apply_v2 db2 $::cc conflict_handler} msg] $msg +} {1 SQLITE_ERROR} + +do_test 6.3.1 { sqlite3_errmsg db2 } {no such table: main.p1} +do_test 6.3.2 { sqlite3_errcode db2 } {SQLITE_ERROR} + +do_test 6.4 { + catchsql { INSERT INTO c1 VALUES(100, 200) } db2 +} {1 {no such table: main.p1}} + finish_test diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index 81ba738340..586b51a83c 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -2815,6 +2815,19 @@ static int sessionAppendDelete( return rc; } +static int sessionPrepare( + sqlite3 *db, + sqlite3_stmt **pp, + char **pzErrmsg, + const char *zSql +){ + int rc = sqlite3_prepare_v2(db, zSql, -1, pp, 0); + if( pzErrmsg && rc!=SQLITE_OK ){ + *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db)); + } + return rc; +} + /* ** Formulate and prepare a SELECT statement to retrieve a row from table ** zTab in database zDb based on its primary key. i.e. @@ -2836,12 +2849,12 @@ static int sessionSelectStmt( int nCol, /* Number of columns in table */ const char **azCol, /* Names of table columns */ u8 *abPK, /* PRIMARY KEY array */ - sqlite3_stmt **ppStmt /* OUT: Prepared SELECT statement */ + sqlite3_stmt **ppStmt, /* OUT: Prepared SELECT statement */ + char **pzErrmsg /* OUT: Error message */ ){ int rc = SQLITE_OK; char *zSql = 0; const char *zSep = ""; - int nSql = -1; int i; SessionBuffer cols = {0, 0, 0}; @@ -2921,7 +2934,7 @@ static int sessionSelectStmt( #endif if( rc==SQLITE_OK ){ - rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, 0); + rc = sessionPrepare(db, ppStmt, pzErrmsg, zSql); } sqlite3_free(zSql); sqlite3_free(nooptest.aBuf); @@ -3085,7 +3098,7 @@ static int sessionGenerateChangeset( /* Build and compile a statement to execute: */ if( rc==SQLITE_OK ){ rc = sessionSelectStmt(db, 0, pSession->zDb, - zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel + zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel, 0 ); } @@ -4294,6 +4307,7 @@ struct SessionApplyCtx { u8 bRebase; /* True to collect rebase information */ u8 bIgnoreNoop; /* True to ignore no-op conflicts */ int bRowid; + char *zErr; /* Error message, if any */ }; /* Number of prepared UPDATE statements to cache. */ @@ -4519,7 +4533,7 @@ static int sessionDeleteRow( } if( rc==SQLITE_OK ){ - rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pDelete, 0); + rc = sessionPrepare(db, &p->pDelete, &p->zErr, (char*)buf.aBuf); } sqlite3_free(buf.aBuf); @@ -4546,7 +4560,7 @@ static int sessionSelectRow( ){ /* TODO */ return sessionSelectStmt(db, p->bIgnoreNoop, - "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect + "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect, &p->zErr ); } @@ -4583,16 +4597,12 @@ static int sessionInsertRow( sessionAppendStr(&buf, ")", &rc); if( rc==SQLITE_OK ){ - rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pInsert, 0); + rc = sessionPrepare(db, &p->pInsert, &p->zErr, (char*)buf.aBuf); } sqlite3_free(buf.aBuf); return rc; } -static int sessionPrepare(sqlite3 *db, sqlite3_stmt **pp, const char *zSql){ - return sqlite3_prepare_v2(db, zSql, -1, pp, 0); -} - /* ** Prepare statements for applying changes to the sqlite_stat1 table. ** These are similar to those created by sessionSelectRow(), @@ -4602,14 +4612,14 @@ static int sessionPrepare(sqlite3 *db, sqlite3_stmt **pp, const char *zSql){ static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){ int rc = sessionSelectRow(db, "sqlite_stat1", p); if( rc==SQLITE_OK ){ - rc = sessionPrepare(db, &p->pInsert, + rc = sessionPrepare(db, &p->pInsert, 0, "INSERT INTO main.sqlite_stat1 VALUES(?1, " "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END, " "?3)" ); } if( rc==SQLITE_OK ){ - rc = sessionPrepare(db, &p->pDelete, + rc = sessionPrepare(db, &p->pDelete, 0, "DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS " "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END " "AND (?4 OR stat IS ?3)" @@ -5392,6 +5402,11 @@ static int sessionChangesetApply( db->flags &= ~((u64)SQLITE_FkNoAction); db->aDb[0].pSchema->schema_cookie -= 32; } + + assert( rc!=SQLITE_OK || sApply.zErr==0 ); + sqlite3_set_errmsg(db, rc, sApply.zErr); + sqlite3_free(sApply.zErr); + sqlite3_mutex_leave(sqlite3_db_mutex(db)); return rc; } diff --git a/manifest b/manifest index 2b7a3014da..e8baa6e416 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\soff-by-one\serror\sin\ssqlite3_rsync.\n[forum:/info/46753431d4|Forum\spost\s46753431d4]. -D 2025-09-11T10:58:49.199 +C Add\sexperimental\sAPI\ssqlite3_set_errmsg().\sUse\sthis\sin\ssqlite3changeset_apply()\sto\sreturn\sany\serror\scode\sand\serror\smessage\svia\sthe\sSQLite\shandle. +D 2025-09-12T15:02:47.133 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -540,7 +540,7 @@ F ext/session/session4.test 823f6f018fcbb8dacf61e2960f8b3b848d492b094f8b495eae1d F ext/session/session5.test 716bc6fafd625ce60dfa62ae128971628c1a1169 F ext/session/session6.test 35279f2ec45448cd2e24a61688219dc6cf7871757716063acf4a8b5455e1e926 F ext/session/session8.test 326f3273abf9d5d2d7d559eee8f5994c4ea74a5d935562454605e6607ee29904 -F ext/session/session9.test 4e3aff62d6b4294498ddbe309076de06f4fddffad4fe5f5a6c033358b01df083 +F ext/session/session9.test 0c4a8fbe7a5031f50855f020f3408e1f07fd7859f1daa1629eadcec3422072d6 F ext/session/sessionA.test 1feeab0b8e03527f08f2f1defb442da25480138f F ext/session/sessionB.test c4fb7f8a688787111606e123a555f18ee04f65bb9f2a4bb2aa71d55ce4e6d02c F ext/session/sessionC.test f8a5508bc059ae646e5ec9bdbca66ad24bc92fe99fda5790ac57e1f59fce2fdf @@ -573,7 +573,7 @@ F ext/session/sessionrowid.test 85187c2f1b38861a5844868126f69f9ec62223a03449a98a F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795 F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc -F ext/session/sqlite3session.c 9205e6e8f389ea44a8118082ce3832374da92284a60e4fb3ea6b1b421f0dbc54 +F ext/session/sqlite3session.c 9cd47bfefb23c114b7a5d9ee5822d941398902f30516bf0ddfb131d94f8bb840 F ext/session/sqlite3session.h 6641184274b1d46594e450793d39aa96053916043234fb9e653c661160a166a7 F ext/session/test_session.c 8766b5973a6323934cb51248f621c3dc87ad2a98f023c3cc280d79e7d78d36fb F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c @@ -705,7 +705,7 @@ F src/insert.c dfd311b0ac2d4f6359e62013db67799757f4d2cc56cca5c10f4888acfbbfa3fd F src/json.c cb87977b1ee25ee7d27505d65a9261b687395bf895342c8ba566b7c01aee2047 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c d7edd8e671237539d795d30daaf888908a2c82e99bade4c78f3be021e8b7d655 -F src/main.c 458e79da86c957a094c1c256136af43e7dfec2c58475ee9cbbda4dc202005021 +F src/main.c a826d261631d1510184476fe4784f6f4ab64a5ac1b38bbc68c60148acd63feeb F src/malloc.c 410e570b30c26cc36e3372577df50f7a96ee3eed5b2b161c6b6b48773c650c5e F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2 @@ -743,7 +743,7 @@ F src/resolve.c f8d1d011aba0964ff1bdccd049d4d2c2fec217efd90d202a4bb775e926b2c25d F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c b95181711d59c36d9789e67f76c4cfec64b99f9629a50be5e6566e117b87d957 F src/shell.c.in c309e6e95b4de2be9dd0fbe4d40f729199a85bcc54d66759a0aef3b3e6504b22 -F src/sqlite.h.in 79dd3963888543f3120536608bf51024c93c7eb163a255098ffd569710189781 +F src/sqlite.h.in 527d13b8cf5ed097cea7f806565eab5b6c783799da89a21223b422308b3b4479 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 0bfd049bb2088cc44c2ad54f2079d1c6e43091a4e1ce8868779b75f6c1484f1e F src/sqliteInt.h 27c73e48878d31ef230ba867d1f8c3af6aed357fd93ccc605d3f1aae007ea62b @@ -2174,8 +2174,11 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 5bc6b9352236df3091ee69b947d0c397264156539d13e5968ec3b9a6e55dc800 -R a5539b6621653da7959fb888474ce821 -U drh -Z 3cde64f623da7bc733a5eed51b57ab95 +P ef3b7be6f2037871f6f1b1944fed3dda28216e7f179080d3be2e2620c031f48c +R 262c29c5815b20dcd06e87f8949e0e9a +T *branch * sqlite3-set-errmsg +T *sym-sqlite3-set-errmsg * +T -sym-trunk * +U dan +Z ca78b995013b1b3c1c01f7a5a981e1b3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.tags b/manifest.tags index bec971799f..94b3af9bd8 100644 --- a/manifest.tags +++ b/manifest.tags @@ -1,2 +1,2 @@ -branch trunk -tag trunk +branch sqlite3-set-errmsg +tag sqlite3-set-errmsg diff --git a/manifest.uuid b/manifest.uuid index ea679c53ec..29425ccd3f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ef3b7be6f2037871f6f1b1944fed3dda28216e7f179080d3be2e2620c031f48c +4d5b60a1e57448f03af2a657fe7cdabb04ebaf9688d5cc700dd8f9892a5cba15 diff --git a/src/main.c b/src/main.c index 53b728f023..e079cbed3f 100644 --- a/src/main.c +++ b/src/main.c @@ -2716,6 +2716,25 @@ const char *sqlite3_errmsg(sqlite3 *db){ return z; } +/* +** Set the error code and error message associated with the database handle. +*/ +int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zMsg){ + int rc = SQLITE_OK; + if( !sqlite3SafetyCheckSickOrOk(db) ){ + return SQLITE_MISUSE_BKPT; + } + sqlite3_mutex_enter(db->mutex); + if( zMsg ){ + sqlite3ErrorWithMsg(db, errcode, "%s", zMsg); + }else{ + sqlite3Error(db, errcode); + } + rc = sqlite3ApiExit(db, rc); + sqlite3_mutex_leave(db->mutex); + return rc; +} + /* ** Return the byte offset of the most recent error */ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 3c17808fc3..05c14f5fd2 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -4206,6 +4206,21 @@ const void *sqlite3_errmsg16(sqlite3*); const char *sqlite3_errstr(int); int sqlite3_error_offset(sqlite3 *db); +/* +** CAPI3REF: Set Error Codes And Message +** METHOD: sqlite3 +** +** Set the error code of the database handle passed as the first argument +** to errcode, and the error message to a copy of nul-terminated string +** zErrMsg. If zErrMsg is passed NULL, then the error message is set to +** the default message associated with the supplied error code. +** +** This function returns SQLITE_OK if the error code and error message are +** successfully set, SQLITE_NOMEM if an OOM occurs, and SQLITE_MISUSE if +** the database handle is NULL or invalid. +*/ +int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zErrMsg); + /* ** CAPI3REF: Prepared Statement Object ** KEYWORDS: {prepared statement} {prepared statements}