diff options
-rw-r--r-- | ext/jni/src/c/sqlite3-jni.c | 8 | ||||
-rw-r--r-- | ext/jni/src/org/sqlite/jni/Sqlite.java | 126 | ||||
-rw-r--r-- | ext/jni/src/org/sqlite/jni/SqliteException.java | 14 | ||||
-rw-r--r-- | ext/jni/src/org/sqlite/jni/Tester2.java | 47 | ||||
-rw-r--r-- | ext/wasm/api/sqlite3-wasm.c | 8 | ||||
-rw-r--r-- | manifest | 26 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/malloc.c | 2 | ||||
-rw-r--r-- | src/pager.c | 2 | ||||
-rw-r--r-- | src/shell.c.in | 6 |
10 files changed, 211 insertions, 30 deletions
diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index ab8dac644..7afc9a0f0 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -43,6 +43,14 @@ /**********************************************************************/ /* SQLITE_ENABLE_... */ +/* +** Unconditionally enable API_ARMOR in the JNI build. It ensures that +** public APIs behave predictable in the face of passing illegal NULLs +** or ranges which might otherwise invoke undefined behavior. +*/ +#undef SQLITE_ENABLE_API_ARMOR +#define SQLITE_ENABLE_API_ARMOR 1 + #ifndef SQLITE_ENABLE_BYTECODE_VTAB # define SQLITE_ENABLE_BYTECODE_VTAB 1 #endif diff --git a/ext/jni/src/org/sqlite/jni/Sqlite.java b/ext/jni/src/org/sqlite/jni/Sqlite.java index b964c57b3..5c2c45629 100644 --- a/ext/jni/src/org/sqlite/jni/Sqlite.java +++ b/ext/jni/src/org/sqlite/jni/Sqlite.java @@ -12,6 +12,7 @@ ** This file is part of the JNI bindings for the sqlite3 C API. */ package org.sqlite.jni; +import java.nio.charset.StandardCharsets; import static org.sqlite.jni.CApi.*; /** @@ -22,7 +23,7 @@ import static org.sqlite.jni.CApi.*; individual instances are tied to a specific database connection. */ public final class Sqlite implements AutoCloseable { - private sqlite3 db = null; + private sqlite3 db; //! Used only by the open() factory functions. private Sqlite(sqlite3 db){ @@ -33,6 +34,9 @@ public final class Sqlite implements AutoCloseable { Returns a newly-opened db connection or throws SqliteException if opening fails. All arguments are as documented for sqlite3_open_v2(). + + Design question: do we want static factory functions or should + this be reformulated as a constructor? */ public static Sqlite open(String filename, int flags, String vfsName){ final OutputPointer.sqlite3 out = new OutputPointer.sqlite3(); @@ -40,7 +44,9 @@ public final class Sqlite implements AutoCloseable { final sqlite3 n = out.take(); if( 0!=rc ){ if( null==n ) throw new SqliteException(rc); - else throw new SqliteException(n); + final SqliteException ex = new SqliteException(n); + n.close(); + throw ex; } return new Sqlite(n); } @@ -64,6 +70,120 @@ public final class Sqlite implements AutoCloseable { Returns this object's underlying native db handle, or null if this instance has been closed. */ - sqlite3 dbHandle(){ return this.db; } + sqlite3 nativeHandle(){ return this.db; } + + private void affirmOpen(){ + if( null==db || 0==db.getNativePointer() ){ + throw new IllegalArgumentException("This database instance is closed."); + } + } + + // private byte[] stringToUtf8(String s){ + // return s==null ? null : s.getBytes(StandardCharsets.UTF_8); + // } + + private void affirmRcOk(int rc){ + if( 0!=rc ){ + throw new SqliteException(db); + } + } + + public final class Stmt implements AutoCloseable { + private Sqlite _db = null; + private sqlite3_stmt stmt = null; + /** Only called by the prepare() factory functions. */ + Stmt(Sqlite db, sqlite3_stmt stmt){ + this._db = db; + this.stmt = stmt; + } + + sqlite3_stmt nativeHandle(){ + return stmt; + } + + private sqlite3_stmt affirmOpen(){ + if( null==stmt || 0==stmt.getNativePointer() ){ + throw new IllegalArgumentException("This Stmt has been finalized."); + } + return stmt; + } + + /** + Corresponds to sqlite3_finalize(), but we cannot override the + name finalize() here because this one requires a different + signature. We do not throw on error here because "destructors + do not throw." If it returns non-0, the object is still + finalized. + */ + public int finalizeStmt(){ + int rc = 0; + if( null!=stmt ){ + sqlite3_finalize(stmt); + stmt = null; + } + return rc; + } + + @Override public void close(){ + finalizeStmt(); + } + + /** + Throws if rc is any value other than 0, SQLITE_ROW, or + SQLITE_DONE, else returns rc. + */ + private int checkRc(int rc){ + switch(rc){ + case 0: + case SQLITE_ROW: + case SQLITE_DONE: return rc; + default: + throw new SqliteException(this); + } + } + + /** + Works like sqlite3_step() but throws SqliteException for any + result other than 0, SQLITE_ROW, or SQLITE_DONE. + */ + public int step(){ + return checkRc(sqlite3_step(affirmOpen())); + } + + public Sqlite db(){ return this._db; } + + /** + Works like sqlite3_reset() but throws on error. + */ + public void reset(){ + checkRc(sqlite3_reset(affirmOpen())); + } + + public void clearBindings(){ + sqlite3_clear_bindings( affirmOpen() ); + } + } + + + /** + prepare() TODOs include: + + - overloads taking byte[] and ByteBuffer. + + - multi-statement processing, like CApi.sqlite3_prepare_multi() + but using a callback specific to the higher-level Stmt class + rather than the sqlite3_stmt class. + */ + public Stmt prepare(String sql, int prepFlags){ + affirmOpen(); + final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt(); + final int rc = sqlite3_prepare_v3(this.db, sql, prepFlags, out); + affirmRcOk(rc); + return new Stmt(this, out.take()); + } + + public Stmt prepare(String sql){ + return prepare(sql, 0); + } } diff --git a/ext/jni/src/org/sqlite/jni/SqliteException.java b/ext/jni/src/org/sqlite/jni/SqliteException.java index 3da5d8c58..c15cb3491 100644 --- a/ext/jni/src/org/sqlite/jni/SqliteException.java +++ b/ext/jni/src/org/sqlite/jni/SqliteException.java @@ -46,7 +46,12 @@ public final class SqliteException extends java.lang.RuntimeException { /** Records the current error state of db (which must not be null and - must refer to an opened db object) then closes it. + must refer to an opened db object). Note that this does NOT close + the db. + + Design note: closing the db on error is likely only useful during + a failed db-open operation, and the place(s) where that can + happen are inside this library, not client-level code. */ public SqliteException(sqlite3 db){ super(sqlite3_errmsg(db)); @@ -54,7 +59,6 @@ public final class SqliteException extends java.lang.RuntimeException { xerrCode = sqlite3_extended_errcode(db); errOffset = sqlite3_error_offset(db); sysErrno = sqlite3_system_errno(db); - db.close(); } /** @@ -62,7 +66,11 @@ public final class SqliteException extends java.lang.RuntimeException { refer to an open database) then closes it. */ public SqliteException(Sqlite db){ - this(db.dbHandle()); + this(db.nativeHandle()); + } + + public SqliteException(Sqlite.Stmt stmt){ + this( stmt.db() ); } public int errcode(){ return errCode; } diff --git a/ext/jni/src/org/sqlite/jni/Tester2.java b/ext/jni/src/org/sqlite/jni/Tester2.java index b7701f1a9..e75f56e06 100644 --- a/ext/jni/src/org/sqlite/jni/Tester2.java +++ b/ext/jni/src/org/sqlite/jni/Tester2.java @@ -126,16 +126,51 @@ public class Tester2 implements Runnable { } } + Sqlite openDb(String name){ + return Sqlite.open(name, SQLITE_OPEN_READWRITE| + SQLITE_OPEN_CREATE| + SQLITE_OPEN_EXRESCODE); + } + + Sqlite openDb(){ return openDb(":memory:"); } + void testOpenDb1(){ - Sqlite db = Sqlite.open(":memory:"); - affirm( 0!=db.dbHandle().getNativePointer() ); + Sqlite db = openDb(); + affirm( 0!=db.nativeHandle().getNativePointer() ); db.close(); - affirm( null==db.dbHandle() ); + affirm( null==db.nativeHandle() ); + + SqliteException ex = null; + try { + db = openDb("/no/such/dir/.../probably"); + }catch(SqliteException e){ + ex = e; + } + affirm( ex!=null ); + affirm( ex.errcode() != 0 ); + affirm( ex.extendedErrcode() != 0 ); + affirm( ex.errorOffset() < 0 ); + // there's no reliable way to predict what ex.systemErrno() might be } - @ManualTest /* because we only want to run this test on demand */ - private void testFail(){ - affirm( false, "Intentional failure." ); + void testPrepare1(){ + try (Sqlite db = openDb()) { + Sqlite.Stmt stmt = db.prepare("SELECT 1"); + affirm( null!=stmt.nativeHandle() ); + affirm( SQLITE_ROW == stmt.step() ); + affirm( SQLITE_DONE == stmt.step() ); + stmt.reset(); + affirm( SQLITE_ROW == stmt.step() ); + affirm( SQLITE_DONE == stmt.step() ); + affirm( 0 == stmt.finalizeStmt() ); + affirm( null==stmt.nativeHandle() ); + + stmt = db.prepare("SELECT 1"); + affirm( SQLITE_ROW == stmt.step() ); + affirm( 0 == stmt.finalizeStmt() ) + /* getting a non-0 out of sqlite3_finalize() is tricky */; + affirm( null==stmt.nativeHandle() ); + } } private void runTests(boolean fromThread) throws Exception { diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index db77010d9..88a679c51 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -84,6 +84,14 @@ /**********************************************************************/ /* SQLITE_ENABLE_... */ +/* +** Unconditionally enable API_ARMOR in the WASM build. It ensures that +** public APIs behave predictable in the face of passing illegal NULLs +** or ranges which might otherwise invoke undefined behavior. +*/ +#undef SQLITE_ENABLE_API_ARMOR +#define SQLITE_ENABLE_API_ARMOR 1 + #ifndef SQLITE_ENABLE_BYTECODE_VTAB # define SQLITE_ENABLE_BYTECODE_VTAB 1 #endif @@ -1,5 +1,5 @@ -C Fix\sthe\suse\sof\san\suninitialized\svalue\sthat\soccurs\swhen\sdoing\sa\sjson_insert()\nof\sa\sstring\svalue\sthat\scontains\sembedded\sU+0000\scharacters. -D 2023-10-11T17:21:16.637 +C Merge\sfixes\sfrom\strunk\sinto\sthe\sjsonb\sbranch. +D 2023-10-12T17:47:50.731 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -238,7 +238,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 6da240c9a11701f3ed569384cd15ef611e8b3c5e3897d265923b14bf0e1eb272 F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c fb8f178d27df828e3c797b4427a0a20545b44f5147ce38d09ce9b465be5a840b +F ext/jni/src/c/sqlite3-jni.c bf432771fbc84da2b5f4037e0bcf20757548aac2fa782b272c4894a9c279964a F ext/jni/src/c/sqlite3-jni.h be1fdff7ab3a2bb357197271c8ac5d2bf6ff59380c106dde3a13be88724bad22 F ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java 95e88ba04f4aac51ffec65693e878e234088b2f21b387f4e4285c8b72b33e436 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 7312486bc65fecdb91753c0a4515799194e031f45edbe16a6373cea18f404dc4 @@ -262,11 +262,11 @@ F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java ec6cd96bff5d3bc5af079cbf1 F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b29971d715a821a4fad3c899113ee8c F ext/jni/src/org/sqlite/jni/SQLTester.java d246c67f93e2fa2603bd106dbb3246ea725c987dffd6e5d42214ae262f750c68 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 6d387bb499fbe3bc13c53315335233dbf6a0c711e8fa7c521683219b041c614c -F ext/jni/src/org/sqlite/jni/Sqlite.java 713f973764de9f918500b8723f347e67d29da226ad34b18e1f37865397c0efcb -F ext/jni/src/org/sqlite/jni/SqliteException.java f5d17a10202c0983fb074f66a0b48cf1e573b1da2eaeda679825e3edc1829706 +F ext/jni/src/org/sqlite/jni/Sqlite.java 1617ea2bf3dfa493b7f031a3187cbfd6837c39bc1d406c4b3edcf9aab941639d +F ext/jni/src/org/sqlite/jni/SqliteException.java e17500e8bca2c68c260d8d0163fe4b7dc8bd0b1b90211201325c4a5566ce75ca F ext/jni/src/org/sqlite/jni/TableColumnMetadata.java 54511b4297fa28dcb3f49b24035e34ced10e3fd44fd0e458e784f4d6b0096dab F ext/jni/src/org/sqlite/jni/Tester1.java f7b85fe24cf6c3e43bdf7e390617657e8137359f804d76921829c2a8c41b6df1 -F ext/jni/src/org/sqlite/jni/Tester2.java 3e7b3c05c08bdbf899684074f095724e1853dc16912dfb53306a03e5c4cbd614 +F ext/jni/src/org/sqlite/jni/Tester2.java 70e005d41060e398ec0f69bd39a8e1c376fd51f81629cf25e877889ec9cb6ec6 F ext/jni/src/org/sqlite/jni/TesterFts5.java d60fe9944a81156b3b5325dd1b0e8e92a1547468f39fd1266d06f7bb6a95fa70 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java f157edd9c72e7d2243c169061487cd7bb51a0d50f3ac976dbcbbacf748ab1fc2 F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java 959d4677a857c9079c6e96ddd10918b946d68359af6252b6f284379069ea3d27 @@ -581,7 +581,7 @@ F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256 F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25 F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js f7c965cf9ac0b66a538cd8f6c156f3f2a235e089821ca78cabd7bce41ce16bf7 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 46c4afa6c50d7369252c104f274ad977a97e91ccfafc38b400fe36e90bdda88e -F ext/wasm/api/sqlite3-wasm.c 65d60439671e24d50d9119ca805ac1c68fb36129e164377eb46f8d037bd88b07 +F ext/wasm/api/sqlite3-wasm.c c8c5b81b838cef2053b1eb6e7a79c44a2caedcf0c9e6b0d12a45d73ce0617be0 F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f F ext/wasm/api/sqlite3-worker1.c-pp.js da509469755035e919c015deea41b4514b5e84c12a1332e6cc8d42cb2cc1fb75 F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8 @@ -678,7 +678,7 @@ F src/json.c fc8783af356d27cfdf14a583e5a7809394269763384cde979c0825a29f13e1d6 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 F src/main.c 618aeb399e993cf561864f4b0cf6a331ee4f355cf663635f8d9da3193a46aa40 -F src/malloc.c 47b82c5daad557d9b963e3873e99c22570fb470719082c6658bf64e3012f7d23 +F src/malloc.c f016922435dc7d1f1f5083a03338a3e91f8c67ce2c5bdcfa4cdef62e612f5fcc F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2 F src/mem2.c c8bfc9446fd0798bddd495eb5d9dbafa7d4b7287d8c22d50a83ac9daa26d8a75 @@ -701,7 +701,7 @@ F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d87210 F src/os_unix.c 2e8b12107f75d1bd16412f312b4c5d5103191807a37836d3b81beb26436ad81b F src/os_win.c 4a50a154aeebc66a1f8fb79c1ff6dd5fe3d005556533361e0d460d41cb6a45a8 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a -F src/pager.c bbd9b93c014679d8148235cc42b73bebda3f598d4f63a63cf7723ddf3087fd58 +F src/pager.c 4aa388e47138551c83ae265e2efd4e01f0aa5afb6a958f45579658847b3430ff F src/pager.h f4d33fec8052603758792045493423b8871a996da2d0973927b7d36cd6070473 F src/parse.y aeb7760d41cfa86465e3adba506500c021597049fd55f82a30e5b7045862c28c F src/pcache.c 040b165f30622a21b7a9a77c6f2e4877a32fb7f22d4c7f0d2a6fa6833a156a75 @@ -715,7 +715,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 0c3046b88901336709cd09f474303a16fc54bce13a2befcab66d0fa6b44ca869 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 7f9155185be78902818b21c2cd3e33f01b4306279a15d6719eb1bbb9779034aa -F src/shell.c.in 6d26db96a7d53e7b499c8ae2f794dfc020a96e64d5757aebd0e4cf743b6de031 +F src/shell.c.in cf0a3387c5bb05ca2fe6073fa7df21aaa11e749ca5b3846b80b586a447e728aa F src/sqlite.h.in 4f39f61c35348f0c56dd2c7a2294d1f0564389a0086dab80ce0960bfd772dc1b F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 2f30b2671f4c03cd27a43f039e11251391066c97d11385f5f963bb40b03038ac @@ -2129,8 +2129,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P fb81d570a3d9b8f55e9f278d5240605c5d0f3c5abde51550797999d03cf193a7 -R d78e19c15a2441ced442368555cb1a45 +P fc5ee9e51ad4556af526a6cefca5ae5a3b1b7affc4edf09832491d6b4f4ba366 29937081a986d88f495ad48748c35ff5829f0ac31dd4ad3e48d180ae2fcb9a0c +R 34f098f7c4eafcb6644778066535a3a0 U drh -Z 3bf927da2cbd54464f0b6fb8e90f7a35 +Z dd04631a3fb3a59b7149d92d361ffe54 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0fb4b428b..643a123ac 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fc5ee9e51ad4556af526a6cefca5ae5a3b1b7affc4edf09832491d6b4f4ba366
\ No newline at end of file +a760bd7bcc63aac41c989bb5f4fbc927f9a93fe9c0aa18da2f0131483ec3f189
\ No newline at end of file diff --git a/src/malloc.c b/src/malloc.c index 48c460060..356750682 100644 --- a/src/malloc.c +++ b/src/malloc.c @@ -896,5 +896,5 @@ int sqlite3ApiExit(sqlite3* db, int rc){ if( db->mallocFailed || rc ){ return apiHandleError(db, rc); } - return rc & db->errMask; + return 0; } diff --git a/src/pager.c b/src/pager.c index af21ad611..1423b6654 100644 --- a/src/pager.c +++ b/src/pager.c @@ -7369,7 +7369,7 @@ int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ } assert( state==pPager->eState ); } - }else if( eMode==PAGER_JOURNALMODE_OFF ){ + }else if( eMode==PAGER_JOURNALMODE_OFF || eMode==PAGER_JOURNALMODE_MEMORY ){ sqlite3OsClose(pPager->jfd); } } diff --git a/src/shell.c.in b/src/shell.c.in index 1699820a6..beea0f9b0 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -8772,8 +8772,10 @@ static int do_meta_command(char *zLine, ShellState *p){ "SELECT rowid FROM sqlite_schema" " WHERE name GLOB 'sqlite_stat[134]'", -1, &pStmt, 0); - doStats = sqlite3_step(pStmt)==SQLITE_ROW; - sqlite3_finalize(pStmt); + if( rc==SQLITE_OK ){ + doStats = sqlite3_step(pStmt)==SQLITE_ROW; + sqlite3_finalize(pStmt); + } } if( doStats==0 ){ raw_printf(p->out, "/* No STAT tables available */\n"); |