Clear some picky warnings, sync w/trunk.
FossilOrigin-Name: ee58425904b36319e016dc69bb5f141bb3565b1723a97490a4b2cfa16b89fbdf
This commit is contained in:
commit
81be0d26bb
58
Makefile.in
58
Makefile.in
@ -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
|
||||
|
||||
#
|
||||
|
11
Makefile.msc
11
Makefile.msc
@ -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
|
||||
|
@ -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
2
configure
vendored
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -545,7 +545,8 @@ int sqlite3Fts3InitAux(sqlite3 *db){
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0 /* xShadowName */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
int rc; /* Return code */
|
||||
|
||||
|
@ -362,7 +362,8 @@ int sqlite3Fts3InitTerm(sqlite3 *db){
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0 /* xShadowName */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
int rc; /* Return code */
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
50
ext/fts5/test/fts5faultG.test
Normal file
50
ext/fts5/test/fts5faultG.test
Normal 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
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
@ -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); }
|
||||
}
|
||||
|
||||
}
|
@ -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 "";
|
||||
}
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
/**
|
@ -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;
|
||||
|
||||
|
||||
/**
|
@ -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 {
|
||||
/**
|
@ -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 {
|
@ -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 {
|
||||
/**
|
File diff suppressed because it is too large
Load Diff
@ -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
|
@ -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
|
||||
*/
|
@ -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 {
|
||||
/**
|
@ -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 {
|
||||
/**
|
@ -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);
|
||||
}
|
@ -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 );
|
||||
}
|
@ -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; }
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
/**
|
@ -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 {
|
||||
/**
|
155
ext/jni/src/org/sqlite/jni/capi/ResultCode.java
Normal file
155
ext/jni/src/org/sqlite/jni/capi/ResultCode.java
Normal 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); }
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
/**
|
@ -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
|
@ -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);
|
||||
}
|
@ -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() {}
|
||||
}
|
@ -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().
|
@ -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");
|
@ -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 {
|
||||
/**
|
@ -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 {
|
||||
/**
|
25
ext/jni/src/org/sqlite/jni/capi/ValueHolder.java
Normal file
25
ext/jni/src/org/sqlite/jni/capi/ValueHolder.java
Normal 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;}
|
||||
}
|
@ -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;
|
||||
|
||||
|
||||
/**
|
@ -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
|
@ -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;
|
@ -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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
@ -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());
|
||||
}
|
||||
}
|
@ -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.
|
@ -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
|
||||
|
@ -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().
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
82
ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java
Normal file
82
ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
37
ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java
Normal file
37
ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java
Normal 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() {}
|
||||
|
||||
}
|
301
ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java
Normal file
301
ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
218
ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java
Normal file
218
ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
82
ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java
Normal file
82
ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java
Normal 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; }
|
||||
|
||||
}
|
584
ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java
Normal file
584
ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java
Normal 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");
|
||||
}
|
||||
}
|
25
ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java
Normal file
25
ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java
Normal 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;}
|
||||
}
|
@ -1061,6 +1061,11 @@ static sqlite3_module lsm1Module = {
|
||||
lsm1Rollback, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
|
||||
|
||||
|
@ -1475,7 +1475,8 @@ static sqlite3_module amatchModule = {
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0 /* xShadowName */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -409,6 +409,11 @@ static sqlite3_module carrayModule = {
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0, /* xShadow */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -939,7 +939,8 @@ static sqlite3_module closureModule = {
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0 /* xShadowName */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
@ -470,7 +470,8 @@ static sqlite3_module completionModule = {
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0 /* xShadowName */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -293,6 +293,7 @@ static sqlite3_module explainModule = {
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
@ -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);
|
||||
|
@ -1058,7 +1058,8 @@ static sqlite3_module deltaparsevtabModule = {
|
||||
/* xSavepoint */ 0,
|
||||
/* xRelease */ 0,
|
||||
/* xRollbackTo */ 0,
|
||||
/* xShadowName */ 0
|
||||
/* xShadowName */ 0,
|
||||
/* xIntegrity */ 0
|
||||
};
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -396,6 +396,7 @@ static sqlite3_module memstatModule = {
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
@ -248,7 +248,8 @@ static sqlite3_module prefixesModule = {
|
||||
/* xSavepoint */ 0,
|
||||
/* xRelease */ 0,
|
||||
/* xRollbackTo */ 0,
|
||||
/* xShadowName */ 0
|
||||
/* xShadowName */ 0,
|
||||
/* xIntegrity */ 0
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -439,7 +439,8 @@ static sqlite3_module qpvtabModule = {
|
||||
/* xSavepoint */ 0,
|
||||
/* xRelease */ 0,
|
||||
/* xRollbackTo */ 0,
|
||||
/* xShadowName */ 0
|
||||
/* xShadowName */ 0,
|
||||
/* xIntegrity */ 0
|
||||
};
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
|
@ -557,7 +557,8 @@ static sqlite3_module seriesModule = {
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0 /* xShadowName */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
@ -3009,6 +3009,11 @@ static sqlite3_module spellfix1Module = {
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
spellfix1Rename, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -314,6 +314,7 @@ static sqlite3_module stmtModule = {
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0, /* xShadowName */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
@ -249,7 +249,8 @@ static sqlite3_module templatevtabModule = {
|
||||
/* xSavepoint */ 0,
|
||||
/* xRelease */ 0,
|
||||
/* xRollbackTo */ 0,
|
||||
/* xShadowName */ 0
|
||||
/* xShadowName */ 0,
|
||||
/* xIntegrity */ 0
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user