1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-01 06:27:03 +03:00

Pervasive changes to console_io.{c,h} in support of simplifying ubiquitous emit ops in shell, and to get better control of console streams that might be opened only via .read or .output commands. Changes to shell to use {s,o,e}put{f,z}(...) calls for initial testing, but this check-in has few such conversions so that most will be in a separate check-in. Many renames to better follow recent coding convention. This code seems to be working, but has not been tested on difficult platforms or with multiple console hosts yet. So it is a WIP.

FossilOrigin-Name: 14762a004cdf37d1e12f26aadff8ed3824893278f22ff141de86dd44d9b250f3
This commit is contained in:
larrybr
2023-11-11 06:20:38 +00:00
56 changed files with 2877 additions and 3460 deletions

View File

@ -9,8 +9,9 @@
** May you share freely, never taking more than you give.
**
********************************************************************************
** This file implements various interfaces used for console I/O by the
** SQLite project command-line tools, as explained in console_io.h .
** This file implements various interfaces used for console and stream I/O
** by the SQLite project command-line tools, as explained in console_io.h .
** Functions prefixed by "SQLITE_INTERNAL_LINKAGE" behave as described there.
*/
#ifndef SQLITE_CDECL
@ -22,11 +23,12 @@
# include <string.h>
# include <stdlib.h>
# include <limits.h>
# include <assert.h>
# include "console_io.h"
# include "sqlite3.h"
#endif
#if defined(_WIN32) || defined(WIN32)
#if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT
# ifndef SHELL_NO_SYSINC
# include <io.h>
# include <fcntl.h>
@ -36,7 +38,6 @@
# endif
# ifdef SHELL_LEGACY_CONSOLE_IO
# define SHELL_CON_TRANSLATE 2 /* Use UTF-8/MBCS translation for console I/O */
extern char *sqlite3_win32_utf8_to_mbcs_v2(const char *, int);
# else
# define SHELL_CON_TRANSLATE 1 /* Use WCHAR Windows APIs for console I/O */
# endif
@ -51,7 +52,7 @@ extern char *sqlite3_win32_utf8_to_mbcs_v2(const char *, int);
static HANDLE handleOfFile(FILE *pf){
int fileDesc = _fileno(pf);
union { intptr_t osfh; HANDLE fh; } fid = {
(fileDesc!=-2)? _get_osfhandle(fileDesc) : (intptr_t)INVALID_HANDLE_VALUE
(fileDesc>=0)? _get_osfhandle(fileDesc) : (intptr_t)INVALID_HANDLE_VALUE
};
return fid.fh;
}
@ -59,35 +60,66 @@ static HANDLE handleOfFile(FILE *pf){
typedef struct PerStreamTags {
#if SHELL_CON_TRANSLATE
DWORD consMode;
HANDLE hx;
DWORD consMode;
#else
short reachesConsole;
#endif
FILE *pf;
} PerStreamTags;
static short fileOfConsole(FILE *pf, PerStreamTags *ppst){
/* Define NULL-like value for things which can validly be 0. */
#define SHELL_INVALID_FILE_PTR ((FILE *)~0)
#if SHELL_CON_TRANSLATE
short rv = 0;
DWORD dwj;
HANDLE fh = handleOfFile(pf);
if( INVALID_HANDLE_VALUE != fh ){
rv = (GetFileType(fh) == FILE_TYPE_CHAR && GetConsoleMode(fh,&dwj));
if( rv ){
ppst->hx = fh;
ppst->pf = pf;
GetConsoleMode(fh, &ppst->consMode);
}
}
return rv;
# define SHELL_INVALID_CONS_MODE 0xFFFF0000
#endif
#if SHELL_CON_TRANSLATE
# define CI_INITIALIZER \
{ INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, SHELL_INVALID_FILE_PTR }
#else
return (short)isatty(fileno(pf));
# define CI_INITIALIZER { 0, SHELL_INVALID_FILE_PTR }
#endif
/* Quickly say whether a known output is going to the console. */
static short pstReachesConsole(PerStreamTags *ppst){
#if SHELL_CON_TRANSLATE
return (ppst->hx != INVALID_HANDLE_VALUE);
#else
return (ppst->reachesConsole != 0);
#endif
}
#define SHELL_INVALID_FILE_PTR ((FILE *)sizeof(FILE*))
#define SHELL_INVALID_CONS_MODE 0xFFFF0000
#if SHELL_CON_TRANSLATE
static void restoreConsoleArb(PerStreamTags *ppst){
if( pstReachesConsole(ppst) ) SetConsoleMode(ppst->hx, ppst->consMode);
}
#else
# define restoreConsoleArb(ppst)
#endif
/* Say whether FILE* appears to be a console, collect associated info. */
static short streamOfConsole(FILE *pf, /* out */ PerStreamTags *ppst){
#if SHELL_CON_TRANSLATE
short rv = 0;
DWORD dwCM = SHELL_INVALID_CONS_MODE;
HANDLE fh = handleOfFile(pf);
ppst->pf = pf;
if( INVALID_HANDLE_VALUE != fh ){
rv = (GetFileType(fh) == FILE_TYPE_CHAR && GetConsoleMode(fh,&dwCM));
}
ppst->hx = (rv)? fh : INVALID_HANDLE_VALUE;
ppst->consMode = dwCM;
return rv;
#else
ppst->pf = pf;
ppst->reachesConsole = ( (short)isatty(fileno(pf)) );
return ppst->reachesConsole;
#endif
}
#if SHELL_CON_TRANSLATE
/* Define console modes for use with the Windows Console API. */
# define SHELL_CONI_MODE \
(ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \
| ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT)
@ -96,109 +128,159 @@ static short fileOfConsole(FILE *pf, PerStreamTags *ppst){
#endif
typedef struct ConsoleInfo {
/* int iInitialFmode[3];
** Above only needed for legacy console I/O for callable CLI.
** Because that state cannot be obtained from each FILE *,
** there will be no exact restoration of console state for
** the CLI when built with SHELL_LEGACY_CONSOLE_IO defined.
*/
PerStreamTags pst[3];
#if SHELL_CON_TRANSLATE
unsigned char haveInput;
unsigned char outputIx;
unsigned char stdinEof;
#endif
ConsoleStdConsStreams cscs;
PerStreamTags pstSetup[3];
PerStreamTags pstDesignated[3];
StreamsAreConsole sacSetup;
StreamsAreConsole sacDesignated;
} ConsoleInfo;
#if SHELL_CON_TRANSLATE
# define CI_INITIALIZER \
{SHELL_INVALID_CONS_MODE, INVALID_HANDLE_VALUE, SHELL_INVALID_FILE_PTR }
#else
# define CI_INITIALIZER { SHELL_INVALID_FILE_PTR }
#endif
static short isValidStreamInfo(PerStreamTags *ppst){
return (ppst->pf != SHELL_INVALID_FILE_PTR);
}
static ConsoleInfo consoleInfo = {
{ /* pst */ CI_INITIALIZER, CI_INITIALIZER, CI_INITIALIZER },
#if SHELL_CON_TRANSLATE
0, 0, 1, /* haveInput, outputIx, stdinEof */
#endif
CSCS_NoConsole
{ /* pstSetup */ CI_INITIALIZER, CI_INITIALIZER, CI_INITIALIZER },
{ /* pstDesignated[] */ CI_INITIALIZER, CI_INITIALIZER, CI_INITIALIZER },
SAC_NoConsole, SAC_NoConsole /* sacSetup, sacDesignated */
};
#undef SHELL_INVALID_FILE_PTR
#undef CI_INITIALIZER
SQLITE_INTERNAL_LINKAGE ConsoleStdConsStreams
SQLITE_INTERNAL_LINKAGE FILE* invalidFileStream = (FILE *)~0;
static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){
#if SHELL_CON_TRANSLATE
if( pstReachesConsole(ppst) ){
DWORD cm = odir? SHELL_CONO_MODE : SHELL_CONI_MODE;
SetConsoleMode(ppst->hx, cm);
# if SHELL_CON_TRANSLATE == 2
_setmode(_fileno(ppst->pf), _O_TEXT);
# endif
}
#else
(void)ppst;
(void)odir;
#endif
}
SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void){
#if SHELL_CON_TRANSLATE
int ix = 0;
while( ix < 6 ){
PerStreamTags *ppst = (ix<3)?
&consoleInfo.pstSetup[ix] : &consoleInfo.pstDesignated[ix-3];
maybeSetupAsConsole(ppst, (ix % 3)>0);
++ix;
}
#endif
}
SQLITE_INTERNAL_LINKAGE StreamsAreConsole
consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){
ConsoleStdConsStreams rv = CSCS_NoConsole;
FILE *apf[3] = { pfIn, pfOut, pfErr };
StreamsAreConsole rv = SAC_NoConsole;
FILE* apf[3] = { pfIn, pfOut, pfErr };
int ix;
for( ix = 2; ix >= 0; --ix ){
PerStreamTags *ppst = &consoleInfo.pst[ix];
if( fileOfConsole(apf[ix], ppst) ){
#if SHELL_CON_TRANSLATE
DWORD cm = (ix==0)? SHELL_CONI_MODE : SHELL_CONO_MODE;
if( ix==0 ){
consoleInfo.haveInput = 1;
consoleInfo.stdinEof = 0;
}else{
consoleInfo.outputIx |= ix;
}
SetConsoleMode(ppst->hx, cm);
#endif
rv |= (CSCS_InConsole<<ix);
PerStreamTags *ppst = &consoleInfo.pstSetup[ix];
if( streamOfConsole(apf[ix], ppst) ){
rv |= (SAC_InConsole<<ix);
}
consoleInfo.pstDesignated[ix] = *ppst;
if( ix > 0 ) fflush(apf[ix]);
#if SHELL_CON_TRANSLATE == 2
_setmode(_fileno(apf[ix]), _O_TEXT);
#endif
}
consoleInfo.cscs = rv;
consoleInfo.sacSetup = rv;
consoleRenewSetup();
return rv;
}
SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){
if( consoleInfo.cscs ){
#if SHELL_CON_TRANSLATE
static ConsoleInfo *pci = &consoleInfo;
if( pci->sacSetup ){
int ix;
for( ix=0; ix<3; ++ix ){
if( consoleInfo.cscs & (CSCS_InConsole<<ix) ){
PerStreamTags *ppst = &consoleInfo.pst[ix];
#if SHELL_CON_TRANSLATE == 2
if( pci->sacSetup & (SAC_InConsole<<ix) ){
PerStreamTags *ppst = &pci->pstSetup[ix];
# if SHELL_CON_TRANSLATE == 2
static int tmode = _O_TEXT;
/* Consider: Read these modes in consoleClassifySetup somehow.
/* Consider: Read this mode in consoleClassifySetup somehow.
** A _get_fmode() call almost works. But not with gcc, yet.
** This has to be done to make the CLI a callable function
** when legacy console I/O is done. (This may never happen.)
*/
_setmode(_fileno(consoleInfo.pst[ix].pf), tmode);
#endif
#if SHELL_CON_TRANSLATE
_setmode(_fileno(pci->pstSetup[ix].pf), tmode);
# endif
SetConsoleMode(ppst->hx, ppst->consMode);
ppst->hx = INVALID_HANDLE_VALUE;
#endif
ppst->pf = SHELL_INVALID_FILE_PTR;
}
consoleInfo.cscs = CSCS_NoConsole;
#if SHELL_CON_TRANSLATE
consoleInfo.stdinEof = consoleInfo.haveInput = consoleInfo.outputIx= 0;
#endif
}
}
#endif
}
static short isConOut(FILE *pf){
if( pf==consoleInfo.pst[1].pf ) return 1;
else if( pf==consoleInfo.pst[2].pf ) return 2;
else return 0;
/* Say whether given FILE* is among those known, via either
** consoleClassifySetup() or set{Output,Error}Stream, as
** readable, and return an associated PerStreamTags pointer
** if so. Otherwise, return 0.
*/
static PerStreamTags * isKnownReadable(FILE *pf){
static PerStreamTags *apst[] = {
&consoleInfo.pstDesignated[0], &consoleInfo.pstSetup[0], 0
};
int ix = 0;
do {
if( apst[ix]->pf == pf ) break;
} while( apst[++ix] != 0 );
return apst[ix];
}
/* Say whether given FILE* is among those known, via either
** consoleClassifySetup() or set{Output,Error}Stream, as
** writable, and return an associated PerStreamTags pointer
** if so. Otherwise, return 0.
*/
static PerStreamTags * isKnownWritable(FILE *pf){
static PerStreamTags *apst[] = {
&consoleInfo.pstDesignated[1], &consoleInfo.pstDesignated[2],
&consoleInfo.pstSetup[1], &consoleInfo.pstSetup[2], 0
};
int ix = 0;
do {
if( apst[ix]->pf == pf ) break;
} while( apst[++ix] != 0 );
return apst[ix];
}
static FILE *designateEmitStream(FILE *pf, unsigned chix){
FILE *rv = consoleInfo.pstDesignated[chix].pf;
if( pf == invalidFileStream ) return rv;
else{
/* Setting a possibly new output stream. */
PerStreamTags *ppst = isKnownWritable(pf);
if( ppst != 0 ){
PerStreamTags pst = *ppst;
consoleInfo.pstDesignated[chix] = pst;
}else streamOfConsole(pf, &consoleInfo.pstDesignated[chix]);
}
return rv;
}
SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf){
return designateEmitStream(pf, 1);
}
SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf){
return designateEmitStream(pf, 2);
}
#if SHELL_CON_TRANSLATE
static void setModeFlushQ(FILE *pf, short bFlush, int mode){
short ico = isConOut(pf);
if( ico>1 || bFlush ) fflush(pf);
if( bFlush ) fflush(pf);
_setmode(_fileno(pf), mode);
}
#else
# define setModeFlushQ(f, b, m) if(isConOut(f)>0||b) fflush(f)
# define setModeFlushQ(f, b, m) if(b) fflush(f)
#endif
SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *pf, short bFlush){
@ -210,15 +292,15 @@ SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){
#undef setModeFlushQ
#if SHELL_CON_TRANSLATE
/* Write plain 0-terminated output to stream known as console. */
static int conioZstrOut(int rch, const char *z){
/* Write plain 0-terminated output to stream known as reaching console. */
static int conioZstrOut(PerStreamTags *ppst, const char *z){
int rv = 0;
if( z!=NULL && *z!=0 ){
int nc;
int nwc;
# if SHELL_CON_TRANSLATE == 2
UINT cocp = GetConsoleOutputCP();
FILE *pfO = consoleInfo.pst[rch].pf;
FILE *pfO = ppst->pf;
if( cocp == CP_UTF8 ){
/* This is not legacy action. But it can work better,
** when the console putatively can handle UTF-8. */
@ -246,7 +328,7 @@ static int conioZstrOut(int rch, const char *z){
}
# elif SHELL_CON_TRANSLATE == 1
/* Translation from UTF-8 to UTF-16, then WCHARs out. */
if( WriteConsoleW(consoleInfo.pst[rch].hx, zw,nwc, 0, NULL) ){
if( WriteConsoleW(ppst->hx, zw,nwc, 0, NULL) ){
rv = nc;
}
# endif
@ -258,42 +340,115 @@ static int conioZstrOut(int rch, const char *z){
return rv;
}
/* For fprintfUtf8() and printfUtf8() when stream is known as console. */
static int conioVmPrintf(int rch, const char *zFormat, va_list ap){
/* For {f,o,e}PrintfUtf8() when stream is known to reach console. */
static int conioVmPrintf(PerStreamTags *ppst, const char *zFormat, va_list ap){
char *z = sqlite3_vmprintf(zFormat, ap);
int rv = conioZstrOut(rch, z);
int rv = conioZstrOut(ppst, z);
sqlite3_free(z);
return rv;
}
#endif
#endif /* SHELL_CON_TRANSLATE */
SQLITE_INTERNAL_LINKAGE int printfUtf8(const char *zFormat, ...){
static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix,
PerStreamTags *ppst){
PerStreamTags *rv = isKnownWritable(pf);
short isValid = (rv!=0)? isValidStreamInfo(rv) : 0;
if( rv != 0 && isValid ) return rv;
streamOfConsole(pf, ppst);
return ppst;
}
/* Get stream info, either for designated output or error stream when
** chix equals 1 or 2, or for an arbitrary stream when chix == 0.
** In either case, ppst references a caller-owned PerStreamTags
** struct which may be filled in if none of the known writable
** streams is being held by consoleInfo. The ppf parameter is an
** output when chix!=0 and an input when chix==0.
*/
static PerStreamTags *
getEmitStreamInfo(unsigned chix, PerStreamTags *ppst,
/* in/out */ FILE **ppf){
PerStreamTags *ppstTry;
FILE *pfEmit;
if( chix > 0 ){
ppstTry = &consoleInfo.pstDesignated[chix];
if( !isValidStreamInfo(ppstTry) ){
ppstTry = &consoleInfo.pstSetup[chix];
pfEmit = ppst->pf;
}else pfEmit = ppstTry->pf;
if( !isValidStreamInfo(ppst) ){
pfEmit = (chix > 1)? stderr : stdout;
ppstTry = ppst;
streamOfConsole(pfEmit, ppstTry);
}
*ppf = pfEmit;
}else{
ppstTry = isKnownWritable(*ppf);
if( ppstTry != 0 ) return ppstTry;
streamOfConsole(*ppf, ppst);
return ppst;
}
return ppstTry;
}
SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...){
va_list ap;
int rv;
FILE *pfOut;
PerStreamTags pst; /* Needed only for heretofore unknown streams. */
PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
va_start(ap, zFormat);
#if SHELL_CON_TRANSLATE
if( SHELL_INVALID_FILE_PTR != consoleInfo.pst[1].pf ){
rv = conioVmPrintf(1, zFormat, ap);
if( pstReachesConsole(ppst) ){
rv = conioVmPrintf(ppst, zFormat, ap);
}else{
#endif
rv = vfprintf(stdout, zFormat, ap);
rv = vfprintf(pfOut, zFormat, ap);
#if SHELL_CON_TRANSLATE
}
#endif
va_end(ap);
return rv;
}
#undef SHELL_INVALID_FILE_PTR
SQLITE_INTERNAL_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){
SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...){
va_list ap;
int rv;
FILE *pfErr;
PerStreamTags pst; /* Needed only for heretofore unknown streams. */
PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
va_start(ap, zFormat);
#if SHELL_CON_TRANSLATE
short rch = isConOut(pfO);
if( rch > 0 ){
rv = conioVmPrintf(rch, zFormat, ap);
}else {
if( pstReachesConsole(ppst) ){
rv = conioVmPrintf(ppst, zFormat, ap);
}else{
#endif
rv = vfprintf(pfErr, zFormat, ap);
#if SHELL_CON_TRANSLATE
}
#endif
va_end(ap);
return rv;
}
SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...){
va_list ap;
int rv;
#if SHELL_CON_TRANSLATE
PerStreamTags pst; /* Needed only for heretofore unknown streams. */
PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
#endif
va_start(ap, zFormat);
#if SHELL_CON_TRANSLATE
if( pstReachesConsole(ppst) ){
maybeSetupAsConsole(ppst, 1);
rv = conioVmPrintf(ppst, zFormat, ap);
if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
}else{
#endif
rv = vfprintf(pfO, zFormat, ap);
#if SHELL_CON_TRANSLATE
@ -303,11 +458,16 @@ SQLITE_INTERNAL_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...){
return rv;
}
SQLITE_INTERNAL_LINKAGE int fputsUtf8(const char *z, FILE *pfO){
SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){
#if SHELL_CON_TRANSLATE
short rch = isConOut(pfO);
if( rch > 0 ){
return conioZstrOut(rch, z);
PerStreamTags pst; /* Needed only for heretofore unknown streams. */
PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
if( pstReachesConsole(ppst) ){
int rv;
maybeSetupAsConsole(ppst, 1);
rv = conioZstrOut(ppst, z);
if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
return rv;
}else {
#endif
return (fputs(z, pfO)<0)? 0 : (int)strlen(z);
@ -316,6 +476,34 @@ SQLITE_INTERNAL_LINKAGE int fputsUtf8(const char *z, FILE *pfO){
#endif
}
SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){
FILE *pfErr;
PerStreamTags pst; /* Needed only for heretofore unknown streams. */
PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
#if SHELL_CON_TRANSLATE
if( pstReachesConsole(ppst) ) return conioZstrOut(ppst, z);
else {
#endif
return (fputs(z, pfErr)<0)? 0 : (int)strlen(z);
#if SHELL_CON_TRANSLATE
}
#endif
}
SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){
FILE *pfOut;
PerStreamTags pst; /* Needed only for heretofore unknown streams. */
PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
#if SHELL_CON_TRANSLATE
if( pstReachesConsole(ppst) ) return conioZstrOut(ppst, z);
else {
#endif
return (fputs(z, pfOut)<0)? 0 : (int)strlen(z);
#if SHELL_CON_TRANSLATE
}
#endif
}
#if SHELL_CON_TRANSLATE==2
static int mbcsToUtf8InPlaceIfValid(char *pc, int nci, int nco, UINT codePage){
WCHAR wcOneCode[2];
@ -329,26 +517,25 @@ static int mbcsToUtf8InPlaceIfValid(char *pc, int nci, int nco, UINT codePage){
}
#endif
SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){
SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){
if( pfIn==0 ) pfIn = stdin;
#if SHELL_CON_TRANSLATE
if( pfIn == consoleInfo.pst[0].pf ){
if( pfIn == consoleInfo.pstSetup[0].pf ){
# if SHELL_CON_TRANSLATE==1
# define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */
WCHAR wcBuf[SHELL_GULP+1];
int lend = 0, noc = 0;
if( consoleInfo.stdinEof ) return 0;
if( ncMax > 0 ) cBuf[0] = 0;
while( noc < ncMax-8-1 && !lend ){
/* There is room for at least 2 more characters and a 0-terminator. */
int na = (ncMax > SHELL_GULP*4+1 + noc)? SHELL_GULP : (ncMax-1 - noc)/4;
# undef SHELL_GULP
DWORD nbr = 0;
BOOL bRC = ReadConsoleW(consoleInfo.pst[0].hx, wcBuf, na, &nbr, 0);
BOOL bRC = ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf, na, &nbr, 0);
if( bRC && nbr>0 && (wcBuf[nbr-1]&0xF800)==0xD800 ){
/* Last WHAR read is first of a UTF-16 surrogate pair. Grab its mate. */
DWORD nbrx;
bRC &= ReadConsoleW(consoleInfo.pst[0].hx, wcBuf+nbr, 1, &nbrx, 0);
bRC &= ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf+nbr, 1, &nbrx, 0);
if( bRC ) nbr += nbrx;
}
if( !bRC || (noc==0 && nbr==0) ) return 0;
@ -359,23 +546,20 @@ SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){
nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,cBuf+noc,nmb,0,0);
noc += nmb;
/* Fixup line-ends as coded by Windows for CR (or "Enter".)
** Note that this is done without regard for any setModeText()
** This is done without regard for any setMode{Text,Binary}()
** call that might have been done on the interactive input.
*/
if( noc > 0 ){
if( cBuf[noc-1]=='\n' ){
lend = 1;
if( noc > 1 && cBuf[noc-2]=='\r' ){
cBuf[noc-2] = '\n';
--noc;
}
if( noc > 1 && cBuf[noc-2]=='\r' ) cBuf[--noc-1] = '\n';
}
}
/* Check for ^Z (anywhere in line) too, to act as EOF. */
while( iseg < noc ){
if( cBuf[iseg]==0x1a ){
consoleInfo.stdinEof = 1;
if( cBuf[iseg]=='\x1a' ){
noc = iseg; /* Chop ^Z and anything following. */
lend = 1; /* Counts as end of line too. */
break;
}
++iseg;
@ -384,9 +568,10 @@ SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){
}else break;
}
/* If got nothing, (after ^Z chop), must be at end-of-file. */
if( noc == 0 ) return 0;
cBuf[noc] = 0;
return cBuf;
if( noc > 0 ){
cBuf[noc] = 0;
return cBuf;
}else return 0;
# elif SHELL_CON_TRANSLATE==2
/* This is not done efficiently because it may never be used.
** Also, it is interactive input so it need not be fast. */
@ -432,7 +617,10 @@ SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn){
/* Treat ct as bona fide MBCS trailing byte, if valid. */
cBuf[nco+ntb] = ct;
nug = mbcsToUtf8InPlaceIfValid(cBuf+nco, 1+ntb, ncMax-nco-1, cicp);
nco += nug;
if( nug > 0 ){
nco += nug;
break;
}
}
if( ct < 0 ) break;
}

View File

@ -9,16 +9,16 @@
** May you share freely, never taking more than you give.
**
********************************************************************************
** This file exposes various interfaces used for console I/O by the
** SQLite project command-line tools. These interfaces are used at
** either source conglomeration time, compilation time, or run time.
** This file exposes various interfaces used for console and other I/O
** by the SQLite project command-line tools. These interfaces are used
** at either source conglomeration time, compilation time, or run time.
** This source provides for either inclusion into conglomerated,
** "single-source" forms or separate compilation then linking. (TBD)
** "single-source" forms or separate compilation then linking.
**
** Platform dependencies are "hidden" here by various stratagems so
** that, provided certain conditions are met, the programs using
** this source or object code compiled from it need no explicit
** conditional compilation in their source for their console I/O.
** that, provided certain conditions are met, the programs using this
** source or object code compiled from it need no explicit conditional
** compilation in their source for their console and stream I/O.
**
** The symbols and functionality exposed here are not a public API.
** This code may change in tandem with other project code as needed.
@ -36,34 +36,44 @@
#endif
/* Define enum for use with following function. */
typedef enum ConsoleStdConsStreams {
CSCS_NoConsole = 0,
CSCS_InConsole = 1, CSCS_OutConsole = 2, CSCS_ErrConsole = 4,
CSCS_AnyConsole = 0x7
} ConsoleStdConsStreams;
typedef enum StreamsAreConsole {
SAC_NoConsole = 0,
SAC_InConsole = 1, SAC_OutConsole = 2, SAC_ErrConsole = 4,
SAC_AnyConsole = 0x7
} StreamsAreConsole;
/*
** Classify the three standard I/O streams according to whether
** they are connected to a console attached to the process.
**
** Returns the bit-wise OR of CSCS_{In,Out,Err}Console values,
** or CSCS_NoConsole if none of the streams reaches a console.
** Returns the bit-wise OR of SAC_{In,Out,Err}Console values,
** or SAC_NoConsole if none of the streams reaches a console.
**
** This function should be called before any I/O is done with
** the given streams. As a side-effect, the given inputs are
** recorded so that later I/O operations on them may be done
** differently than the C library FILE* I/O would be done,
** iff the stream is used for the I/O functions that follow.
** iff the stream is used for the I/O functions that follow,
** and to support the ones that use an implicit stream.
**
** On some platforms, stream or console mode alteration (aka
** "Setup") may be made which is undone by consoleRestore().
*/
SQLITE_INTERNAL_LINKAGE StreamsAreConsole
consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr );
/* A usual call for convenience: */
#define SQLITE_STD_CONSOLE_INIT() consoleClassifySetup(stdin,stdout,stderr)
/*
** After an initial call to consoleClassifySetup(...), renew
** the same setup it effected. (A call not after is an error.)
** This will restore state altered by consoleRestore();
**
** Applications which run an inferior (child) process which
** inherits the same I/O streams may call this function after
** such a process exits to guard against console mode changes.
*/
SQLITE_INTERNAL_LINKAGE ConsoleStdConsStreams
consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr );
SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void);
/*
** Undo any side-effects left by consoleClassifySetup(...).
@ -71,8 +81,8 @@ consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr );
** This should be called after consoleClassifySetup() and
** before the process terminates normally. It is suitable
** for use with the atexit() C library procedure. After
** this call, no I/O should be done with the console
** until consoleClassifySetup(...) is called again.
** this call, no console I/O should be done until one of
** console{Classify or Renew}Setup(...) is called again.
**
** Applications which run an inferior (child) process that
** inherits the same I/O streams might call this procedure
@ -82,24 +92,49 @@ consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr );
SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void );
/*
** Render output like fprintf(). If the output is going to the
** Set stream to be used for the functions below which write
** to "the designated X stream", where X is Output or Error.
** Returns the previous value.
**
** Alternatively, pass the special value, invalidFileStream,
** to get the designated stream value without setting it.
**
** Before the designated streams are set, they default to
** those passed to consoleClassifySetup(...), and before
** that is called they default to stdout and stderr.
**
** It is error to close a stream so designated, then, without
** designating another, use the corresponding {o,e}Emit(...).
*/
SQLITE_INTERNAL_LINKAGE FILE *invalidFileStream;
SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf);
SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf);
/*
** Emit output like fprintf(). If the output is going to the
** console and translation from UTF-8 is necessary, perform
** the needed translation. Otherwise, write formatted output
** to the provided stream almost as-is, possibly with newline
** translation as specified by set{Binary,Text}Mode().
*/
SQLITE_INTERNAL_LINKAGE int fprintfUtf8(FILE *pfO, const char *zFormat, ...);
/* Like fprintfUtf8 except stream is always the recorded output. */
SQLITE_INTERNAL_LINKAGE int printfUtf8(const char *zFormat, ...);
SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...);
/* Like fPrintfUtf8 except stream is always the designated output. */
SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...);
/* Like fPrintfUtf8 except stream is always the designated error. */
SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...);
/*
** Render output like fputs(). If the output is going to the
** Emit output like fputs(). If the output is going to the
** console and translation from UTF-8 is necessary, perform
** the needed translation. Otherwise, write given text to the
** provided stream almost as-is, possibly with newline
** translation as specified by set{Binary,Text}Mode().
*/
SQLITE_INTERNAL_LINKAGE int fputsUtf8(const char *z, FILE *pfO);
SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO);
/* Like fPutsUtf8 except stream is always the designated output. */
SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z);
/* Like fPutsUtf8 except stream is always the designated error. */
SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z);
/*
** Collect input like fgets(...) with special provisions for input
@ -108,7 +143,9 @@ SQLITE_INTERNAL_LINKAGE int fputsUtf8(const char *z, FILE *pfO);
** translation may be done as set by set{Binary,Text}Mode(). As a
** convenience, pfIn==NULL is treated as stdin.
*/
SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn);
SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn);
/* Like fGetsUtf8 except stream is always the designated input. */
SQLITE_INTERNAL_LINKAGE char* iGetsUtf8(char *cBuf, int ncMax);
/*
** Set given stream for binary mode, where newline translation is
@ -126,10 +163,12 @@ SQLITE_INTERNAL_LINKAGE char* fgetsUtf8(char *cBuf, int ncMax, FILE *pfIn);
SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *, short bFlush);
SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *, short bFlush);
#if 0 /* For use with line editor. (not yet used) */
typedef struct Prompts {
int numPrompts;
const char **azPrompts;
} Prompts;
#endif
/*
** Macros for use of a line editor.

View File

@ -432,10 +432,8 @@ static Fts5HashEntry *fts5HashEntryMerge(
}
/*
** Extract all tokens from hash table iHash and link them into a list
** in sorted order. The hash table is cleared before returning. It is
** the responsibility of the caller to free the elements of the returned
** list.
** Link all tokens from hash table iHash into a list in sorted order. The
** tokens are not removed from the hash table.
*/
static int fts5HashEntrySort(
Fts5Hash *pHash,

View File

@ -2719,6 +2719,14 @@ static void fts5SegIterHashInit(
pLeaf->p = (u8*)pList;
}
}
/* The call to sqlite3Fts5HashScanInit() causes the hash table to
** fill the size field of all existing position lists. This means they
** can no longer be appended to. Since the only scenario in which they
** can be appended to is if the previous operation on this table was
** a DELETE, by clearing the Fts5Index.bDelete flag we can avoid this
** possibility altogether. */
p->bDelete = 0;
}else{
p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data),
(const char*)pTerm, nTerm, (void**)&pLeaf, &nList
@ -6204,7 +6212,7 @@ int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){
/* Flush the hash table to disk if required */
if( iRowid<p->iWriteRowid
|| (iRowid==p->iWriteRowid && p->bDelete==0)
|| (p->nPendingData > p->pConfig->nHashSize)
|| (p->nPendingData > p->pConfig->nHashSize)
){
fts5IndexFlush(p);
}

View File

@ -52,6 +52,36 @@ do_execsql_test 2.1 {
SELECT * FROM t2('to*');
} {top to tommy}
#-------------------------------------------------------------------------
foreach {tn newrowid} {
1 122
2 123
3 124
} {
reset_db
do_execsql_test 3.$tn.0 {
CREATE VIRTUAL TABLE t12 USING fts5(x);
INSERT INTO t12(rowid, x) VALUES(123, 'wwww');
}
do_execsql_test 3.$tn.1 {
BEGIN;
DELETE FROM t12 WHERE rowid=123;
SELECT * FROM t12('wwww*');
INSERT INTO t12(rowid, x) VALUES($newrowid, 'wwww');
SELECT * FROM t12('wwww*');
END;
} {wwww}
do_execsql_test 3.$tn.2 {
INSERT INTO t12(t12) VALUES('integrity-check');
}
do_execsql_test 3.$tn.3 {
SELECT rowid FROM t12('wwww*');
} $newrowid
}
finish_test
finish_test

View File

@ -35,6 +35,8 @@ $(dir.bld.c):
javac.flags ?= -Xlint:unchecked -Xlint:deprecation
java.flags ?=
javac.flags += -encoding utf8
# -------------^^^^^^^^^^^^^^ required for Windows builds
jnicheck ?= 1
ifeq (1,$(jnicheck))
java.flags += -Xcheck:jni
@ -110,6 +112,7 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\
WindowFunction.java \
XDestroyCallback.java \
sqlite3.java \
sqlite3_blob.java \
sqlite3_context.java \
sqlite3_stmt.java \
sqlite3_value.java \
@ -160,12 +163,13 @@ endif
CLASS_FILES :=
define CLASSFILE_DEPS
all: $(1).class
$(1).class: $(1).java
CLASS_FILES += $(1).class
endef
$(foreach B,$(basename \
$(JAVA_FILES.main) $(JAVA_FILES.unittest) $(JAVA_FILES.tester)),\
$(eval $(call CLASSFILE_DEPS,$(B))))
$(CLASS_FILES): $(JAVA_FILES) $(MAKEFILE)
$(CLASS_FILES): $(MAKEFILE)
$(bin.javac) $(javac.flags) -h $(dir.bld.c) -cp $(classpath) $(JAVA_FILES)
#.PHONY: classfiles
@ -227,7 +231,8 @@ SQLITE_OPT += -DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_OFFSET_SQL_FUNC \
-DSQLITE_ENABLE_PREUPDATE_HOOK \
-DSQLITE_ENABLE_NORMALIZE \
-DSQLITE_ENABLE_SQLLOG
-DSQLITE_ENABLE_SQLLOG \
-DSQLITE_ENABLE_COLUMN_METADATA
endif
ifeq (1,$(opt.debug))

View File

@ -185,6 +185,8 @@
**
** This use of intptr_t is the _only_ reason we require <stdint.h>
** which, in turn, requires building with -std=c99 (or later).
**
** See also: the notes for LongPtrGet_T.
*/
#define S3JniCast_L2P(JLongAsPtr) (void*)((intptr_t)(JLongAsPtr))
#define S3JniCast_P2L(PTR) (jlong)((intptr_t)(PTR))
@ -201,8 +203,8 @@
**
** https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#jni_interface_functions_and_pointers
*/
#define JniArgsEnvObj JNIEnv * const env, jobject jSelf
#define JniArgsEnvClass JNIEnv * const env, jclass jKlazz
#define JniArgsEnvObj JNIEnv * env, jobject jSelf
#define JniArgsEnvClass JNIEnv * env, jclass jKlazz
/*
** Helpers to account for -Xcheck:jni warnings about not having
** checked for exceptions.
@ -651,6 +653,17 @@ struct S3JniGlobalType {
jmethodID ctorLong1 /* the Long(long) constructor */;
jmethodID ctorStringBA /* the String(byte[],Charset) constructor */;
jmethodID stringGetBytes /* the String.getBytes(Charset) method */;
/*
ByteBuffer may or may not be supported via JNI on any given
platform:
https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#nio_support
We only store a ref to the following if JNI support for
ByteBuffer is available (which we determine during static init).
*/
jclass cByteBuffer /* global ref to java.nio.ByteBuffer */;
} g;
/*
** The list of Java-side auto-extensions
@ -1474,7 +1487,7 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject jNph,
#define PtrGet_sqlite3_stmt(OBJ) PtrGet_T(sqlite3_stmt, OBJ)
#define PtrGet_sqlite3_value(OBJ) PtrGet_T(sqlite3_value, OBJ)
/*
** S3JniLongPtr_T(X,Y) expects X to be an unqualified sqlite3 struct
** LongPtrGet_T(X,Y) expects X to be an unqualified sqlite3 struct
** type name and Y to be a native pointer to such an object in the
** form of a jlong value. The jlong is simply cast to (X*). This
** approach is, as of 2023-09-27, supplanting the former approach. We
@ -1482,13 +1495,22 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject jNph,
** the C side, because it's reportedly significantly faster. The
** intptr_t part here is necessary for compatibility with (at least)
** ARM32.
**
** 2023-11-09: testing has not revealed any measurable performance
** difference between the approach of passing type T to C compared to
** passing pointer-to-T to C, and adding support for the latter
** everywhere requires sigificantly more code. As of this writing, the
** older/simpler approach is being applied except for (A) where the
** newer approach has already been applied and (B) hot-spot APIs where
** a difference of microseconds (i.e. below our testing measurement
** threshold) might add up.
*/
#define S3JniLongPtr_T(T,JLongAsPtr) (T*)((intptr_t)(JLongAsPtr))
#define S3JniLongPtr_sqlite3(JLongAsPtr) S3JniLongPtr_T(sqlite3,JLongAsPtr)
#define S3JniLongPtr_sqlite3_backup(JLongAsPtr) S3JniLongPtr_T(sqlite3_backup,JLongAsPtr)
#define S3JniLongPtr_sqlite3_blob(JLongAsPtr) S3JniLongPtr_T(sqlite3_blob,JLongAsPtr)
#define S3JniLongPtr_sqlite3_stmt(JLongAsPtr) S3JniLongPtr_T(sqlite3_stmt,JLongAsPtr)
#define S3JniLongPtr_sqlite3_value(JLongAsPtr) S3JniLongPtr_T(sqlite3_value,JLongAsPtr)
#define LongPtrGet_T(T,JLongAsPtr) (T*)((intptr_t)(JLongAsPtr))
#define LongPtrGet_sqlite3(JLongAsPtr) LongPtrGet_T(sqlite3,JLongAsPtr)
#define LongPtrGet_sqlite3_backup(JLongAsPtr) LongPtrGet_T(sqlite3_backup,JLongAsPtr)
#define LongPtrGet_sqlite3_blob(JLongAsPtr) LongPtrGet_T(sqlite3_blob,JLongAsPtr)
#define LongPtrGet_sqlite3_stmt(JLongAsPtr) LongPtrGet_T(sqlite3_stmt,JLongAsPtr)
#define LongPtrGet_sqlite3_value(JLongAsPtr) LongPtrGet_T(sqlite3_value,JLongAsPtr)
/*
** Extracts the new S3JniDb instance from the free-list, or allocates
** one if needed, associates it with pDb, and returns. Returns NULL
@ -1547,7 +1569,7 @@ static void S3JniDb_xDestroy(void *p){
#define S3JniDb_from_c(sqlite3Ptr) \
((sqlite3Ptr) ? S3JniDb_from_clientdata(sqlite3Ptr) : 0)
#define S3JniDb_from_jlong(sqlite3PtrAsLong) \
S3JniDb_from_c(S3JniLongPtr_T(sqlite3,sqlite3PtrAsLong))
S3JniDb_from_c(LongPtrGet_T(sqlite3,sqlite3PtrAsLong))
/*
** Unref any Java-side state in (S3JniAutoExtension*) AX and zero out
@ -2052,12 +2074,12 @@ static void udf_xInverse(sqlite3_context* cx, int argc,
/** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*)). */
#define WRAP_INT_STMT(JniNameSuffix,CName) \
JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt){ \
return (jint)CName(S3JniLongPtr_sqlite3_stmt(jpStmt)); \
return (jint)CName(LongPtrGet_sqlite3_stmt(jpStmt)); \
}
/** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*,int)). */
#define WRAP_INT_STMT_INT(JniNameSuffix,CName) \
JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt, jint n){ \
return (jint)CName(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)n); \
return (jint)CName(LongPtrGet_sqlite3_stmt(jpStmt), (int)n); \
}
/** Create a trivial JNI wrapper for (boolean CName(sqlite3_stmt*)). */
#define WRAP_BOOL_STMT(JniNameSuffix,CName) \
@ -2068,41 +2090,41 @@ static void udf_xInverse(sqlite3_context* cx, int argc,
#define WRAP_STR_STMT_INT(JniNameSuffix,CName) \
JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt, jint ndx){ \
return s3jni_utf8_to_jstring( \
CName(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx), \
CName(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx), \
-1); \
}
/** Create a trivial JNI wrapper for (boolean CName(sqlite3*)). */
#define WRAP_BOOL_DB(JniNameSuffix,CName) \
JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \
return CName(S3JniLongPtr_sqlite3(jpDb)) ? JNI_TRUE : JNI_FALSE; \
return CName(LongPtrGet_sqlite3(jpDb)) ? JNI_TRUE : JNI_FALSE; \
}
/** Create a trivial JNI wrapper for (int CName(sqlite3*)). */
#define WRAP_INT_DB(JniNameSuffix,CName) \
JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \
return (jint)CName(S3JniLongPtr_sqlite3(jpDb)); \
return (jint)CName(LongPtrGet_sqlite3(jpDb)); \
}
/** Create a trivial JNI wrapper for (int64 CName(sqlite3*)). */
#define WRAP_INT64_DB(JniNameSuffix,CName) \
JniDecl(jlong,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \
return (jlong)CName(S3JniLongPtr_sqlite3(jpDb)); \
return (jlong)CName(LongPtrGet_sqlite3(jpDb)); \
}
/** Create a trivial JNI wrapper for (jstring CName(sqlite3*,int)). */
#define WRAP_STR_DB_INT(JniNameSuffix,CName) \
JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jlong jpDb, jint ndx){ \
return s3jni_utf8_to_jstring( \
CName(S3JniLongPtr_sqlite3(jpDb), (int)ndx), \
CName(LongPtrGet_sqlite3(jpDb), (int)ndx), \
-1); \
}
/** Create a trivial JNI wrapper for (int CName(sqlite3_value*)). */
#define WRAP_INT_SVALUE(JniNameSuffix,CName,DfltOnNull) \
JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpSValue){ \
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSValue); \
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSValue); \
return (jint)(sv ? CName(sv): DfltOnNull); \
}
/** Create a trivial JNI wrapper for (boolean CName(sqlite3_value*)). */
#define WRAP_BOOL_SVALUE(JniNameSuffix,CName,DfltOnNull) \
JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jlong jpSValue){ \
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSValue); \
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSValue); \
return (jint)(sv ? CName(sv) : DfltOnNull) \
? JNI_TRUE : JNI_FALSE; \
}
@ -2115,9 +2137,11 @@ WRAP_INT_STMT_INT(1column_1bytes16, sqlite3_column_bytes16)
WRAP_INT_STMT(1column_1count, sqlite3_column_count)
WRAP_STR_STMT_INT(1column_1decltype, sqlite3_column_decltype)
WRAP_STR_STMT_INT(1column_1name, sqlite3_column_name)
#ifdef SQLITE_ENABLE_COLUMN_METADATA
WRAP_STR_STMT_INT(1column_1database_1name, sqlite3_column_database_name)
WRAP_STR_STMT_INT(1column_1origin_1name, sqlite3_column_origin_name)
WRAP_STR_STMT_INT(1column_1table_1name, sqlite3_column_table_name)
#endif
WRAP_INT_STMT_INT(1column_1type, sqlite3_column_type)
WRAP_INT_STMT(1data_1count, sqlite3_data_count)
WRAP_STR_DB_INT(1db_1name, sqlite3_db_name)
@ -2315,7 +2339,7 @@ S3JniApi(sqlite3_backup_finish(),jint,1backup_1finish)(
){
int rc = 0;
if( jpBack!=0 ){
rc = sqlite3_backup_finish( S3JniLongPtr_sqlite3_backup(jpBack) );
rc = sqlite3_backup_finish( LongPtrGet_sqlite3_backup(jpBack) );
}
return rc;
}
@ -2324,8 +2348,8 @@ S3JniApi(sqlite3_backup_init(),jobject,1backup_1init)(
JniArgsEnvClass, jlong jpDbDest, jstring jTDest,
jlong jpDbSrc, jstring jTSrc
){
sqlite3 * const pDest = S3JniLongPtr_sqlite3(jpDbDest);
sqlite3 * const pSrc = S3JniLongPtr_sqlite3(jpDbSrc);
sqlite3 * const pDest = LongPtrGet_sqlite3(jpDbDest);
sqlite3 * const pSrc = LongPtrGet_sqlite3(jpDbSrc);
char * const zDest = s3jni_jstring_to_utf8(jTDest, 0);
char * const zSrc = s3jni_jstring_to_utf8(jTSrc, 0);
jobject rv = 0;
@ -2348,19 +2372,19 @@ S3JniApi(sqlite3_backup_init(),jobject,1backup_1init)(
S3JniApi(sqlite3_backup_pagecount(),jint,1backup_1pagecount)(
JniArgsEnvClass, jlong jpBack
){
return sqlite3_backup_pagecount(S3JniLongPtr_sqlite3_backup(jpBack));
return sqlite3_backup_pagecount(LongPtrGet_sqlite3_backup(jpBack));
}
S3JniApi(sqlite3_backup_remaining(),jint,1backup_1remaining)(
JniArgsEnvClass, jlong jpBack
){
return sqlite3_backup_remaining(S3JniLongPtr_sqlite3_backup(jpBack));
return sqlite3_backup_remaining(LongPtrGet_sqlite3_backup(jpBack));
}
S3JniApi(sqlite3_backup_step(),jint,1backup_1step)(
JniArgsEnvClass, jlong jpBack, jint nPage
){
return sqlite3_backup_step(S3JniLongPtr_sqlite3_backup(jpBack), (int)nPage);
return sqlite3_backup_step(LongPtrGet_sqlite3_backup(jpBack), (int)nPage);
}
S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)(
@ -2373,13 +2397,13 @@ S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)(
if( nMax>nBA ){
nMax = nBA;
}
rc = sqlite3_bind_blob(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx,
rc = sqlite3_bind_blob(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx,
pBuf, (int)nMax, SQLITE_TRANSIENT);
s3jni_jbyteArray_release(baData, pBuf);
}else{
rc = baData
? SQLITE_NOMEM
: sqlite3_bind_null( S3JniLongPtr_sqlite3_stmt(jpStmt), ndx );
: sqlite3_bind_null( LongPtrGet_sqlite3_stmt(jpStmt), ndx );
}
return (jint)rc;
}
@ -2387,20 +2411,20 @@ S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)(
S3JniApi(sqlite3_bind_double(),jint,1bind_1double)(
JniArgsEnvClass, jlong jpStmt, jint ndx, jdouble val
){
return (jint)sqlite3_bind_double(S3JniLongPtr_sqlite3_stmt(jpStmt),
return (jint)sqlite3_bind_double(LongPtrGet_sqlite3_stmt(jpStmt),
(int)ndx, (double)val);
}
S3JniApi(sqlite3_bind_int(),jint,1bind_1int)(
JniArgsEnvClass, jlong jpStmt, jint ndx, jint val
){
return (jint)sqlite3_bind_int(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx, (int)val);
return (jint)sqlite3_bind_int(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)val);
}
S3JniApi(sqlite3_bind_int64(),jint,1bind_1int64)(
JniArgsEnvClass, jlong jpStmt, jint ndx, jlong val
){
return (jint)sqlite3_bind_int64(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_int64)val);
return (jint)sqlite3_bind_int64(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_int64)val);
}
/*
@ -2409,7 +2433,7 @@ S3JniApi(sqlite3_bind_int64(),jint,1bind_1int64)(
S3JniApi(sqlite3_bind_java_object(),jint,1bind_1java_1object)(
JniArgsEnvClass, jlong jpStmt, jint ndx, jobject val
){
sqlite3_stmt * const pStmt = S3JniLongPtr_sqlite3_stmt(jpStmt);
sqlite3_stmt * const pStmt = LongPtrGet_sqlite3_stmt(jpStmt);
int rc = SQLITE_MISUSE;
if(pStmt){
@ -2429,13 +2453,13 @@ S3JniApi(sqlite3_bind_java_object(),jint,1bind_1java_1object)(
S3JniApi(sqlite3_bind_null(),jint,1bind_1null)(
JniArgsEnvClass, jlong jpStmt, jint ndx
){
return (jint)sqlite3_bind_null(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx);
return (jint)sqlite3_bind_null(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx);
}
S3JniApi(sqlite3_bind_parameter_count(),jint,1bind_1parameter_1count)(
JniArgsEnvClass, jlong jpStmt
){
return (jint)sqlite3_bind_parameter_count(S3JniLongPtr_sqlite3_stmt(jpStmt));
return (jint)sqlite3_bind_parameter_count(LongPtrGet_sqlite3_stmt(jpStmt));
}
S3JniApi(sqlite3_bind_parameter_index(),jint,1bind_1parameter_1index)(
@ -2444,7 +2468,7 @@ S3JniApi(sqlite3_bind_parameter_index(),jint,1bind_1parameter_1index)(
int rc = 0;
jbyte * const pBuf = s3jni_jbyteArray_bytes(jName);
if( pBuf ){
rc = sqlite3_bind_parameter_index(S3JniLongPtr_sqlite3_stmt(jpStmt),
rc = sqlite3_bind_parameter_index(LongPtrGet_sqlite3_stmt(jpStmt),
(const char *)pBuf);
s3jni_jbyteArray_release(jName, pBuf);
}
@ -2455,7 +2479,7 @@ S3JniApi(sqlite3_bind_parameter_name(),jstring,1bind_1parameter_1name)(
JniArgsEnvClass, jlong jpStmt, jint ndx
){
const char *z =
sqlite3_bind_parameter_name(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx);
sqlite3_bind_parameter_name(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx);
return z ? s3jni_utf8_to_jstring(z, -1) : 0;
}
@ -2477,14 +2501,14 @@ static int s3jni__bind_text(int is16, JNIEnv *env, jlong jpStmt, jint ndx,
such cases, we do not expose the byte-limit arguments in the
public API. */
rc = is16
? sqlite3_bind_text16(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx,
? sqlite3_bind_text16(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx,
pBuf, (int)nMax, SQLITE_TRANSIENT)
: sqlite3_bind_text(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx,
: sqlite3_bind_text(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx,
(const char *)pBuf,
(int)nMax, SQLITE_TRANSIENT);
}else{
rc = baData
? sqlite3_bind_null(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx)
? sqlite3_bind_null(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx)
: SQLITE_NOMEM;
}
s3jni_jbyteArray_release(baData, pBuf);
@ -2508,9 +2532,9 @@ S3JniApi(sqlite3_bind_value(),jint,1bind_1value)(
JniArgsEnvClass, jlong jpStmt, jint ndx, jlong jpValue
){
int rc = 0;
sqlite3_stmt * pStmt = S3JniLongPtr_sqlite3_stmt(jpStmt);
sqlite3_stmt * pStmt = LongPtrGet_sqlite3_stmt(jpStmt);
if( pStmt ){
sqlite3_value *v = S3JniLongPtr_sqlite3_value(jpValue);
sqlite3_value *v = LongPtrGet_sqlite3_value(jpValue);
if( v ){
rc = sqlite3_bind_value(pStmt, (int)ndx, v);
}else{
@ -2525,27 +2549,27 @@ S3JniApi(sqlite3_bind_value(),jint,1bind_1value)(
S3JniApi(sqlite3_bind_zeroblob(),jint,1bind_1zeroblob)(
JniArgsEnvClass, jlong jpStmt, jint ndx, jint n
){
return (jint)sqlite3_bind_zeroblob(S3JniLongPtr_sqlite3_stmt(jpStmt),
return (jint)sqlite3_bind_zeroblob(LongPtrGet_sqlite3_stmt(jpStmt),
(int)ndx, (int)n);
}
S3JniApi(sqlite3_bind_zeroblob64(),jint,1bind_1zeroblob64)(
JniArgsEnvClass, jlong jpStmt, jint ndx, jlong n
){
return (jint)sqlite3_bind_zeroblob64(S3JniLongPtr_sqlite3_stmt(jpStmt),
return (jint)sqlite3_bind_zeroblob64(LongPtrGet_sqlite3_stmt(jpStmt),
(int)ndx, (sqlite3_uint64)n);
}
S3JniApi(sqlite3_blob_bytes(),jint,1blob_1bytes)(
JniArgsEnvClass, jlong jpBlob
){
return sqlite3_blob_bytes(S3JniLongPtr_sqlite3_blob(jpBlob));
return sqlite3_blob_bytes(LongPtrGet_sqlite3_blob(jpBlob));
}
S3JniApi(sqlite3_blob_close(),jint,1blob_1close)(
JniArgsEnvClass, jlong jpBlob
){
sqlite3_blob * const b = S3JniLongPtr_sqlite3_blob(jpBlob);
sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob);
return b ? (jint)sqlite3_blob_close(b) : SQLITE_MISUSE;
}
@ -2553,7 +2577,7 @@ S3JniApi(sqlite3_blob_open(),jint,1blob_1open)(
JniArgsEnvClass, jlong jpDb, jstring jDbName, jstring jTbl, jstring jCol,
jlong jRowId, jint flags, jobject jOut
){
sqlite3 * const db = S3JniLongPtr_sqlite3(jpDb);
sqlite3 * const db = LongPtrGet_sqlite3(jpDb);
sqlite3_blob * pBlob = 0;
char * zDbName = 0, * zTableName = 0, * zColumnName = 0;
int rc;
@ -2587,7 +2611,7 @@ S3JniApi(sqlite3_blob_read(),jint,1blob_1read)(
int rc = jTgt ? (pBa ? SQLITE_MISUSE : SQLITE_NOMEM) : SQLITE_MISUSE;
if( pBa ){
jsize const nTgt = (*env)->GetArrayLength(env, jTgt);
rc = sqlite3_blob_read(S3JniLongPtr_sqlite3_blob(jpBlob), pBa,
rc = sqlite3_blob_read(LongPtrGet_sqlite3_blob(jpBlob), pBa,
(int)nTgt, (int)iOffset);
if( 0==rc ){
s3jni_jbyteArray_commit(jTgt, pBa);
@ -2601,14 +2625,14 @@ S3JniApi(sqlite3_blob_read(),jint,1blob_1read)(
S3JniApi(sqlite3_blob_reopen(),jint,1blob_1reopen)(
JniArgsEnvClass, jlong jpBlob, jlong iNewRowId
){
return (jint)sqlite3_blob_reopen(S3JniLongPtr_sqlite3_blob(jpBlob),
return (jint)sqlite3_blob_reopen(LongPtrGet_sqlite3_blob(jpBlob),
(sqlite3_int64)iNewRowId);
}
S3JniApi(sqlite3_blob_write(),jint,1blob_1write)(
JniArgsEnvClass, jlong jpBlob, jbyteArray jBa, jint iOffset
){
sqlite3_blob * const b = S3JniLongPtr_sqlite3_blob(jpBlob);
sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob);
jbyte * const pBuf = b ? s3jni_jbyteArray_bytes(jBa) : 0;
const jsize nBA = pBuf ? (*env)->GetArrayLength(env, jBa) : 0;
int rc = SQLITE_MISUSE;
@ -2817,7 +2841,7 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)(
}else{
jclass const klazz = (*env)->GetObjectClass(env, jHook);
jmethodID const xCallback = (*env)->GetMethodID(
env, klazz, "call", "(Lorg/sqlite/jni/capi/sqlite3;ILjava/lang/String;)I"
env, klazz, "call", "(Lorg/sqlite/jni/capi/sqlite3;ILjava/lang/String;)V"
);
S3JniUnrefLocal(klazz);
S3JniIfThrew {
@ -2870,7 +2894,7 @@ S3JniApi(sqlite3_column_int64(),jlong,1column_1int64)(
S3JniApi(sqlite3_column_java_object(),jobject,1column_1java_1object)(
JniArgsEnvClass, jlong jpStmt, jint ndx
){
sqlite3_stmt * const stmt = S3JniLongPtr_sqlite3_stmt(jpStmt);
sqlite3_stmt * const stmt = LongPtrGet_sqlite3_stmt(jpStmt);
jobject rv = 0;
if( stmt ){
sqlite3 * const db = sqlite3_db_handle(stmt);
@ -2942,7 +2966,10 @@ static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){
? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback)
: (int)((*env)->CallVoidMethod(env, hook.jObj, hook.midCallback), 0);
S3JniIfThrew{
rc = s3jni_db_exception(ps->pDb, SQLITE_ERROR, "hook callback threw");
rc = s3jni_db_exception(ps->pDb, SQLITE_ERROR,
isCommit
? "Commit hook callback threw"
: "Rollback hook callback threw");
}
S3JniHook_localundup(hook);
}
@ -3045,7 +3072,7 @@ S3JniApi(sqlite3_compileoption_used(),jboolean,1compileoption_1used)(
return rc;
}
S3JniApi(sqlite3_complete(),int,1complete)(
S3JniApi(sqlite3_complete(),jint,1complete)(
JniArgsEnvClass, jbyteArray jSql
){
jbyte * const pBuf = s3jni_jbyteArray_bytes(jSql);
@ -3429,7 +3456,6 @@ S3JniApi(
}
break;
}
case 0:
default:
rc = SQLITE_MISUSE;
}
@ -3492,7 +3518,7 @@ S3JniApi(sqlite3_db_readonly(),jint,1db_1readonly)(
return (jint)rc;
}
S3JniApi(sqlite3_db_release_memory(),int,1db_1release_1memory)(
S3JniApi(sqlite3_db_release_memory(),jint,1db_1release_1memory)(
JniArgsEnvClass, jobject jDb
){
sqlite3 * const pDb = PtrGet_sqlite3(jDb);
@ -3594,19 +3620,21 @@ S3JniApi(sqlite3_normalized_sql(),jstring,1normalized_1sql)(
#endif
}
S3JniApi(sqlite3_extended_result_codes(),jboolean,1extended_1result_1codes)(
S3JniApi(sqlite3_extended_result_codes(),jint,1extended_1result_1codes)(
JniArgsEnvClass, jobject jpDb, jboolean onoff
){
sqlite3 * const pDb = PtrGet_sqlite3(jpDb);
int const rc = pDb ? sqlite3_extended_result_codes(pDb, onoff ? 1 : 0) : 0;
return rc ? JNI_TRUE : JNI_FALSE;
int const rc = pDb
? sqlite3_extended_result_codes(pDb, onoff ? 1 : 0)
: SQLITE_MISUSE;
return rc;
}
S3JniApi(sqlite3_finalize(),jint,1finalize)(
JniArgsEnvClass, jlong jpStmt
){
return jpStmt
? sqlite3_finalize(S3JniLongPtr_sqlite3_stmt(jpStmt))
? sqlite3_finalize(LongPtrGet_sqlite3_stmt(jpStmt))
: 0;
}
@ -3655,6 +3683,11 @@ JniDecl(jboolean,1java_1uncache_1thread)(JniArgsEnvClass){
return rc ? JNI_TRUE : JNI_FALSE;
}
JniDecl(jboolean,1jni_1supports_1nio)(JniArgsEnvClass){
return SJG.g.cByteBuffer ? JNI_TRUE : JNI_FALSE;
}
S3JniApi(sqlite3_keyword_check(),jboolean,1keyword_1check)(
JniArgsEnvClass, jstring jWord
){
@ -3835,7 +3868,7 @@ jint sqlite3_jni_prepare_v123( int prepVersion, JNIEnv * const env, jclass self,
sqlite3_stmt * pStmt = 0;
jobject jStmt = 0;
const char * zTail = 0;
sqlite3 * const pDb = S3JniLongPtr_sqlite3(jpDb);
sqlite3 * const pDb = LongPtrGet_sqlite3(jpDb);
jbyte * const pBuf = pDb ? s3jni_jbyteArray_bytes(baSql) : 0;
int rc = SQLITE_ERROR;
@ -3990,11 +4023,11 @@ static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb,
#if !defined(SQLITE_ENABLE_PREUPDATE_HOOK)
/* We need no-op impls for preupdate_{count,depth,blobwrite}() */
S3JniApi(sqlite3_preupdate_blobwrite(),int,1preupdate_1blobwrite)(
S3JniApi(sqlite3_preupdate_blobwrite(),jint,1preupdate_1blobwrite)(
JniArgsEnvClass, jlong jDb){ return SQLITE_MISUSE; }
S3JniApi(sqlite3_preupdate_count(),int,1preupdate_1count)(
S3JniApi(sqlite3_preupdate_count(),jint,1preupdate_1count)(
JniArgsEnvClass, jlong jDb){ return SQLITE_MISUSE; }
S3JniApi(sqlite3_preupdate_depth(),int,1preupdate_1depth)(
S3JniApi(sqlite3_preupdate_depth(),jint,1preupdate_1depth)(
JniArgsEnvClass, jlong jDb){ return SQLITE_MISUSE; }
#endif /* !SQLITE_ENABLE_PREUPDATE_HOOK */
@ -4089,7 +4122,7 @@ S3JniApi(sqlite3_preupdate_hook(),jobject,1preupdate_1hook)(
static int s3jni_preupdate_newold(JNIEnv * const env, int isNew, jlong jpDb,
jint iCol, jobject jOut){
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
sqlite3 * const pDb = S3JniLongPtr_sqlite3(jpDb);
sqlite3 * const pDb = LongPtrGet_sqlite3(jpDb);
int rc = SQLITE_MISUSE;
if( pDb ){
sqlite3_value * pOut = 0;
@ -4310,7 +4343,7 @@ S3JniApi(sqlite3_result_double(),void,1result_1double)(
}
S3JniApi(sqlite3_result_error(),void,1result_1error)(
JniArgsEnvClass, jobject jpCx, jbyteArray baMsg, int eTextRep
JniArgsEnvClass, jobject jpCx, jbyteArray baMsg, jint eTextRep
){
const char * zUnspecified = "Unspecified error.";
jsize const baLen = (*env)->GetArrayLength(env, baMsg);
@ -4570,20 +4603,8 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)(
S3JniEnv_uncache( SJG.envCache.aHead->env );
}
} S3JniEnv_mutex_leave;
#if 0
/*
** Is automatically closing any still-open dbs a good idea? We will
** get rid of the perDb list once sqlite3 gets a per-db client
** state, at which point we won't have a central list of databases
** to close.
*/
S3JniDb_mutex_enter;
while( SJG.perDb.pHead ){
s3jni_close_db(env, SJG.perDb.pHead->jDb, 2);
}
S3JniDb_mutex_leave;
#endif
/* Do not clear S3JniGlobal.jvm: it's legal to restart the lib. */
/* Do not clear S3JniGlobal.jvm or S3JniGlobal.g: it's legal to
** restart the lib. */
return sqlite3_shutdown();
}
@ -4664,13 +4685,13 @@ S3JniApi(sqlite3_sql(),jstring,1sql)(
}
S3JniApi(sqlite3_step(),jint,1step)(
JniArgsEnvClass,jobject jStmt
JniArgsEnvClass, jlong jpStmt
){
sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jStmt);
sqlite3_stmt * const pStmt = LongPtrGet_sqlite3_stmt(jpStmt);
return pStmt ? (jint)sqlite3_step(pStmt) : (jint)SQLITE_MISUSE;
}
S3JniApi(sqlite3_table_column_metadata(),int,1table_1column_1metadata)(
S3JniApi(sqlite3_table_column_metadata(),jint,1table_1column_1metadata)(
JniArgsEnvClass, jobject jDb, jstring jDbName, jstring jTableName,
jstring jColumnName, jobject jDataType, jobject jCollSeq, jobject jNotNull,
jobject jPrimaryKey, jobject jAutoinc
@ -4846,7 +4867,7 @@ S3JniApi(sqlite3_update_hook(),jobject,1update_1hook)(
S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)(
JniArgsEnvClass, jlong jpSVal
){
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal);
const jbyte * pBytes = sv ? sqlite3_value_blob(sv) : 0;
int const nLen = pBytes ? sqlite3_value_bytes(sv) : 0;
@ -4856,17 +4877,17 @@ S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)(
: NULL;
}
S3JniApi(sqlite3_value_bytes(),int,1value_1bytes)(
S3JniApi(sqlite3_value_bytes(),jint,1value_1bytes)(
JniArgsEnvClass, jlong jpSVal
){
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal);
return sv ? sqlite3_value_bytes(sv) : 0;
}
S3JniApi(sqlite3_value_bytes16(),int,1value_1bytes16)(
S3JniApi(sqlite3_value_bytes16(),jint,1value_1bytes16)(
JniArgsEnvClass, jlong jpSVal
){
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal);
return sv ? sqlite3_value_bytes16(sv) : 0;
}
@ -4874,7 +4895,7 @@ S3JniApi(sqlite3_value_bytes16(),int,1value_1bytes16)(
S3JniApi(sqlite3_value_double(),jdouble,1value_1double)(
JniArgsEnvClass, jlong jpSVal
){
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal);
return (jdouble) (sv ? sqlite3_value_double(sv) : 0.0);
}
@ -4882,7 +4903,7 @@ S3JniApi(sqlite3_value_double(),jdouble,1value_1double)(
S3JniApi(sqlite3_value_dup(),jobject,1value_1dup)(
JniArgsEnvClass, jlong jpSVal
){
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal);
sqlite3_value * const sd = sv ? sqlite3_value_dup(sv) : 0;
jobject rv = sd ? new_java_sqlite3_value(env, sd) : 0;
if( sd && !rv ) {
@ -4895,7 +4916,7 @@ S3JniApi(sqlite3_value_dup(),jobject,1value_1dup)(
S3JniApi(sqlite3_value_free(),void,1value_1free)(
JniArgsEnvClass, jlong jpSVal
){
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal);
if( sv ){
sqlite3_value_free(sv);
}
@ -4904,21 +4925,21 @@ S3JniApi(sqlite3_value_free(),void,1value_1free)(
S3JniApi(sqlite3_value_int(),jint,1value_1int)(
JniArgsEnvClass, jlong jpSVal
){
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal);
return (jint) (sv ? sqlite3_value_int(sv) : 0);
}
S3JniApi(sqlite3_value_int64(),jlong,1value_1int64)(
JniArgsEnvClass, jlong jpSVal
){
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal);
return (jlong) (sv ? sqlite3_value_int64(sv) : 0LL);
}
S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)(
JniArgsEnvClass, jlong jpSVal
){
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal);
return sv
? sqlite3_value_pointer(sv, s3jni__value_jref_key)
: 0;
@ -4927,7 +4948,7 @@ S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)(
S3JniApi(sqlite3_value_text(),jbyteArray,1value_1text)(
JniArgsEnvClass, jlong jpSVal
){
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal);
const unsigned char * const p = sv ? sqlite3_value_text(sv) : 0;
int const n = p ? sqlite3_value_bytes(sv) : 0;
return p ? s3jni_new_jbyteArray(p, n) : 0;
@ -4938,7 +4959,7 @@ S3JniApi(sqlite3_value_text(),jbyteArray,1value_1text)(
S3JniApi(sqlite3_value_text(),jstring,1value_1text)(
JniArgsEnvClass, jlong jpSVal
){
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal);
const unsigned char * const p = sv ? sqlite3_value_text(sv) : 0;
int const n = p ? sqlite3_value_bytes(sv) : 0;
return p ? s3jni_utf8_to_jstring( (const char *)p, n) : 0;
@ -4948,7 +4969,7 @@ S3JniApi(sqlite3_value_text(),jstring,1value_1text)(
S3JniApi(sqlite3_value_text16(),jstring,1value_1text16)(
JniArgsEnvClass, jlong jpSVal
){
sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal);
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal);
const int n = sv ? sqlite3_value_bytes16(sv) : 0;
const void * const p = sv ? sqlite3_value_text16(sv) : 0;
return p ? s3jni_text16_to_jstring(env, p, n) : 0;
@ -5530,7 +5551,7 @@ JniDeclFtsXA(jlong,xRowid)(JniArgsEnvObj,jobject jCtx){
return (jlong)ext->xRowid(PtrGet_Fts5Context(jCtx));
}
JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){
JniDeclFtsXA(jint,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){
Fts5ExtDecl;
int rc;
S3JniFts5AuxData * pAux;
@ -5924,6 +5945,19 @@ Java_org_sqlite_jni_capi_CApi_init(JniArgsEnvClass){
s3jni_oom_fatal( SJG.metrics.mutex );
#endif
{
/* Test whether this JVM supports direct memory access via
ByteBuffer. */
unsigned char buf[16] = {0};
jobject bb = (*env)->NewDirectByteBuffer(env, buf, 16);
if( bb ){
SJG.g.cByteBuffer = (*env)->GetObjectClass(env, bb);
S3JniUnrefLocal(bb);
}else{
SJG.g.cByteBuffer = 0;
}
}
sqlite3_shutdown()
/* So that it becomes legal for Java-level code to call
** sqlite3_config(). */;

View File

@ -427,8 +427,6 @@ extern "C" {
#define org_sqlite_jni_capi_CApi_SQLITE_OPEN_EXRESCODE 33554432L
#undef org_sqlite_jni_capi_CApi_SQLITE_PREPARE_PERSISTENT
#define org_sqlite_jni_capi_CApi_SQLITE_PREPARE_PERSISTENT 1L
#undef org_sqlite_jni_capi_CApi_SQLITE_PREPARE_NORMALIZE
#define org_sqlite_jni_capi_CApi_SQLITE_PREPARE_NORMALIZE 2L
#undef org_sqlite_jni_capi_CApi_SQLITE_PREPARE_NO_VTAB
#define org_sqlite_jni_capi_CApi_SQLITE_PREPARE_NO_VTAB 4L
#undef org_sqlite_jni_capi_CApi_SQLITE_OK
@ -707,8 +705,12 @@ extern "C" {
#define org_sqlite_jni_capi_CApi_SQLITE_DETERMINISTIC 2048L
#undef org_sqlite_jni_capi_CApi_SQLITE_DIRECTONLY
#define org_sqlite_jni_capi_CApi_SQLITE_DIRECTONLY 524288L
#undef org_sqlite_jni_capi_CApi_SQLITE_SUBTYPE
#define org_sqlite_jni_capi_CApi_SQLITE_SUBTYPE 1048576L
#undef org_sqlite_jni_capi_CApi_SQLITE_INNOCUOUS
#define org_sqlite_jni_capi_CApi_SQLITE_INNOCUOUS 2097152L
#undef org_sqlite_jni_capi_CApi_SQLITE_RESULT_SUBTYPE
#define org_sqlite_jni_capi_CApi_SQLITE_RESULT_SUBTYPE 16777216L
#undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_SCAN_UNIQUE
#define org_sqlite_jni_capi_CApi_SQLITE_INDEX_SCAN_UNIQUE 1L
#undef org_sqlite_jni_capi_CApi_SQLITE_INDEX_CONSTRAINT_EQ
@ -775,6 +777,14 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_init
JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1java_1uncache_1thread
(JNIEnv *, jclass);
/*
* Class: org_sqlite_jni_capi_CApi
* Method: sqlite3_jni_supports_nio
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1jni_1supports_1nio
(JNIEnv *, jclass);
/*
* Class: org_sqlite_jni_capi_CApi
* Method: sqlite3_aggregate_context
@ -1402,9 +1412,9 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1extended_1errcode
/*
* Class: org_sqlite_jni_capi_CApi
* Method: sqlite3_extended_result_codes
* Signature: (Lorg/sqlite/jni/capi/sqlite3;Z)Z
* Signature: (Lorg/sqlite/jni/capi/sqlite3;Z)I
*/
JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1extended_1result_1codes
JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1extended_1result_1codes
(JNIEnv *, jclass, jobject, jboolean);
/*
@ -1866,10 +1876,10 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1status64
/*
* Class: org_sqlite_jni_capi_CApi
* Method: sqlite3_step
* Signature: (Lorg/sqlite/jni/capi/sqlite3_stmt;)I
* Signature: (J)I
*/
JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1step
(JNIEnv *, jclass, jobject);
(JNIEnv *, jclass, jlong);
/*
* Class: org_sqlite_jni_capi_CApi

View File

@ -18,12 +18,12 @@ package org.sqlite.jni.annotation;
null or point to closed/finalized C-side resources.
<p>In the case of Java types which map directly to C struct types
(e.g. {@link org.sqlite.jni.sqlite3}, {@link
org.sqlite.jni.sqlite3_stmt}, and {@link
org.sqlite.jni.sqlite3_context}), a closed/finalized resource is
also considered to be null for purposes this annotation because the
C-side effect of passing such a handle is the same as if null is
passed.</p>
(e.g. {@link org.sqlite.jni.capi.sqlite3}, {@link
org.sqlite.jni.capi.sqlite3_stmt}, and {@link
org.sqlite.jni.capi.sqlite3_context}), a closed/finalized resource
is also considered to be null for purposes this annotation because
the C-side effect of passing such a handle is the same as if null
is passed.</p>
<p>When used in the context of Java interfaces which are called
from the C APIs, this annotation communicates that the C API will
@ -31,12 +31,23 @@ package org.sqlite.jni.annotation;
<p>Passing a null, for this annotation's definition of null, for
any parameter marked with this annoation specifically invokes
undefined behavior.</p>
undefined behavior (see below).</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>
behavior (see below). Such values are treated as C pointers in the
JNI layer.</p>
<p><b>Undefined behaviour:</b> the JNI build uses the {@code
SQLITE_ENABLE_API_ARMOR} build flag, meaning that the C code
invoked with invalid NULL pointers and the like will not invoke
undefined behavior in the conventional C sense, but may, for
example, return result codes which are not documented for the
affected APIs or may otherwise behave unpredictably. In no known
cases will such arguments result in C-level code dereferencing a
NULL pointer or accessing out-of-bounds (or otherwise invalid)
memory. In other words, they may cause unexpected behavior but
should never cause an outright crash or security issue.</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
@ -48,7 +59,7 @@ package org.sqlite.jni.annotation;
code.</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
org.sqlite.jni 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>

View File

@ -20,7 +20,8 @@ import org.sqlite.jni.annotation.*;
public interface AuthorizerCallback extends CallbackProxy {
/**
Must function as described for the C-level
sqlite3_set_authorizer() callback.
sqlite3_set_authorizer() callback. If it throws, the error is
converted to a db-level error and the exception is suppressed.
*/
int call(int opId, @Nullable String s1, @Nullable String s2,
@Nullable String s3, @Nullable String s4);

View File

@ -123,6 +123,12 @@ public final class CApi {
*/
public static native boolean sqlite3_java_uncache_thread();
/**
Returns true if this JVM has JNI-level support for direct memory
access using java.nio.ByteBuffer, else returns false.
*/
public static native boolean sqlite3_jni_supports_nio();
//////////////////////////////////////////////////////////////////////
// Maintenance reminder: please keep the sqlite3_.... functions
// alphabetized. The SQLITE_... values. on the other hand, are
@ -164,13 +170,13 @@ public final class CApi {
*/
public static native int sqlite3_auto_extension(@NotNull AutoExtensionCallback callback);
static native int sqlite3_backup_finish(@NotNull long ptrToBackup);
private static native int sqlite3_backup_finish(@NotNull long ptrToBackup);
public static int sqlite3_backup_finish(@NotNull sqlite3_backup b){
return sqlite3_backup_finish(b.clearNativePointer());
return null==b ? 0 : sqlite3_backup_finish(b.clearNativePointer());
}
static native sqlite3_backup sqlite3_backup_init(
private static native sqlite3_backup sqlite3_backup_init(
@NotNull long ptrToDbDest, @NotNull String destTableName,
@NotNull long ptrToDbSrc, @NotNull String srcTableName
);
@ -183,25 +189,25 @@ public final class CApi {
dbSrc.getNativePointer(), srcTableName );
}
static native int sqlite3_backup_pagecount(@NotNull long ptrToBackup);
private static native int sqlite3_backup_pagecount(@NotNull long ptrToBackup);
public static int sqlite3_backup_pagecount(@NotNull sqlite3_backup b){
return sqlite3_backup_pagecount(b.getNativePointer());
}
static native int sqlite3_backup_remaining(@NotNull long ptrToBackup);
private static native int sqlite3_backup_remaining(@NotNull long ptrToBackup);
public static int sqlite3_backup_remaining(@NotNull sqlite3_backup b){
return sqlite3_backup_remaining(b.getNativePointer());
}
static native int sqlite3_backup_step(@NotNull long ptrToBackup, int nPage);
private static native int sqlite3_backup_step(@NotNull long ptrToBackup, int nPage);
public static int sqlite3_backup_step(@NotNull sqlite3_backup b, int nPage){
return sqlite3_backup_step(b.getNativePointer(), nPage);
}
static native int sqlite3_bind_blob(
private static native int sqlite3_bind_blob(
@NotNull long ptrToStmt, int ndx, @Nullable byte[] data, int n
);
@ -223,7 +229,7 @@ public final class CApi {
: sqlite3_bind_blob(stmt.getNativePointer(), ndx, data, data.length);
}
static native int sqlite3_bind_double(
private static native int sqlite3_bind_double(
@NotNull long ptrToStmt, int ndx, double v
);
@ -233,7 +239,7 @@ public final class CApi {
return sqlite3_bind_double(stmt.getNativePointer(), ndx, v);
}
static native int sqlite3_bind_int(
private static native int sqlite3_bind_int(
@NotNull long ptrToStmt, int ndx, int v
);
@ -243,7 +249,7 @@ public final class CApi {
return sqlite3_bind_int(stmt.getNativePointer(), ndx, v);
}
static native int sqlite3_bind_int64(
private static native int sqlite3_bind_int64(
@NotNull long ptrToStmt, int ndx, long v
);
@ -251,7 +257,7 @@ public final class CApi {
return sqlite3_bind_int64( stmt.getNativePointer(), ndx, v );
}
static native int sqlite3_bind_java_object(
private static native int sqlite3_bind_java_object(
@NotNull long ptrToStmt, int ndx, @Nullable Object o
);
@ -267,13 +273,13 @@ public final class CApi {
return sqlite3_bind_java_object(stmt.getNativePointer(), ndx, o);
}
static native int sqlite3_bind_null(@NotNull long ptrToStmt, int ndx);
private static native int sqlite3_bind_null(@NotNull long ptrToStmt, int ndx);
public static int sqlite3_bind_null(@NotNull sqlite3_stmt stmt, int ndx){
return sqlite3_bind_null(stmt.getNativePointer(), ndx);
}
static native int sqlite3_bind_parameter_count(@NotNull long ptrToStmt);
private static native int sqlite3_bind_parameter_count(@NotNull long ptrToStmt);
public static int sqlite3_bind_parameter_count(@NotNull sqlite3_stmt stmt){
return sqlite3_bind_parameter_count(stmt.getNativePointer());
@ -300,7 +306,7 @@ public final class CApi {
return null==utf8 ? 0 : sqlite3_bind_parameter_index(stmt.getNativePointer(), utf8);
}
static native String sqlite3_bind_parameter_name(
private static native String sqlite3_bind_parameter_name(
@NotNull long ptrToStmt, int index
);
@ -308,7 +314,7 @@ public final class CApi {
return sqlite3_bind_parameter_name(stmt.getNativePointer(), index);
}
static native int sqlite3_bind_text(
private static native int sqlite3_bind_text(
@NotNull long ptrToStmt, int ndx, @Nullable byte[] utf8, int maxBytes
);
@ -352,7 +358,7 @@ public final class CApi {
: sqlite3_bind_text(stmt.getNativePointer(), ndx, utf8, utf8.length);
}
static native int sqlite3_bind_text16(
private static native int sqlite3_bind_text16(
@NotNull long ptrToStmt, int ndx, @Nullable byte[] data, int maxBytes
);
@ -393,7 +399,7 @@ public final class CApi {
: sqlite3_bind_text16(stmt.getNativePointer(), ndx, data, data.length);
}
static native int sqlite3_bind_value(@NotNull long ptrToStmt, int ndx, long ptrToValue);
private static native int sqlite3_bind_value(@NotNull long ptrToStmt, int ndx, long ptrToValue);
/**
Functions like the C-level sqlite3_bind_value(), or
@ -404,13 +410,13 @@ public final class CApi {
null==val ? 0L : val.getNativePointer());
}
static native int sqlite3_bind_zeroblob(@NotNull long ptrToStmt, int ndx, int n);
private static native int sqlite3_bind_zeroblob(@NotNull long ptrToStmt, int ndx, int n);
public static int sqlite3_bind_zeroblob(@NotNull sqlite3_stmt stmt, int ndx, int n){
return sqlite3_bind_zeroblob(stmt.getNativePointer(), ndx, n);
}
static native int sqlite3_bind_zeroblob64(
private static native int sqlite3_bind_zeroblob64(
@NotNull long ptrToStmt, int ndx, long n
);
@ -418,19 +424,19 @@ public final class CApi {
return sqlite3_bind_zeroblob64(stmt.getNativePointer(), ndx, n);
}
static native int sqlite3_blob_bytes(@NotNull long ptrToBlob);
private static native int sqlite3_blob_bytes(@NotNull long ptrToBlob);
public static int sqlite3_blob_bytes(@NotNull sqlite3_blob blob){
return sqlite3_blob_bytes(blob.getNativePointer());
}
static native int sqlite3_blob_close(@Nullable long ptrToBlob);
private static native int sqlite3_blob_close(@Nullable long ptrToBlob);
public static int sqlite3_blob_close(@Nullable sqlite3_blob blob){
return sqlite3_blob_close(blob.clearNativePointer());
return null==blob ? 0 : sqlite3_blob_close(blob.clearNativePointer());
}
static native int sqlite3_blob_open(
private static native int sqlite3_blob_open(
@NotNull long ptrToDb, @NotNull String dbName,
@NotNull String tableName, @NotNull String columnName,
long iRow, int flags, @NotNull OutputPointer.sqlite3_blob out
@ -458,7 +464,7 @@ public final class CApi {
return out.take();
};
static native int sqlite3_blob_read(
private static native int sqlite3_blob_read(
@NotNull long ptrToBlob, @NotNull byte[] target, int iOffset
);
@ -468,7 +474,7 @@ public final class CApi {
return sqlite3_blob_read(b.getNativePointer(), target, iOffset);
}
static native int sqlite3_blob_reopen(
private static native int sqlite3_blob_reopen(
@NotNull long ptrToBlob, long newRowId
);
@ -476,7 +482,7 @@ public final class CApi {
return sqlite3_blob_reopen(b.getNativePointer(), newRowId);
}
static native int sqlite3_blob_write(
private static native int sqlite3_blob_write(
@NotNull long ptrToBlob, @NotNull byte[] bytes, int iOffset
);
@ -486,7 +492,7 @@ public final class CApi {
return sqlite3_blob_write(b.getNativePointer(), bytes, iOffset);
}
static native int sqlite3_busy_handler(
private static native int sqlite3_busy_handler(
@NotNull long ptrToDb, @Nullable BusyHandlerCallback handler
);
@ -501,7 +507,7 @@ public final class CApi {
return sqlite3_busy_handler(db.getNativePointer(), handler);
}
static native int sqlite3_busy_timeout(@NotNull long ptrToDb, int ms);
private static native int sqlite3_busy_timeout(@NotNull long ptrToDb, int ms);
public static int sqlite3_busy_timeout(@NotNull sqlite3 db, int ms){
return sqlite3_busy_timeout(db.getNativePointer(), ms);
@ -511,64 +517,59 @@ public final class CApi {
@NotNull AutoExtensionCallback ax
);
static native int sqlite3_changes(@NotNull long ptrToDb);
private static native int sqlite3_changes(@NotNull long ptrToDb);
public static int sqlite3_changes(@NotNull sqlite3 db){
return sqlite3_changes(db.getNativePointer());
}
static native long sqlite3_changes64(@NotNull long ptrToDb);
private static native long sqlite3_changes64(@NotNull long ptrToDb);
public static long sqlite3_changes64(@NotNull sqlite3 db){
return sqlite3_changes64(db.getNativePointer());
}
static native int sqlite3_clear_bindings(@NotNull long ptrToStmt);
private static native int sqlite3_clear_bindings(@NotNull long ptrToStmt);
public static int sqlite3_clear_bindings(@NotNull sqlite3_stmt stmt){
return sqlite3_clear_bindings(stmt.getNativePointer());
}
static native int sqlite3_close(@Nullable long ptrToDb);
private static native int sqlite3_close(@Nullable long ptrToDb);
public static int sqlite3_close(@Nullable sqlite3 db){
int rc = 0;
if( null!=db ){
rc = sqlite3_close(db.getNativePointer());
if( 0==rc ) db.clearNativePointer();
}
return rc;
return null==db ? 0 : sqlite3_close(db.clearNativePointer());
}
static native int sqlite3_close_v2(@Nullable long ptrToDb);
private static native int sqlite3_close_v2(@Nullable long ptrToDb);
public static int sqlite3_close_v2(@Nullable sqlite3 db){
return db==null ? 0 : sqlite3_close_v2(db.clearNativePointer());
return null==db ? 0 : sqlite3_close_v2(db.clearNativePointer());
}
public static native byte[] sqlite3_column_blob(
@NotNull sqlite3_stmt stmt, int ndx
);
static native int sqlite3_column_bytes(@NotNull long ptrToStmt, int ndx);
private static native int sqlite3_column_bytes(@NotNull long ptrToStmt, int ndx);
public static int sqlite3_column_bytes(@NotNull sqlite3_stmt stmt, int ndx){
return sqlite3_column_bytes(stmt.getNativePointer(), ndx);
}
static native int sqlite3_column_bytes16(@NotNull long ptrToStmt, int ndx);
private static native int sqlite3_column_bytes16(@NotNull long ptrToStmt, int ndx);
public static int sqlite3_column_bytes16(@NotNull sqlite3_stmt stmt, int ndx){
return sqlite3_column_bytes16(stmt.getNativePointer(), ndx);
}
static native int sqlite3_column_count(@NotNull long ptrToStmt);
private static native int sqlite3_column_count(@NotNull long ptrToStmt);
public static int sqlite3_column_count(@NotNull sqlite3_stmt stmt){
return sqlite3_column_count(stmt.getNativePointer());
}
static native String sqlite3_column_decltype(@NotNull long ptrToStmt, int ndx);
private static native String sqlite3_column_decltype(@NotNull long ptrToStmt, int ndx);
public static String sqlite3_column_decltype(@NotNull sqlite3_stmt stmt, int ndx){
return sqlite3_column_decltype(stmt.getNativePointer(), ndx);
@ -586,7 +587,7 @@ public final class CApi {
@NotNull sqlite3_stmt stmt, int ndx
);
static native Object sqlite3_column_java_object(
private static native Object sqlite3_column_java_object(
@NotNull long ptrToStmt, int ndx
);
@ -616,26 +617,35 @@ public final class CApi {
return type.isInstance(o) ? (T)o : null;
}
static native String sqlite3_column_name(@NotNull long ptrToStmt, int ndx);
private static native String sqlite3_column_name(@NotNull long ptrToStmt, int ndx);
public static String sqlite3_column_name(@NotNull sqlite3_stmt stmt, int ndx){
return sqlite3_column_name(stmt.getNativePointer(), ndx);
}
static native String sqlite3_column_database_name(@NotNull long ptrToStmt, int ndx);
private static native String sqlite3_column_database_name(@NotNull long ptrToStmt, int ndx);
/**
Only available if built with SQLITE_ENABLE_COLUMN_METADATA.
*/
public static String sqlite3_column_database_name(@NotNull sqlite3_stmt stmt, int ndx){
return sqlite3_column_database_name(stmt.getNativePointer(), ndx);
}
static native String sqlite3_column_origin_name(@NotNull long ptrToStmt, int ndx);
private static native String sqlite3_column_origin_name(@NotNull long ptrToStmt, int ndx);
/**
Only available if built with SQLITE_ENABLE_COLUMN_METADATA.
*/
public static String sqlite3_column_origin_name(@NotNull sqlite3_stmt stmt, int ndx){
return sqlite3_column_origin_name(stmt.getNativePointer(), ndx);
}
static native String sqlite3_column_table_name(@NotNull long ptrToStmt, int ndx);
private static native String sqlite3_column_table_name(@NotNull long ptrToStmt, int ndx);
/**
Only available if built with SQLITE_ENABLE_COLUMN_METADATA.
*/
public static String sqlite3_column_table_name(@NotNull sqlite3_stmt stmt, int ndx){
return sqlite3_column_table_name(stmt.getNativePointer(), ndx);
}
@ -693,7 +703,7 @@ public final class CApi {
// return rv;
// }
static native int sqlite3_column_type(@NotNull long ptrToStmt, int ndx);
private static native int sqlite3_column_type(@NotNull long ptrToStmt, int ndx);
public static int sqlite3_column_type(@NotNull sqlite3_stmt stmt, int ndx){
return sqlite3_column_type(stmt.getNativePointer(), ndx);
@ -703,7 +713,7 @@ public final class CApi {
@NotNull sqlite3_stmt stmt, int ndx
);
static native int sqlite3_collation_needed(
private static native int sqlite3_collation_needed(
@NotNull long ptrToDb, @Nullable CollationNeededCallback callback
);
@ -717,7 +727,7 @@ public final class CApi {
return sqlite3_collation_needed(db.getNativePointer(), callback);
}
static native CommitHookCallback sqlite3_commit_hook(
private static native CommitHookCallback sqlite3_commit_hook(
@NotNull long ptrToDb, @Nullable CommitHookCallback hook
);
@ -816,7 +826,7 @@ public final class CApi {
int nArg, int eTextRep, @NotNull SQLFunction func
);
static native int sqlite3_data_count(@NotNull long ptrToStmt);
private static native int sqlite3_data_count(@NotNull long ptrToStmt);
public static int sqlite3_data_count(@NotNull sqlite3_stmt stmt){
return sqlite3_data_count(stmt.getNativePointer());
@ -828,7 +838,7 @@ public final class CApi {
SQLITE_DBCONFIG_... options which uses this call form.
<p>Unlike the C API, this returns SQLITE_MISUSE if its db argument
are null (as opposed to invoking UB).
is null (as opposed to invoking UB).
*/
public static native int sqlite3_db_config(
@NotNull sqlite3 db, int op, int onOff, @Nullable OutputPointer.Int32 out
@ -851,7 +861,6 @@ public final class CApi {
return null==db ? null : sqlite3_db_name(db.getNativePointer(), ndx);
}
public static native String sqlite3_db_filename(
@NotNull sqlite3 db, @NotNull String dbName
);
@ -871,7 +880,7 @@ public final class CApi {
public static native String sqlite3_errmsg(@NotNull sqlite3 db);
static native int sqlite3_error_offset(@NotNull long ptrToDb);
private static native int sqlite3_error_offset(@NotNull long ptrToDb);
/**
Note that the returned byte offset values assume UTF-8-encoded
@ -885,17 +894,17 @@ public final class CApi {
public static native String sqlite3_expanded_sql(@NotNull sqlite3_stmt stmt);
static native int sqlite3_extended_errcode(@NotNull long ptrToDb);
private static native int sqlite3_extended_errcode(@NotNull long ptrToDb);
public static int sqlite3_extended_errcode(@NotNull sqlite3 db){
return sqlite3_extended_errcode(db.getNativePointer());
}
public static native boolean sqlite3_extended_result_codes(
@NotNull sqlite3 db, boolean onoff
public static native int sqlite3_extended_result_codes(
@NotNull sqlite3 db, boolean on
);
static native boolean sqlite3_get_autocommit(@NotNull long ptrToDb);
private static native boolean sqlite3_get_autocommit(@NotNull long ptrToDb);
public static boolean sqlite3_get_autocommit(@NotNull sqlite3 db){
return sqlite3_get_autocommit(db.getNativePointer());
@ -905,7 +914,7 @@ public final class CApi {
@NotNull sqlite3_context cx, int n
);
static native int sqlite3_finalize(long ptrToStmt);
private static native int sqlite3_finalize(long ptrToStmt);
public static int sqlite3_finalize(@NotNull sqlite3_stmt stmt){
return null==stmt ? 0 : sqlite3_finalize(stmt.clearNativePointer());
@ -1199,26 +1208,26 @@ public final class CApi {
*/
public static int sqlite3_prepare_multi(
@NotNull sqlite3 db, @NotNull byte[] sqlUtf8,
int preFlags,
int prepFlags,
@NotNull PrepareMultiCallback p){
final OutputPointer.Int32 oTail = new OutputPointer.Int32();
int pos = 0, n = 1;
byte[] sqlChunk = sqlUtf8;
int rc = 0;
final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
while(0==rc && pos<sqlChunk.length){
while( 0==rc && pos<sqlChunk.length ){
sqlite3_stmt stmt = null;
if(pos > 0){
if( pos>0 ){
sqlChunk = Arrays.copyOfRange(sqlChunk, pos,
sqlChunk.length);
}
if( 0==sqlChunk.length ) break;
rc = sqlite3_prepare_v3(db, sqlChunk, preFlags, outStmt, oTail);
rc = sqlite3_prepare_v3(db, sqlChunk, prepFlags, outStmt, oTail);
if( 0!=rc ) break;
pos = oTail.value;
stmt = outStmt.take();
if( null == stmt ){
// empty statement was parsed.
if( null==stmt ){
// empty statement (whitespace/comments)
continue;
}
rc = p.call(stmt);
@ -1277,7 +1286,7 @@ public final class CApi {
return sqlite3_prepare_multi(db, sql, 0, p);
}
static native int sqlite3_preupdate_blobwrite(@NotNull long ptrToDb);
private static native int sqlite3_preupdate_blobwrite(@NotNull long ptrToDb);
/**
If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this
@ -1288,7 +1297,7 @@ public final class CApi {
return sqlite3_preupdate_blobwrite(db.getNativePointer());
}
static native int sqlite3_preupdate_count(@NotNull long ptrToDb);
private static native int sqlite3_preupdate_count(@NotNull long ptrToDb);
/**
If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this
@ -1299,7 +1308,7 @@ public final class CApi {
return sqlite3_preupdate_count(db.getNativePointer());
}
static native int sqlite3_preupdate_depth(@NotNull long ptrToDb);
private static native int sqlite3_preupdate_depth(@NotNull long ptrToDb);
/**
If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this
@ -1310,7 +1319,7 @@ public final class CApi {
return sqlite3_preupdate_depth(db.getNativePointer());
}
static native PreupdateHookCallback sqlite3_preupdate_hook(
private static native PreupdateHookCallback sqlite3_preupdate_hook(
@NotNull long ptrToDb, @Nullable PreupdateHookCallback hook
);
@ -1325,7 +1334,7 @@ public final class CApi {
return sqlite3_preupdate_hook(db.getNativePointer(), hook);
}
static native int sqlite3_preupdate_new(@NotNull long ptrToDb, int col,
private static native int sqlite3_preupdate_new(@NotNull long ptrToDb, int col,
@NotNull OutputPointer.sqlite3_value out);
/**
@ -1348,7 +1357,7 @@ public final class CApi {
return out.take();
}
static native int sqlite3_preupdate_old(@NotNull long ptrToDb, int col,
private static native int sqlite3_preupdate_old(@NotNull long ptrToDb, int col,
@NotNull OutputPointer.sqlite3_value out);
/**
@ -1399,7 +1408,7 @@ public final class CApi {
results in the C-level sqlite3_result_error() being called with a
complaint about the invalid argument.
*/
static native void sqlite3_result_error(
private static native void sqlite3_result_error(
@NotNull sqlite3_context cx, @NotNull byte[] msg, int eTextRep
);
@ -1471,9 +1480,6 @@ public final class CApi {
cross-language semantic mismatch and (B) Java doesn't need that
argument for its intended purpose (type safety).
<p>Note that there is no sqlite3_column_java_object(), as the
C-level API has no sqlite3_column_pointer() to proxy.
@see #sqlite3_value_java_object
@see #sqlite3_bind_java_object
*/
@ -1676,7 +1682,7 @@ public final class CApi {
}
}
static native RollbackHookCallback sqlite3_rollback_hook(
private static native RollbackHookCallback sqlite3_rollback_hook(
@NotNull long ptrToDb, @Nullable RollbackHookCallback hook
);
@ -1728,20 +1734,24 @@ public final class CApi {
@NotNull OutputPointer.Int64 pHighwater, boolean reset
);
public static native int sqlite3_step(@NotNull sqlite3_stmt stmt);
private static native int sqlite3_step(@NotNull long ptrToStmt);
public static int sqlite3_step(@NotNull sqlite3_stmt stmt){
return null==stmt ? SQLITE_MISUSE : sqlite3_step(stmt.getNativePointer());
}
public static native boolean sqlite3_stmt_busy(@NotNull sqlite3_stmt stmt);
static native int sqlite3_stmt_explain(@NotNull long ptrToStmt, int op);
private static native int sqlite3_stmt_explain(@NotNull long ptrToStmt, int op);
public static int sqlite3_stmt_explain(@NotNull sqlite3_stmt stmt, int op){
return sqlite3_stmt_explain(stmt.getNativePointer(), op);
return null==stmt ? SQLITE_MISUSE : sqlite3_stmt_explain(stmt.getNativePointer(), op);
}
static native int sqlite3_stmt_isexplain(@NotNull long ptrToStmt);
private static native int sqlite3_stmt_isexplain(@NotNull long ptrToStmt);
public static int sqlite3_stmt_isexplain(@NotNull sqlite3_stmt stmt){
return sqlite3_stmt_isexplain(stmt.getNativePointer());
return null==stmt ? 0 : sqlite3_stmt_isexplain(stmt.getNativePointer());
}
public static native boolean sqlite3_stmt_readonly(@NotNull sqlite3_stmt stmt);
@ -1762,7 +1772,7 @@ public final class CApi {
signature is the public-facing one.
*/
private static native int sqlite3_strglob(
@NotNull byte[] glob, @NotNull byte[] nullTerminatedUtf8
@NotNull byte[] glob, @NotNull byte[] nulTerminatedUtf8
);
public static int sqlite3_strglob(
@ -1776,7 +1786,7 @@ public final class CApi {
The LIKE counterpart of the private sqlite3_strglob() method.
*/
private static native int sqlite3_strlike(
@NotNull byte[] glob, @NotNull byte[] nullTerminatedUtf8,
@NotNull byte[] glob, @NotNull byte[] nulTerminatedUtf8,
int escChar
);
@ -1788,7 +1798,7 @@ public final class CApi {
(int)escChar);
}
static native int sqlite3_system_errno(@NotNull long ptrToDb);
private static native int sqlite3_system_errno(@NotNull long ptrToDb);
public static int sqlite3_system_errno(@NotNull sqlite3 db){
return sqlite3_system_errno(db.getNativePointer());
@ -1834,13 +1844,13 @@ public final class CApi {
public static native int sqlite3_threadsafe();
static native int sqlite3_total_changes(@NotNull long ptrToDb);
private static native int sqlite3_total_changes(@NotNull long ptrToDb);
public static int sqlite3_total_changes(@NotNull sqlite3 db){
return sqlite3_total_changes(db.getNativePointer());
}
static native long sqlite3_total_changes64(@NotNull long ptrToDb);
private static native long sqlite3_total_changes64(@NotNull long ptrToDb);
public static long sqlite3_total_changes64(@NotNull sqlite3 db){
return sqlite3_total_changes64(db.getNativePointer());
@ -1863,7 +1873,7 @@ public final class CApi {
@NotNull sqlite3 db, @Nullable String zSchema
);
static native UpdateHookCallback sqlite3_update_hook(
private static native UpdateHookCallback sqlite3_update_hook(
@NotNull long ptrToDb, @Nullable UpdateHookCallback hook
);
@ -1883,67 +1893,67 @@ public final class CApi {
sqlite3_create_function().
*/
static native byte[] sqlite3_value_blob(@NotNull long ptrToValue);
private static native byte[] sqlite3_value_blob(@NotNull long ptrToValue);
public static byte[] sqlite3_value_blob(@NotNull sqlite3_value v){
return sqlite3_value_blob(v.getNativePointer());
}
static native int sqlite3_value_bytes(@NotNull long ptrToValue);
private static native int sqlite3_value_bytes(@NotNull long ptrToValue);
public static int sqlite3_value_bytes(@NotNull sqlite3_value v){
return sqlite3_value_bytes(v.getNativePointer());
}
static native int sqlite3_value_bytes16(@NotNull long ptrToValue);
private static native int sqlite3_value_bytes16(@NotNull long ptrToValue);
public static int sqlite3_value_bytes16(@NotNull sqlite3_value v){
return sqlite3_value_bytes16(v.getNativePointer());
}
static native double sqlite3_value_double(@NotNull long ptrToValue);
private static native double sqlite3_value_double(@NotNull long ptrToValue);
public static double sqlite3_value_double(@NotNull sqlite3_value v){
return sqlite3_value_double(v.getNativePointer());
}
static native sqlite3_value sqlite3_value_dup(@NotNull long ptrToValue);
private static native sqlite3_value sqlite3_value_dup(@NotNull long ptrToValue);
public static sqlite3_value sqlite3_value_dup(@NotNull sqlite3_value v){
return sqlite3_value_dup(v.getNativePointer());
}
static native int sqlite3_value_encoding(@NotNull long ptrToValue);
private static native int sqlite3_value_encoding(@NotNull long ptrToValue);
public static int sqlite3_value_encoding(@NotNull sqlite3_value v){
return sqlite3_value_encoding(v.getNativePointer());
}
static native void sqlite3_value_free(@Nullable long ptrToValue);
private static native void sqlite3_value_free(@Nullable long ptrToValue);
public static void sqlite3_value_free(@Nullable sqlite3_value v){
sqlite3_value_free(v.getNativePointer());
if( null!=v ) sqlite3_value_free(v.clearNativePointer());
}
static native boolean sqlite3_value_frombind(@NotNull long ptrToValue);
private static native boolean sqlite3_value_frombind(@NotNull long ptrToValue);
public static boolean sqlite3_value_frombind(@NotNull sqlite3_value v){
return sqlite3_value_frombind(v.getNativePointer());
}
static native int sqlite3_value_int(@NotNull long ptrToValue);
private static native int sqlite3_value_int(@NotNull long ptrToValue);
public static int sqlite3_value_int(@NotNull sqlite3_value v){
return sqlite3_value_int(v.getNativePointer());
}
static native long sqlite3_value_int64(@NotNull long ptrToValue);
private static native long sqlite3_value_int64(@NotNull long ptrToValue);
public static long sqlite3_value_int64(@NotNull sqlite3_value v){
return sqlite3_value_int64(v.getNativePointer());
}
static native Object sqlite3_value_java_object(@NotNull long ptrToValue);
private static native Object sqlite3_value_java_object(@NotNull long ptrToValue);
/**
If the given value was set using {@link
@ -1969,25 +1979,25 @@ public final class CApi {
return type.isInstance(o) ? (T)o : null;
}
static native int sqlite3_value_nochange(@NotNull long ptrToValue);
private static native int sqlite3_value_nochange(@NotNull long ptrToValue);
public static int sqlite3_value_nochange(@NotNull sqlite3_value v){
return sqlite3_value_nochange(v.getNativePointer());
}
static native int sqlite3_value_numeric_type(@NotNull long ptrToValue);
private static native int sqlite3_value_numeric_type(@NotNull long ptrToValue);
public static int sqlite3_value_numeric_type(@NotNull sqlite3_value v){
return sqlite3_value_numeric_type(v.getNativePointer());
}
static native int sqlite3_value_subtype(@NotNull long ptrToValue);
private static native int sqlite3_value_subtype(@NotNull long ptrToValue);
public static int sqlite3_value_subtype(@NotNull sqlite3_value v){
return sqlite3_value_subtype(v.getNativePointer());
}
static native byte[] sqlite3_value_text(@NotNull long ptrToValue);
private static native byte[] sqlite3_value_text(@NotNull long ptrToValue);
/**
Functions identially to the C API, and this note is just to
@ -1999,13 +2009,13 @@ public final class CApi {
return sqlite3_value_text(v.getNativePointer());
}
static native String sqlite3_value_text16(@NotNull long ptrToValue);
private static native String sqlite3_value_text16(@NotNull long ptrToValue);
public static String sqlite3_value_text16(@NotNull sqlite3_value v){
return sqlite3_value_text16(v.getNativePointer());
}
static native int sqlite3_value_type(@NotNull long ptrToValue);
private static native int sqlite3_value_type(@NotNull long ptrToValue);
public static int sqlite3_value_type(@NotNull sqlite3_value v){
return sqlite3_value_type(v.getNativePointer());
@ -2280,7 +2290,6 @@ public final class CApi {
// prepare flags
public static final int SQLITE_PREPARE_PERSISTENT = 1;
public static final int SQLITE_PREPARE_NORMALIZE = 2;
public static final int SQLITE_PREPARE_NO_VTAB = 4;
// result codes
@ -2436,9 +2445,11 @@ public final class CApi {
public static final int SQLITE_TXN_WRITE = 2;
// udf flags
public static final int SQLITE_DETERMINISTIC = 0x000000800;
public static final int SQLITE_DIRECTONLY = 0x000080000;
public static final int SQLITE_INNOCUOUS = 0x000200000;
public static final int SQLITE_DETERMINISTIC = 0x000000800;
public static final int SQLITE_DIRECTONLY = 0x000080000;
public static final int SQLITE_SUBTYPE = 0x000100000;
public static final int SQLITE_INNOCUOUS = 0x000200000;
public static final int SQLITE_RESULT_SUBTYPE = 0x001000000;
// virtual tables
public static final int SQLITE_INDEX_SCAN_UNIQUE = 1;

View File

@ -21,8 +21,9 @@ public interface CollationNeededCallback extends CallbackProxy {
Has the same semantics as the C-level sqlite3_create_collation()
callback.
<p>If it throws, the exception message is passed on to the db and
the exception is suppressed.
<p>Because the C API has no mechanism for reporting errors
from this callbacks, any exceptions thrown by this callback
are suppressed.
*/
int call(sqlite3 db, int eTextRep, String collationName);
void call(sqlite3 db, int eTextRep, String collationName);
}

View File

@ -19,7 +19,8 @@ package org.sqlite.jni.capi;
public interface CommitHookCallback extends CallbackProxy {
/**
Works as documented for the C-level sqlite3_commit_hook()
callback. Must not throw.
callback. If it throws, the exception is translated into
a db-level error.
*/
int call();
}

View File

@ -19,7 +19,8 @@ package org.sqlite.jni.capi;
public interface PreupdateHookCallback extends CallbackProxy {
/**
Must function as described for the C-level sqlite3_preupdate_hook()
callback.
callback. If it throws, the exception is translated to a
db-level error and the exception is suppressed.
*/
void call(sqlite3 db, int op, String dbName, String dbTable,
long iKey1, long iKey2 );

View File

@ -18,8 +18,9 @@ package org.sqlite.jni.capi;
*/
public interface RollbackHookCallback extends CallbackProxy {
/**
Works as documented for the C-level sqlite3_rollback_hook()
callback.
Must function as documented for the C-level sqlite3_rollback_hook()
callback. If it throws, the exception is translated into
a db-level error.
*/
void call();
}

View File

@ -46,7 +46,7 @@ public class Tester1 implements Runnable {
//! 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;
private static int listRunTests = 0;
//! True to squelch all out() and outln() output.
private static boolean quietMode = false;
//! Total number of runTests() calls.
@ -327,7 +327,7 @@ public class Tester1 implements Runnable {
rc = sqlite3_prepare_v3(db, "INSERT INTO t2(a) VALUES(1),(2),(3)",
SQLITE_PREPARE_NORMALIZE, outStmt);
0, outStmt);
affirm(0 == rc);
stmt = outStmt.get();
affirm(0 != stmt.getNativePointer());
@ -382,6 +382,15 @@ public class Tester1 implements Runnable {
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
affirm( sqlite3_stmt_readonly(stmt) );
affirm( !sqlite3_stmt_busy(stmt) );
if( sqlite3_compileoption_used("ENABLE_COLUMN_METADATA") ){
/* Unlike in native C code, JNI won't trigger an
UnsatisfiedLinkError until these are called (on Linux, at
least). */
affirm("t".equals(sqlite3_column_table_name(stmt,0)));
affirm("main".equals(sqlite3_column_database_name(stmt,0)));
affirm("a".equals(sqlite3_column_origin_name(stmt,0)));
}
int total2 = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){
affirm( sqlite3_stmt_busy(stmt) );
@ -593,9 +602,9 @@ public class Tester1 implements Runnable {
};
final CollationNeededCallback collLoader = new CollationNeededCallback(){
@Override
public int call(sqlite3 dbArg, int eTextRep, String collationName){
public void call(sqlite3 dbArg, int eTextRep, String collationName){
affirm(dbArg == db/* as opposed to a temporary object*/);
return sqlite3_create_collation(dbArg, "reversi", eTextRep, myCollation);
sqlite3_create_collation(dbArg, "reversi", eTextRep, myCollation);
}
};
int rc = sqlite3_collation_needed(db, collLoader);
@ -1031,48 +1040,48 @@ public class Tester1 implements Runnable {
@SingleThreadOnly /* because threads inherently break this test */
private static void testBusy(){
final String dbName = "_busy-handler.db";
final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3();
final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
int rc = sqlite3_open(dbName, outDb);
++metrics.dbOpen;
affirm( 0 == rc );
final sqlite3 db1 = outDb.get();
execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)");
rc = sqlite3_open(dbName, outDb);
++metrics.dbOpen;
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(){
@Override public int call(int n){
//outln("busy handler #"+n);
return n > 2 ? 0 : ++xBusyCalled.value;
}
};
rc = sqlite3_busy_handler(db2, handler);
affirm(0 == rc);
// Force a locked condition...
execSql(db1, "BEGIN EXCLUSIVE");
rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt);
affirm( SQLITE_BUSY == rc);
affirm( null == outStmt.get() );
affirm( 3 == xBusyCalled.value );
sqlite3_close_v2(db1);
sqlite3_close_v2(db2);
try{
final java.io.File f = new java.io.File(dbName);
f.delete();
}catch(Exception e){
/* ignore */
final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3();
final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
int rc = sqlite3_open(dbName, outDb);
++metrics.dbOpen;
affirm( 0 == rc );
final sqlite3 db1 = outDb.get();
execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)");
rc = sqlite3_open(dbName, outDb);
++metrics.dbOpen;
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) ) );
affirm( SQLITE_MISUSE == sqlite3_db_config(db1, 0, 0, null) );
final ValueHolder<Integer> xBusyCalled = new ValueHolder<>(0);
BusyHandlerCallback handler = new BusyHandlerCallback(){
@Override public int call(int n){
//outln("busy handler #"+n);
return n > 2 ? 0 : ++xBusyCalled.value;
}
};
rc = sqlite3_busy_handler(db2, handler);
affirm(0 == rc);
// Force a locked condition...
execSql(db1, "BEGIN EXCLUSIVE");
rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt);
affirm( SQLITE_BUSY == rc);
affirm( null == outStmt.get() );
affirm( 3 == xBusyCalled.value );
sqlite3_close_v2(db1);
sqlite3_close_v2(db2);
}finally{
try{(new java.io.File(dbName)).delete();}
catch(Exception e){/* ignore */}
}
}
@ -1096,6 +1105,7 @@ public class Tester1 implements Runnable {
private void testCommitHook(){
final sqlite3 db = createNewDb();
sqlite3_extended_result_codes(db, true);
final ValueHolder<Integer> counter = new ValueHolder<>(0);
final ValueHolder<Integer> hookResult = new ValueHolder<>(0);
final CommitHookCallback theHook = new CommitHookCallback(){
@ -1138,7 +1148,7 @@ public class Tester1 implements Runnable {
affirm( 5 == counter.value );
hookResult.value = SQLITE_ERROR;
int rc = execSql(db, false, "BEGIN; update t set a='j' where a='i'; COMMIT;");
affirm( SQLITE_CONSTRAINT == rc );
affirm( SQLITE_CONSTRAINT_COMMITHOOK == rc );
affirm( 6 == counter.value );
sqlite3_close_v2(db);
}
@ -1357,6 +1367,9 @@ public class Tester1 implements Runnable {
authRc.value = SQLITE_DENY;
int rc = execSql(db, false, "UPDATE t SET a=2");
affirm( SQLITE_AUTH==rc );
sqlite3_set_authorizer(db, null);
rc = execSql(db, false, "UPDATE t SET a=2");
affirm( 0==rc );
// TODO: expand these tests considerably
sqlite3_close(db);
}
@ -1418,7 +1431,7 @@ public class Tester1 implements Runnable {
val.value = 0;
final AutoExtensionCallback ax2 = new AutoExtensionCallback(){
@Override public synchronized int call(sqlite3 db){
@Override public int call(sqlite3 db){
++val.value;
return 0;
}
@ -1629,7 +1642,7 @@ public class Tester1 implements Runnable {
sqlite3_finalize(stmt);
b = sqlite3_blob_open(db, "main", "t", "a",
sqlite3_last_insert_rowid(db), 1);
sqlite3_last_insert_rowid(db), 0);
affirm( null!=b );
rc = sqlite3_blob_reopen(b, 2);
affirm( 0==rc );
@ -1701,7 +1714,7 @@ public class Tester1 implements Runnable {
mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) );
java.util.Collections.shuffle(mlist);
}
if( listRunTests ){
if( (!fromThread && listRunTests>0) || listRunTests>1 ){
synchronized(this.getClass()){
if( !fromThread ){
out("Initial test"," list: ");
@ -1763,8 +1776,11 @@ public class Tester1 implements Runnable {
-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.
which are hard-coded. In multi-threaded mode, use this twice to
to emit the list run by each thread (which may differ from the initial
list, in particular if -shuffle is used).
-fail: forces an exception to be thrown during the test run. Use
with -shuffle to make its appearance unpredictable.
@ -1793,7 +1809,7 @@ public class Tester1 implements Runnable {
}else if(arg.equals("shuffle")){
shuffle = true;
}else if(arg.equals("list-tests")){
listRunTests = true;
++listRunTests;
}else if(arg.equals("fail")){
forceFail = true;
}else if(arg.equals("sqllog")){
@ -1904,6 +1920,7 @@ public class Tester1 implements Runnable {
sqlite3_libversion_number(),"\n",
sqlite3_libversion(),"\n",SQLITE_SOURCE_ID,"\n",
"SQLITE_THREADSAFE=",sqlite3_threadsafe());
outln("JVM NIO support? ",sqlite3_jni_supports_nio() ? "YES" : "NO");
final boolean showLoopCount = (nRepeat>1 && nThread>1);
if( showLoopCount ){
outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each.");

View File

@ -19,7 +19,8 @@ package org.sqlite.jni.capi;
public interface UpdateHookCallback extends CallbackProxy {
/**
Must function as described for the C-level sqlite3_update_hook()
callback.
callback. If it throws, the exception is translated into
a db-level error.
*/
void call(int opId, String dbName, String tableName, long rowId);
}

View File

@ -38,6 +38,6 @@ public final class sqlite3 extends NativePointerHolder<sqlite3>
}
@Override public void close(){
CApi.sqlite3_close_v2(this.clearNativePointer());
CApi.sqlite3_close_v2(this);
}
}

View File

@ -25,7 +25,6 @@ public final class sqlite3_blob extends NativePointerHolder<sqlite3_blob>
private sqlite3_blob(){}
@Override public void close(){
CApi.sqlite3_blob_close(this.clearNativePointer());
CApi.sqlite3_blob_close(this);
}
}

View File

@ -25,6 +25,6 @@ public final class sqlite3_stmt extends NativePointerHolder<sqlite3_stmt>
private sqlite3_stmt(){}
@Override public void close(){
CApi.sqlite3_finalize(this.clearNativePointer());
CApi.sqlite3_finalize(this);
}
}

View File

@ -12,10 +12,6 @@
** 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

View File

@ -25,13 +25,10 @@ public interface SqlFunction {
public static final int DETERMINISTIC = CApi.SQLITE_DETERMINISTIC;
public static final int INNOCUOUS = CApi.SQLITE_INNOCUOUS;
public static final int DIRECTONLY = CApi.SQLITE_DIRECTONLY;
public static final int SUBTYPE = CApi.SQLITE_SUBTYPE;
public static final int RESULT_SUBTYPE = CApi.SQLITE_RESULT_SUBTYPE;
public static final int UTF8 = CApi.SQLITE_UTF8;
public static final int UTF16 = CApi.SQLITE_UTF16;
// /**
// For Window functions only and is not currently bound because
// doing so may require exposing sqlite3_value for effective use.
// */
// public static final int SUBTYPE = CApi.SQLITE_SUBTYPE;
/**
The Arguments type is an abstraction on top of the lower-level
@ -56,7 +53,7 @@ public interface SqlFunction {
*/
Arguments(sqlite3_context cx, sqlite3_value args[]){
this.cx = cx;
this.args = args==null ? new sqlite3_value[0] : args;;
this.args = args==null ? new sqlite3_value[0] : args;
this.length = this.args.length;
}
@ -76,6 +73,16 @@ public interface SqlFunction {
//! Returns the underlying sqlite3_context for these arguments.
sqlite3_context getContext(){return cx;}
/**
Returns the Sqlite (db) object associated with this UDF call,
or null if the UDF is somehow called without such an object or
the db has been closed in an untimely manner (e.g. closed by a
UDF call).
*/
public Sqlite getDb(){
return Sqlite.fromNative( CApi.sqlite3_context_db_handle(cx) );
}
public int getArgCount(){ return args.length; }
public int getInt(int argNdx){return CApi.sqlite3_value_int(valueAt(argNdx));}
@ -107,6 +114,10 @@ public interface SqlFunction {
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);}
/**
Analog to sqlite3_result_value(), using the Value object at the
given argument index.
*/
public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));}
public void resultSubtype(int subtype){CApi.sqlite3_result_subtype(cx, subtype);}
public void resultZeroBlob(long n){
@ -153,7 +164,7 @@ public interface SqlFunction {
}
/**
Wrapper for a single SqlFunction argument. Primarily intended
Represents a single SqlFunction argument. Primarily intended
for use with the Arguments class's Iterable interface.
*/
public final static class Arg {

File diff suppressed because it is too large Load Diff

View File

@ -50,7 +50,7 @@ public final class SqliteException extends java.lang.RuntimeException {
/**
Records the current error state of db (which must not be null and
must refer to an opened db object). Note that this does NOT close
must refer to an opened db object). Note that this does not close
the db.
Design note: closing the db on error is really only useful during
@ -74,7 +74,7 @@ public final class SqliteException extends java.lang.RuntimeException {
}
public SqliteException(Sqlite.Stmt stmt){
this(stmt.db());
this(stmt.getDb());
}
public int errcode(){ return errCode; }

View File

@ -46,7 +46,7 @@ public class Tester2 implements Runnable {
//! 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;
private static int listRunTests = 0;
//! True to squelch all out() and outln() output.
private static boolean quietMode = false;
//! Total number of runTests() calls.
@ -125,7 +125,7 @@ public class Tester2 implements Runnable {
}
public static void execSql(Sqlite db, String[] sql){
public static void execSql(Sqlite db, String sql[]){
execSql(db, String.join("", sql));
}
@ -133,6 +133,9 @@ public class Tester2 implements Runnable {
Executes all SQL statements in the given string. If throwOnError
is true then it will throw for any prepare/step errors, else it
will return the corresponding non-0 result code.
TODO: reimplement this in the high-level API once it has the
multi-prepare capability.
*/
public static int execSql(Sqlite dbw, boolean throwOnError, String sql){
final sqlite3 db = dbw.nativeHandle();
@ -163,7 +166,7 @@ public class Tester2 implements Runnable {
}
CApi.sqlite3_finalize(stmt);
affirm(0 == stmt.getNativePointer());
if(CApi.SQLITE_DONE!=rc){
if(Sqlite.DONE!=rc){
break;
}
}
@ -181,7 +184,7 @@ public class Tester2 implements Runnable {
@SingleThreadOnly /* because it's thread-agnostic */
private void test1(){
affirm(CApi.sqlite3_libversion_number() == CApi.SQLITE_VERSION_NUMBER);
affirm(Sqlite.libVersionNumber() == CApi.SQLITE_VERSION_NUMBER);
}
/* Copy/paste/rename this to add new tests. */
@ -211,15 +214,24 @@ public class Tester2 implements Runnable {
void testOpenDb1(){
Sqlite db = openDb();
affirm( 0!=db.nativeHandle().getNativePointer() );
affirm( "main".equals( db.dbName(0) ) );
db.setMainDbName("foo");
affirm( "foo".equals( db.dbName(0) ) );
affirm( db.dbConfig(Sqlite.DBCONFIG_DEFENSIVE, true)
/* The underlying 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( !db.dbConfig(Sqlite.DBCONFIG_DEFENSIVE, false) );
SqliteException ex = null;
try{ db.dbConfig(0, false); }
catch(SqliteException e){ ex = e; }
affirm( null!=ex );
ex = null;
db.close();
affirm( null==db.nativeHandle() );
SqliteException ex = null;
try {
db = openDb("/no/such/dir/.../probably");
}catch(SqliteException e){
ex = e;
}
try{ db = openDb("/no/such/dir/.../probably"); }
catch(SqliteException e){ ex = e; }
affirm( ex!=null );
affirm( ex.errcode() != 0 );
affirm( ex.extendedErrcode() != 0 );
@ -232,6 +244,7 @@ public class Tester2 implements Runnable {
Sqlite.Stmt stmt = db.prepare("SELECT ?1");
Exception e = null;
affirm( null!=stmt.nativeHandle() );
affirm( db == stmt.getDb() );
affirm( 1==stmt.bindParameterCount() );
affirm( "?1".equals(stmt.bindParameterName(1)) );
affirm( null==stmt.bindParameterName(2) );
@ -285,21 +298,30 @@ public class Tester2 implements Runnable {
final ValueHolder<Integer> vh = new ValueHolder<>(0);
final ScalarFunction f = new ScalarFunction(){
public void xFunc(SqlFunction.Arguments args){
affirm( db == args.getDb() );
for( SqlFunction.Arguments.Arg arg : args ){
vh.value += arg.getInt();
}
args.resultInt(vh.value);
}
public void xDestroy(){
++xDestroyCalled.value;
}
};
db.createFunction("myfunc", -1, f);
execSql(db, "select myfunc(1,2,3)");
Sqlite.Stmt q = db.prepare("select myfunc(1,2,3)");
affirm( q.step() );
affirm( 6 == vh.value );
vh.value = 0;
execSql(db, "select myfunc(-1,-2,-3)");
affirm( -6 == vh.value );
affirm( 6 == q.columnInt(0) );
q.finalizeStmt();
affirm( 0 == xDestroyCalled.value );
vh.value = 0;
q = db.prepare("select myfunc(-1,-2,-3)");
affirm( q.step() );
affirm( -6 == vh.value );
affirm( -6 == q.columnInt(0) );
affirm( 0 == xDestroyCalled.value );
q.finalizeStmt();
}
affirm( 1 == xDestroyCalled.value );
}
@ -444,6 +466,508 @@ public class Tester2 implements Runnable {
db.close();
}
private void testTrace(){
final Sqlite db = openDb();
final ValueHolder<Integer> counter = new ValueHolder<>(0);
/* Ensure that characters outside of the UTF BMP survive the trip
from Java to sqlite3 and back to Java. (At no small efficiency
penalty.) */
final String nonBmpChar = "😃";
db.trace(
Sqlite.TRACE_ALL,
new Sqlite.TraceCallback(){
@Override public void call(int traceFlag, Object pNative, Object x){
++counter.value;
//outln("TRACE "+traceFlag+" pNative = "+pNative.getClass().getName());
switch(traceFlag){
case Sqlite.TRACE_STMT:
affirm(pNative instanceof Sqlite.Stmt);
//outln("TRACE_STMT sql = "+x);
affirm(x instanceof String);
affirm( ((String)x).indexOf(nonBmpChar) > 0 );
break;
case Sqlite.TRACE_PROFILE:
affirm(pNative instanceof Sqlite.Stmt);
affirm(x instanceof Long);
//outln("TRACE_PROFILE time = "+x);
break;
case Sqlite.TRACE_ROW:
affirm(pNative instanceof Sqlite.Stmt);
affirm(null == x);
//outln("TRACE_ROW = "+sqlite3_column_text16((sqlite3_stmt)pNative, 0));
break;
case Sqlite.TRACE_CLOSE:
affirm(pNative instanceof Sqlite);
affirm(null == x);
break;
default:
affirm(false /*cannot happen*/);
break;
}
}
});
execSql(db, "SELECT coalesce(null,null,'"+nonBmpChar+"'); "+
"SELECT 'w"+nonBmpChar+"orld'");
affirm( 6 == counter.value );
db.close();
affirm( 7 == counter.value );
}
private void testStatus(){
final Sqlite db = openDb();
execSql(db, "create table t(a); insert into t values(1),(2),(3)");
Sqlite.Status s = Sqlite.libStatus(Sqlite.STATUS_MEMORY_USED, false);
affirm( s.current > 0 );
affirm( s.peak >= s.current );
s = db.status(Sqlite.DBSTATUS_SCHEMA_USED, false);
affirm( s.current > 0 );
affirm( s.peak == 0 /* always 0 for SCHEMA_USED */ );
db.close();
}
@SingleThreadOnly /* because multiple threads legitimately make these
results unpredictable */
private synchronized void testAutoExtension(){
final ValueHolder<Integer> val = new ValueHolder<>(0);
final ValueHolder<String> toss = new ValueHolder<>(null);
final Sqlite.AutoExtension ax = new Sqlite.AutoExtension(){
@Override public void call(Sqlite db){
++val.value;
if( null!=toss.value ){
throw new RuntimeException(toss.value);
}
}
};
Sqlite.addAutoExtension(ax);
openDb().close();
affirm( 1==val.value );
openDb().close();
affirm( 2==val.value );
Sqlite.clearAutoExtensions();
openDb().close();
affirm( 2==val.value );
Sqlite.addAutoExtension( ax );
Sqlite.addAutoExtension( ax ); // Must not add a second entry
Sqlite.addAutoExtension( ax ); // or a third one
openDb().close();
affirm( 3==val.value );
Sqlite db = openDb();
affirm( 4==val.value );
execSql(db, "ATTACH ':memory:' as foo");
affirm( 4==val.value, "ATTACH uses the same connection, not sub-connections." );
db.close();
db = null;
Sqlite.removeAutoExtension(ax);
openDb().close();
affirm( 4==val.value );
Sqlite.addAutoExtension(ax);
Exception err = null;
toss.value = "Throwing from auto_extension.";
try{
openDb();
}catch(Exception e){
err = e;
}
affirm( err!=null );
affirm( err.getMessage().indexOf(toss.value)>=0 );
toss.value = null;
val.value = 0;
final Sqlite.AutoExtension ax2 = new Sqlite.AutoExtension(){
@Override public void call(Sqlite db){
++val.value;
}
};
Sqlite.addAutoExtension(ax2);
openDb().close();
affirm( 2 == val.value );
Sqlite.removeAutoExtension(ax);
openDb().close();
affirm( 3 == val.value );
Sqlite.addAutoExtension(ax);
openDb().close();
affirm( 5 == val.value );
Sqlite.removeAutoExtension(ax2);
openDb().close();
affirm( 6 == val.value );
Sqlite.addAutoExtension(ax2);
openDb().close();
affirm( 8 == val.value );
Sqlite.clearAutoExtensions();
openDb().close();
affirm( 8 == val.value );
}
private void testBackup(){
final Sqlite dbDest = openDb();
try (Sqlite dbSrc = openDb()) {
execSql(dbSrc, new String[]{
"pragma page_size=512; VACUUM;",
"create table t(a);",
"insert into t(a) values(1),(2),(3);"
});
Exception e = null;
try {
dbSrc.initBackup("main",dbSrc,"main");
}catch(Exception x){
e = x;
}
affirm( e instanceof SqliteException );
e = null;
try (Sqlite.Backup b = dbDest.initBackup("main",dbSrc,"main")) {
affirm( null!=b );
int rc;
while( Sqlite.DONE!=(rc = b.step(1)) ){
affirm( 0==rc );
}
affirm( b.pageCount() > 0 );
b.finish();
}
}
try (Sqlite.Stmt q = dbDest.prepare("SELECT sum(a) from t")) {
q.step();
affirm( q.columnInt(0) == 6 );
}
dbDest.close();
}
private void testCollation(){
final Sqlite db = openDb();
execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')");
final Sqlite.Collation myCollation = new Sqlite.Collation() {
private String myState =
"this is local state. There is much like it, but this is mine.";
@Override
// Reverse-sorts its inputs...
public int call(byte[] lhs, byte[] rhs){
int len = lhs.length > rhs.length ? rhs.length : lhs.length;
int c = 0, i = 0;
for(i = 0; i < len; ++i){
c = lhs[i] - rhs[i];
if(0 != c) break;
}
if(0==c){
if(i < lhs.length) c = 1;
else if(i < rhs.length) c = -1;
}
return -c;
}
};
final Sqlite.CollationNeeded collLoader = new Sqlite.CollationNeeded(){
@Override
public void call(Sqlite dbArg, int eTextRep, String collationName){
affirm(dbArg == db);
db.createCollation("reversi", eTextRep, myCollation);
}
};
db.onCollationNeeded(collLoader);
Sqlite.Stmt stmt = db.prepare("SELECT a FROM t ORDER BY a COLLATE reversi");
int counter = 0;
while( stmt.step() ){
final String val = stmt.columnText16(0);
++counter;
switch(counter){
case 1: affirm("c".equals(val)); break;
case 2: affirm("b".equals(val)); break;
case 3: affirm("a".equals(val)); break;
}
}
affirm(3 == counter);
stmt.finalizeStmt();
stmt = db.prepare("SELECT a FROM t ORDER BY a");
counter = 0;
while( stmt.step() ){
final String val = stmt.columnText16(0);
++counter;
//outln("Non-REVERSI'd row#"+counter+": "+val);
switch(counter){
case 3: affirm("c".equals(val)); break;
case 2: affirm("b".equals(val)); break;
case 1: affirm("a".equals(val)); break;
}
}
affirm(3 == counter);
stmt.finalizeStmt();
db.onCollationNeeded(null);
db.close();
}
@SingleThreadOnly /* because threads inherently break this test */
private void testBusy(){
final String dbName = "_busy-handler.db";
try{
Sqlite db1 = openDb(dbName);
++metrics.dbOpen;
execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)");
Sqlite db2 = openDb(dbName);
++metrics.dbOpen;
final ValueHolder<Integer> xBusyCalled = new ValueHolder<>(0);
Sqlite.BusyHandler handler = new Sqlite.BusyHandler(){
@Override public int call(int n){
return n > 2 ? 0 : ++xBusyCalled.value;
}
};
db2.setBusyHandler(handler);
// Force a locked condition...
execSql(db1, "BEGIN EXCLUSIVE");
int rc = 0;
SqliteException ex = null;
try{
db2.prepare("SELECT * from t");
}catch(SqliteException x){
ex = x;
}
affirm( null!=ex );
affirm( Sqlite.BUSY == ex.errcode() );
affirm( 3 == xBusyCalled.value );
db1.close();
db2.close();
}finally{
try{(new java.io.File(dbName)).delete();}
catch(Exception e){/* ignore */}
}
}
private void testCommitHook(){
final Sqlite db = openDb();
final ValueHolder<Integer> counter = new ValueHolder<>(0);
final ValueHolder<Integer> hookResult = new ValueHolder<>(0);
final Sqlite.CommitHook theHook = new Sqlite.CommitHook(){
@Override public int call(){
++counter.value;
return hookResult.value;
}
};
Sqlite.CommitHook oldHook = db.setCommitHook(theHook);
affirm( null == oldHook );
execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')");
affirm( 2 == counter.value );
execSql(db, "BEGIN; SELECT 1; SELECT 2; COMMIT;");
affirm( 2 == counter.value /* NOT invoked if no changes are made */ );
execSql(db, "BEGIN; update t set a='d' where a='c'; COMMIT;");
affirm( 3 == counter.value );
oldHook = db.setCommitHook(theHook);
affirm( theHook == oldHook );
execSql(db, "BEGIN; update t set a='e' where a='d'; COMMIT;");
affirm( 4 == counter.value );
oldHook = db.setCommitHook(null);
affirm( theHook == oldHook );
execSql(db, "BEGIN; update t set a='f' where a='e'; COMMIT;");
affirm( 4 == counter.value );
oldHook = db.setCommitHook(null);
affirm( null == oldHook );
execSql(db, "BEGIN; update t set a='g' where a='f'; COMMIT;");
affirm( 4 == counter.value );
final Sqlite.CommitHook newHook = new Sqlite.CommitHook(){
@Override public int call(){return 0;}
};
oldHook = db.setCommitHook(newHook);
affirm( null == oldHook );
execSql(db, "BEGIN; update t set a='h' where a='g'; COMMIT;");
affirm( 4 == counter.value );
oldHook = db.setCommitHook(theHook);
affirm( newHook == oldHook );
execSql(db, "BEGIN; update t set a='i' where a='h'; COMMIT;");
affirm( 5 == counter.value );
hookResult.value = CApi.SQLITE_ERROR;
int rc = execSql(db, false, "BEGIN; update t set a='j' where a='i'; COMMIT;");
affirm( CApi.SQLITE_CONSTRAINT_COMMITHOOK == rc );
affirm( 6 == counter.value );
db.close();
}
private void testRollbackHook(){
final Sqlite db = openDb();
final ValueHolder<Integer> counter = new ValueHolder<>(0);
final Sqlite.RollbackHook theHook = new Sqlite.RollbackHook(){
@Override public void call(){
++counter.value;
}
};
Sqlite.RollbackHook oldHook = db.setRollbackHook(theHook);
affirm( null == oldHook );
execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')");
affirm( 0 == counter.value );
execSql(db, false, "BEGIN; SELECT 1; SELECT 2; ROLLBACK;");
affirm( 1 == counter.value /* contra to commit hook, is invoked if no changes are made */ );
final Sqlite.RollbackHook newHook = new Sqlite.RollbackHook(){
@Override public void call(){}
};
oldHook = db.setRollbackHook(newHook);
affirm( theHook == oldHook );
execSql(db, false, "BEGIN; SELECT 1; ROLLBACK;");
affirm( 1 == counter.value );
oldHook = db.setRollbackHook(theHook);
affirm( newHook == oldHook );
execSql(db, false, "BEGIN; SELECT 1; ROLLBACK;");
affirm( 2 == counter.value );
int rc = execSql(db, false, "BEGIN; SELECT 1; ROLLBACK;");
affirm( 0 == rc );
affirm( 3 == counter.value );
db.close();
}
private void testUpdateHook(){
final Sqlite db = openDb();
final ValueHolder<Integer> counter = new ValueHolder<>(0);
final ValueHolder<Integer> expectedOp = new ValueHolder<>(0);
final Sqlite.UpdateHook theHook = new Sqlite.UpdateHook(){
@Override
public void call(int opId, String dbName, String tableName, long rowId){
++counter.value;
if( 0!=expectedOp.value ){
affirm( expectedOp.value == opId );
}
}
};
Sqlite.UpdateHook oldHook = db.setUpdateHook(theHook);
affirm( null == oldHook );
expectedOp.value = Sqlite.INSERT;
execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')");
affirm( 3 == counter.value );
expectedOp.value = Sqlite.UPDATE;
execSql(db, "update t set a='d' where a='c';");
affirm( 4 == counter.value );
oldHook = db.setUpdateHook(theHook);
affirm( theHook == oldHook );
expectedOp.value = Sqlite.DELETE;
execSql(db, "DELETE FROM t where a='d'");
affirm( 5 == counter.value );
oldHook = db.setUpdateHook(null);
affirm( theHook == oldHook );
execSql(db, "update t set a='e' where a='b';");
affirm( 5 == counter.value );
oldHook = db.setUpdateHook(null);
affirm( null == oldHook );
final Sqlite.UpdateHook newHook = new Sqlite.UpdateHook(){
@Override public void call(int opId, String dbName, String tableName, long rowId){
}
};
oldHook = db.setUpdateHook(newHook);
affirm( null == oldHook );
execSql(db, "update t set a='h' where a='a'");
affirm( 5 == counter.value );
oldHook = db.setUpdateHook(theHook);
affirm( newHook == oldHook );
expectedOp.value = Sqlite.UPDATE;
execSql(db, "update t set a='i' where a='h'");
affirm( 6 == counter.value );
db.close();
}
private void testProgress(){
final Sqlite db = openDb();
final ValueHolder<Integer> counter = new ValueHolder<>(0);
db.setProgressHandler(1, new Sqlite.ProgressHandler(){
@Override public int call(){
++counter.value;
return 0;
}
});
execSql(db, "SELECT 1; SELECT 2;");
affirm( counter.value > 0 );
int nOld = counter.value;
db.setProgressHandler(0, null);
execSql(db, "SELECT 1; SELECT 2;");
affirm( nOld == counter.value );
db.close();
}
private void testAuthorizer(){
final Sqlite db = openDb();
final ValueHolder<Integer> counter = new ValueHolder<>(0);
final ValueHolder<Integer> authRc = new ValueHolder<>(0);
final Sqlite.Authorizer auth = new Sqlite.Authorizer(){
public int call(int op, String s0, String s1, String s2, String s3){
++counter.value;
//outln("xAuth(): "+s0+" "+s1+" "+s2+" "+s3);
return authRc.value;
}
};
execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')");
db.setAuthorizer(auth);
execSql(db, "UPDATE t SET a=1");
affirm( 1 == counter.value );
authRc.value = Sqlite.DENY;
int rc = execSql(db, false, "UPDATE t SET a=2");
affirm( Sqlite.AUTH==rc );
db.setAuthorizer(null);
rc = execSql(db, false, "UPDATE t SET a=2");
affirm( 0==rc );
db.close();
}
private void testBlobOpen(){
final Sqlite db = openDb();
execSql(db, "CREATE TABLE T(a BLOB);"
+"INSERT INTO t(rowid,a) VALUES(1, 'def'),(2, 'XYZ');"
);
Sqlite.Blob b = db.blobOpen("main", "t", "a",
db.lastInsertRowId(), true);
affirm( 3==b.bytes() );
b.write(new byte[] {100, 101, 102 /*"DEF"*/}, 0);
b.close();
Sqlite.Stmt stmt = db.prepare("SELECT length(a), a FROM t ORDER BY a");
affirm( stmt.step() );
affirm( 3 == stmt.columnInt(0) );
affirm( "def".equals(stmt.columnText16(1)) );
stmt.finalizeStmt();
b = db.blobOpen("main", "t", "a", db.lastInsertRowId(), false);
b.reopen(2);
final byte[] tgt = new byte[3];
b.read( tgt, 0 );
affirm( 100==tgt[0] && 101==tgt[1] && 102==tgt[2], "DEF" );
b.close();
db.close();
}
void testPrepareMulti(){
final ValueHolder<Integer> fCount = new ValueHolder<>(0);
final ValueHolder<Integer> mCount = new ValueHolder<>(0);
try (Sqlite db = openDb()) {
execSql(db, "create table t(a); insert into t(a) values(1),(2),(3)");
db.createFunction("counter", -1, new ScalarFunction(){
@Override public void xFunc(SqlFunction.Arguments args){
++fCount.value;
args.resultNull();
}
}
);
final Sqlite.PrepareMulti pm = new Sqlite.PrepareMultiFinalize(
new Sqlite.PrepareMulti() {
@Override public void call(Sqlite.Stmt q){
++mCount.value;
while(q.step()){}
}
}
);
final String sql = "select counter(*) from t;"+
"select counter(*) from t; /* comment */"+
"select counter(*) from t; -- comment\n"
;
db.prepareMulti(sql, pm);
}
affirm( 3 == mCount.value );
affirm( 9 == fCount.value );
}
private void runTests(boolean fromThread) throws Exception {
List<java.lang.reflect.Method> mlist = testMethods;
affirm( null!=mlist );
@ -451,7 +975,7 @@ public class Tester2 implements Runnable {
mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) );
java.util.Collections.shuffle(mlist);
}
if( listRunTests ){
if( (!fromThread && listRunTests>0) || listRunTests>1 ){
synchronized(this.getClass()){
if( !fromThread ){
out("Initial test"," list: ");
@ -514,7 +1038,9 @@ public class Tester2 implements Runnable {
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.
which are hard-coded. In multi-threaded mode, use this twice to
to emit the list run by each thread (which may differ from the initial
list, in particular if -shuffle is used).
-fail: forces an exception to be thrown during the test run. Use
with -shuffle to make its appearance unpredictable.
@ -543,7 +1069,7 @@ public class Tester2 implements Runnable {
}else if(arg.equals("shuffle")){
shuffle = true;
}else if(arg.equals("list-tests")){
listRunTests = true;
++listRunTests;
}else if(arg.equals("fail")){
forceFail = true;
}else if(arg.equals("sqllog")){

View File

@ -12,10 +12,6 @@
** 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;
/**
A SqlFunction implementation for window functions. The T type

View File

@ -877,7 +877,7 @@ const char * sqlite3_wasm_enum_json(void){
DefInt(SQLITE_STMTSTATUS_FILTER_HIT);
DefInt(SQLITE_STMTSTATUS_MEMUSED);
} _DefGroup;
DefGroup(syncFlags) {
DefInt(SQLITE_SYNC_NORMAL);
DefInt(SQLITE_SYNC_FULL);
@ -901,6 +901,8 @@ const char * sqlite3_wasm_enum_json(void){
DefInt(SQLITE_DETERMINISTIC);
DefInt(SQLITE_DIRECTONLY);
DefInt(SQLITE_INNOCUOUS);
DefInt(SQLITE_SUBTYPE);
DefInt(SQLITE_RESULT_SUBTYPE);
} _DefGroup;
DefGroup(version) {

View File

@ -1688,7 +1688,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
wasm.sqlite3_wasm_vfs_unlink(0, filename);
}
}
}/*sqlite3_js_vfs_create_file()*/)
}/*sqlite3_js_posix_create_file()*/)
////////////////////////////////////////////////////////////////////
.t({
@ -2605,28 +2605,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
}
}
}/*kvvfs sanity checks*/)
.t({
name: 'kvvfs sqlite3_js_vfs_create_file()',
predicate: ()=>"kvvfs does not currently support this",
test: function(sqlite3){
let db;
try {
db = new this.JDb(this.kvvfsDbFile);
const exp = capi.sqlite3_js_db_export(db);
db.close();
this.kvvfsUnlink();
capi.sqlite3_js_vfs_create_file("kvvfs", this.kvvfsDbFile, exp);
db = new this.JDb(filename);
T.assert(6 === db.selectValue('select count(*) from kvvfs'));
}finally{
db.close();
this.kvvfsUnlink();
}
delete this.kvvfsDbFile;
delete this.kvvfsUnlink;
delete this.JDb;
}
}/*kvvfs sqlite3_js_vfs_create_file()*/)
;/* end kvvfs tests */
////////////////////////////////////////////////////////////////////////