Clear some picky warnings, sync w/trunk.

FossilOrigin-Name: ee58425904b36319e016dc69bb5f141bb3565b1723a97490a4b2cfa16b89fbdf
This commit is contained in:
larrybr 2023-10-23 01:55:35 +00:00
commit 81be0d26bb
224 changed files with 8267 additions and 3697 deletions

View File

@ -766,13 +766,22 @@ mptest: mptester$(TEXE)
$(MPTEST2) --journalmode DELETE
has_tclsh84:
sh $(TOP)/tool/cktclsh.sh 8.4 $(TCLSH_CMD)
touch has_tclsh84
has_tclsh85:
sh $(TOP)/tool/cktclsh.sh 8.5 $(TCLSH_CMD)
touch has_tclsh85
# This target creates a directory named "tsrc" and fills it with
# copies of all of the C source code and header files needed to
# build on the target system. Some of the C source code and header
# files are automatically generated. This target takes care of
# all that automatic generation.
#
.target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl fts5.c
.target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl has_tclsh84 fts5.c
rm -rf tsrc
mkdir tsrc
cp -f $(SRC) tsrc
@ -782,15 +791,15 @@ mptest: mptester$(TEXE)
cp fts5.c fts5.h tsrc
touch .target_source
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl src-verify
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl src-verify has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl $(AMALGAMATION_LINE_MACROS)
cp tsrc/sqlite3ext.h .
cp $(TOP)/ext/session/sqlite3session.h .
sqlite3r.h: sqlite3.h
sqlite3r.h: sqlite3.h has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) --enable-recover >sqlite3r.h
sqlite3r.c: sqlite3.c sqlite3r.h
sqlite3r.c: sqlite3.c sqlite3r.h has_tclsh84
cp $(TOP)/ext/recover/sqlite3recover.c tsrc/
cp $(TOP)/ext/recover/sqlite3recover.h tsrc/
cp $(TOP)/ext/recover/dbdata.c tsrc/
@ -805,7 +814,7 @@ tclsqlite3.c: sqlite3.c
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl
sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/split-sqlite3c.tcl
# Rule to build the amalgamation
@ -1093,10 +1102,10 @@ tclsqlite3$(TEXE): tclsqlite-shell.lo libsqlite3.la
# Rules to build opcodes.c and opcodes.h
#
opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl
opcodes.c: opcodes.h $(TOP)/tool/mkopcodec.tcl has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mkopcodec.tcl opcodes.h >opcodes.c
opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl
opcodes.h: parse.h $(TOP)/src/vdbe.c $(TOP)/tool/mkopcodeh.tcl has_tclsh84
cat parse.h $(TOP)/src/vdbe.c | $(TCLSH_CMD) $(TOP)/tool/mkopcodeh.tcl >opcodes.h
# Rules to build parse.c and parse.h - the outputs of lemon.
@ -1107,10 +1116,10 @@ parse.c: $(TOP)/src/parse.y lemon$(BEXE)
cp $(TOP)/src/parse.y .
./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) -S parse.y
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
sqlite3rc.h: $(TOP)/src/sqlite3.rc $(TOP)/VERSION
sqlite3rc.h: $(TOP)/src/sqlite3.rc $(TOP)/VERSION has_tclsh84
echo '#ifndef SQLITE_RESOURCE_VERSION' >$@
echo -n '#define SQLITE_RESOURCE_VERSION ' >>$@
cat $(TOP)/VERSION | $(TCLSH_CMD) $(TOP)/tool/replace.tcl exact . , >>$@
@ -1146,7 +1155,7 @@ SHELL_SRC = \
$(TOP)/ext/recover/sqlite3recover.h \
$(TOP)/src/test_windirent.c
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl
shell.c: $(SHELL_SRC) $(TOP)/tool/mkshellc.tcl has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mkshellc.tcl >shell.c
@ -1234,7 +1243,7 @@ fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon$(BEXE)
fts5parse.h: fts5parse.c
fts5.c: $(FTS5_SRC)
fts5.c: $(FTS5_SRC) has_tclsh84
$(TCLSH_CMD) $(TOP)/ext/fts5/tool/mkfts5c.tcl
cp $(TOP)/ext/fts5/fts5.h .
@ -1268,7 +1277,7 @@ TESTFIXTURE_SRC1 = sqlite3.c
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c
TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION))
testfixture$(TEXE): $(TESTFIXTURE_SRC)
testfixture$(TEXE): has_tclsh85 $(TESTFIXTURE_SRC)
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \
-o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS)
@ -1319,17 +1328,23 @@ testrunner: testfixture$(TEXE)
# Runs both fuzztest and testrunner, consecutively.
#
devtest: testfixture$(TEXE) fuzztest testrunner
devtest: srctree-check testfixture$(TEXE) fuzztest testrunner
mdevtest:
mdevtest: srctree-check has_tclsh85
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest
sdevtest:
sdevtest: has_tclsh85
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl sdevtest
# Validate that various generated files in the source tree
# are up-to-date.
#
srctree-check: $(TOP)/tool/srctree-check.tcl
$(TCLSH_CMD) $(TOP)/tool/srctree-check.tcl
# Testing for a release
#
releasetest: testfixture$(TEXE)
releasetest: srctree-check testfixture$(TEXE)
./testfixture$(TEXE) $(TOP)/test/testrunner.tcl release
# Minimal testing that runs in less than 3 minutes
@ -1340,7 +1355,7 @@ quicktest: ./testfixture$(TEXE)
# This is the common case. Run many tests that do not take too long,
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
#
test: fuzztest sourcetest $(TESTPROGS) tcltest
test: srctree-check fuzztest sourcetest $(TESTPROGS) tcltest
# Run a test using valgrind. This can take a really long time
# because valgrind is so much slower than a native machine.
@ -1358,13 +1373,13 @@ smoketest: $(TESTPROGS) fuzzcheck$(TEXE)
shelltest: $(TESTPROGS)
./testfixture$(TEXT) $(TOP)/test/permutations.test shell
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in
sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in has_tclsh85
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqlite3_analyzer.c.in >sqlite3_analyzer.c
sqlite3_analyzer$(TEXE): sqlite3_analyzer.c
$(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS)
sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in
sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in has_tclsh85
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in >sqltclsh.c
sqltclsh$(TEXE): sqltclsh.c
@ -1383,7 +1398,7 @@ CHECKER_DEPS =\
$(TOP)/ext/misc/btreeinfo.c \
$(TOP)/ext/repair/sqlite3_checker.c.in
sqlite3_checker.c: $(CHECKER_DEPS)
sqlite3_checker.c: $(CHECKER_DEPS) has_tclsh85
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/ext/repair/sqlite3_checker.c.in >$@
sqlite3_checker$(TEXE): sqlite3_checker.c
@ -1549,6 +1564,7 @@ clean:
rm -f threadtest5
rm -f src-verify
rm -f custom.rws
rm -f has_tclsh84 has_tclsh85
distclean: clean
rm -f sqlite_cfg.h config.log config.status libtool Makefile sqlite3.pc \
@ -1593,7 +1609,7 @@ fiddle: sqlite3.c shell.c
@echo 'Updating custom dictionary from tool/custom.txt'
aspell --lang=en create master ./custom.rws < $<
misspell: ./custom.rws
misspell: ./custom.rws has_tclsh84
$(TCLSH_CMD) ./tool/spellsift.tcl ./src/*.c ./src/*.h ./src/*.in
#

View File

@ -52,8 +52,8 @@ MINIMAL_AMALGAMATION = 0
USE_STDCALL = 0
!ENDIF
# Set this non-0 to use structured exception handling (SEH) for WAL mode
# in the core library.
# Use the USE_SEH=0 option on the nmake command line to omit structured
# exception handling (SEH) support. SEH is on by default.
#
!IFNDEF USE_SEH
USE_SEH = 1
@ -403,10 +403,11 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
!ENDIF
# Should structured exception handling (SEH) be enabled for WAL mode in
# the core library?
# the core library? It is on by default. Only omit it if the
# USE_SEH=0 option is provided on the nmake command-line.
#
!IF $(USE_SEH)!=0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_USE_SEH=1
!IF $(USE_SEH)==0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_OMIT_SEH=1
!ENDIF
# These are the "extended" SQLite compilation options used when compiling for

View File

@ -52,8 +52,8 @@ MINIMAL_AMALGAMATION = 0
USE_STDCALL = 0
!ENDIF
# Set this non-0 to use structured exception handling (SEH) for WAL mode
# in the core library.
# Use the USE_SEH=0 option on the nmake command line to omit structured
# exception handling (SEH) support. SEH is on by default.
#
!IFNDEF USE_SEH
USE_SEH = 1
@ -325,10 +325,11 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
!ENDIF
# Should structured exception handling (SEH) be enabled for WAL mode in
# the core library?
# the core library? It is on by default. Only omit it if the
# USE_SEH=0 option is provided on the nmake command-line.
#
!IF $(USE_SEH)!=0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_USE_SEH=1
!IF $(USE_SEH)==0
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_OMIT_SEH=1
!ENDIF
# These are the "extended" SQLite compilation options used when compiling for

2
configure vendored
View File

@ -11345,7 +11345,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build type" >&5
$as_echo_n "checking build type... " >&6; }
if test "${enable_debug}" = "yes" ; then
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0"
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: debug" >&5
$as_echo "debug" >&6; }
else

View File

@ -632,7 +632,7 @@ AC_SEARCH_LIBS(fdatasync, [rt])
AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],[enable debugging & verbose explain]))
AC_MSG_CHECKING([build type])
if test "${enable_debug}" = "yes" ; then
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0"
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall"
AC_MSG_RESULT([debug])
else
TARGET_DEBUG="-DNDEBUG"

View File

@ -662,6 +662,7 @@ static int idxRegisterVtab(sqlite3expert *p){
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0, /* xIntegrity */
};
return sqlite3_create_module(p->dbv, "expert", &expertModule, (void*)p);
@ -1866,9 +1867,9 @@ int registerUDFs(sqlite3 *dbSrc, sqlite3 *dbDst){
while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
int nargs = sqlite3_column_int(pStmt,3);
int flags = sqlite3_column_int(pStmt,4);
const unsigned char *name = sqlite3_column_text(pStmt,0);
const unsigned char *type = sqlite3_column_text(pStmt,1);
const unsigned char *enc = sqlite3_column_text(pStmt,2);
const char *name = (char*)sqlite3_column_text(pStmt,0);
const char *type = (char*)sqlite3_column_text(pStmt,1);
const char *enc = (char*)sqlite3_column_text(pStmt,2);
if( name==0 || type==0 || enc==0 ) rc = SQLITE_NOMEM;
else{
int ienc = SQLITE_UTF8;

View File

@ -640,6 +640,7 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid");
sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
sqlite3_vtab_config(p->db, SQLITE_VTAB_INNOCUOUS);
/* Create a list of user columns for the virtual table */
zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]);
@ -3889,6 +3890,8 @@ static int fts3RenameMethod(
rc = sqlite3Fts3PendingTermsFlush(p);
}
p->bIgnoreSavepoint = 1;
if( p->zContentTbl==0 ){
fts3DbExec(&rc, db,
"ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
@ -3916,6 +3919,8 @@ static int fts3RenameMethod(
"ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';",
p->zDb, p->zName, zName
);
p->bIgnoreSavepoint = 0;
return rc;
}
@ -3926,12 +3931,28 @@ static int fts3RenameMethod(
*/
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
int rc = SQLITE_OK;
UNUSED_PARAMETER(iSavepoint);
assert( ((Fts3Table *)pVtab)->inTransaction );
assert( ((Fts3Table *)pVtab)->mxSavepoint <= iSavepoint );
TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){
rc = fts3SyncMethod(pVtab);
Fts3Table *pTab = (Fts3Table*)pVtab;
assert( pTab->inTransaction );
assert( pTab->mxSavepoint<=iSavepoint );
TESTONLY( pTab->mxSavepoint = iSavepoint );
if( pTab->bIgnoreSavepoint==0 ){
if( fts3HashCount(&pTab->aIndex[0].hPending)>0 ){
char *zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')",
pTab->zDb, pTab->zName, pTab->zName
);
if( zSql ){
pTab->bIgnoreSavepoint = 1;
rc = sqlite3_exec(pTab->db, zSql, 0, 0, 0);
pTab->bIgnoreSavepoint = 0;
sqlite3_free(zSql);
}else{
rc = SQLITE_NOMEM;
}
}
if( rc==SQLITE_OK ){
pTab->iSavepoint = iSavepoint+1;
}
}
return rc;
}
@ -3942,12 +3963,11 @@ static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
** This is a no-op.
*/
static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
UNUSED_PARAMETER(iSavepoint);
UNUSED_PARAMETER(pVtab);
assert( p->inTransaction );
assert( p->mxSavepoint >= iSavepoint );
TESTONLY( p->mxSavepoint = iSavepoint-1 );
Fts3Table *pTab = (Fts3Table*)pVtab;
assert( pTab->inTransaction );
assert( pTab->mxSavepoint >= iSavepoint );
TESTONLY( pTab->mxSavepoint = iSavepoint-1 );
pTab->iSavepoint = iSavepoint;
return SQLITE_OK;
}
@ -3957,11 +3977,13 @@ static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
** Discard the contents of the pending terms table.
*/
static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts3Table *p = (Fts3Table*)pVtab;
Fts3Table *pTab = (Fts3Table*)pVtab;
UNUSED_PARAMETER(iSavepoint);
assert( p->inTransaction );
TESTONLY( p->mxSavepoint = iSavepoint );
sqlite3Fts3PendingTermsClear(p);
assert( pTab->inTransaction );
TESTONLY( pTab->mxSavepoint = iSavepoint );
if( (iSavepoint+1)<=pTab->iSavepoint ){
sqlite3Fts3PendingTermsClear(pTab);
}
return SQLITE_OK;
}

View File

@ -265,6 +265,7 @@ struct Fts3Table {
int nPgsz; /* Page size for host database */
char *zSegmentsTbl; /* Name of %_segments table */
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
int iSavepoint;
/*
** The following array of hash tables is used to buffer pending index

View File

@ -545,7 +545,8 @@ int sqlite3Fts3InitAux(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
int rc; /* Return code */

View File

@ -362,7 +362,8 @@ int sqlite3Fts3InitTerm(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
int rc; /* Return code */

View File

@ -445,7 +445,8 @@ int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash, void(*xDestroy)(void*)){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
int rc; /* Return code */

View File

@ -3325,7 +3325,6 @@ int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
rc = fts3SegmentMerge(p, p->iPrevLangid, i, FTS3_SEGCURSOR_PENDING);
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
sqlite3Fts3PendingTermsClear(p);
/* Determine the auto-incr-merge setting if unknown. If enabled,
** estimate the number of leaf blocks of content to be written
@ -3347,6 +3346,10 @@ int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
rc = sqlite3_reset(pStmt);
}
}
if( rc==SQLITE_OK ){
sqlite3Fts3PendingTermsClear(p);
}
return rc;
}
@ -4034,9 +4037,13 @@ static int fts3IncrmergeAppend(
nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist;
/* If the current block is not empty, and if adding this term/doclist
** to the current block would make it larger than Fts3Table.nNodeSize
** bytes, write this block out to the database. */
if( pLeaf->block.n>0 && (pLeaf->block.n + nSpace)>p->nNodeSize ){
** to the current block would make it larger than Fts3Table.nNodeSize bytes,
** and if there is still room for another leaf page, write this block out to
** the database. */
if( pLeaf->block.n>0
&& (pLeaf->block.n + nSpace)>p->nNodeSize
&& pLeaf->iBlock < (pWriter->iStart + pWriter->nLeafEst)
){
rc = fts3WriteSegment(p, pLeaf->iBlock, pLeaf->block.a, pLeaf->block.n);
pWriter->nWork++;
@ -4368,7 +4375,7 @@ static int fts3IncrmergeLoad(
rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
blobGrowBuffer(&pNode->block,
MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
);
);
if( rc==SQLITE_OK ){
memcpy(pNode->block.a, aBlock, nBlock);
pNode->block.n = nBlock;
@ -5433,8 +5440,11 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
rc = fts3DoIncrmerge(p, &zVal[6]);
}else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){
rc = fts3DoAutoincrmerge(p, &zVal[10]);
}else if( nVal==5 && 0==sqlite3_strnicmp(zVal, "flush", 5) ){
rc = sqlite3Fts3PendingTermsFlush(p);
}
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
}else{
else{
int v;
if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
v = atoi(&zVal[9]);
@ -5452,8 +5462,8 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
if( v>=4 && v<=FTS3_MERGE_COUNT && (v&1)==0 ) p->nMergeCount = v;
rc = SQLITE_OK;
}
#endif
}
#endif
return rc;
}

View File

@ -2904,7 +2904,6 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){
assert_nc( i2!=0 );
pRes->bTermEq = 1;
if( p1->iRowid==p2->iRowid ){
p1->bDel = p2->bDel;
return i2;
}
res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1;
@ -5132,34 +5131,39 @@ static void fts5DoSecureDelete(
/* Set variable bLastInDoclist to true if this entry happens to be
** the last rowid in the doclist for its term. */
if( iNextOff>=iPgIdx ){
int pgno = pSeg->iLeafPgno+1;
fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist);
iNextOff = iPgIdx;
}else{
/* Loop through the page-footer. If iNextOff (offset of the
** entry following the one we are removing) is equal to the
** offset of a key on this page, then the entry is the last
** in its doclist. */
int iKeyOff = 0;
for(iIdx=0; iIdx<nIdx; /* no-op */){
u32 iVal = 0;
iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
iKeyOff += iVal;
if( iKeyOff==iNextOff ){
bLastInDoclist = 1;
if( pSeg->bDel==0 ){
if( iNextOff>=iPgIdx ){
int pgno = pSeg->iLeafPgno+1;
fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist);
iNextOff = iPgIdx;
}else{
/* Loop through the page-footer. If iNextOff (offset of the
** entry following the one we are removing) is equal to the
** offset of a key on this page, then the entry is the last
** in its doclist. */
int iKeyOff = 0;
for(iIdx=0; iIdx<nIdx; /* no-op */){
u32 iVal = 0;
iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
iKeyOff += iVal;
if( iKeyOff==iNextOff ){
bLastInDoclist = 1;
}
}
}
/* If this is (a) the first rowid on a page and (b) is not followed by
** another position list on the same page, set the "first-rowid" field
** of the header to 0. */
if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist || iNextOff==iPgIdx) ){
fts5PutU16(&aPg[0], 0);
}
}
/* If this is (a) the first rowid on a page and (b) is not followed by
** another position list on the same page, set the "first-rowid" field
** of the header to 0. */
if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist || iNextOff==iPgIdx) ){
fts5PutU16(&aPg[0], 0);
}
if( bLastInDoclist==0 ){
if( pSeg->bDel ){
iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta);
aPg[iOff++] = 0x01;
}else if( bLastInDoclist==0 ){
if( iNextOff!=iPgIdx ){
u64 iNextDelta = 0;
iNextOff += fts5GetVarint(&aPg[iNextOff], &iNextDelta);
@ -5271,6 +5275,15 @@ static void fts5DoSecureDelete(
}
}
/* Assuming no error has occurred, this block does final edits to the
** leaf page before writing it back to disk. Input variables are:
**
** nPg: Total initial size of leaf page.
** iPgIdx: Initial offset of page footer.
**
** iOff: Offset to move data to
** iNextOff: Offset to move data from
*/
if( p->rc==SQLITE_OK ){
const int nMove = nPg - iNextOff; /* Number of bytes to move */
int nShift = iNextOff - iOff; /* Distance to move them */
@ -5471,10 +5484,16 @@ static void fts5FlushOneHash(Fts5Index *p){
fts5WriteFlushLeaf(p, &writer);
}
}else{
int bDummy;
int nPos;
int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy);
nCopy += nPos;
int bDel = 0;
int nPos = 0;
int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDel);
if( bDel && bSecureDelete ){
fts5BufferAppendVarint(&p->rc, pBuf, nPos*2);
iOff += nCopy;
nCopy = nPos;
}else{
nCopy += nPos;
}
if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){
/* The entire poslist will fit on the current leaf. So copy
** it in one go. */
@ -5512,7 +5531,6 @@ static void fts5FlushOneHash(Fts5Index *p){
assert( pBuf->n<=pBuf->nSpace );
if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash);
}
sqlite3Fts5HashClear(pHash);
fts5WriteFinish(p, &writer, &pgnoLast);
assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 );
@ -5545,7 +5563,6 @@ static void fts5FlushOneHash(Fts5Index *p){
fts5IndexCrisismerge(p, &pStruct);
fts5StructureWrite(p, pStruct);
fts5StructureRelease(pStruct);
p->nContentlessDelete = 0;
}
/*
@ -5556,8 +5573,12 @@ static void fts5IndexFlush(Fts5Index *p){
if( p->nPendingData || p->nContentlessDelete ){
assert( p->pHash );
fts5FlushOneHash(p);
p->nPendingData = 0;
p->nPendingRow = 0;
if( p->rc==SQLITE_OK ){
sqlite3Fts5HashClear(p->pHash);
p->nPendingData = 0;
p->nPendingRow = 0;
p->nContentlessDelete = 0;
}
}
}
@ -8299,7 +8320,8 @@ int sqlite3Fts5IndexInit(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
rc = sqlite3_create_module(db, "fts5_structure", &fts5structure_module, 0);
}

View File

@ -117,6 +117,8 @@ struct Fts5FullTable {
Fts5Storage *pStorage; /* Document store */
Fts5Global *pGlobal; /* Global (connection wide) data */
Fts5Cursor *pSortCsr; /* Sort data from this cursor */
int iSavepoint; /* Successful xSavepoint()+1 */
int bInSavepoint;
#ifdef SQLITE_DEBUG
struct Fts5TransactionState ts;
#endif
@ -408,6 +410,9 @@ static int fts5InitVtab(
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1);
}
if( rc==SQLITE_OK ){
rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
if( rc!=SQLITE_OK ){
fts5FreeVtab(pTab);
@ -1530,6 +1535,7 @@ static int fts5SpecialInsert(
Fts5Config *pConfig = pTab->p.pConfig;
int rc = SQLITE_OK;
int bError = 0;
int bLoadConfig = 0;
if( 0==sqlite3_stricmp("delete-all", zCmd) ){
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
@ -1541,6 +1547,7 @@ static int fts5SpecialInsert(
}else{
rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
}
bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
if( pConfig->eContent==FTS5_CONTENT_NONE ){
fts5SetVtabError(pTab,
@ -1550,6 +1557,7 @@ static int fts5SpecialInsert(
}else{
rc = sqlite3Fts5StorageRebuild(pTab->pStorage);
}
bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("optimize", zCmd) ){
rc = sqlite3Fts5StorageOptimize(pTab->pStorage);
}else if( 0==sqlite3_stricmp("merge", zCmd) ){
@ -1562,6 +1570,8 @@ static int fts5SpecialInsert(
}else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
pConfig->bPrefixIndex = sqlite3_value_int(pVal);
#endif
}else if( 0==sqlite3_stricmp("flush", zCmd) ){
rc = sqlite3Fts5FlushToDisk(&pTab->p);
}else{
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
if( rc==SQLITE_OK ){
@ -1575,6 +1585,12 @@ static int fts5SpecialInsert(
}
}
}
if( rc==SQLITE_OK && bLoadConfig ){
pTab->p.pConfig->iCookie--;
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
}
return rc;
}
@ -2597,8 +2613,12 @@ static int fts5RenameMethod(
sqlite3_vtab *pVtab, /* Virtual table handle */
const char *zName /* New name of table */
){
int rc;
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
return sqlite3Fts5StorageRename(pTab->pStorage, zName);
pTab->bInSavepoint = 1;
rc = sqlite3Fts5StorageRename(pTab->pStorage, zName);
pTab->bInSavepoint = 0;
return rc;
}
int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
@ -2612,9 +2632,29 @@ int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
** Flush the contents of the pending-terms table to disk.
*/
static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_SAVEPOINT, iSavepoint);
return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
int rc = SQLITE_OK;
char *zSql = 0;
fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
if( pTab->bInSavepoint==0 ){
zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')",
pTab->p.pConfig->zDb, pTab->p.pConfig->zName, pTab->p.pConfig->zName
);
if( zSql ){
pTab->bInSavepoint = 1;
rc = sqlite3_exec(pTab->p.pConfig->db, zSql, 0, 0, 0);
pTab->bInSavepoint = 0;
sqlite3_free(zSql);
}else{
rc = SQLITE_NOMEM;
}
if( rc==SQLITE_OK ){
pTab->iSavepoint = iSavepoint+1;
}
}
return rc;
}
/*
@ -2623,9 +2663,16 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
** This is a no-op.
*/
static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_RELEASE, iSavepoint);
return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
int rc = SQLITE_OK;
fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint);
if( (iSavepoint+1)<pTab->iSavepoint ){
rc = sqlite3Fts5FlushToDisk(&pTab->p);
if( rc==SQLITE_OK ){
pTab->iSavepoint = iSavepoint;
}
}
return rc;
}
/*
@ -2635,11 +2682,14 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
*/
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
int rc = SQLITE_OK;
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
fts5TripCursors(pTab);
pTab->p.pConfig->pgsz = 0;
return sqlite3Fts5StorageRollback(pTab->pStorage);
if( (iSavepoint+1)<=pTab->iSavepoint ){
rc = sqlite3Fts5StorageRollback(pTab->pStorage);
}
return rc;
}
/*

View File

@ -1184,7 +1184,9 @@ int sqlite3Fts5StorageSync(Fts5Storage *p){
i64 iLastRowid = sqlite3_last_insert_rowid(p->pConfig->db);
if( p->bTotalsValid ){
rc = fts5StorageSaveTotals(p);
p->bTotalsValid = 0;
if( rc==SQLITE_OK ){
p->bTotalsValid = 0;
}
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexSync(p->pIndex);

View File

@ -472,7 +472,8 @@ int sqlite3Fts5TestRegisterTok(sqlite3 *db, fts5_api *pApi){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
int rc; /* Return code */

View File

@ -783,7 +783,8 @@ int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0
/* xShadowName */ 0,
/* xIntegrity */ 0
};
void *p = (void*)pGlobal;

View File

@ -0,0 +1,50 @@
# 2010 June 15
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
source [file join [file dirname [info script]] fts5_common.tcl]
source $testdir/malloc_common.tcl
set testprefix fts5faultG
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
set ::testprefix fts5faultG
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING fts5(a);
INSERT INTO t1 VALUES('test renaming the table');
INSERT INTO t1 VALUES(' after it has been written');
INSERT INTO t1 VALUES(' actually other stuff instead');
}
faultsim_save_and_close
do_faultsim_test 1 -faults oom* -prep {
faultsim_restore_and_reopen
execsql {
BEGIN;
DELETE FROM t1 WHERE rowid=2;
}
} -body {
execsql {
DELETE FROM t1;
}
} -test {
catchsql { COMMIT }
faultsim_integrity_check
faultsim_test_result {0 {}}
}
finish_test

View File

@ -71,7 +71,7 @@ ifcapable fts3 {
do_catchsql_test 3.2 {
DROP TABLE vt1;
} {1 {SQL logic error}}
} {0 {}}
do_execsql_test 3.3 {
SAVEPOINT x;

View File

@ -18,7 +18,7 @@ db progress 1 progress_handler
set ::PHC 0
proc progress_handler {args} {
incr ::PHC
if {($::PHC % 100000)==0} breakpoint
# if {($::PHC % 100000)==0} breakpoint
return 0
}
@ -73,12 +73,12 @@ do_execsql_test 2.2 {
#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
INSERT INTO t1(t1, rank) VALUES('secure-delete', $sd)
}
do_execsql_test 2.1 {
do_execsql_test 3.1 {
BEGIN;
INSERT INTO t1(rowid, x)
VALUES(51869, 'when whenever where weress what turn'),
@ -91,5 +91,51 @@ do_execsql_test 3.2 {
INSERT INTO t1(t1) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
INSERT INTO t1(rowid, x) VALUES(10, 'one two');
}
do_execsql_test 4.1 {
UPDATE t1 SET x = 'one three' WHERE rowid=10;
INSERT INTO t1(t1, rank) VALUES('secure-delete', 1);
}
do_execsql_test 4.2 {
DELETE FROM t1 WHERE rowid=10;
}
do_execsql_test 4.3 {
INSERT INTO t1(t1) VALUES('integrity-check');
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 5.0 {
CREATE VIRTUAL TABLE t1 USING fts5(content);
INSERT INTO t1(t1,rank) VALUES('secure-delete',1);
INSERT INTO t1 VALUES('active'),('boomer'),('atom'),('atomic'),
('alpha channel backup abandon test aback boomer atom alpha active');
DELETE FROM t1 WHERE t1 MATCH 'abandon';
}
do_execsql_test 5.1 {
INSERT INTO t1(t1) VALUES('rebuild');
}
do_execsql_test 5.2 {
DELETE FROM t1 WHERE rowid NOTNULL<5;
}
db close
sqlite3 db test.db
do_execsql_test 5.3 {
PRAGMA integrity_check;
} {ok}
finish_test

View File

@ -26,8 +26,9 @@ dir.src.c := $(dir.src)/c
dir.bld := $(dir.jni)/bld
dir.bld.c := $(dir.bld)
dir.src.jni := $(dir.src)/org/sqlite/jni
dir.src.jni.tester := $(dir.src.jni)/tester
dir.src.capi := $(dir.src.jni)/capi
dir.src.fts5 := $(dir.src.jni)/fts5
dir.tests := $(dir.src)/tests
mkdir ?= mkdir -p
$(dir.bld.c):
$(mkdir) $@
@ -45,9 +46,9 @@ DISTCLEAN_FILES := $(dir.jni)/*~ $(dir.src.c)/*~ $(dir.src.jni)/*~
sqlite3-jni.h := $(dir.src.c)/sqlite3-jni.h
.NOTPARALLEL: $(sqlite3-jni.h)
SQLite3Jni.java := src/org/sqlite/jni/SQLite3Jni.java
SQLTester.java := src/org/sqlite/jni/tester/SQLTester.java
SQLite3Jni.class := $(SQLite3Jni.java:.java=.class)
CApi.java := $(dir.src.capi)/CApi.java
SQLTester.java := $(dir.src.capi)/SQLTester.java
CApi.class := $(CApi.java:.java=.class)
SQLTester.class := $(SQLTester.java:.java=.class)
########################################################################
@ -61,8 +62,12 @@ SQLTester.class := $(SQLTester.java:.java=.class)
# which the fts5 APIs have been stripped unless that feature is
# intended to be stripped for good.
enable.fts5 ?= 1
# If enable.tester is 0, the org/sqlite/jni/tester/* bits are elided.
enable.tester ?= $(if $(wildcard $(dir.src.jni.tester)/SQLTester.java),1,0)
ifeq (,$(wildcard $(dir.tests)/*))
enable.tester := 0
else
enable.tester := 1
endif
# bin.version-info = binary to output various sqlite3 version info
# building the distribution zip file.
@ -73,10 +78,10 @@ $(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile
# Be explicit about which Java files to compile so that we can work on
# in-progress files without requiring them to be in a compilable statae.
JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\
annotation/Canonical.java \
annotation/NotNull.java \
annotation/Nullable.java \
JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\
NotNull.java \
Nullable.java \
) $(patsubst %,$(dir.src.capi)/%,\
AbstractCollationCallback.java \
AggregateFunction.java \
AuthorizerCallback.java \
@ -97,21 +102,34 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\
ScalarFunction.java \
SQLFunction.java \
CallbackProxy.java \
SQLite3Jni.java \
CApi.java \
TableColumnMetadata.java \
TraceV2Callback.java \
UpdateHookCallback.java \
ValueHolder.java \
WindowFunction.java \
XDestroyCallback.java \
sqlite3.java \
sqlite3_context.java \
sqlite3_stmt.java \
sqlite3_value.java \
) $(patsubst %,$(dir.src.jni)/wrapper1/%,\
AggregateFunction.java \
ScalarFunction.java \
SqlFunction.java \
Sqlite.java \
SqliteException.java \
ValueHolder.java \
)
JAVA_FILES.unittest := $(patsubst %,$(dir.src.jni)/%,\
Tester1.java \
capi/Tester1.java \
wrapper1/Tester2.java \
)
ifeq (1,$(enable.fts5))
JAVA_FILES.unittest += $(patsubst %,$(dir.src.fts5)/%,\
TesterFts5.java \
)
JAVA_FILES.main += $(patsubst %,$(dir.src.fts5)/%,\
fts5_api.java \
fts5_extension_function.java \
@ -121,11 +139,10 @@ ifeq (1,$(enable.fts5))
Fts5ExtensionApi.java \
Fts5PhraseIter.java \
Fts5Tokenizer.java \
TesterFts5.java \
XTokenizeCallback.java \
)
endif
JAVA_FILES.tester := $(dir.src.jni.tester)/SQLTester.java
JAVA_FILES.tester := $(SQLTester.java)
JAVA_FILES.package.info := \
$(dir.src.jni)/package-info.java \
$(dir.src.jni)/annotation/package-info.java
@ -188,7 +205,19 @@ opt.fatal-oom ?= 1
opt.debug ?= 1
opt.metrics ?= 1
SQLITE_OPT = \
-DSQLITE_ENABLE_RTREE \
-DSQLITE_THREADSAFE=$(opt.threadsafe) \
-DSQLITE_TEMP_STORE=2 \
-DSQLITE_USE_URI=1 \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_C=$(sqlite3.c) \
-DSQLITE_JNI_FATAL_OOM=$(opt.fatal-oom) \
-DSQLITE_JNI_ENABLE_METRICS=$(opt.metrics)
opt.extras ?= 1
ifeq (1,$(opt.extras))
SQLITE_OPT += -DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_STMTVTAB \
-DSQLITE_ENABLE_DBPAGE_VTAB \
@ -196,21 +225,14 @@ SQLITE_OPT = \
-DSQLITE_ENABLE_BYTECODE_VTAB \
-DSQLITE_ENABLE_OFFSET_SQL_FUNC \
-DSQLITE_ENABLE_PREUPDATE_HOOK \
-DSQLITE_ENABLE_SQLLOG \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_THREADSAFE=$(opt.threadsafe) \
-DSQLITE_TEMP_STORE=2 \
-DSQLITE_USE_URI=1 \
-DSQLITE_C=$(sqlite3.c) \
-DSQLITE_JNI_FATAL_OOM=$(opt.fatal-oom) \
-DSQLITE_JNI_ENABLE_METRICS=$(opt.metrics)
-DSQLITE_ENABLE_NORMALIZE \
-DSQLITE_ENABLE_SQLLOG
endif
ifeq (1,$(opt.debug))
SQLITE_OPT += -DSQLITE_DEBUG -g -DDEBUG -UNDEBUG
else
SQLITE_OPT += -O2
SQLITE_OPT += -Os
endif
ifeq (1,$(enable.fts5))
@ -232,19 +254,18 @@ $$(dir.bld.c)/org_sqlite_jni$(3)_$(2).h: $(1)/$(2).java
endef
# Invoke ADD_JNI_H once for each Java file which includes JNI
# declarations:
$(eval $(call ADD_JNI_H,$(dir.src.jni),SQLite3Jni,))
$(eval $(call ADD_JNI_H,$(dir.src.capi),CApi,_capi))
$(eval $(call ADD_JNI_H,$(dir.src.capi),SQLTester,_capi))
ifeq (1,$(enable.fts5))
$(eval $(call ADD_JNI_H,$(dir.src.fts5),Fts5ExtensionApi,_fts5))
$(eval $(call ADD_JNI_H,$(dir.src.fts5),fts5_api,_fts5))
$(eval $(call ADD_JNI_H,$(dir.src.fts5),fts5_tokenizer,_fts5))
endif
ifeq (1,$(enable.tester))
$(eval $(call ADD_JNI_H,$(dir.src.jni.tester),SQLTester,_tester))
endif
$(sqlite3-jni.h.in): $(dir.bld.c)
#package.dll.cfiles :=
package.dll.cflags = \
-std=c99 \
-fPIC \
-I. \
-I$(dir $(sqlite3.h)) \
@ -252,17 +273,18 @@ package.dll.cflags = \
-I$(JDK_HOME)/include \
$(patsubst %,-I%,$(patsubst %.h,,$(wildcard $(JDK_HOME)/include/*))) \
-Wall
# Using (-Wall -Wextra) triggers an untennable number of
# gcc warnings from sqlite3.c for mundane things like
# unused parameters.
#
# The gross $(patsubst...) above is to include the platform-specific
# subdir which lives under $(JDK_HOME)/include and is a required
# include path for client-level code.
#
# Using (-Wall -Wextra) triggers an untennable number of
# gcc warnings from sqlite3.c for mundane things like
# unused parameters.
########################################################################
ifeq (1,$(enable.tester))
package.dll.cflags += -DSQLITE_JNI_ENABLE_SQLTester
endif
$(sqlite3-jni.h): $(sqlite3-jni.h.in) $(MAKEFILE)
@cat $(sqlite3-jni.h.in) > $@.tmp
@if cmp $@ $@.tmp >/dev/null; then \
@ -284,19 +306,23 @@ $(package.dll): $(sqlite3-jni.c) $(MAKEFILE)
all: $(package.dll)
.PHONY: test test-one
test.flags ?=
test.main.flags = -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath) \
org.sqlite.jni.Tester1
Tester1.flags ?=
Tester2.flags ?=
test.flags.jvm = -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath)
test.deps := $(CLASS_FILES) $(package.dll)
test-one: $(test.deps)
$(bin.java) $(test.main.flags) $(test.flags)
$(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 $(Tester1.flags)
$(bin.java) $(test.flags.jvm) org.sqlite.jni.wrapper1.Tester2 $(Tester2.flags)
test-sqllog: $(test.deps)
@echo "Testing with -sqllog..."
$(bin.java) $(test.main.flags) -sqllog
$(bin.java) $(test.flags.jvm) -sqllog
test-mt: $(test.deps)
@echo "Testing in multi-threaded mode:";
$(bin.java) $(test.main.flags) -t 7 -r 50 -shuffle $(test.flags)
$(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 \
-t 7 -r 50 -shuffle $(Tester1.flags)
$(bin.java) $(test.flags.jvm) org.sqlite.jni.wrapper1.Tester2 \
-t 7 -r 50 -shuffle $(Tester2.flags)
test: test-one test-mt
tests: test test-sqllog
@ -308,21 +334,21 @@ ifeq (1,$(enable.tester))
tester-local: $(CLASS_FILES.tester) $(package.dll)
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath) \
org.sqlite.jni.tester.SQLTester $(tester.flags) $(tester.scripts)
org.sqlite.jni.capi.SQLTester $(tester.flags) $(tester.scripts)
tester: tester-local
else
tester:
@echo "SQLTester support is disabled. Build with enable.tester=1 to enable it."
@echo "SQLTester support is disabled."
endif
tester.extdir.default := src/tests/ext
tester.extdir.default := $(dir.tests)/ext
tester.extdir ?= $(tester.extdir.default)
tester.extern-scripts := $(wildcard $(tester.extdir)/*.test)
ifneq (,$(tester.extern-scripts))
tester-ext:
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
$(java.flags) -cp $(classpath) \
org.sqlite.jni.tester.SQLTester $(tester.flags) $(tester.extern-scripts)
org.sqlite.jni.capi.SQLTester $(tester.flags) $(tester.extern-scripts)
else
tester-ext:
@echo "******************************************************"; \
@ -362,13 +388,12 @@ JAVA_FILES.jar := $(JAVA_FILES.main) $(JAVA_FILES.unittest) $(JAVA_FILES.package
CLASS_FILES.jar := $(filter-out %/package-info.class,$(JAVA_FILES.jar:.java=.class))
$(package.jar.in): $(package.dll) $(MAKEFILE)
ls -1 \
$(dir.src.jni)/*.java $(dir.src.jni)/*.class \
$(dir.src.jni)/annotation/*.java $(dir.src.jni)/annotation/*.class \
$(dir.src.jni)/*/*.java $(dir.src.jni)/*/*.class \
| sed -e 's,^$(dir.src)/,,' | sort > $@
$(package.jar): $(CLASS_FILES.jar) $(MAKEFILE) $(package.jar.in)
@rm -f $(dir.src)/c/*~ $(dir.src.jni)/*~
cd $(dir.src); $(bin.jar) -cfe ../$@ org.sqlite.jni.Tester1 @$(package.jar.in)
cd $(dir.src); $(bin.jar) -cfe ../$@ org.sqlite.jni.capi.Tester1 @$(package.jar.in)
@ls -la $@
@echo "To use this jar you will need the -Djava.library.path=DIR/CONTAINING/libsqlite3-jni.so flag."
@echo "e.g. java -Djava.library.path=bld -jar $@"
@ -381,11 +406,12 @@ run-jar: $(package.jar) $(package.dll)
# javadoc...
dir.doc := $(dir.jni)/javadoc
doc.index := $(dir.doc)/index.html
javadoc.exclude := -exclude org.sqlite.jni.tester \
-exclude org.sqlite.jni.fts5
javadoc.exclude := -exclude org.sqlite.jni.fts5
# ^^^^ 2023-09-13: elide the fts5 parts from the public docs for
# the time being, as it's not clear where the Java bindings for
# those bits are going.
# javadoc.exclude += -exclude org.sqlite.jni.capi
# ^^^^ exclude the capi API only for certain builds (TBD)
$(doc.index): $(JAVA_FILES.main) $(MAKEFILE)
@if [ -d $(dir.doc) ]; then rm -fr $(dir.doc)/*; fi
$(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet \
@ -406,7 +432,7 @@ docserve: $(doc.index)
# Clean up...
CLEAN_FILES += $(dir.bld.c)/* \
$(dir.src.jni)/*.class \
$(dir.src.jni.tester)/*.class \
$(dir.src.jni)/*/*.class \
$(package.dll) \
hs_err_pid*.log

View File

@ -55,7 +55,7 @@ Hello World
```java
import org.sqlite.jni.*;
import static org.sqlite.jni.SQLite3Jni.*;
import static org.sqlite.jni.CApi.*;
...

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,155 +0,0 @@
/*
** 2023-07-21
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
/**
This enum contains all of the core and "extended" result codes used
by the sqlite3 library. It is provided not for use with the C-style
API (with which it won't work) but for higher-level code which may
find it useful to map SQLite result codes to human-readable names.
*/
public enum ResultCode {
SQLITE_OK(SQLite3Jni.SQLITE_OK),
SQLITE_ERROR(SQLite3Jni.SQLITE_ERROR),
SQLITE_INTERNAL(SQLite3Jni.SQLITE_INTERNAL),
SQLITE_PERM(SQLite3Jni.SQLITE_PERM),
SQLITE_ABORT(SQLite3Jni.SQLITE_ABORT),
SQLITE_BUSY(SQLite3Jni.SQLITE_BUSY),
SQLITE_LOCKED(SQLite3Jni.SQLITE_LOCKED),
SQLITE_NOMEM(SQLite3Jni.SQLITE_NOMEM),
SQLITE_READONLY(SQLite3Jni.SQLITE_READONLY),
SQLITE_INTERRUPT(SQLite3Jni.SQLITE_INTERRUPT),
SQLITE_IOERR(SQLite3Jni.SQLITE_IOERR),
SQLITE_CORRUPT(SQLite3Jni.SQLITE_CORRUPT),
SQLITE_NOTFOUND(SQLite3Jni.SQLITE_NOTFOUND),
SQLITE_FULL(SQLite3Jni.SQLITE_FULL),
SQLITE_CANTOPEN(SQLite3Jni.SQLITE_CANTOPEN),
SQLITE_PROTOCOL(SQLite3Jni.SQLITE_PROTOCOL),
SQLITE_EMPTY(SQLite3Jni.SQLITE_EMPTY),
SQLITE_SCHEMA(SQLite3Jni.SQLITE_SCHEMA),
SQLITE_TOOBIG(SQLite3Jni.SQLITE_TOOBIG),
SQLITE_CONSTRAINT(SQLite3Jni.SQLITE_CONSTRAINT),
SQLITE_MISMATCH(SQLite3Jni.SQLITE_MISMATCH),
SQLITE_MISUSE(SQLite3Jni.SQLITE_MISUSE),
SQLITE_NOLFS(SQLite3Jni.SQLITE_NOLFS),
SQLITE_AUTH(SQLite3Jni.SQLITE_AUTH),
SQLITE_FORMAT(SQLite3Jni.SQLITE_FORMAT),
SQLITE_RANGE(SQLite3Jni.SQLITE_RANGE),
SQLITE_NOTADB(SQLite3Jni.SQLITE_NOTADB),
SQLITE_NOTICE(SQLite3Jni.SQLITE_NOTICE),
SQLITE_WARNING(SQLite3Jni.SQLITE_WARNING),
SQLITE_ROW(SQLite3Jni.SQLITE_ROW),
SQLITE_DONE(SQLite3Jni.SQLITE_DONE),
SQLITE_ERROR_MISSING_COLLSEQ(SQLite3Jni.SQLITE_ERROR_MISSING_COLLSEQ),
SQLITE_ERROR_RETRY(SQLite3Jni.SQLITE_ERROR_RETRY),
SQLITE_ERROR_SNAPSHOT(SQLite3Jni.SQLITE_ERROR_SNAPSHOT),
SQLITE_IOERR_READ(SQLite3Jni.SQLITE_IOERR_READ),
SQLITE_IOERR_SHORT_READ(SQLite3Jni.SQLITE_IOERR_SHORT_READ),
SQLITE_IOERR_WRITE(SQLite3Jni.SQLITE_IOERR_WRITE),
SQLITE_IOERR_FSYNC(SQLite3Jni.SQLITE_IOERR_FSYNC),
SQLITE_IOERR_DIR_FSYNC(SQLite3Jni.SQLITE_IOERR_DIR_FSYNC),
SQLITE_IOERR_TRUNCATE(SQLite3Jni.SQLITE_IOERR_TRUNCATE),
SQLITE_IOERR_FSTAT(SQLite3Jni.SQLITE_IOERR_FSTAT),
SQLITE_IOERR_UNLOCK(SQLite3Jni.SQLITE_IOERR_UNLOCK),
SQLITE_IOERR_RDLOCK(SQLite3Jni.SQLITE_IOERR_RDLOCK),
SQLITE_IOERR_DELETE(SQLite3Jni.SQLITE_IOERR_DELETE),
SQLITE_IOERR_BLOCKED(SQLite3Jni.SQLITE_IOERR_BLOCKED),
SQLITE_IOERR_NOMEM(SQLite3Jni.SQLITE_IOERR_NOMEM),
SQLITE_IOERR_ACCESS(SQLite3Jni.SQLITE_IOERR_ACCESS),
SQLITE_IOERR_CHECKRESERVEDLOCK(SQLite3Jni.SQLITE_IOERR_CHECKRESERVEDLOCK),
SQLITE_IOERR_LOCK(SQLite3Jni.SQLITE_IOERR_LOCK),
SQLITE_IOERR_CLOSE(SQLite3Jni.SQLITE_IOERR_CLOSE),
SQLITE_IOERR_DIR_CLOSE(SQLite3Jni.SQLITE_IOERR_DIR_CLOSE),
SQLITE_IOERR_SHMOPEN(SQLite3Jni.SQLITE_IOERR_SHMOPEN),
SQLITE_IOERR_SHMSIZE(SQLite3Jni.SQLITE_IOERR_SHMSIZE),
SQLITE_IOERR_SHMLOCK(SQLite3Jni.SQLITE_IOERR_SHMLOCK),
SQLITE_IOERR_SHMMAP(SQLite3Jni.SQLITE_IOERR_SHMMAP),
SQLITE_IOERR_SEEK(SQLite3Jni.SQLITE_IOERR_SEEK),
SQLITE_IOERR_DELETE_NOENT(SQLite3Jni.SQLITE_IOERR_DELETE_NOENT),
SQLITE_IOERR_MMAP(SQLite3Jni.SQLITE_IOERR_MMAP),
SQLITE_IOERR_GETTEMPPATH(SQLite3Jni.SQLITE_IOERR_GETTEMPPATH),
SQLITE_IOERR_CONVPATH(SQLite3Jni.SQLITE_IOERR_CONVPATH),
SQLITE_IOERR_VNODE(SQLite3Jni.SQLITE_IOERR_VNODE),
SQLITE_IOERR_AUTH(SQLite3Jni.SQLITE_IOERR_AUTH),
SQLITE_IOERR_BEGIN_ATOMIC(SQLite3Jni.SQLITE_IOERR_BEGIN_ATOMIC),
SQLITE_IOERR_COMMIT_ATOMIC(SQLite3Jni.SQLITE_IOERR_COMMIT_ATOMIC),
SQLITE_IOERR_ROLLBACK_ATOMIC(SQLite3Jni.SQLITE_IOERR_ROLLBACK_ATOMIC),
SQLITE_IOERR_DATA(SQLite3Jni.SQLITE_IOERR_DATA),
SQLITE_IOERR_CORRUPTFS(SQLite3Jni.SQLITE_IOERR_CORRUPTFS),
SQLITE_LOCKED_SHAREDCACHE(SQLite3Jni.SQLITE_LOCKED_SHAREDCACHE),
SQLITE_LOCKED_VTAB(SQLite3Jni.SQLITE_LOCKED_VTAB),
SQLITE_BUSY_RECOVERY(SQLite3Jni.SQLITE_BUSY_RECOVERY),
SQLITE_BUSY_SNAPSHOT(SQLite3Jni.SQLITE_BUSY_SNAPSHOT),
SQLITE_BUSY_TIMEOUT(SQLite3Jni.SQLITE_BUSY_TIMEOUT),
SQLITE_CANTOPEN_NOTEMPDIR(SQLite3Jni.SQLITE_CANTOPEN_NOTEMPDIR),
SQLITE_CANTOPEN_ISDIR(SQLite3Jni.SQLITE_CANTOPEN_ISDIR),
SQLITE_CANTOPEN_FULLPATH(SQLite3Jni.SQLITE_CANTOPEN_FULLPATH),
SQLITE_CANTOPEN_CONVPATH(SQLite3Jni.SQLITE_CANTOPEN_CONVPATH),
SQLITE_CANTOPEN_SYMLINK(SQLite3Jni.SQLITE_CANTOPEN_SYMLINK),
SQLITE_CORRUPT_VTAB(SQLite3Jni.SQLITE_CORRUPT_VTAB),
SQLITE_CORRUPT_SEQUENCE(SQLite3Jni.SQLITE_CORRUPT_SEQUENCE),
SQLITE_CORRUPT_INDEX(SQLite3Jni.SQLITE_CORRUPT_INDEX),
SQLITE_READONLY_RECOVERY(SQLite3Jni.SQLITE_READONLY_RECOVERY),
SQLITE_READONLY_CANTLOCK(SQLite3Jni.SQLITE_READONLY_CANTLOCK),
SQLITE_READONLY_ROLLBACK(SQLite3Jni.SQLITE_READONLY_ROLLBACK),
SQLITE_READONLY_DBMOVED(SQLite3Jni.SQLITE_READONLY_DBMOVED),
SQLITE_READONLY_CANTINIT(SQLite3Jni.SQLITE_READONLY_CANTINIT),
SQLITE_READONLY_DIRECTORY(SQLite3Jni.SQLITE_READONLY_DIRECTORY),
SQLITE_ABORT_ROLLBACK(SQLite3Jni.SQLITE_ABORT_ROLLBACK),
SQLITE_CONSTRAINT_CHECK(SQLite3Jni.SQLITE_CONSTRAINT_CHECK),
SQLITE_CONSTRAINT_COMMITHOOK(SQLite3Jni.SQLITE_CONSTRAINT_COMMITHOOK),
SQLITE_CONSTRAINT_FOREIGNKEY(SQLite3Jni.SQLITE_CONSTRAINT_FOREIGNKEY),
SQLITE_CONSTRAINT_FUNCTION(SQLite3Jni.SQLITE_CONSTRAINT_FUNCTION),
SQLITE_CONSTRAINT_NOTNULL(SQLite3Jni.SQLITE_CONSTRAINT_NOTNULL),
SQLITE_CONSTRAINT_PRIMARYKEY(SQLite3Jni.SQLITE_CONSTRAINT_PRIMARYKEY),
SQLITE_CONSTRAINT_TRIGGER(SQLite3Jni.SQLITE_CONSTRAINT_TRIGGER),
SQLITE_CONSTRAINT_UNIQUE(SQLite3Jni.SQLITE_CONSTRAINT_UNIQUE),
SQLITE_CONSTRAINT_VTAB(SQLite3Jni.SQLITE_CONSTRAINT_VTAB),
SQLITE_CONSTRAINT_ROWID(SQLite3Jni.SQLITE_CONSTRAINT_ROWID),
SQLITE_CONSTRAINT_PINNED(SQLite3Jni.SQLITE_CONSTRAINT_PINNED),
SQLITE_CONSTRAINT_DATATYPE(SQLite3Jni.SQLITE_CONSTRAINT_DATATYPE),
SQLITE_NOTICE_RECOVER_WAL(SQLite3Jni.SQLITE_NOTICE_RECOVER_WAL),
SQLITE_NOTICE_RECOVER_ROLLBACK(SQLite3Jni.SQLITE_NOTICE_RECOVER_ROLLBACK),
SQLITE_WARNING_AUTOINDEX(SQLite3Jni.SQLITE_WARNING_AUTOINDEX),
SQLITE_AUTH_USER(SQLite3Jni.SQLITE_AUTH_USER),
SQLITE_OK_LOAD_PERMANENTLY(SQLite3Jni.SQLITE_OK_LOAD_PERMANENTLY);
public final int value;
ResultCode(int rc){
value = rc;
ResultCodeMap.set(rc, this);
}
/**
Returns the entry from this enum for the given result code, or
null if no match is found.
*/
public static ResultCode getEntryForInt(int rc){
return ResultCodeMap.get(rc);
}
/**
Internal level of indirection required because we cannot initialize
static enum members in an enum before the enum constructor is
invoked.
*/
private static final class ResultCodeMap {
private static final java.util.Map<Integer,ResultCode> i2e
= new java.util.HashMap<>();
private static void set(int rc, ResultCode e){ i2e.put(rc, e); }
private static ResultCode get(int rc){ return i2e.get(rc); }
}
}

View File

@ -1,35 +0,0 @@
package org.sqlite.jni.annotation;
/**
This annotation is for marking functions as "canonical", meaning
that they map directly to a function in the core sqlite3 C API. The
intent is to distinguish them from functions added specifically to
the Java API.
<p>Canonical functions, unless specifically documented, have the
same semantics as their counterparts in <a
href="https://sqlite.org/c3ref/intro.html">the C API
documentation</a>, despite their signatures perhaps differing
slightly. Canonical forms may be native or implemented in Java.
Sometimes multiple overloads are labeled as Canonical because one
or more of them are just type- or encoding-related conversion
wrappers but provide identical semantics (e.g. from a String to a
byte[]). The Java API adds a number of convenience overloads to
simplify use, as well as a few Java-specific functions, and those
are never flagged as @Canonical.
<p>In some cases, the canonical version of a function is private
and exposed to Java via public overloads.
<p>The comment property can be used to add a comment.
*/
@java.lang.annotation.Documented
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD)
public @interface Canonical{
/**
Brief comments about the binding, e.g. noting any major
semantic differences.
*/
String comment() default "";
}

View File

@ -1,3 +1,16 @@
/*
** 2023-09-27
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file houses the NotNull annotaion for the sqlite3 C API.
*/
package org.sqlite.jni.annotation;
/**
@ -20,6 +33,11 @@ package org.sqlite.jni.annotation;
any parameter marked with this annoation specifically invokes
undefined behavior.</p>
<p>Passing 0 (i.e. C NULL) or a negative value for any long-type
parameter marked with this annoation specifically invokes undefined
behavior. Such values are treated as C pointers in the JNI
layer.</p>
<p>Note that the C-style API does not throw any exceptions on its
own because it has a no-throw policy in order to retain its C-style
semantics, but it may trigger NullPointerExceptions (or similar) if
@ -29,10 +47,11 @@ package org.sqlite.jni.annotation;
programmatically ensure that NotNull is conformed to in client
code.</p>
<p>This annotation is solely for the use by the classes in this
package but is made public so that javadoc will link to it from the
annotated functions. It is not part of the public API and
client-level code must not rely on it.</p>
<p>This annotation is solely for the use by the classes in the
org.sqlite package and subpackages, but is made public so that
javadoc will link to it from the annotated functions. It is not
part of the public API and client-level code must not rely on
it.</p>
*/
@java.lang.annotation.Documented
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)

View File

@ -1,3 +1,16 @@
/*
** 2023-09-27
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file houses the Nullable annotaion for the sqlite3 C API.
*/
package org.sqlite.jni.annotation;
/**

View File

@ -1,4 +1,17 @@
/*
** 2023-09-27
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
*/
/**
This package houses annotations specific a JNI binding to the SQLite3 C API.
This package houses annotations specific to the JNI bindings of the
SQLite3 C API.
*/
package org.sqlite.jni.annotation;

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
import org.sqlite.jni.annotation.NotNull;
/**

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**

View File

@ -11,11 +11,11 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
import org.sqlite.jni.annotation.*;
/**
Callback for use with {@link SQLite3Jni#sqlite3_set_authorizer}.
Callback for use with {@link CApi#sqlite3_set_authorizer}.
*/
public interface AuthorizerCallback extends CallbackProxy {
/**

View File

@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with the {@link SQLite3Jni#sqlite3_auto_extension}
Callback for use with the {@link CApi#sqlite3_auto_extension}
family of APIs.
*/
public interface AutoExtensionCallback extends CallbackProxy {

View File

@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_busy_handler}.
Callback for use with {@link CApi#sqlite3_busy_handler}.
*/
public interface BusyHandlerCallback extends CallbackProxy {
/**

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
This marker interface exists soley for use as a documentation and
class-grouping tool. It should be applied to interfaces or

View File

@ -11,11 +11,11 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
import org.sqlite.jni.annotation.NotNull;
/**
Callback for use with {@link SQLite3Jni#sqlite3_create_collation}.
Callback for use with {@link CApi#sqlite3_create_collation}.
@see AbstractCollationCallback
*/

View File

@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_collation_needed}.
Callback for use with {@link CApi#sqlite3_collation_needed}.
*/
public interface CollationNeededCallback extends CallbackProxy {
/**

View File

@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_commit_hook}.
Callback for use with {@link CApi#sqlite3_commit_hook}.
*/
public interface CommitHookCallback extends CallbackProxy {
/**

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A callback for use with sqlite3_config().
@ -19,7 +19,7 @@ package org.sqlite.jni;
public interface ConfigLogCallback {
/**
Must function as described for a C-level callback for
{@link SQLite3Jni#sqlite3_config(ConfigLogCallback)}, with the slight signature change.
{@link CApi#sqlite3_config(ConfigLogCallback)}, with the slight signature change.
*/
void call(int errCode, String msg);
}

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A callback for use with sqlite3_config().
@ -19,7 +19,7 @@ package org.sqlite.jni;
public interface ConfigSqllogCallback {
/**
Must function as described for a C-level callback for
{@link SQLite3Jni#sqlite3_config(ConfigSqllogCallback)}, with the slight signature change.
{@link CApi#sqlite3_config(ConfigSqllogCallback)}, with the slight signature change.
*/
void call(sqlite3 db, String msg, int msgType );
}

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A helper for passing pointers between JNI C code and Java, in
@ -29,5 +29,18 @@ package org.sqlite.jni;
public class NativePointerHolder<ContextType> {
//! Only set from JNI, where access permissions don't matter.
private volatile long nativePointer = 0;
/**
For use ONLY by package-level APIs which act as proxies for
close/finalize operations. Such ops must call this to zero out
the pointer so that this object is not carrying a stale
pointer. This function returns the prior value of the pointer and
sets it to 0.
*/
final long clearNativePointer() {
final long rv = nativePointer;
nativePointer= 0;
return rv;
}
public final long getNativePointer(){ return nativePointer; }
}

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Helper classes for handling JNI output pointers.
@ -49,16 +49,16 @@ public final class OutputPointer {
code.
*/
public static final class sqlite3 {
private org.sqlite.jni.sqlite3 value;
private org.sqlite.jni.capi.sqlite3 value;
/** Initializes with a null value. */
public sqlite3(){value = null;}
/** Sets the current value to null. */
public void clear(){value = null;}
/** Returns the current value. */
public final org.sqlite.jni.sqlite3 get(){return value;}
public final org.sqlite.jni.capi.sqlite3 get(){return value;}
/** Equivalent to calling get() then clear(). */
public final org.sqlite.jni.sqlite3 take(){
final org.sqlite.jni.sqlite3 v = value;
public final org.sqlite.jni.capi.sqlite3 take(){
final org.sqlite.jni.capi.sqlite3 v = value;
value = null;
return v;
}
@ -70,16 +70,16 @@ public final class OutputPointer {
code.
*/
public static final class sqlite3_blob {
private org.sqlite.jni.sqlite3_blob value;
private org.sqlite.jni.capi.sqlite3_blob value;
/** Initializes with a null value. */
public sqlite3_blob(){value = null;}
/** Sets the current value to null. */
public void clear(){value = null;}
/** Returns the current value. */
public final org.sqlite.jni.sqlite3_blob get(){return value;}
public final org.sqlite.jni.capi.sqlite3_blob get(){return value;}
/** Equivalent to calling get() then clear(). */
public final org.sqlite.jni.sqlite3_blob take(){
final org.sqlite.jni.sqlite3_blob v = value;
public final org.sqlite.jni.capi.sqlite3_blob take(){
final org.sqlite.jni.capi.sqlite3_blob v = value;
value = null;
return v;
}
@ -92,16 +92,16 @@ public final class OutputPointer {
code.
*/
public static final class sqlite3_stmt {
private org.sqlite.jni.sqlite3_stmt value;
private org.sqlite.jni.capi.sqlite3_stmt value;
/** Initializes with a null value. */
public sqlite3_stmt(){value = null;}
/** Sets the current value to null. */
public void clear(){value = null;}
/** Returns the current value. */
public final org.sqlite.jni.sqlite3_stmt get(){return value;}
public final org.sqlite.jni.capi.sqlite3_stmt get(){return value;}
/** Equivalent to calling get() then clear(). */
public final org.sqlite.jni.sqlite3_stmt take(){
final org.sqlite.jni.sqlite3_stmt v = value;
public final org.sqlite.jni.capi.sqlite3_stmt take(){
final org.sqlite.jni.capi.sqlite3_stmt v = value;
value = null;
return v;
}
@ -114,16 +114,16 @@ public final class OutputPointer {
code.
*/
public static final class sqlite3_value {
private org.sqlite.jni.sqlite3_value value;
private org.sqlite.jni.capi.sqlite3_value value;
/** Initializes with a null value. */
public sqlite3_value(){value = null;}
/** Sets the current value to null. */
public void clear(){value = null;}
/** Returns the current value. */
public final org.sqlite.jni.sqlite3_value get(){return value;}
public final org.sqlite.jni.capi.sqlite3_value get(){return value;}
/** Equivalent to calling get() then clear(). */
public final org.sqlite.jni.sqlite3_value take(){
final org.sqlite.jni.sqlite3_value v = value;
public final org.sqlite.jni.capi.sqlite3_value take(){
final org.sqlite.jni.capi.sqlite3_value v = value;
value = null;
return v;
}

View File

@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_prepare_multi}.
Callback for use with {@link CApi#sqlite3_prepare_multi}.
*/
public interface PrepareMultiCallback extends CallbackProxy {
@ -53,7 +53,7 @@ public interface PrepareMultiCallback extends CallbackProxy {
try {
return this.p.call(st);
}finally{
SQLite3Jni.sqlite3_finalize(st);
CApi.sqlite3_finalize(st);
}
}
}
@ -70,9 +70,9 @@ public interface PrepareMultiCallback extends CallbackProxy {
else the result of the final step is returned.
*/
@Override public int call(sqlite3_stmt st){
int rc = SQLite3Jni.SQLITE_DONE;
while( SQLite3Jni.SQLITE_ROW == (rc = SQLite3Jni.sqlite3_step(st)) ){}
return SQLite3Jni.SQLITE_DONE==rc ? 0 : rc;
int rc = CApi.SQLITE_DONE;
while( CApi.SQLITE_ROW == (rc = CApi.sqlite3_step(st)) ){}
return CApi.SQLITE_DONE==rc ? 0 : rc;
}
}
}

View File

@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_preupdate_hook}.
Callback for use with {@link CApi#sqlite3_preupdate_hook}.
*/
public interface PreupdateHookCallback extends CallbackProxy {
/**

View File

@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_progress_handler}.
Callback for use with {@link CApi#sqlite3_progress_handler}.
*/
public interface ProgressHandlerCallback extends CallbackProxy {
/**

View File

@ -0,0 +1,155 @@
/*
** 2023-07-21
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.capi;
/**
This enum contains all of the core and "extended" result codes used
by the sqlite3 library. It is provided not for use with the C-style
API (with which it won't work) but for higher-level code which may
find it useful to map SQLite result codes to human-readable names.
*/
public enum ResultCode {
SQLITE_OK(CApi.SQLITE_OK),
SQLITE_ERROR(CApi.SQLITE_ERROR),
SQLITE_INTERNAL(CApi.SQLITE_INTERNAL),
SQLITE_PERM(CApi.SQLITE_PERM),
SQLITE_ABORT(CApi.SQLITE_ABORT),
SQLITE_BUSY(CApi.SQLITE_BUSY),
SQLITE_LOCKED(CApi.SQLITE_LOCKED),
SQLITE_NOMEM(CApi.SQLITE_NOMEM),
SQLITE_READONLY(CApi.SQLITE_READONLY),
SQLITE_INTERRUPT(CApi.SQLITE_INTERRUPT),
SQLITE_IOERR(CApi.SQLITE_IOERR),
SQLITE_CORRUPT(CApi.SQLITE_CORRUPT),
SQLITE_NOTFOUND(CApi.SQLITE_NOTFOUND),
SQLITE_FULL(CApi.SQLITE_FULL),
SQLITE_CANTOPEN(CApi.SQLITE_CANTOPEN),
SQLITE_PROTOCOL(CApi.SQLITE_PROTOCOL),
SQLITE_EMPTY(CApi.SQLITE_EMPTY),
SQLITE_SCHEMA(CApi.SQLITE_SCHEMA),
SQLITE_TOOBIG(CApi.SQLITE_TOOBIG),
SQLITE_CONSTRAINT(CApi.SQLITE_CONSTRAINT),
SQLITE_MISMATCH(CApi.SQLITE_MISMATCH),
SQLITE_MISUSE(CApi.SQLITE_MISUSE),
SQLITE_NOLFS(CApi.SQLITE_NOLFS),
SQLITE_AUTH(CApi.SQLITE_AUTH),
SQLITE_FORMAT(CApi.SQLITE_FORMAT),
SQLITE_RANGE(CApi.SQLITE_RANGE),
SQLITE_NOTADB(CApi.SQLITE_NOTADB),
SQLITE_NOTICE(CApi.SQLITE_NOTICE),
SQLITE_WARNING(CApi.SQLITE_WARNING),
SQLITE_ROW(CApi.SQLITE_ROW),
SQLITE_DONE(CApi.SQLITE_DONE),
SQLITE_ERROR_MISSING_COLLSEQ(CApi.SQLITE_ERROR_MISSING_COLLSEQ),
SQLITE_ERROR_RETRY(CApi.SQLITE_ERROR_RETRY),
SQLITE_ERROR_SNAPSHOT(CApi.SQLITE_ERROR_SNAPSHOT),
SQLITE_IOERR_READ(CApi.SQLITE_IOERR_READ),
SQLITE_IOERR_SHORT_READ(CApi.SQLITE_IOERR_SHORT_READ),
SQLITE_IOERR_WRITE(CApi.SQLITE_IOERR_WRITE),
SQLITE_IOERR_FSYNC(CApi.SQLITE_IOERR_FSYNC),
SQLITE_IOERR_DIR_FSYNC(CApi.SQLITE_IOERR_DIR_FSYNC),
SQLITE_IOERR_TRUNCATE(CApi.SQLITE_IOERR_TRUNCATE),
SQLITE_IOERR_FSTAT(CApi.SQLITE_IOERR_FSTAT),
SQLITE_IOERR_UNLOCK(CApi.SQLITE_IOERR_UNLOCK),
SQLITE_IOERR_RDLOCK(CApi.SQLITE_IOERR_RDLOCK),
SQLITE_IOERR_DELETE(CApi.SQLITE_IOERR_DELETE),
SQLITE_IOERR_BLOCKED(CApi.SQLITE_IOERR_BLOCKED),
SQLITE_IOERR_NOMEM(CApi.SQLITE_IOERR_NOMEM),
SQLITE_IOERR_ACCESS(CApi.SQLITE_IOERR_ACCESS),
SQLITE_IOERR_CHECKRESERVEDLOCK(CApi.SQLITE_IOERR_CHECKRESERVEDLOCK),
SQLITE_IOERR_LOCK(CApi.SQLITE_IOERR_LOCK),
SQLITE_IOERR_CLOSE(CApi.SQLITE_IOERR_CLOSE),
SQLITE_IOERR_DIR_CLOSE(CApi.SQLITE_IOERR_DIR_CLOSE),
SQLITE_IOERR_SHMOPEN(CApi.SQLITE_IOERR_SHMOPEN),
SQLITE_IOERR_SHMSIZE(CApi.SQLITE_IOERR_SHMSIZE),
SQLITE_IOERR_SHMLOCK(CApi.SQLITE_IOERR_SHMLOCK),
SQLITE_IOERR_SHMMAP(CApi.SQLITE_IOERR_SHMMAP),
SQLITE_IOERR_SEEK(CApi.SQLITE_IOERR_SEEK),
SQLITE_IOERR_DELETE_NOENT(CApi.SQLITE_IOERR_DELETE_NOENT),
SQLITE_IOERR_MMAP(CApi.SQLITE_IOERR_MMAP),
SQLITE_IOERR_GETTEMPPATH(CApi.SQLITE_IOERR_GETTEMPPATH),
SQLITE_IOERR_CONVPATH(CApi.SQLITE_IOERR_CONVPATH),
SQLITE_IOERR_VNODE(CApi.SQLITE_IOERR_VNODE),
SQLITE_IOERR_AUTH(CApi.SQLITE_IOERR_AUTH),
SQLITE_IOERR_BEGIN_ATOMIC(CApi.SQLITE_IOERR_BEGIN_ATOMIC),
SQLITE_IOERR_COMMIT_ATOMIC(CApi.SQLITE_IOERR_COMMIT_ATOMIC),
SQLITE_IOERR_ROLLBACK_ATOMIC(CApi.SQLITE_IOERR_ROLLBACK_ATOMIC),
SQLITE_IOERR_DATA(CApi.SQLITE_IOERR_DATA),
SQLITE_IOERR_CORRUPTFS(CApi.SQLITE_IOERR_CORRUPTFS),
SQLITE_LOCKED_SHAREDCACHE(CApi.SQLITE_LOCKED_SHAREDCACHE),
SQLITE_LOCKED_VTAB(CApi.SQLITE_LOCKED_VTAB),
SQLITE_BUSY_RECOVERY(CApi.SQLITE_BUSY_RECOVERY),
SQLITE_BUSY_SNAPSHOT(CApi.SQLITE_BUSY_SNAPSHOT),
SQLITE_BUSY_TIMEOUT(CApi.SQLITE_BUSY_TIMEOUT),
SQLITE_CANTOPEN_NOTEMPDIR(CApi.SQLITE_CANTOPEN_NOTEMPDIR),
SQLITE_CANTOPEN_ISDIR(CApi.SQLITE_CANTOPEN_ISDIR),
SQLITE_CANTOPEN_FULLPATH(CApi.SQLITE_CANTOPEN_FULLPATH),
SQLITE_CANTOPEN_CONVPATH(CApi.SQLITE_CANTOPEN_CONVPATH),
SQLITE_CANTOPEN_SYMLINK(CApi.SQLITE_CANTOPEN_SYMLINK),
SQLITE_CORRUPT_VTAB(CApi.SQLITE_CORRUPT_VTAB),
SQLITE_CORRUPT_SEQUENCE(CApi.SQLITE_CORRUPT_SEQUENCE),
SQLITE_CORRUPT_INDEX(CApi.SQLITE_CORRUPT_INDEX),
SQLITE_READONLY_RECOVERY(CApi.SQLITE_READONLY_RECOVERY),
SQLITE_READONLY_CANTLOCK(CApi.SQLITE_READONLY_CANTLOCK),
SQLITE_READONLY_ROLLBACK(CApi.SQLITE_READONLY_ROLLBACK),
SQLITE_READONLY_DBMOVED(CApi.SQLITE_READONLY_DBMOVED),
SQLITE_READONLY_CANTINIT(CApi.SQLITE_READONLY_CANTINIT),
SQLITE_READONLY_DIRECTORY(CApi.SQLITE_READONLY_DIRECTORY),
SQLITE_ABORT_ROLLBACK(CApi.SQLITE_ABORT_ROLLBACK),
SQLITE_CONSTRAINT_CHECK(CApi.SQLITE_CONSTRAINT_CHECK),
SQLITE_CONSTRAINT_COMMITHOOK(CApi.SQLITE_CONSTRAINT_COMMITHOOK),
SQLITE_CONSTRAINT_FOREIGNKEY(CApi.SQLITE_CONSTRAINT_FOREIGNKEY),
SQLITE_CONSTRAINT_FUNCTION(CApi.SQLITE_CONSTRAINT_FUNCTION),
SQLITE_CONSTRAINT_NOTNULL(CApi.SQLITE_CONSTRAINT_NOTNULL),
SQLITE_CONSTRAINT_PRIMARYKEY(CApi.SQLITE_CONSTRAINT_PRIMARYKEY),
SQLITE_CONSTRAINT_TRIGGER(CApi.SQLITE_CONSTRAINT_TRIGGER),
SQLITE_CONSTRAINT_UNIQUE(CApi.SQLITE_CONSTRAINT_UNIQUE),
SQLITE_CONSTRAINT_VTAB(CApi.SQLITE_CONSTRAINT_VTAB),
SQLITE_CONSTRAINT_ROWID(CApi.SQLITE_CONSTRAINT_ROWID),
SQLITE_CONSTRAINT_PINNED(CApi.SQLITE_CONSTRAINT_PINNED),
SQLITE_CONSTRAINT_DATATYPE(CApi.SQLITE_CONSTRAINT_DATATYPE),
SQLITE_NOTICE_RECOVER_WAL(CApi.SQLITE_NOTICE_RECOVER_WAL),
SQLITE_NOTICE_RECOVER_ROLLBACK(CApi.SQLITE_NOTICE_RECOVER_ROLLBACK),
SQLITE_WARNING_AUTOINDEX(CApi.SQLITE_WARNING_AUTOINDEX),
SQLITE_AUTH_USER(CApi.SQLITE_AUTH_USER),
SQLITE_OK_LOAD_PERMANENTLY(CApi.SQLITE_OK_LOAD_PERMANENTLY);
public final int value;
ResultCode(int rc){
value = rc;
ResultCodeMap.set(rc, this);
}
/**
Returns the entry from this enum for the given result code, or
null if no match is found.
*/
public static ResultCode getEntryForInt(int rc){
return ResultCodeMap.get(rc);
}
/**
Internal level of indirection required because we cannot initialize
static enum members in an enum before the enum constructor is
invoked.
*/
private static final class ResultCodeMap {
private static final java.util.Map<Integer,ResultCode> i2e
= new java.util.HashMap<>();
private static void set(int rc, ResultCode e){ i2e.put(rc, e); }
private static ResultCode get(int rc){ return i2e.get(rc); }
}
}

View File

@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_rollback_hook}.
Callback for use with {@link CApi#sqlite3_rollback_hook}.
*/
public interface RollbackHookCallback extends CallbackProxy {
/**

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
SQLFunction is used in conjunction with the

View File

@ -12,16 +12,13 @@
** This file contains the main application entry pointer for the
** SQLTester framework.
*/
package org.sqlite.jni.tester;
package org.sqlite.jni.capi;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.nio.charset.StandardCharsets;
import java.util.regex.*;
import org.sqlite.jni.*;
import static org.sqlite.jni.SQLite3Jni.*;
import org.sqlite.jni.sqlite3;
import static org.sqlite.jni.capi.CApi.*;
/**
Modes for how to escape (or not) column values and names from
@ -71,7 +68,7 @@ class SQLTesterException extends RuntimeException {
class DbException extends SQLTesterException {
DbException(sqlite3 db, int rc, boolean closeDb){
super("DB error #"+rc+": "+sqlite3_errmsg16(db),true);
super("DB error #"+rc+": "+sqlite3_errmsg(db),true);
if( closeDb ) sqlite3_close_v2(db);
}
DbException(sqlite3 db, int rc){
@ -150,12 +147,15 @@ class Outer {
}
/**
This class provides an application which aims to implement the
<p>This class provides an application which aims to implement the
rudimentary SQL-driven test tool described in the accompanying
{@code test-script-interpreter.md}.
<p>This is a work in progress.
<p>This class is an internal testing tool, not part of the public
interface but is (A) in the same package as the library because
access permissions require it to be so and (B) the JDK8 javadoc
offers no way to filter individual classes out of the doc
generation process (it can only exclude packages, but see (A)).
<p>An instance of this application provides a core set of services
which TestScript instances use for processing testing logic.
@ -457,8 +457,8 @@ public class SQLTester {
}
private void appendDbErr(sqlite3 db, StringBuilder sb, int rc){
sb.append(org.sqlite.jni.ResultCode.getEntryForInt(rc)).append(' ');
final String msg = escapeSqlValue(sqlite3_errmsg16(db));
sb.append(org.sqlite.jni.capi.ResultCode.getEntryForInt(rc)).append(' ');
final String msg = escapeSqlValue(sqlite3_errmsg(db));
if( '{' == msg.charAt(0) ){
sb.append(msg);
}else{
@ -668,7 +668,7 @@ public class SQLTester {
static {
System.loadLibrary("sqlite3-jni")
/* Interestingly, when SQLTester is the main app, we have to
load that lib from here. The same load from SQLite3Jni does
load that lib from here. The same load from CApi does
not happen early enough. Without this,
installCustomExtensions() is an unresolved symbol. */;
}
@ -932,7 +932,7 @@ class RunCommand extends Command {
final int rc = t.execSql(db, false, ResultBufferMode.NONE,
ResultRowMode.ONELINE, sql);
if( 0!=rc && t.isVerbose() ){
String msg = sqlite3_errmsg16(db);
String msg = sqlite3_errmsg(db);
ts.verbose1(argv[0]," non-fatal command error #",rc,": ",
msg,"\nfor SQL:\n",sql);
}

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
@ -27,7 +27,7 @@ public abstract class ScalarFunction implements SQLFunction {
/**
Optionally override to be notified when the UDF is finalized by
SQLite. This implementation does nothing.
SQLite. This default implementation does nothing.
*/
public void xDestroy() {}
}

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A wrapper object for use with sqlite3_table_column_metadata().

View File

@ -11,8 +11,8 @@
*************************************************************************
** This file contains a set of tests for the sqlite3 JNI bindings.
*/
package org.sqlite.jni;
import static org.sqlite.jni.SQLite3Jni.*;
package org.sqlite.jni.capi;
import static org.sqlite.jni.capi.CApi.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.ArrayList;
@ -38,17 +38,6 @@ import java.util.concurrent.Future;
@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
@interface SingleThreadOnly{}
/**
A helper class which simply holds a single value. Its current use
is for communicating values out of anonymous classes, as doing so
requires a "final" reference.
*/
class ValueHolder<T> {
public T value;
public ValueHolder(){}
public ValueHolder(T v){value = v;}
}
public class Tester1 implements Runnable {
//! True when running in multi-threaded mode.
private static boolean mtMode = false;
@ -146,7 +135,7 @@ public class Tester1 implements Runnable {
sqlite3 db = out.take();
if( 0!=rc ){
final String msg =
null==db ? sqlite3_errstr(rc) : sqlite3_errmsg16(db);
null==db ? sqlite3_errstr(rc) : sqlite3_errmsg(db);
sqlite3_close(db);
throw new RuntimeException("Opening db failed: "+msg);
}
@ -197,7 +186,7 @@ public class Tester1 implements Runnable {
if(SQLITE_ROW==rc || SQLITE_DONE==rc) rc = 0;
if( 0!=rc && throwOnError){
throw new RuntimeException("db op failed with rc="
+rc+": "+sqlite3_errmsg16(db));
+rc+": "+sqlite3_errmsg(db));
}
return rc;
}
@ -249,12 +238,18 @@ public class Tester1 implements Runnable {
++metrics.dbOpen;
sqlite3 db = out.get();
affirm(0 == rc);
affirm(0 < db.getNativePointer());
affirm(db.getNativePointer()!=0);
sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, null)
/* This function has different mangled names in jdk8 vs jdk19,
and this call is here to ensure that the build fails
if it cannot find both names. */;
affirm( 0==sqlite3_db_readonly(db,"main") );
affirm( 0==sqlite3_db_readonly(db,null) );
affirm( 0>sqlite3_db_readonly(db,"nope") );
affirm( 0>sqlite3_db_readonly(null,null) );
affirm( 0==sqlite3_last_insert_rowid(null) );
// These interrupt checks are only to make sure that the JNI binding
// has the proper exported symbol names. They don't actually test
// anything useful.
@ -273,7 +268,7 @@ public class Tester1 implements Runnable {
++metrics.dbOpen;
affirm(0 == rc);
sqlite3 db = out.get();
affirm(0 < db.getNativePointer());
affirm(0 != db.getNativePointer());
sqlite3_close_v2(db);
affirm(0 == db.getNativePointer());
}
@ -289,9 +284,6 @@ public class Tester1 implements Runnable {
affirm( !sqlite3_stmt_readonly(stmt) );
affirm( db == sqlite3_db_handle(stmt) );
rc = sqlite3_step(stmt);
if( SQLITE_DONE != rc ){
outln("step failed ??? ",rc, " ",sqlite3_errmsg16(db));
}
affirm(SQLITE_DONE == rc);
sqlite3_finalize(stmt);
affirm( null == sqlite3_db_handle(stmt) );
@ -346,7 +338,7 @@ public class Tester1 implements Runnable {
stmt = sqlite3_prepare(db, "intentional error");
affirm( null==stmt );
affirm( 0!=sqlite3_errcode(db) );
affirm( 0==sqlite3_errmsg16(db).indexOf("near \"intentional\"") );
affirm( 0==sqlite3_errmsg(db).indexOf("near \"intentional\"") );
sqlite3_finalize(stmt);
stmt = sqlite3_prepare(db, "/* empty input*/\n-- comments only");
affirm( null==stmt );
@ -389,65 +381,80 @@ public class Tester1 implements Runnable {
affirm(sqlite3_total_changes64(db) > changesT64);
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
affirm( sqlite3_stmt_readonly(stmt) );
affirm( !sqlite3_stmt_busy(stmt) );
int total2 = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){
affirm( sqlite3_stmt_busy(stmt) );
total2 += sqlite3_column_int(stmt, 0);
sqlite3_value sv = sqlite3_column_value(stmt, 0);
affirm( null != sv );
affirm( 0 != sv.getNativePointer() );
affirm( SQLITE_INTEGER == sqlite3_value_type(sv) );
}
affirm( !sqlite3_stmt_busy(stmt) );
sqlite3_finalize(stmt);
affirm(total1 == total2);
// sqlite3_value_frombind() checks...
stmt = prepare(db, "SELECT 1, ?");
sqlite3_bind_int(stmt, 1, 2);
rc = sqlite3_step(stmt);
affirm( SQLITE_ROW==rc );
affirm( !sqlite3_value_frombind(sqlite3_column_value(stmt, 0)) );
affirm( sqlite3_value_frombind(sqlite3_column_value(stmt, 1)) );
sqlite3_finalize(stmt);
sqlite3_close_v2(db);
affirm(0 == db.getNativePointer());
}
private void testBindFetchInt64(){
sqlite3 db = createNewDb();
execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
long total1 = 0;
for(long i = 0xffffffff; i < 0xffffffff + 3; ++i ){
total1 += i;
sqlite3_bind_int64(stmt, 1, i);
sqlite3_step(stmt);
sqlite3_reset(stmt);
try (sqlite3 db = createNewDb()){
execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
long total1 = 0;
for(long i = 0xffffffff; i < 0xffffffff + 3; ++i ){
total1 += i;
sqlite3_bind_int64(stmt, 1, i);
sqlite3_step(stmt);
sqlite3_reset(stmt);
}
sqlite3_finalize(stmt);
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
long total2 = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){
total2 += sqlite3_column_int64(stmt, 0);
}
sqlite3_finalize(stmt);
affirm(total1 == total2);
//sqlite3_close_v2(db);
}
sqlite3_finalize(stmt);
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
long total2 = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){
total2 += sqlite3_column_int64(stmt, 0);
}
sqlite3_finalize(stmt);
affirm(total1 == total2);
sqlite3_close_v2(db);
}
private void testBindFetchDouble(){
sqlite3 db = createNewDb();
execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
double total1 = 0;
for(double i = 1.5; i < 5.0; i = i + 1.0 ){
total1 += i;
sqlite3_bind_double(stmt, 1, i);
sqlite3_step(stmt);
sqlite3_reset(stmt);
try (sqlite3 db = createNewDb()){
execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
double total1 = 0;
for(double i = 1.5; i < 5.0; i = i + 1.0 ){
total1 += i;
sqlite3_bind_double(stmt, 1, i);
sqlite3_step(stmt);
sqlite3_reset(stmt);
}
sqlite3_finalize(stmt);
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
double total2 = 0;
int counter = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){
++counter;
total2 += sqlite3_column_double(stmt, 0);
}
affirm(4 == counter);
sqlite3_finalize(stmt);
affirm(total2<=total1+0.01 && total2>=total1-0.01);
//sqlite3_close_v2(db);
}
sqlite3_finalize(stmt);
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
double total2 = 0;
int counter = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){
++counter;
total2 += sqlite3_column_double(stmt, 0);
}
affirm(4 == counter);
sqlite3_finalize(stmt);
affirm(total2<=total1+0.01 && total2>=total1-0.01);
sqlite3_close_v2(db);
}
private void testBindFetchText(){
@ -491,20 +498,25 @@ public class Tester1 implements Runnable {
affirm(3 == n);
affirm("w😃rldhell🤩!🤩".equals(sbuf.toString()));
stmt = prepare(db, "SELECT ?, ?");
rc = sqlite3_bind_text(stmt, 1, "");
affirm( 0==rc );
rc = sqlite3_bind_text(stmt, 2, (String)null);
affirm( 0==rc );
rc = sqlite3_step(stmt);
affirm( SQLITE_ROW==rc );
byte[] colBa = sqlite3_column_text(stmt, 0);
affirm( 0==colBa.length );
colBa = sqlite3_column_text(stmt, 1);
affirm( null==colBa );
sqlite3_finalize(stmt);
try( sqlite3_stmt stmt2 = prepare(db, "SELECT ?, ?") ){
rc = sqlite3_bind_text(stmt2, 1, "");
affirm( 0==rc );
rc = sqlite3_bind_text(stmt2, 2, (String)null);
affirm( 0==rc );
rc = sqlite3_step(stmt2);
affirm( SQLITE_ROW==rc );
byte[] colBa = sqlite3_column_text(stmt2, 0);
affirm( 0==colBa.length );
colBa = sqlite3_column_text(stmt2, 1);
affirm( null==colBa );
//sqlite3_finalize(stmt);
}
sqlite3_close_v2(db);
if(true){
sqlite3_close_v2(db);
}else{
// Let the Object.finalize() override deal with it.
}
}
private void testBindFetchBlob(){
@ -543,7 +555,10 @@ public class Tester1 implements Runnable {
sqlite3_finalize(stmt);
stmt = prepare(db, "SELECT ?");
sqlite3_bind_text(stmt, 1, "hell😃");
affirm( "SELECT 'hell😃'".equals(sqlite3_expanded_sql(stmt)) );
final String expect = "SELECT 'hell😃'";
affirm( expect.equals(sqlite3_expanded_sql(stmt)) );
String n = sqlite3_normalized_sql(stmt);
affirm( null==n || "SELECT?;".equals(n) );
sqlite3_finalize(stmt);
sqlite3_close(db);
}
@ -668,6 +683,8 @@ public class Tester1 implements Runnable {
// These ValueHolders are just to confirm that the func did what we want...
final ValueHolder<Boolean> xDestroyCalled = new ValueHolder<>(false);
final ValueHolder<Integer> xFuncAccum = new ValueHolder<>(0);
final ValueHolder<sqlite3_value[]> neverEverDoThisInClientCode = new ValueHolder<>(null);
final ValueHolder<sqlite3_context> neverEverDoThisInClientCode2 = new ValueHolder<>(null);
// Create an SQLFunction instance using one of its 3 subclasses:
// Scalar, Aggregate, or Window:
@ -678,6 +695,15 @@ public class Tester1 implements Runnable {
new ScalarFunction(){
public void xFunc(sqlite3_context cx, sqlite3_value[] args){
affirm(db == sqlite3_context_db_handle(cx));
if( null==neverEverDoThisInClientCode.value ){
/* !!!NEVER!!! hold a reference to an sqlite3_value or
sqlite3_context object like this in client code! They
are ONLY legal for the duration of their single
call. We do it here ONLY to test that the defenses
against clients doing this are working. */
neverEverDoThisInClientCode2.value = cx;
neverEverDoThisInClientCode.value = args;
}
int result = 0;
for( sqlite3_value v : args ) result += sqlite3_value_int(v);
xFuncAccum.value += result;// just for post-run testing
@ -703,6 +729,13 @@ public class Tester1 implements Runnable {
affirm(1 == n);
affirm(6 == xFuncAccum.value);
affirm( !xDestroyCalled.value );
affirm( null!=neverEverDoThisInClientCode.value );
affirm( null!=neverEverDoThisInClientCode2.value );
affirm( 0<neverEverDoThisInClientCode.value.length );
affirm( 0==neverEverDoThisInClientCode2.value.getNativePointer() );
for( sqlite3_value sv : neverEverDoThisInClientCode.value ){
affirm( 0==sv.getNativePointer() );
}
sqlite3_close_v2(db);
affirm( xDestroyCalled.value );
}
@ -729,7 +762,7 @@ public class Tester1 implements Runnable {
rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
affirm( 0 != rc );
affirm( sqlite3_errmsg16(db).indexOf("an xFinal") > 0 );
affirm( sqlite3_errmsg(db).indexOf("an xFinal") > 0 );
SQLFunction funcSc = new ScalarFunction(){
@Override public void xFunc(sqlite3_context cx, sqlite3_value[] args){
@ -743,7 +776,7 @@ public class Tester1 implements Runnable {
rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
affirm( 0 != rc );
affirm( sqlite3_errmsg16(db).indexOf("an xFunc") > 0 );
affirm( sqlite3_errmsg(db).indexOf("an xFunc") > 0 );
rc = sqlite3_create_function(db, "mysca", 1, -1, funcSc);
affirm( SQLITE_FORMAT==rc, "invalid encoding value." );
sqlite3_close_v2(db);
@ -914,7 +947,7 @@ public class Tester1 implements Runnable {
private void listBoundMethods(){
if(false){
final java.lang.reflect.Field[] declaredFields =
SQLite3Jni.class.getDeclaredFields();
CApi.class.getDeclaredFields();
outln("Bound constants:\n");
for(java.lang.reflect.Field field : declaredFields) {
if(java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
@ -923,7 +956,7 @@ public class Tester1 implements Runnable {
}
}
final java.lang.reflect.Method[] declaredMethods =
SQLite3Jni.class.getDeclaredMethods();
CApi.class.getDeclaredMethods();
final java.util.List<String> funcList = new java.util.ArrayList<>();
for(java.lang.reflect.Method m : declaredMethods){
if((m.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0){
@ -1008,8 +1041,11 @@ public class Tester1 implements Runnable {
affirm( 0 == rc );
affirm( outDb.get() != db1 );
final sqlite3 db2 = outDb.get();
affirm( "main".equals( sqlite3_db_name(db1, 0) ) );
rc = sqlite3_db_config(db1, SQLITE_DBCONFIG_MAINDBNAME, "foo");
affirm( sqlite3_db_filename(db1, "foo").endsWith(dbName) );
affirm( "foo".equals( sqlite3_db_name(db1, 0) ) );
final ValueHolder<Integer> xBusyCalled = new ValueHolder<>(0);
BusyHandlerCallback handler = new BusyHandlerCallback(){
@ -1025,7 +1061,7 @@ public class Tester1 implements Runnable {
execSql(db1, "BEGIN EXCLUSIVE");
rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt);
affirm( SQLITE_BUSY == rc);
assert( null == outStmt.get() );
affirm( null == outStmt.get() );
affirm( 3 == xBusyCalled.value );
sqlite3_close_v2(db1);
sqlite3_close_v2(db2);
@ -1436,7 +1472,7 @@ public class Tester1 implements Runnable {
affirm( "noCase".equals(zCollSeq.value) );
affirm( "duck".equals(zDataType.value) );
final TableColumnMetadata m =
TableColumnMetadata m =
sqlite3_table_column_metadata(db, "main", "t", "a");
affirm( null != m );
affirm( bPrimaryKey.value == m.isPrimaryKey() );
@ -1446,6 +1482,16 @@ public class Tester1 implements Runnable {
affirm( zDataType.value.equals(m.getDataType()) );
affirm( null == sqlite3_table_column_metadata(db, "nope", "t", "a") );
affirm( null == sqlite3_table_column_metadata(db, "main", "nope", "a") );
m = sqlite3_table_column_metadata(db, "main", "t", null)
/* Check only for existence of table */;
affirm( null != m );
affirm( m.isPrimaryKey() );
affirm( !m.isAutoincrement() );
affirm( !m.isNotNull() );
affirm( "BINARY".equalsIgnoreCase(m.getCollation()) );
affirm( "INTEGER".equalsIgnoreCase(m.getDataType()) );
sqlite3_close_v2(db);
}
@ -1508,35 +1554,34 @@ public class Tester1 implements Runnable {
}
private void testBackup(){
final sqlite3 db1 = createNewDb();
final sqlite3 db2 = createNewDb();
final sqlite3 dbDest = createNewDb();
execSql(db1, new String[]{
"pragma page_size=512; VACUUM;",
"create table t(a);",
"insert into t(a) values(1),(2),(3);"
});
affirm( null==sqlite3_backup_init(db1,"main",db1,"main") );
final sqlite3_backup b = sqlite3_backup_init(db2,"main",db1,"main");
affirm( null!=b );
affirm( b.getNativePointer()!=0 );
int rc;
while( SQLITE_DONE!=(rc = sqlite3_backup_step(b, 1)) ){
affirm( 0==rc );
try (sqlite3 dbSrc = createNewDb()) {
execSql(dbSrc, new String[]{
"pragma page_size=512; VACUUM;",
"create table t(a);",
"insert into t(a) values(1),(2),(3);"
});
affirm( null==sqlite3_backup_init(dbSrc,"main",dbSrc,"main") );
try (sqlite3_backup b = sqlite3_backup_init(dbDest,"main",dbSrc,"main")) {
affirm( null!=b );
affirm( b.getNativePointer()!=0 );
int rc;
while( SQLITE_DONE!=(rc = sqlite3_backup_step(b, 1)) ){
affirm( 0==rc );
}
affirm( sqlite3_backup_pagecount(b) > 0 );
rc = sqlite3_backup_finish(b);
affirm( 0==rc );
affirm( b.getNativePointer()==0 );
}
}
affirm( sqlite3_backup_pagecount(b) > 0 );
rc = sqlite3_backup_finish(b);
affirm( 0==rc );
affirm( b.getNativePointer()==0 );
sqlite3_close_v2(db1);
final sqlite3_stmt stmt = prepare(db2,"SELECT sum(a) from t");
sqlite3_step(stmt);
affirm( sqlite3_column_int(stmt,0) == 6 );
sqlite3_finalize(stmt);
sqlite3_close_v2(db2);
try (sqlite3_stmt stmt = prepare(dbDest,"SELECT sum(a) from t")) {
sqlite3_step(stmt);
affirm( sqlite3_column_int(stmt,0) == 6 );
}
sqlite3_close_v2(dbDest);
}
private void testRandomness(){
@ -1906,29 +1951,24 @@ public class Tester1 implements Runnable {
sqlite3_shutdown();
int nMethods = 0;
int nNatives = 0;
int nCanonical = 0;
final java.lang.reflect.Method[] declaredMethods =
SQLite3Jni.class.getDeclaredMethods();
CApi.class.getDeclaredMethods();
for(java.lang.reflect.Method m : declaredMethods){
final int mod = m.getModifiers();
if( 0!=(mod & java.lang.reflect.Modifier.STATIC) ){
final String name = m.getName();
if(name.startsWith("sqlite3_")){
++nMethods;
if( m.isAnnotationPresent( org.sqlite.jni.annotation.Canonical.class ) ){
++nCanonical;
}
if( 0!=(mod & java.lang.reflect.Modifier.NATIVE) ){
++nNatives;
}
}
}
}
outln("\tSQLite3Jni.sqlite3_*() methods: "+
outln("\tCApi.sqlite3_*() methods: "+
nMethods+" total, with "+
nNatives+" native, "+
(nMethods - nNatives)+" Java, ",
nCanonical," @Canonical"
(nMethods - nNatives)+" Java"
);
outln("\tTotal test time = "
+(timeEnd - timeStart)+"ms");

View File

@ -11,11 +11,11 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
import org.sqlite.jni.annotation.Nullable;
/**
Callback for use with {@link SQLite3Jni#sqlite3_trace_v2}.
Callback for use with {@link CApi#sqlite3_trace_v2}.
*/
public interface TraceV2Callback extends CallbackProxy {
/**

View File

@ -11,10 +11,10 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for use with {@link SQLite3Jni#sqlite3_update_hook}.
Callback for use with {@link CApi#sqlite3_update_hook}.
*/
public interface UpdateHookCallback extends CallbackProxy {
/**

View File

@ -0,0 +1,25 @@
/*
** 2023-10-16
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains a set of tests for the sqlite3 JNI bindings.
*/
package org.sqlite.jni.capi;
/**
A helper class which simply holds a single value. Its primary use
is for communicating values out of anonymous classes, as doing so
requires a "final" reference.
*/
public class ValueHolder<T> {
public T value;
public ValueHolder(){}
public ValueHolder(T v){value = v;}
}

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file declares JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
Callback for a hook called by SQLite when certain client-provided

View File

@ -2,7 +2,7 @@
This package houses a JNI binding to the SQLite3 C API.
<p>The primary interfaces are in {@link
org.sqlite.jni.SQLite3Jni}.</p>
org.sqlite.jni.capi.CApi}.</p>
<h1>API Goals and Requirements</h1>
@ -86,4 +86,4 @@
undefined behavior.</p>
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A wrapper for communicating C-level (sqlite3*) instances with
@ -19,19 +19,25 @@ package org.sqlite.jni;
simply provide a type-safe way to communicate it between Java
and C via JNI.
*/
public final class sqlite3 extends NativePointerHolder<sqlite3> {
public final class sqlite3 extends NativePointerHolder<sqlite3>
implements AutoCloseable {
// Only invoked from JNI
private sqlite3(){}
public String toString(){
long ptr = getNativePointer();
final long ptr = getNativePointer();
if( 0==ptr ){
return sqlite3.class.getSimpleName()+"@null";
}
String fn = SQLite3Jni.sqlite3_db_filename(this, "main");
final String fn = CApi.sqlite3_db_filename(this, "main");
return sqlite3.class.getSimpleName()
+"@"+String.format("0x%08x",ptr)
+"["+((null == fn) ? "<unnamed>" : fn)+"]"
;
}
@Override public void close(){
CApi.sqlite3_close_v2(this.clearNativePointer());
}
}

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A wrapper for passing C-level (sqlite3_backup*) instances around in
@ -19,7 +19,13 @@ package org.sqlite.jni;
simply provide a type-safe way to communicate it between Java and C
via JNI.
*/
public final class sqlite3_backup extends NativePointerHolder<sqlite3_backup> {
public final class sqlite3_backup extends NativePointerHolder<sqlite3_backup>
implements AutoCloseable {
// Only invoked from JNI.
private sqlite3_backup(){}
@Override public void close(){
CApi.sqlite3_backup_finish(this);
}
}

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A wrapper for passing C-level (sqlite3_blob*) instances around in
@ -19,7 +19,13 @@ package org.sqlite.jni;
simply provide a type-safe way to communicate it between Java and C
via JNI.
*/
public final class sqlite3_blob extends NativePointerHolder<sqlite3_blob> {
public final class sqlite3_blob extends NativePointerHolder<sqlite3_blob>
implements AutoCloseable {
// Only invoked from JNI.
private sqlite3_blob(){}
@Override public void close(){
CApi.sqlite3_blob_close(this.clearNativePointer());
}
}

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
sqlite3_context instances are used in conjunction with user-defined
@ -71,7 +71,7 @@ public final class sqlite3_context extends NativePointerHolder<sqlite3_context>
*/
public synchronized Long getAggregateContext(boolean initIfNeeded){
if( aggregateContext==null ){
aggregateContext = SQLite3Jni.sqlite3_aggregate_context(this, initIfNeeded);
aggregateContext = CApi.sqlite3_aggregate_context(this, initIfNeeded);
if( !initIfNeeded && null==aggregateContext ) aggregateContext = 0L;
}
return (null==aggregateContext || 0!=aggregateContext) ? aggregateContext : null;

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
/**
A wrapper for communicating C-level (sqlite3_stmt*) instances with
@ -19,7 +19,12 @@ package org.sqlite.jni;
simply provide a type-safe way to communicate it between Java and C
via JNI.
*/
public final class sqlite3_stmt extends NativePointerHolder<sqlite3_stmt> {
public final class sqlite3_stmt extends NativePointerHolder<sqlite3_stmt>
implements AutoCloseable {
// Only invoked from JNI.
private sqlite3_stmt(){}
@Override public void close(){
CApi.sqlite3_finalize(this.clearNativePointer());
}
}

View File

@ -11,7 +11,7 @@
*************************************************************************
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni;
package org.sqlite.jni.capi;
public final class sqlite3_value extends NativePointerHolder<sqlite3_value> {
//! Invoked only from JNI.

View File

@ -12,7 +12,7 @@
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.fts5;
import org.sqlite.jni.*;
import org.sqlite.jni.capi.*;
/**
A wrapper for communicating C-level (Fts5Context*) instances with

View File

@ -13,14 +13,10 @@
*/
package org.sqlite.jni.fts5;
import java.nio.charset.StandardCharsets;
import org.sqlite.jni.*;
import org.sqlite.jni.capi.*;
import org.sqlite.jni.annotation.*;
/**
ALMOST COMPLETELY UNTESTED.
FAR FROM COMPLETE and the feasibility of binding this to Java
is still undetermined. This might be removed.
*/
public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi> {
//! Only called from JNI
@ -37,67 +33,51 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
*/
public static native Fts5ExtensionApi getInstance();
@Canonical
public native int xColumnCount(@NotNull Fts5Context fcx);
@Canonical
public native int xColumnSize(@NotNull Fts5Context cx, int iCol,
@NotNull OutputPointer.Int32 pnToken);
@Canonical
public native int xColumnText(@NotNull Fts5Context cx, int iCol,
@NotNull OutputPointer.String txt);
@Canonical
public native int xColumnTotalSize(@NotNull Fts5Context fcx, int iCol,
@NotNull OutputPointer.Int64 pnToken);
@Canonical
public native Object xGetAuxdata(@NotNull Fts5Context cx, boolean clearIt);
@Canonical
public native int xInst(@NotNull Fts5Context cx, int iIdx,
@NotNull OutputPointer.Int32 piPhrase,
@NotNull OutputPointer.Int32 piCol,
@NotNull OutputPointer.Int32 piOff);
@Canonical
public native int xInstCount(@NotNull Fts5Context fcx,
@NotNull OutputPointer.Int32 pnInst);
@Canonical
public native int xPhraseCount(@NotNull Fts5Context fcx);
@Canonical
public native int xPhraseFirst(@NotNull Fts5Context cx, int iPhrase,
@NotNull Fts5PhraseIter iter,
@NotNull OutputPointer.Int32 iCol,
@NotNull OutputPointer.Int32 iOff);
@Canonical
public native int xPhraseFirstColumn(@NotNull Fts5Context cx, int iPhrase,
@NotNull Fts5PhraseIter iter,
@NotNull OutputPointer.Int32 iCol);
@Canonical
public native void xPhraseNext(@NotNull Fts5Context cx,
@NotNull Fts5PhraseIter iter,
@NotNull OutputPointer.Int32 iCol,
@NotNull OutputPointer.Int32 iOff);
@Canonical
public native void xPhraseNextColumn(@NotNull Fts5Context cx,
@NotNull Fts5PhraseIter iter,
@NotNull OutputPointer.Int32 iCol);
@Canonical
public native int xPhraseSize(@NotNull Fts5Context fcx, int iPhrase);
@Canonical
public native int xQueryPhrase(@NotNull Fts5Context cx, int iPhrase,
@NotNull XQueryPhraseCallback callback);
@Canonical
public native int xRowCount(@NotNull Fts5Context fcx,
@NotNull OutputPointer.Int64 nRow);
@Canonical
public native long xRowid(@NotNull Fts5Context cx);
/* Note that the JNI binding lacks the C version's xDelete()
callback argument. Instead, if pAux has an xDestroy() method, it
@ -106,14 +86,11 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
pAux held by the JNI layer will be relinquished regardless of
whether pAux has an xDestroy() method. */
@Canonical
public native int xSetAuxdata(@NotNull Fts5Context cx, @Nullable Object pAux);
@Canonical
public native int xTokenize(@NotNull Fts5Context cx, @NotNull byte[] pText,
@NotNull XTokenizeCallback callback);
@Canonical
public native Object xUserData(Fts5Context cx);
//^^^ returns the pointer passed as the 3rd arg to the C-level
// fts5_api::xCreateFunction().

View File

@ -12,7 +12,7 @@
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.fts5;
import org.sqlite.jni.NativePointerHolder;
import org.sqlite.jni.capi.NativePointerHolder;
/**
A wrapper for C-level Fts5PhraseIter. They are only modified and

View File

@ -12,7 +12,7 @@
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.fts5;
import org.sqlite.jni.NativePointerHolder;
import org.sqlite.jni.capi.NativePointerHolder;
/**
INCOMPLETE AND COMPLETELY UNTESTED.

View File

@ -12,9 +12,10 @@
** This file contains a set of tests for the sqlite3 JNI bindings.
*/
package org.sqlite.jni.fts5;
import static org.sqlite.jni.SQLite3Jni.*;
import static org.sqlite.jni.Tester1.*;
import org.sqlite.jni.*;
import static org.sqlite.jni.capi.CApi.*;
import static org.sqlite.jni.capi.Tester1.*;
import org.sqlite.jni.capi.*;
import org.sqlite.jni.fts5.*;
import java.util.*;
@ -103,7 +104,7 @@ public class TesterFts5 {
}
});
if( rc!=SQLITE_OK ){
throw new RuntimeException(sqlite3_errmsg16(db));
throw new RuntimeException(sqlite3_errmsg(db));
}
/* Convert to array and return */

View File

@ -13,11 +13,9 @@
*/
package org.sqlite.jni.fts5;
import org.sqlite.jni.annotation.*;
import org.sqlite.jni.*;
import org.sqlite.jni.capi.*;
/**
INCOMPLETE AND COMPLETELY UNTESTED.
A wrapper for communicating C-level (fts5_api*) instances with
Java. These wrappers do not own their associated pointer, they
simply provide a type-safe way to communicate it between Java and C
@ -35,7 +33,6 @@ public final class fts5_api extends NativePointerHolder<fts5_api> {
*/
public static synchronized native fts5_api getInstanceForDb(@NotNull sqlite3 db);
@Canonical
public synchronized native int xCreateFunction(@NotNull String name,
@Nullable Object userData,
@NotNull fts5_extension_function xFunction);

View File

@ -12,8 +12,8 @@
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.fts5;
import org.sqlite.jni.sqlite3_context;
import org.sqlite.jni.sqlite3_value;
import org.sqlite.jni.capi.sqlite3_context;
import org.sqlite.jni.capi.sqlite3_value;
/**
JNI-level wrapper for C's fts5_extension_function type.

View File

@ -12,12 +12,10 @@
** This file is part of the JNI bindings for the sqlite3 C API.
*/
package org.sqlite.jni.fts5;
import org.sqlite.jni.NativePointerHolder;
import org.sqlite.jni.capi.NativePointerHolder;
import org.sqlite.jni.annotation.NotNull;
/**
INCOMPLETE AND COMPLETELY UNTESTED.
A wrapper for communicating C-level (fts5_tokenizer*) instances with
Java. These wrappers do not own their associated pointer, they
simply provide a type-safe way to communicate it between Java and C

View File

@ -0,0 +1,82 @@
/*
** 2023-10-16
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the wrapper1 interface for sqlite3.
*/
package org.sqlite.jni.wrapper1;
import org.sqlite.jni.capi.CApi;
import org.sqlite.jni.annotation.*;
import org.sqlite.jni.capi.sqlite3_context;
import org.sqlite.jni.capi.sqlite3_value;
/**
EXPERIMENTAL/INCOMPLETE/UNTESTED
A SqlFunction implementation for aggregate functions. The T type
represents the type of data accumulated by this aggregate while it
works. e.g. a SUM()-like UDF might use Integer or Long and a
CONCAT()-like UDF might use a StringBuilder or a List<String>.
*/
public abstract class AggregateFunction<T> implements SqlFunction {
/**
As for the xStep() argument of the C API's
sqlite3_create_function(). If this function throws, the
exception is reported via sqlite3_result_error().
*/
public abstract void xStep(SqlFunction.Arguments args);
/**
As for the xFinal() argument of the C API's
sqlite3_create_function(). If this function throws, it is
translated into sqlite3_result_error().
Note that the passed-in object will not actually contain any
arguments for xFinal() but will contain the context object needed
for setting the call's result or error state.
*/
public abstract void xFinal(SqlFunction.Arguments args);
/**
Optionally override to be notified when the UDF is finalized by
SQLite.
*/
public void xDestroy() {}
/** Per-invocation state for the UDF. */
private final SqlFunction.PerContextState<T> map =
new SqlFunction.PerContextState<>();
/**
To be called from the implementation's xStep() method, as well
as the xValue() and xInverse() methods of the {@link WindowFunction}
subclass, to fetch the current per-call UDF state. On the
first call to this method for any given sqlite3_context
argument, the context is set to the given initial value. On all other
calls, the 2nd argument is ignored.
@see SQLFunction.PerContextState#getAggregateState
*/
protected final ValueHolder<T> getAggregateState(SqlFunction.Arguments args, T initialValue){
return map.getAggregateState(args, initialValue);
}
/**
To be called from the implementation's xFinal() method to fetch
the final state of the UDF and remove its mapping.
see SQLFunction.PerContextState#takeAggregateState
*/
protected final T takeAggregateState(SqlFunction.Arguments args){
return map.takeAggregateState(args);
}
}

View File

@ -0,0 +1,37 @@
/*
** 2023-10-16
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the wrapper1 interface for sqlite3.
*/
package org.sqlite.jni.wrapper1;
import org.sqlite.jni.capi.CApi;
import org.sqlite.jni.annotation.*;
import org.sqlite.jni.capi.sqlite3_context;
import org.sqlite.jni.capi.sqlite3_value;
/**
The SqlFunction type for scalar SQL functions.
*/
public abstract class ScalarFunction implements SqlFunction {
/**
As for the xFunc() argument of the C API's
sqlite3_create_function(). If this function throws, it is
translated into an sqlite3_result_error().
*/
public abstract void xFunc(SqlFunction.Arguments args);
/**
Optionally override to be notified when the UDF is finalized by
SQLite. This default implementation does nothing.
*/
public void xDestroy() {}
}

View File

@ -0,0 +1,301 @@
/*
** 2023-10-16
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the wrapper1 interface for sqlite3.
*/
package org.sqlite.jni.wrapper1;
import org.sqlite.jni.capi.CApi;
import org.sqlite.jni.capi.sqlite3_context;
import org.sqlite.jni.capi.sqlite3_value;
/**
Base marker interface for SQLite's three types of User-Defined SQL
Functions (UDFs): Scalar, Aggregate, and Window functions.
*/
public interface SqlFunction {
/**
The Arguments type is an abstraction on top of the lower-level
UDF function argument types. It provides _most_ of the functionality
of the lower-level interface, insofar as possible without "leaking"
those types into this API.
*/
public final static class Arguments implements Iterable<SqlFunction.Arguments.Arg>{
private final sqlite3_context cx;
private final sqlite3_value args[];
public final int length;
/**
Must be passed the context and arguments for the UDF call this
object is wrapping. Intended to be used by internal proxy
classes which "convert" the lower-level interface into this
package's higher-level interface, e.g. ScalarAdapter and
AggregateAdapter.
Passing null for the args is equivalent to passing a length-0
array.
*/
Arguments(sqlite3_context cx, sqlite3_value args[]){
this.cx = cx;
this.args = args==null ? new sqlite3_value[0] : args;;
this.length = this.args.length;
}
/**
Wrapper for a single SqlFunction argument. Primarily intended
for use with the Arguments class's Iterable interface.
*/
public final static class Arg {
private final Arguments a;
private final int ndx;
/* Only for use by the Arguments class. */
private Arg(Arguments a, int ndx){
this.a = a;
this.ndx = ndx;
}
/** Returns this argument's index in its parent argument list. */
public int getIndex(){return ndx;}
public int getInt(){return a.getInt(ndx);}
public long getInt64(){return a.getInt64(ndx);}
public double getDouble(){return a.getDouble(ndx);}
public byte[] getBlob(){return a.getBlob(ndx);}
public byte[] getText(){return a.getText(ndx);}
public String getText16(){return a.getText16(ndx);}
public int getBytes(){return a.getBytes(ndx);}
public int getBytes16(){return a.getBytes16(ndx);}
public Object getObject(){return a.getObject(ndx);}
public <T> T getObjectCasted(Class<T> type){ return a.getObjectCasted(ndx, type); }
public int getType(){return a.getType(ndx);}
public Object getAuxData(){return a.getAuxData(ndx);}
public void setAuxData(Object o){a.setAuxData(ndx, o);}
}
@Override
public java.util.Iterator<SqlFunction.Arguments.Arg> iterator(){
final Arg[] proxies = new Arg[args.length];
for( int i = 0; i < args.length; ++i ){
proxies[i] = new Arg(this, i);
}
return java.util.Arrays.stream(proxies).iterator();
}
/**
Returns the sqlite3_value at the given argument index or throws
an IllegalArgumentException exception if ndx is out of range.
*/
private sqlite3_value valueAt(int ndx){
if(ndx<0 || ndx>=args.length){
throw new IllegalArgumentException(
"SQL function argument index "+ndx+" is out of range."
);
}
return args[ndx];
}
sqlite3_context getContext(){return cx;}
public int getArgCount(){ return args.length; }
public int getInt(int arg){return CApi.sqlite3_value_int(valueAt(arg));}
public long getInt64(int arg){return CApi.sqlite3_value_int64(valueAt(arg));}
public double getDouble(int arg){return CApi.sqlite3_value_double(valueAt(arg));}
public byte[] getBlob(int arg){return CApi.sqlite3_value_blob(valueAt(arg));}
public byte[] getText(int arg){return CApi.sqlite3_value_text(valueAt(arg));}
public String getText16(int arg){return CApi.sqlite3_value_text16(valueAt(arg));}
public int getBytes(int arg){return CApi.sqlite3_value_bytes(valueAt(arg));}
public int getBytes16(int arg){return CApi.sqlite3_value_bytes16(valueAt(arg));}
public Object getObject(int arg){return CApi.sqlite3_value_java_object(valueAt(arg));}
public <T> T getObjectCasted(int arg, Class<T> type){
return CApi.sqlite3_value_java_casted(valueAt(arg), type);
}
public int getType(int arg){return CApi.sqlite3_value_type(valueAt(arg));}
public int getSubtype(int arg){return CApi.sqlite3_value_subtype(valueAt(arg));}
public int getNumericType(int arg){return CApi.sqlite3_value_numeric_type(valueAt(arg));}
public int getNoChange(int arg){return CApi.sqlite3_value_nochange(valueAt(arg));}
public boolean getFromBind(int arg){return CApi.sqlite3_value_frombind(valueAt(arg));}
public int getEncoding(int arg){return CApi.sqlite3_value_encoding(valueAt(arg));}
public void resultInt(int v){ CApi.sqlite3_result_int(cx, v); }
public void resultInt64(long v){ CApi.sqlite3_result_int64(cx, v); }
public void resultDouble(double v){ CApi.sqlite3_result_double(cx, v); }
public void resultError(String msg){CApi.sqlite3_result_error(cx, msg);}
public void resultError(Exception e){CApi.sqlite3_result_error(cx, e);}
public void resultErrorTooBig(){CApi.sqlite3_result_error_toobig(cx);}
public void resultErrorCode(int rc){CApi.sqlite3_result_error_code(cx, rc);}
public void resultObject(Object o){CApi.sqlite3_result_java_object(cx, o);}
public void resultNull(){CApi.sqlite3_result_null(cx);}
public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));}
public void resultZeroBlob(long n){
// Throw on error? If n is too big,
// sqlite3_result_error_toobig() is automatically called.
CApi.sqlite3_result_zeroblob64(cx, n);
}
public void resultBlob(byte[] blob){CApi.sqlite3_result_blob(cx, blob);}
public void resultText(byte[] utf8){CApi.sqlite3_result_text(cx, utf8);}
public void resultText(String txt){CApi.sqlite3_result_text(cx, txt);}
public void resultText16(byte[] utf16){CApi.sqlite3_result_text16(cx, utf16);}
public void resultText16(String txt){CApi.sqlite3_result_text16(cx, txt);}
public void setAuxData(int arg, Object o){
/* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html
The value of the N parameter to these interfaces should be
non-negative. Future enhancements may make use of negative N
values to define new kinds of function caching behavior.
*/
valueAt(arg);
CApi.sqlite3_set_auxdata(cx, arg, o);
}
public Object getAuxData(int arg){
valueAt(arg);
return CApi.sqlite3_get_auxdata(cx, arg);
}
}
/**
PerContextState assists aggregate and window functions in
managing their accumulator state across calls to the UDF's
callbacks.
<p>T must be of a type which can be legally stored as a value in
java.util.HashMap<KeyType,T>.
<p>If a given aggregate or window function is called multiple times
in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)...,
then the clients need some way of knowing which call is which so
that they can map their state between their various UDF callbacks
and reset it via xFinal(). This class takes care of such
mappings.
<p>This class works by mapping
sqlite3_context.getAggregateContext() to a single piece of
state, of a client-defined type (the T part of this class), which
persists across a "matching set" of the UDF's callbacks.
<p>This class is a helper providing commonly-needed functionality
- it is not required for use with aggregate or window functions.
Client UDFs are free to perform such mappings using custom
approaches. The provided {@link AggregateFunction} and {@link
WindowFunction} classes use this.
*/
public static final class PerContextState<T> {
private final java.util.Map<Long,ValueHolder<T>> map
= new java.util.HashMap<>();
/**
Should be called from a UDF's xStep(), xValue(), and xInverse()
methods, passing it that method's first argument and an initial
value for the persistent state. If there is currently no
mapping for the given context within the map, one is created
using the given initial value, else the existing one is used
and the 2nd argument is ignored. It returns a ValueHolder<T>
which can be used to modify that state directly without
requiring that the client update the underlying map's entry.
<p>The caller is obligated to eventually call
takeAggregateState() to clear the mapping.
*/
public ValueHolder<T> getAggregateState(SqlFunction.Arguments args, T initialValue){
final Long key = args.getContext().getAggregateContext(true);
ValueHolder<T> rc = null==key ? null : map.get(key);
if( null==rc ){
map.put(key, rc = new ValueHolder<>(initialValue));
}
return rc;
}
/**
Should be called from a UDF's xFinal() method and passed that
method's first argument. This function removes the value
associated with with the arguments' aggregate context from the
map and returns it, returning null if no other UDF method has
been called to set up such a mapping. The latter condition will
be the case if a UDF is used in a statement which has no result
rows.
*/
public T takeAggregateState(SqlFunction.Arguments args){
final ValueHolder<T> h = map.remove(args.getContext().getAggregateContext(false));
return null==h ? null : h.value;
}
}
/**
Internal-use adapter for wrapping this package's ScalarFunction
for use with the org.sqlite.jni.capi.ScalarFunction interface.
*/
static final class ScalarAdapter extends org.sqlite.jni.capi.ScalarFunction {
final ScalarFunction impl;
ScalarAdapter(ScalarFunction impl){
this.impl = impl;
}
/**
Proxies this.impl.xFunc(), adapting the call arguments to that
function's signature. If the proxy throws, it's translated to
sqlite_result_error() with the exception's message.
*/
public void xFunc(sqlite3_context cx, sqlite3_value[] args){
try{
impl.xFunc( new SqlFunction.Arguments(cx, args) );
}catch(Exception e){
CApi.sqlite3_result_error(cx, e);
}
}
public void xDestroy(){
impl.xDestroy();
}
}
/**
Internal-use adapter for wrapping this package's AggregateFunction
for use with the org.sqlite.jni.capi.AggregateFunction interface.
*/
static final class AggregateAdapter extends org.sqlite.jni.capi.AggregateFunction {
final AggregateFunction impl;
AggregateAdapter(AggregateFunction impl){
this.impl = impl;
}
/**
Proxies this.impl.xStep(), adapting the call arguments to that
function's signature. If the proxied function throws, it is
translated to sqlite_result_error() with the exception's
message.
*/
public void xStep(sqlite3_context cx, sqlite3_value[] args){
try{
impl.xStep( new SqlFunction.Arguments(cx, args) );
}catch(Exception e){
CApi.sqlite3_result_error(cx, e);
}
}
/**
As for the xFinal() argument of the C API's sqlite3_create_function().
If the proxied function throws, it is translated into a sqlite3_result_error().
*/
public void xFinal(sqlite3_context cx){
try{
impl.xFinal( new SqlFunction.Arguments(cx, null) );
}catch(Exception e){
CApi.sqlite3_result_error(cx, e);
}
}
public void xDestroy(){
impl.xDestroy();
}
}
}

View File

@ -0,0 +1,218 @@
/*
** 2023-10-09
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the wrapper1 interface for sqlite3.
*/
package org.sqlite.jni.wrapper1;
import java.nio.charset.StandardCharsets;
import static org.sqlite.jni.capi.CApi.*;
import org.sqlite.jni.capi.CApi;
import org.sqlite.jni.capi.sqlite3;
import org.sqlite.jni.capi.sqlite3_stmt;
import org.sqlite.jni.capi.OutputPointer;
/**
This class represents a database connection, analog to the C-side
sqlite3 class but with added argument validation, exceptions, and
similar "smoothing of sharp edges" to make the API safe to use from
Java. It also acts as a namespace for other types for which
individual instances are tied to a specific database connection.
*/
public final class Sqlite implements AutoCloseable {
private sqlite3 db;
//! Used only by the open() factory functions.
private Sqlite(sqlite3 db){
this.db = db;
}
/**
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();
final int rc = sqlite3_open_v2(filename, out, flags, vfsName);
final sqlite3 n = out.take();
if( 0!=rc ){
if( null==n ) throw new SqliteException(rc);
final SqliteException ex = new SqliteException(n);
n.close();
throw ex;
}
return new Sqlite(n);
}
public static Sqlite open(String filename, int flags){
return open(filename, flags, null);
}
public static Sqlite open(String filename){
return open(filename, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, null);
}
@Override public void close(){
if(null!=this.db){
this.db.close();
this.db = null;
}
}
/**
Returns this object's underlying native db handle, or null if
this instance has been closed. This is very specifically not
public.
*/
sqlite3 nativeHandle(){ return this.db; }
private sqlite3 affirmOpen(){
if( null==db || 0==db.getNativePointer() ){
throw new IllegalArgumentException("This database instance is closed.");
}
return this.db;
}
// 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);
}
}
/**
Corresponds to the sqlite3_stmt class. Use Sqlite.prepare() to
create new instances.
*/
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. It does 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){
final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt();
final int rc = sqlite3_prepare_v3(affirmOpen(), sql, prepFlags, out);
affirmRcOk(rc);
return new Stmt(this, out.take());
}
public Stmt prepare(String sql){
return prepare(sql, 0);
}
public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f ){
int rc = CApi.sqlite3_create_function(affirmOpen(), name, nArg, eTextRep,
new SqlFunction.ScalarAdapter(f));
if( 0!=rc ) throw new SqliteException(db);
}
public void createFunction(String name, int nArg, ScalarFunction f){
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
}
public void createFunction(String name, int nArg, int eTextRep, AggregateFunction f ){
int rc = CApi.sqlite3_create_function(affirmOpen(), name, nArg, eTextRep,
new SqlFunction.AggregateAdapter(f));
if( 0!=rc ) throw new SqliteException(db);
}
public void createFunction(String name, int nArg, AggregateFunction f){
this.createFunction(name, nArg, CApi.SQLITE_UTF8, f);
}
}

View File

@ -0,0 +1,82 @@
/*
** 2023-10-09
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the wrapper1 interface for sqlite3.
*/
package org.sqlite.jni.wrapper1;
import static org.sqlite.jni.capi.CApi.*;
import org.sqlite.jni.capi.sqlite3;
/**
A wrapper for communicating C-level (sqlite3*) instances with
Java. These wrappers do not own their associated pointer, they
simply provide a type-safe way to communicate it between Java
and C via JNI.
*/
public final class SqliteException extends java.lang.RuntimeException {
int errCode = SQLITE_ERROR;
int xerrCode = SQLITE_ERROR;
int errOffset = -1;
int sysErrno = 0;
/**
Records the given error string and uses SQLITE_ERROR for both the
error code and extended error code.
*/
public SqliteException(String msg){
super(msg);
}
/**
Uses sqlite3_errstr(sqlite3ResultCode) for the error string and
sets both the error code and extended error code to the given
value.
*/
public SqliteException(int sqlite3ResultCode){
super(sqlite3_errstr(sqlite3ResultCode));
errCode = xerrCode = sqlite3ResultCode;
}
/**
Records the current error state of db (which must not be null and
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.
*/
SqliteException(sqlite3 db){
super(sqlite3_errmsg(db));
errCode = sqlite3_errcode(db);
xerrCode = sqlite3_extended_errcode(db);
errOffset = sqlite3_error_offset(db);
sysErrno = sqlite3_system_errno(db);
}
/**
Records the current error state of db (which must not be null and
must refer to an open database).
*/
public SqliteException(Sqlite db){
this(db.nativeHandle());
}
public SqliteException(Sqlite.Stmt stmt){
this( stmt.db() );
}
public int errcode(){ return errCode; }
public int extendedErrcode(){ return xerrCode; }
public int errorOffset(){ return errOffset; }
public int systemErrno(){ return sysErrno; }
}

View File

@ -0,0 +1,584 @@
/*
** 2023-10-09
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains a set of tests for the sqlite3 JNI bindings.
*/
package org.sqlite.jni.wrapper1;
//import static org.sqlite.jni.capi.CApi.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.sqlite.jni.capi.*;
/**
An annotation for Tester2 tests which we do not want to run in
reflection-driven test mode because either they are not suitable
for multi-threaded threaded mode or we have to control their execution
order.
*/
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
@interface ManualTest{}
/**
Annotation for Tester2 tests which mark those which must be skipped
in multi-threaded mode.
*/
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
@interface SingleThreadOnly{}
public class Tester2 implements Runnable {
//! True when running in multi-threaded mode.
private static boolean mtMode = false;
//! True to sleep briefly between tests.
private static boolean takeNaps = false;
//! True to shuffle the order of the tests.
private static boolean shuffle = false;
//! True to dump the list of to-run tests to stdout.
private static boolean listRunTests = false;
//! True to squelch all out() and outln() output.
private static boolean quietMode = false;
//! Total number of runTests() calls.
private static int nTestRuns = 0;
//! List of test*() methods to run.
private static List<java.lang.reflect.Method> testMethods = null;
//! List of exceptions collected by run()
private static List<Exception> listErrors = new ArrayList<>();
private static final class Metrics {
//! Number of times createNewDb() (or equivalent) is invoked.
volatile int dbOpen = 0;
}
//! Instance ID.
private Integer tId;
Tester2(Integer id){
tId = id;
}
static final Metrics metrics = new Metrics();
public static synchronized void outln(){
if( !quietMode ){
System.out.println("");
}
}
public static synchronized void outPrefix(){
if( !quietMode ){
System.out.print(Thread.currentThread().getName()+": ");
}
}
public static synchronized void outln(Object val){
if( !quietMode ){
outPrefix();
System.out.println(val);
}
}
public static synchronized void out(Object val){
if( !quietMode ){
System.out.print(val);
}
}
@SuppressWarnings("unchecked")
public static synchronized void out(Object... vals){
if( !quietMode ){
outPrefix();
for(Object v : vals) out(v);
}
}
@SuppressWarnings("unchecked")
public static synchronized void outln(Object... vals){
if( !quietMode ){
out(vals); out("\n");
}
}
static volatile int affirmCount = 0;
public static synchronized int affirm(Boolean v, String comment){
++affirmCount;
if( false ) assert( v /* prefer assert over exception if it's enabled because
the JNI layer sometimes has to suppress exceptions,
so they might be squelched on their way back to the
top. */);
if( !v ) throw new RuntimeException(comment);
return affirmCount;
}
public static void affirm(Boolean v){
affirm(v, "Affirmation failed.");
}
public static void execSql(Sqlite db, String[] sql){
execSql(db, String.join("", sql));
}
public static int execSql(Sqlite dbw, boolean throwOnError, String sql){
final sqlite3 db = dbw.nativeHandle();
OutputPointer.Int32 oTail = new OutputPointer.Int32();
final byte[] sqlUtf8 = sql.getBytes(StandardCharsets.UTF_8);
int pos = 0, n = 1;
byte[] sqlChunk = sqlUtf8;
int rc = 0;
sqlite3_stmt stmt = null;
final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
while(pos < sqlChunk.length){
if(pos > 0){
sqlChunk = Arrays.copyOfRange(sqlChunk, pos,
sqlChunk.length);
}
if( 0==sqlChunk.length ) break;
rc = CApi.sqlite3_prepare_v2(db, sqlChunk, outStmt, oTail);
if(throwOnError) affirm(0 == rc);
else if( 0!=rc ) break;
pos = oTail.value;
stmt = outStmt.take();
if( null == stmt ){
// empty statement was parsed.
continue;
}
affirm(0 != stmt.getNativePointer());
while( CApi.SQLITE_ROW == (rc = CApi.sqlite3_step(stmt)) ){
}
CApi.sqlite3_finalize(stmt);
affirm(0 == stmt.getNativePointer());
if(0!=rc && CApi.SQLITE_ROW!=rc && CApi.SQLITE_DONE!=rc){
break;
}
}
CApi.sqlite3_finalize(stmt);
if(CApi.SQLITE_ROW==rc || CApi.SQLITE_DONE==rc) rc = 0;
if( 0!=rc && throwOnError){
throw new SqliteException(db);
}
return rc;
}
static void execSql(Sqlite db, String sql){
execSql(db, true, sql);
}
@SingleThreadOnly /* because it's thread-agnostic */
private void test1(){
affirm(CApi.sqlite3_libversion_number() == CApi.SQLITE_VERSION_NUMBER);
}
/* Copy/paste/rename this to add new tests. */
private void _testTemplate(){
//final sqlite3 db = createNewDb();
//sqlite3_stmt stmt = prepare(db,"SELECT 1");
//sqlite3_finalize(stmt);
//sqlite3_close_v2(db);
}
private void nap() throws InterruptedException {
if( takeNaps ){
Thread.sleep(java.util.concurrent.ThreadLocalRandom.current().nextInt(3, 17), 0);
}
}
Sqlite openDb(String name){
final Sqlite db = Sqlite.open(name, CApi.SQLITE_OPEN_READWRITE|
CApi.SQLITE_OPEN_CREATE|
CApi.SQLITE_OPEN_EXRESCODE);
++metrics.dbOpen;
return db;
}
Sqlite openDb(){ return openDb(":memory:"); }
void testOpenDb1(){
Sqlite db = openDb();
affirm( 0!=db.nativeHandle().getNativePointer() );
db.close();
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
}
void testPrepare1(){
try (Sqlite db = openDb()) {
Sqlite.Stmt stmt = db.prepare("SELECT 1");
affirm( null!=stmt.nativeHandle() );
affirm( CApi.SQLITE_ROW == stmt.step() );
affirm( CApi.SQLITE_DONE == stmt.step() );
stmt.reset();
affirm( CApi.SQLITE_ROW == stmt.step() );
affirm( CApi.SQLITE_DONE == stmt.step() );
affirm( 0 == stmt.finalizeStmt() );
affirm( null==stmt.nativeHandle() );
stmt = db.prepare("SELECT 1");
affirm( CApi.SQLITE_ROW == stmt.step() );
affirm( 0 == stmt.finalizeStmt() )
/* getting a non-0 out of sqlite3_finalize() is tricky */;
affirm( null==stmt.nativeHandle() );
}
}
void testUdfScalar(){
final ValueHolder<Integer> xDestroyCalled = new ValueHolder<>(0);
try (Sqlite db = openDb()) {
execSql(db, "create table t(a); insert into t(a) values(1),(2),(3)");
final ValueHolder<Integer> vh = new ValueHolder<>(0);
final ScalarFunction f = new ScalarFunction(){
public void xFunc(SqlFunction.Arguments args){
for( SqlFunction.Arguments.Arg arg : args ){
vh.value += arg.getInt();
}
}
public void xDestroy(){
++xDestroyCalled.value;
}
};
db.createFunction("myfunc", -1, f);
execSql(db, "select myfunc(1,2,3)");
affirm( 6 == vh.value );
vh.value = 0;
execSql(db, "select myfunc(-1,-2,-3)");
affirm( -6 == vh.value );
affirm( 0 == xDestroyCalled.value );
}
affirm( 1 == xDestroyCalled.value );
}
void testUdfAggregate(){
final ValueHolder<Integer> xDestroyCalled = new ValueHolder<>(0);
final ValueHolder<Integer> vh = new ValueHolder<>(0);
try (Sqlite db = openDb()) {
execSql(db, "create table t(a); insert into t(a) values(1),(2),(3)");
final AggregateFunction f = new AggregateFunction<Integer>(){
public void xStep(SqlFunction.Arguments args){
final ValueHolder<Integer> agg = this.getAggregateState(args, 0);
for( SqlFunction.Arguments.Arg arg : args ){
agg.value += arg.getInt();
}
}
public void xFinal(SqlFunction.Arguments args){
final Integer v = this.takeAggregateState(args);
if( null==v ) args.resultNull();
else args.resultInt(v);
vh.value = v;
}
public void xDestroy(){
++xDestroyCalled.value;
}
};
db.createFunction("myagg", -1, f);
execSql(db, "select myagg(a) from t");
affirm( 6 == vh.value );
affirm( 0 == xDestroyCalled.value );
}
affirm( 1 == xDestroyCalled.value );
}
private void runTests(boolean fromThread) throws Exception {
List<java.lang.reflect.Method> mlist = testMethods;
affirm( null!=mlist );
if( shuffle ){
mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) );
java.util.Collections.shuffle(mlist);
}
if( listRunTests ){
synchronized(this.getClass()){
if( !fromThread ){
out("Initial test"," list: ");
for(java.lang.reflect.Method m : testMethods){
out(m.getName()+" ");
}
outln();
outln("(That list excludes some which are hard-coded to run.)");
}
out("Running"," tests: ");
for(java.lang.reflect.Method m : mlist){
out(m.getName()+" ");
}
outln();
}
}
for(java.lang.reflect.Method m : mlist){
nap();
try{
m.invoke(this);
}catch(java.lang.reflect.InvocationTargetException e){
outln("FAILURE: ",m.getName(),"(): ", e.getCause());
throw e;
}
}
synchronized( this.getClass() ){
++nTestRuns;
}
}
public void run() {
try {
runTests(0!=this.tId);
}catch(Exception e){
synchronized( listErrors ){
listErrors.add(e);
}
}finally{
affirm( CApi.sqlite3_java_uncache_thread() );
affirm( !CApi.sqlite3_java_uncache_thread() );
}
}
/**
Runs the basic sqlite3 JNI binding sanity-check suite.
CLI flags:
-q|-quiet: disables most test output.
-t|-thread N: runs the tests in N threads
concurrently. Default=1.
-r|-repeat N: repeats the tests in a loop N times, each one
consisting of the -thread value's threads.
-shuffle: randomizes the order of most of the test functions.
-naps: sleep small random intervals between tests in order to add
some chaos for cross-thread contention.
-list-tests: outputs the list of tests being run, minus some
which are hard-coded. This is noisy in multi-threaded mode.
-fail: forces an exception to be thrown during the test run. Use
with -shuffle to make its appearance unpredictable.
-v: emit some developer-mode info at the end.
*/
public static void main(String[] args) throws Exception {
Integer nThread = 1;
boolean doSomethingForDev = false;
Integer nRepeat = 1;
boolean forceFail = false;
boolean sqlLog = false;
boolean configLog = false;
boolean squelchTestOutput = false;
for( int i = 0; i < args.length; ){
String arg = args[i++];
if(arg.startsWith("-")){
arg = arg.replaceFirst("-+","");
if(arg.equals("v")){
doSomethingForDev = true;
//listBoundMethods();
}else if(arg.equals("t") || arg.equals("thread")){
nThread = Integer.parseInt(args[i++]);
}else if(arg.equals("r") || arg.equals("repeat")){
nRepeat = Integer.parseInt(args[i++]);
}else if(arg.equals("shuffle")){
shuffle = true;
}else if(arg.equals("list-tests")){
listRunTests = true;
}else if(arg.equals("fail")){
forceFail = true;
}else if(arg.equals("sqllog")){
sqlLog = true;
}else if(arg.equals("configlog")){
configLog = true;
}else if(arg.equals("naps")){
takeNaps = true;
}else if(arg.equals("q") || arg.equals("quiet")){
squelchTestOutput = true;
}else{
throw new IllegalArgumentException("Unhandled flag:"+arg);
}
}
}
if( sqlLog ){
if( CApi.sqlite3_compileoption_used("ENABLE_SQLLOG") ){
final ConfigSqllogCallback log = new ConfigSqllogCallback() {
@Override public void call(sqlite3 db, String msg, int op){
switch(op){
case 0: outln("Opening db: ",db); break;
case 1: outln("SQL ",db,": ",msg); break;
case 2: outln("Closing db: ",db); break;
}
}
};
int rc = CApi.sqlite3_config( log );
affirm( 0==rc );
rc = CApi.sqlite3_config( (ConfigSqllogCallback)null );
affirm( 0==rc );
rc = CApi.sqlite3_config( log );
affirm( 0==rc );
}else{
outln("WARNING: -sqllog is not active because library was built ",
"without SQLITE_ENABLE_SQLLOG.");
}
}
if( configLog ){
final ConfigLogCallback log = new ConfigLogCallback() {
@Override public void call(int code, String msg){
outln("ConfigLogCallback: ",ResultCode.getEntryForInt(code),": ", msg);
};
};
int rc = CApi.sqlite3_config( log );
affirm( 0==rc );
rc = CApi.sqlite3_config( (ConfigLogCallback)null );
affirm( 0==rc );
rc = CApi.sqlite3_config( log );
affirm( 0==rc );
}
quietMode = squelchTestOutput;
outln("If you just saw warning messages regarding CallStaticObjectMethod, ",
"you are very likely seeing the side effects of a known openjdk8 ",
"bug. It is unsightly but does not affect the library.");
{
// Build list of tests to run from the methods named test*().
testMethods = new ArrayList<>();
int nSkipped = 0;
for(final java.lang.reflect.Method m : Tester2.class.getDeclaredMethods()){
final String name = m.getName();
if( name.equals("testFail") ){
if( forceFail ){
testMethods.add(m);
}
}else if( !m.isAnnotationPresent( ManualTest.class ) ){
if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){
if( 0==nSkipped++ ){
out("Skipping tests in multi-thread mode:");
}
out(" "+name+"()");
}else if( name.startsWith("test") ){
testMethods.add(m);
}
}
}
if( nSkipped>0 ) out("\n");
}
final long timeStart = System.currentTimeMillis();
int nLoop = 0;
switch( CApi.sqlite3_threadsafe() ){ /* Sanity checking */
case 0:
affirm( CApi.SQLITE_ERROR==CApi.sqlite3_config( CApi.SQLITE_CONFIG_SINGLETHREAD ),
"Could not switch to single-thread mode." );
affirm( CApi.SQLITE_ERROR==CApi.sqlite3_config( CApi.SQLITE_CONFIG_MULTITHREAD ),
"Could switch to multithread mode." );
affirm( CApi.SQLITE_ERROR==CApi.sqlite3_config( CApi.SQLITE_CONFIG_SERIALIZED ),
"Could not switch to serialized threading mode." );
outln("This is a single-threaded build. Not using threads.");
nThread = 1;
break;
case 1:
case 2:
affirm( 0==CApi.sqlite3_config( CApi.SQLITE_CONFIG_SINGLETHREAD ),
"Could not switch to single-thread mode." );
affirm( 0==CApi.sqlite3_config( CApi.SQLITE_CONFIG_MULTITHREAD ),
"Could not switch to multithread mode." );
affirm( 0==CApi.sqlite3_config( CApi.SQLITE_CONFIG_SERIALIZED ),
"Could not switch to serialized threading mode." );
break;
default:
affirm( false, "Unhandled SQLITE_THREADSAFE value." );
}
outln("libversion_number: ",
CApi.sqlite3_libversion_number(),"\n",
CApi.sqlite3_libversion(),"\n",CApi.SQLITE_SOURCE_ID,"\n",
"SQLITE_THREADSAFE=",CApi.sqlite3_threadsafe());
final boolean showLoopCount = (nRepeat>1 && nThread>1);
if( showLoopCount ){
outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each.");
}
if( takeNaps ) outln("Napping between tests is enabled.");
for( int n = 0; n < nRepeat; ++n ){
++nLoop;
if( showLoopCount ) out((1==nLoop ? "" : " ")+nLoop);
if( nThread<=1 ){
new Tester2(0).runTests(false);
continue;
}
Tester2.mtMode = true;
final ExecutorService ex = Executors.newFixedThreadPool( nThread );
for( int i = 0; i < nThread; ++i ){
ex.submit( new Tester2(i), i );
}
ex.shutdown();
try{
ex.awaitTermination(nThread*200, java.util.concurrent.TimeUnit.MILLISECONDS);
ex.shutdownNow();
}catch (InterruptedException ie){
ex.shutdownNow();
Thread.currentThread().interrupt();
}
if( !listErrors.isEmpty() ){
quietMode = false;
outln("TEST ERRORS:");
Exception err = null;
for( Exception e : listErrors ){
e.printStackTrace();
if( null==err ) err = e;
}
if( null!=err ) throw err;
}
}
if( showLoopCount ) outln();
quietMode = false;
final long timeEnd = System.currentTimeMillis();
outln("Tests done. Metrics across ",nTestRuns," total iteration(s):");
outln("\tAssertions checked: ",affirmCount);
outln("\tDatabases opened: ",metrics.dbOpen);
if( doSomethingForDev ){
CApi.sqlite3_jni_internal_details();
}
affirm( 0==CApi.sqlite3_release_memory(1) );
CApi.sqlite3_shutdown();
int nMethods = 0;
int nNatives = 0;
int nCanonical = 0;
final java.lang.reflect.Method[] declaredMethods =
CApi.class.getDeclaredMethods();
for(java.lang.reflect.Method m : declaredMethods){
final int mod = m.getModifiers();
if( 0!=(mod & java.lang.reflect.Modifier.STATIC) ){
final String name = m.getName();
if(name.startsWith("sqlite3_")){
++nMethods;
if( 0!=(mod & java.lang.reflect.Modifier.NATIVE) ){
++nNatives;
}
}
}
}
outln("\tCApi.sqlite3_*() methods: "+
nMethods+" total, with "+
nNatives+" native, "+
(nMethods - nNatives)+" Java"
);
outln("\tTotal test time = "
+(timeEnd - timeStart)+"ms");
}
}

View File

@ -0,0 +1,25 @@
/*
** 2023-10-16
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains a set of tests for the sqlite3 JNI bindings.
*/
package org.sqlite.jni.wrapper1;
/**
A helper class which simply holds a single value. Its primary use
is for communicating values out of anonymous classes, as doing so
requires a "final" reference.
*/
public class ValueHolder<T> {
public T value;
public ValueHolder(){}
public ValueHolder(T v){value = v;}
}

View File

@ -1061,6 +1061,11 @@ static sqlite3_module lsm1Module = {
lsm1Rollback, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};

View File

@ -1475,7 +1475,8 @@ static sqlite3_module amatchModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */

View File

@ -411,7 +411,8 @@ int sqlite3BinfoRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
return sqlite3_create_module(db, "sqlite_btreeinfo", &binfo_module, 0);
}

View File

@ -409,6 +409,11 @@ static sqlite3_module carrayModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadow */
0 /* xIntegrity */
};
/*

View File

@ -939,7 +939,8 @@ static sqlite3_module closureModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */

View File

@ -470,7 +470,8 @@ static sqlite3_module completionModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */

View File

@ -897,6 +897,11 @@ static sqlite3_module CsvModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#ifdef SQLITE_TEST
@ -929,6 +934,11 @@ static sqlite3_module CsvModuleFauxWrite = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_TEST */

View File

@ -293,6 +293,7 @@ static sqlite3_module explainModule = {
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */

View File

@ -983,6 +983,7 @@ static int fsdirRegister(sqlite3 *db){
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0);

View File

@ -1058,7 +1058,8 @@ static sqlite3_module deltaparsevtabModule = {
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0
/* xShadowName */ 0,
/* xIntegrity */ 0
};

View File

@ -1165,6 +1165,11 @@ static sqlite3_module fuzzerModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */

View File

@ -396,6 +396,7 @@ static sqlite3_module memstatModule = {
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */

View File

@ -248,7 +248,8 @@ static sqlite3_module prefixesModule = {
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0
/* xShadowName */ 0,
/* xIntegrity */ 0
};
/*

View File

@ -439,7 +439,8 @@ static sqlite3_module qpvtabModule = {
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0
/* xShadowName */ 0,
/* xIntegrity */ 0
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */

View File

@ -557,7 +557,8 @@ static sqlite3_module seriesModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */

View File

@ -3009,6 +3009,11 @@ static sqlite3_module spellfix1Module = {
0, /* xRollback */
0, /* xFindMethod */
spellfix1Rename, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
/*

View File

@ -314,6 +314,7 @@ static sqlite3_module stmtModule = {
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */

View File

@ -249,7 +249,8 @@ static sqlite3_module templatevtabModule = {
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0
/* xShadowName */ 0,
/* xIntegrity */ 0
};

View File

@ -1351,7 +1351,8 @@ static int createUnionVtab(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
0, /* xShadowName */
0 /* xIntegrity */
};
int rc;

Some files were not shown because too many files have changed in this diff Show More