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
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.
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};
#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);
/* 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
);
}
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. */
}
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);
){
/* 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
);
}
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(),
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)"
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;
}
-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
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
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
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
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
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.
-branch trunk
-tag trunk
+branch sqlite3-set-errmsg
+tag sqlite3-set-errmsg
-ef3b7be6f2037871f6f1b1944fed3dda28216e7f179080d3be2e2620c031f48c
+4d5b60a1e57448f03af2a657fe7cdabb04ebaf9688d5cc700dd8f9892a5cba15
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
*/
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}