diff --git a/GNUmakefile b/GNUmakefile index 7abd2bd3..e775fb47 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -7,7 +7,7 @@ VERSION_CODE := 28 VERSION_NUMBER := 0.0.24-wip VERSION_NAME := Honey bunches of boats. -SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3460100.zip +SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3470000.zip BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool diff --git a/deps/sqlite/shell.c b/deps/sqlite/shell.c index b08671e1..f92668be 100644 --- a/deps/sqlite/shell.c +++ b/deps/sqlite/shell.c @@ -210,8 +210,6 @@ typedef unsigned char u8; # ifndef strdup # define strdup _strdup # endif -# undef popen -# define popen _popen # undef pclose # define pclose _pclose # endif @@ -255,6 +253,343 @@ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); #endif +/************************* Begin ../ext/misc/sqlite3_stdio.h ******************/ +/* +** 2024-09-24 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This header file contains definitions of interfaces that provide +** cross-platform I/O for UTF-8 content. +** +** On most platforms, the interfaces definitions in this file are +** just #defines. For example sqlite3_fopen() is a macro that resolves +** to the standard fopen() in the C-library. +** +** But Windows does not have a standard C-library, at least not one that +** can handle UTF-8. So for windows build, the interfaces resolve to new +** C-language routines contained in the separate sqlite3_stdio.c source file. +** +** So on all non-Windows platforms, simply #include this header file and +** use the interfaces defined herein. Then to run your application on Windows, +** also link in the accompanying sqlite3_stdio.c source file when compiling +** to get compatible interfaces. +*/ +#ifndef _SQLITE3_STDIO_H_ +#define _SQLITE3_STDIO_H_ 1 +#ifdef _WIN32 +/**** Definitions For Windows ****/ +#include +#include + +FILE *sqlite3_fopen(const char *zFilename, const char *zMode); +FILE *sqlite3_popen(const char *zCommand, const char *type); +char *sqlite3_fgets(char *s, int size, FILE *stream); +int sqlite3_fputs(const char *s, FILE *stream); +int sqlite3_fprintf(FILE *stream, const char *format, ...); +void sqlite3_fsetmode(FILE *stream, int mode); + + +#else +/**** Definitions For All Other Platforms ****/ +#include +#define sqlite3_fopen fopen +#define sqlite3_popen popen +#define sqlite3_fgets fgets +#define sqlite3_fputs fputs +#define sqlite3_fprintf fprintf +#define sqlite3_fsetmode(F,X) /*no-op*/ + +#endif +#endif /* _SQLITE3_STDIO_H_ */ + +/************************* End ../ext/misc/sqlite3_stdio.h ********************/ +/************************* Begin ../ext/misc/sqlite3_stdio.c ******************/ +/* +** 2024-09-24 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** Implementation of standard I/O interfaces for UTF-8 that are missing +** on Windows. +*/ +#ifdef _WIN32 /* This file is a no-op on all platforms except Windows */ +#ifndef _SQLITE3_STDIO_H_ +/* #include "sqlite3_stdio.h" */ +#endif +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +/* #include "sqlite3.h" */ +#include +#include +#include +#include + +/* +** If the SQLITE_U8TEXT_ONLY option is defined, then use O_U8TEXT +** when appropriate on all output. (Sometimes use O_BINARY when +** rendering ASCII text in cases where NL-to-CRLF expansion would +** not be correct.) +** +** If the SQLITE_U8TEXT_STDIO option is defined, then use O_U8TEXT +** when appropriate when writing to stdout or stderr. Use O_BINARY +** or O_TEXT (depending on things like the .mode and the .crlf setting +** in the CLI, or other context clues in other applications) for all +** other output channels. +** +** The default behavior, if neither of the above is defined is to +** use O_U8TEXT when writing to the Windows console (or anything +** else for which _isatty() returns true) and to use O_BINARY or O_TEXT +** for all other output channels. +*/ +#if defined(SQLITE_U8TEXT_ONLY) +# define UseWtextForOutput(fd) 1 +# define UseWtextForInput(fd) 1 +# define IsConsole(fd) _isatty(_fileno(fd)) +#elif defined(SQLITE_U8TEXT_STDIO) +# define UseWtextForOutput(fd) ((fd)==stdout || (fd)==stderr) +# define UseWtextForInput(fd) ((fd)==stdin) +# define IsConsole(fd) _isatty(_fileno(fd)) +#else +# define UseWtextForOutput(fd) _isatty(_fileno(fd)) +# define UseWtextForInput(fd) _isatty(_fileno(fd)) +# define IsConsole(fd) 1 +#endif + +/* +** Global variables determine if simulated O_BINARY mode is to be +** used for stdout or other, respectively. Simulated O_BINARY mode +** means the mode is usually O_BINARY, but switches to O_U8TEXT for +** unicode characters U+0080 or greater (any character that has a +** multi-byte representation in UTF-8). This is the only way we +** have found to render Unicode characters on a Windows console while +** at the same time avoiding undesirable \n to \r\n translation. +*/ +static int simBinaryStdout = 0; +static int simBinaryOther = 0; + + +/* +** Determine if simulated binary mode should be used for output to fd +*/ +static int UseBinaryWText(FILE *fd){ + if( fd==stdout || fd==stderr ){ + return simBinaryStdout; + }else{ + return simBinaryOther; + } +} + + +/* +** Work-alike for the fopen() routine from the standard C library. +*/ +FILE *sqlite3_fopen(const char *zFilename, const char *zMode){ + FILE *fp = 0; + wchar_t *b1, *b2; + int sz1, sz2; + + sz1 = (int)strlen(zFilename); + sz2 = (int)strlen(zMode); + b1 = malloc( (sz1+1)*sizeof(b1[0]) ); + b2 = malloc( (sz2+1)*sizeof(b1[0]) ); + if( b1 && b2 ){ + sz1 = MultiByteToWideChar(CP_UTF8, 0, zFilename, sz1, b1, sz1); + b1[sz1] = 0; + sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2); + b2[sz2] = 0; + fp = _wfopen(b1, b2); + } + free(b1); + free(b2); + simBinaryOther = 0; + return fp; +} + + +/* +** Work-alike for the popen() routine from the standard C library. +*/ +FILE *sqlite3_popen(const char *zCommand, const char *zMode){ + FILE *fp = 0; + wchar_t *b1, *b2; + int sz1, sz2; + + sz1 = (int)strlen(zCommand); + sz2 = (int)strlen(zMode); + b1 = malloc( (sz1+1)*sizeof(b1[0]) ); + b2 = malloc( (sz2+1)*sizeof(b1[0]) ); + if( b1 && b2 ){ + sz1 = MultiByteToWideChar(CP_UTF8, 0, zCommand, sz1, b1, sz1); + b1[sz1] = 0; + sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2); + b2[sz2] = 0; + fp = _wpopen(b1, b2); + } + free(b1); + free(b2); + return fp; +} + +/* +** Work-alike for fgets() from the standard C library. +*/ +char *sqlite3_fgets(char *buf, int sz, FILE *in){ + if( UseWtextForInput(in) ){ + /* When reading from the command-prompt in Windows, it is necessary + ** to use _O_WTEXT input mode to read UTF-16 characters, then translate + ** that into UTF-8. Otherwise, non-ASCII characters all get translated + ** into '?'. + */ + wchar_t *b1 = malloc( sz*sizeof(wchar_t) ); + if( b1==0 ) return 0; + _setmode(_fileno(in), IsConsole(in) ? _O_WTEXT : _O_U8TEXT); + if( fgetws(b1, sz/4, in)==0 ){ + sqlite3_free(b1); + return 0; + } + WideCharToMultiByte(CP_UTF8, 0, b1, -1, buf, sz, 0, 0); + sqlite3_free(b1); + return buf; + }else{ + /* Reading from a file or other input source, just read bytes without + ** any translation. */ + return fgets(buf, sz, in); + } +} + +/* +** Send ASCII text as O_BINARY. But for Unicode characters U+0080 and +** greater, switch to O_U8TEXT. +*/ +static void piecemealOutput(wchar_t *b1, int sz, FILE *out){ + int i; + wchar_t c; + while( sz>0 ){ + for(i=0; i=0x80; i++){} + if( i>0 ){ + c = b1[i]; + b1[i] = 0; + fflush(out); + _setmode(_fileno(out), _O_U8TEXT); + fputws(b1, out); + fflush(out); + b1 += i; + b1[0] = c; + sz -= i; + }else{ + fflush(out); + _setmode(_fileno(out), _O_TEXT); + _setmode(_fileno(out), _O_BINARY); + fwrite(&b1[0], 1, 1, out); + for(i=1; i -#else -# define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */ +# define SQLITE_CIO_NO_FLUSH #endif -#ifndef SQLITE3_H -/* # include "sqlite3.h" */ -#endif - -#ifndef SQLITE_CIO_NO_CLASSIFY - -/* Define enum for use with following function. */ -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 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, -** 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 void consoleRenewSetup(void); - -/* -** Undo any side-effects left by consoleClassifySetup(...). -** -** 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 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 -** before so that said process will have a console setup -** however users have configured it or come to expect. -*/ -SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ); - -#else /* defined(SQLITE_CIO_NO_CLASSIFY) */ -# define consoleClassifySetup(i,o,e) -# define consoleRenewSetup() -# define consoleRestore() -#endif /* defined(SQLITE_CIO_NO_CLASSIFY) */ - -#ifndef SQLITE_CIO_NO_REDIRECT -/* -** 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); -# ifdef CONSIO_SET_ERROR_STREAM -SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf); -# endif -#else -# define setOutputStream(pf) -# define setErrorStream(pf) -#endif /* !defined(SQLITE_CIO_NO_REDIRECT) */ - -#ifndef SQLITE_CIO_NO_TRANSLATE -/* -** 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 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, ...); - -/* -** 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); -/* 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); - -/* -** Emit output like fPutsUtf8(), except that the length of the -** accepted char or character sequence is limited by nAccept. -** -** Returns the number of accepted char values. -*/ -#ifdef CONSIO_SPUTB -SQLITE_INTERNAL_LINKAGE int -fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept); -/* Like fPutbUtf8 except stream is always the designated output. */ -#endif -SQLITE_INTERNAL_LINKAGE int -oPutbUtf8(const char *cBuf, int nAccept); -/* Like fPutbUtf8 except stream is always the designated error. */ -#ifdef CONSIO_EPUTB -SQLITE_INTERNAL_LINKAGE int -ePutbUtf8(const char *cBuf, int nAccept); -#endif - -/* -** Collect input like fgets(...) with special provisions for input -** from the console on platforms that require same. Defers to the -** C library fgets() when input is not from the console. Newline -** 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); -/* Like fGetsUtf8 except stream is always the designated input. */ -/* SQLITE_INTERNAL_LINKAGE char* iGetsUtf8(char *cBuf, int ncMax); */ - -#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ - -#ifndef SQLITE_CIO_NO_SETMODE -/* -** Set given stream for binary mode, where newline translation is -** not done, or for text mode where, for some platforms, newlines -** are translated to the platform's conventional char sequence. -** If bFlush true, flush the stream. -** -** An additional side-effect is that if the stream is one passed -** to consoleClassifySetup() as an output, it is flushed first. -** -** Note that binary/text mode has no effect on console I/O -** translation. On all platforms, newline to the console starts -** a new line and CR,LF chars from the console become a newline. -*/ -SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *, short bFlush); -SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *, short bFlush); -#endif - -#ifdef SQLITE_CIO_PROMPTED_IN -typedef struct Prompts { - int numPrompts; - const char **azPrompts; -} Prompts; - -/* -** Macros for use of a line editor. -** -** The following macros define operations involving use of a -** line-editing library or simple console interaction. -** A "T" argument is a text (char *) buffer or filename. -** A "N" argument is an integer. -** -** SHELL_ADD_HISTORY(T) // Record text as line(s) of history. -** SHELL_READ_HISTORY(T) // Read history from file named by T. -** SHELL_WRITE_HISTORY(T) // Write history to file named by T. -** SHELL_STIFLE_HISTORY(N) // Limit history to N entries. -** -** A console program which does interactive console input is -** expected to call: -** SHELL_READ_HISTORY(T) before collecting such input; -** SHELL_ADD_HISTORY(T) as record-worthy input is taken; -** SHELL_STIFLE_HISTORY(N) after console input ceases; then -** SHELL_WRITE_HISTORY(T) before the program exits. -*/ - -/* -** Retrieve a single line of input text from an input stream. -** -** If pfIn is the input stream passed to consoleClassifySetup(), -** and azPrompt is not NULL, then a prompt is issued before the -** line is collected, as selected by the isContinuation flag. -** Array azPrompt[{0,1}] holds the {main,continuation} prompt. -** -** If zBufPrior is not NULL then it is a buffer from a prior -** call to this routine that can be reused, or will be freed. -** -** The result is stored in space obtained from malloc() and -** must either be freed by the caller or else passed back to -** this function as zBufPrior for reuse. -** -** This function may call upon services of a line-editing -** library to interactively collect line edited input. -*/ -SQLITE_INTERNAL_LINKAGE char * -shellGetLine(FILE *pfIn, char *zBufPrior, int nLen, - short isContinuation, Prompts azPrompt); -#endif /* defined(SQLITE_CIO_PROMPTED_IN) */ -/* -** TBD: Define an interface for application(s) to generate -** completion candidates for use by the line-editor. -** -** This may be premature; the CLI is the only application -** that does this. Yet, getting line-editing melded into -** console I/O is desirable because a line-editing library -** may have to establish console operating mode, possibly -** in a way that interferes with the above functionality. -*/ - -#if !(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE)) -/* Skip over as much z[] input char sequence as is valid UTF-8, -** limited per nAccept char's or whole characters and containing -** no char cn such that ((1<=0 => char count, nAccept<0 => character - */ -SQLITE_INTERNAL_LINKAGE const char* -zSkipValidUtf8(const char *z, int nAccept, long ccm); - -#endif - -/************************* End ../ext/consio/console_io.h ********************/ -/************************* Begin ../ext/consio/console_io.c ******************/ -/* -** 2023 November 4 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -******************************************************************************** -** This file 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 -# define SQLITE_CDECL -#endif - -#ifndef SHELL_NO_SYSINC -# include -# include -# include -# include -# include -/* # include "sqlite3.h" */ -#endif -#ifndef HAVE_CONSOLE_IO_H -# include "console_io.h" -#endif -#if defined(_MSC_VER) -# pragma warning(disable : 4204) -#endif - -#ifndef SQLITE_CIO_NO_TRANSLATE -# if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT -# ifndef SHELL_NO_SYSINC -# include -# include -# undef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# include -# endif -# define CIO_WIN_WC_XLATE 1 /* Use WCHAR Windows APIs for console I/O */ -# else -# ifndef SHELL_NO_SYSINC -# include -# endif -# define CIO_WIN_WC_XLATE 0 /* Use plain C library stream I/O at console */ -# endif -#else -# define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */ -#endif - -#if CIO_WIN_WC_XLATE -static HANDLE handleOfFile(FILE *pf){ - int fileDesc = _fileno(pf); - union { intptr_t osfh; HANDLE fh; } fid = { - (fileDesc>=0)? _get_osfhandle(fileDesc) : (intptr_t)INVALID_HANDLE_VALUE - }; - return fid.fh; -} -#endif - -#ifndef SQLITE_CIO_NO_TRANSLATE -typedef struct PerStreamTags { -# if CIO_WIN_WC_XLATE - HANDLE hx; - DWORD consMode; - char acIncomplete[4]; -# else - short reachesConsole; -# endif - FILE *pf; -} PerStreamTags; - -/* Define NULL-like value for things which can validly be 0. */ -# define SHELL_INVALID_FILE_PTR ((FILE *)~0) -# if CIO_WIN_WC_XLATE -# define SHELL_INVALID_CONS_MODE 0xFFFF0000 -# endif - -# if CIO_WIN_WC_XLATE -# define PST_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \ - {0,0,0,0}, SHELL_INVALID_FILE_PTR } -# else -# define PST_INITIALIZER { 0, SHELL_INVALID_FILE_PTR } -# endif - -/* Quickly say whether a known output is going to the console. */ -# if CIO_WIN_WC_XLATE -static short pstReachesConsole(PerStreamTags *ppst){ - return (ppst->hx != INVALID_HANDLE_VALUE); -} -# else -# define pstReachesConsole(ppst) 0 -# endif - -# if CIO_WIN_WC_XLATE -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 CIO_WIN_WC_XLATE - 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 -} - -# ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING -# define ENABLE_VIRTUAL_TERMINAL_PROCESSING (0x4) -# endif - -# if CIO_WIN_WC_XLATE -/* 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) -# define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \ - | ENABLE_VIRTUAL_TERMINAL_PROCESSING) -# endif - -typedef struct ConsoleInfo { - PerStreamTags pstSetup[3]; - PerStreamTags pstDesignated[3]; - StreamsAreConsole sacSetup; -} ConsoleInfo; - -static short isValidStreamInfo(PerStreamTags *ppst){ - return (ppst->pf != SHELL_INVALID_FILE_PTR); -} - -static ConsoleInfo consoleInfo = { - { /* pstSetup */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER }, - { /* pstDesignated[] */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER }, - SAC_NoConsole /* sacSetup */ -}; - -SQLITE_INTERNAL_LINKAGE FILE* invalidFileStream = (FILE *)~0; - -# if CIO_WIN_WC_XLATE -static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){ - if( pstReachesConsole(ppst) ){ - DWORD cm = odir? SHELL_CONO_MODE : SHELL_CONI_MODE; - SetConsoleMode(ppst->hx, cm); - } -} -# else -# define maybeSetupAsConsole(ppst,odir) -# endif - -SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void){ -# if CIO_WIN_WC_XLATE - 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 ){ - StreamsAreConsole rv = SAC_NoConsole; - FILE* apf[3] = { pfIn, pfOut, pfErr }; - int ix; - for( ix = 2; ix >= 0; --ix ){ - PerStreamTags *ppst = &consoleInfo.pstSetup[ix]; - if( streamOfConsole(apf[ix], ppst) ){ - rv |= (SAC_InConsole< 0 ) fflush(apf[ix]); - } - consoleInfo.sacSetup = rv; - consoleRenewSetup(); - return rv; -} - -SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ -# if CIO_WIN_WC_XLATE - static ConsoleInfo *pci = &consoleInfo; - if( pci->sacSetup ){ - int ix; - for( ix=0; ix<3; ++ix ){ - if( pci->sacSetup & (SAC_InConsole<pstSetup[ix]; - SetConsoleMode(ppst->hx, ppst->consMode); - } - } - } -# endif -} -#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ - -#ifdef SQLITE_CIO_INPUT_REDIR -/* 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]; -} -#endif - -#ifndef SQLITE_CIO_NO_TRANSLATE -/* 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); -} -# ifdef CONSIO_SET_ERROR_STREAM -SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf){ - return designateEmitStream(pf, 2); -} -# endif -#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ - -#ifndef SQLITE_CIO_NO_SETMODE -# if CIO_WIN_WC_XLATE -static void setModeFlushQ(FILE *pf, short bFlush, int mode){ - if( bFlush ) fflush(pf); - _setmode(_fileno(pf), mode); -} -# else -# define setModeFlushQ(f, b, m) if(b) fflush(f) -# endif - -SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *pf, short bFlush){ - setModeFlushQ(pf, bFlush, _O_BINARY); -} -SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){ - setModeFlushQ(pf, bFlush, _O_TEXT); -} -# undef setModeFlushQ - -#else /* defined(SQLITE_CIO_NO_SETMODE) */ -# define setBinaryMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0) -# define setTextMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0) -#endif /* defined(SQLITE_CIO_NO_SETMODE) */ - -#ifndef SQLITE_CIO_NO_TRANSLATE -# if CIO_WIN_WC_XLATE -/* Write buffer cBuf as output to stream known to reach console, -** limited to ncTake char's. Return ncTake on success, else 0. */ -static int conZstrEmit(PerStreamTags *ppst, const char *z, int ncTake){ - int rv = 0; - if( z!=NULL ){ - int nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, 0,0); - if( nwc > 0 ){ - WCHAR *zw = sqlite3_malloc64(nwc*sizeof(WCHAR)); - if( zw!=NULL ){ - nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, zw,nwc); - if( nwc > 0 ){ - /* Translation from UTF-8 to UTF-16, then WCHARs out. */ - if( WriteConsoleW(ppst->hx, zw,nwc, 0, NULL) ){ - rv = ncTake; - } - } - sqlite3_free(zw); - } - } - } - return rv; -} - -/* 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); - if( z ){ - int rv = conZstrEmit(ppst, z, (int)strlen(z)); - sqlite3_free(z); - return rv; - }else return 0; -} -# endif /* CIO_WIN_WC_XLATE */ - -# ifdef CONSIO_GET_EMIT_STREAM -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; -} -# endif - -/* 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 a -** byref output when chix!=0 and a byref 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(ppstTry) ){ - 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 = PST_INITIALIZER; /* for unknown streams */ -# if CIO_WIN_WC_XLATE - PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); -# else - getEmitStreamInfo(1, &pst, &pfOut); -# endif - assert(zFormat!=0); - va_start(ap, zFormat); -# if CIO_WIN_WC_XLATE - if( pstReachesConsole(ppst) ){ - rv = conioVmPrintf(ppst, zFormat, ap); - }else{ -# endif - rv = vfprintf(pfOut, zFormat, ap); -# if CIO_WIN_WC_XLATE - } -# endif - va_end(ap); - return rv; -} - -SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...){ - va_list ap; - int rv; - FILE *pfErr; - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -# if CIO_WIN_WC_XLATE - PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); -# else - getEmitStreamInfo(2, &pst, &pfErr); -# endif - assert(zFormat!=0); - va_start(ap, zFormat); -# if CIO_WIN_WC_XLATE - if( pstReachesConsole(ppst) ){ - rv = conioVmPrintf(ppst, zFormat, ap); - }else{ -# endif - rv = vfprintf(pfErr, zFormat, ap); -# if CIO_WIN_WC_XLATE - } -# endif - va_end(ap); - return rv; -} - -SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...){ - va_list ap; - int rv; - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -# if CIO_WIN_WC_XLATE - PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); -# else - getEmitStreamInfo(0, &pst, &pfO); -# endif - assert(zFormat!=0); - va_start(ap, zFormat); -# if CIO_WIN_WC_XLATE - 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 CIO_WIN_WC_XLATE - } -# endif - va_end(ap); - return rv; -} - -SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){ - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -# if CIO_WIN_WC_XLATE - PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); -# else - getEmitStreamInfo(0, &pst, &pfO); -# endif - assert(z!=0); -# if CIO_WIN_WC_XLATE - if( pstReachesConsole(ppst) ){ - int rv; - maybeSetupAsConsole(ppst, 1); - rv = conZstrEmit(ppst, z, (int)strlen(z)); - if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); - return rv; - }else { -# endif - return (fputs(z, pfO)<0)? 0 : (int)strlen(z); -# if CIO_WIN_WC_XLATE - } -# endif -} - -SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){ - FILE *pfErr; - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -# if CIO_WIN_WC_XLATE - PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); -# else - getEmitStreamInfo(2, &pst, &pfErr); -# endif - assert(z!=0); -# if CIO_WIN_WC_XLATE - if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); - else { -# endif - return (fputs(z, pfErr)<0)? 0 : (int)strlen(z); -# if CIO_WIN_WC_XLATE - } -# endif -} - -SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){ - FILE *pfOut; - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -# if CIO_WIN_WC_XLATE - PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); -# else - getEmitStreamInfo(1, &pst, &pfOut); -# endif - assert(z!=0); -# if CIO_WIN_WC_XLATE - if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); - else { -# endif - return (fputs(z, pfOut)<0)? 0 : (int)strlen(z); -# if CIO_WIN_WC_XLATE - } -# endif -} - -#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ - -#if !(defined(SQLITE_CIO_NO_UTF8SCAN) && defined(SQLITE_CIO_NO_TRANSLATE)) -/* Skip over as much z[] input char sequence as is valid UTF-8, -** limited per nAccept char's or whole characters and containing -** no char cn such that ((1<=0 => char count, nAccept<0 => character - */ -SQLITE_INTERNAL_LINKAGE const char* -zSkipValidUtf8(const char *z, int nAccept, long ccm){ - int ng = (nAccept<0)? -nAccept : 0; - const char *pcLimit = (nAccept>=0)? z+nAccept : 0; - assert(z!=0); - while( (pcLimit)? (z= pcLimit ) return z; - else{ - char ct = *zt++; - if( ct==0 || (zt-z)>4 || (ct & 0xC0)!=0x80 ){ - /* Trailing bytes are too few, too many, or invalid. */ - return z; - } - } - } while( ((c <<= 1) & 0x40) == 0x40 ); /* Eat lead byte's count. */ - z = zt; - } - } - return z; -} -#endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/ - -#ifndef SQLITE_CIO_NO_TRANSLATE -# ifdef CONSIO_SPUTB -SQLITE_INTERNAL_LINKAGE int -fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){ - assert(pfO!=0); -# if CIO_WIN_WC_XLATE - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ - PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); - if( pstReachesConsole(ppst) ){ - int rv; - maybeSetupAsConsole(ppst, 1); - rv = conZstrEmit(ppst, cBuf, nAccept); - if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); - return rv; - }else { -# endif - return (int)fwrite(cBuf, 1, nAccept, pfO); -# if CIO_WIN_WC_XLATE - } -# endif -} -# endif - -SQLITE_INTERNAL_LINKAGE int -oPutbUtf8(const char *cBuf, int nAccept){ - FILE *pfOut; - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ -# if CIO_WIN_WC_XLATE - PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); -# else - getEmitStreamInfo(1, &pst, &pfOut); -# endif -# if CIO_WIN_WC_XLATE - if( pstReachesConsole(ppst) ){ - return conZstrEmit(ppst, cBuf, nAccept); - }else { -# endif - return (int)fwrite(cBuf, 1, nAccept, pfOut); -# if CIO_WIN_WC_XLATE - } -# endif -} - -# ifdef CONSIO_EPUTB -SQLITE_INTERNAL_LINKAGE int -ePutbUtf8(const char *cBuf, int nAccept){ - FILE *pfErr; - PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ - PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); -# if CIO_WIN_WC_XLATE - if( pstReachesConsole(ppst) ){ - return conZstrEmit(ppst, cBuf, nAccept); - }else { -# endif - return (int)fwrite(cBuf, 1, nAccept, pfErr); -# if CIO_WIN_WC_XLATE - } -# endif -} -# endif /* defined(CONSIO_EPUTB) */ - -SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ - if( pfIn==0 ) pfIn = stdin; -# if CIO_WIN_WC_XLATE - if( pfIn == consoleInfo.pstSetup[0].pf - && (consoleInfo.sacSetup & SAC_InConsole)!=0 ){ -# if CIO_WIN_WC_XLATE==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( 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.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.pstSetup[0].hx, wcBuf+nbr, 1, &nbrx, 0); - if( bRC ) nbr += nbrx; - } - if( !bRC || (noc==0 && nbr==0) ) return 0; - if( nbr > 0 ){ - int nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,0,0,0,0); - if( nmb != 0 && noc+nmb <= ncMax ){ - int iseg = noc; - 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".) - ** 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-1] = '\n'; - } - } - /* Check for ^Z (anywhere in line) too, to act as EOF. */ - while( iseg < noc ){ - if( cBuf[iseg]=='\x1a' ){ - noc = iseg; /* Chop ^Z and anything following. */ - lend = 1; /* Counts as end of line too. */ - break; - } - ++iseg; - } - }else break; /* Drop apparent garbage in. (Could assert.) */ - }else break; - } - /* If got nothing, (after ^Z chop), must be at end-of-file. */ - if( noc > 0 ){ - cBuf[noc] = 0; - return cBuf; - }else return 0; -# endif - }else{ -# endif - return fgets(cBuf, ncMax, pfIn); -# if CIO_WIN_WC_XLATE - } -# endif -} -#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ - -#if defined(_MSC_VER) -# pragma warning(default : 4204) -#endif - -#undef SHELL_INVALID_FILE_PTR - -/************************* End ../ext/consio/console_io.c ********************/ - -#ifndef SQLITE_SHELL_FIDDLE - -/* From here onward, fgets() is redirected to the console_io library. */ -# define fgets(b,n,f) fGetsUtf8(b,n,f) -/* - * Define macros for emitting output text in various ways: - * sputz(s, z) => emit 0-terminated string z to given stream s - * sputf(s, f, ...) => emit varargs per format f to given stream s - * oputz(z) => emit 0-terminated string z to default stream - * oputf(f, ...) => emit varargs per format f to default stream - * eputz(z) => emit 0-terminated string z to error stream - * eputf(f, ...) => emit varargs per format f to error stream - * oputb(b, n) => emit char buffer b[0..n-1] to default stream - * - * Note that the default stream is whatever has been last set via: - * setOutputStream(FILE *pf) - * This is normally the stream that CLI normal output goes to. - * For the stand-alone CLI, it is stdout with no .output redirect. - * - * The ?putz(z) forms are required for the Fiddle builds for string literal - * output, in aid of enforcing format string to argument correspondence. - */ -# define sputz(s,z) fPutsUtf8(z,s) -# define sputf fPrintfUtf8 -# define oputz(z) oPutsUtf8(z) -# define oputf oPrintfUtf8 -# define eputz(z) ePutsUtf8(z) -# define eputf ePrintfUtf8 -# define oputb(buf,na) oPutbUtf8(buf,na) - -#else -/* For Fiddle, all console handling and emit redirection is omitted. */ -/* These next 3 macros are for emitting formatted output. When complaints - * from the WASM build are issued for non-formatted output, (when a mere - * string literal is to be emitted, the ?putz(z) forms should be used. - * (This permits compile-time checking of format string / argument mismatch.) - */ -# define oputf(fmt, ...) printf(fmt,__VA_ARGS__) -# define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__) -# define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__) -/* These next 3 macros are for emitting simple string literals. */ -# define oputz(z) fputs(z,stdout) -# define eputz(z) fputs(z,stderr) -# define sputz(fp,z) fputs(z,fp) -# define oputb(buf,na) fwrite(buf,1,na,stdout) -#endif +#define eputz(z) sqlite3_fputs(z,stderr) +#define sputz(fp,z) sqlite3_fputs(z,fp) /* True if the timer is enabled */ static int enableTimer = 0; @@ -1329,6 +649,7 @@ struct rusage { #define getrusage(A,B) memset(B,0,sizeof(*B)) #endif + /* Saved resource information for the beginning of an operation */ static struct rusage sBegin; /* CPU time at start */ static sqlite3_int64 iBegin; /* Wall-clock time at start */ @@ -1352,12 +673,12 @@ static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ /* ** Print the timing results. */ -static void endTimer(void){ +static void endTimer(FILE *out){ if( enableTimer ){ sqlite3_int64 iEnd = timeOfDay(); struct rusage sEnd; getrusage(RUSAGE_SELF, &sEnd); - sputf(stdout, "Run Time: real %.3f user %f sys %f\n", + sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n", (iEnd - iBegin)*0.001, timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); @@ -1365,7 +686,7 @@ static void endTimer(void){ } #define BEGIN_TIMER beginTimer() -#define END_TIMER endTimer() +#define END_TIMER(X) endTimer(X) #define HAS_TIMER 1 #elif (defined(_WIN32) || defined(WIN32)) @@ -1431,12 +752,12 @@ static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ /* ** Print the timing results. */ -static void endTimer(void){ +static void endTimer(FILE *out){ if( enableTimer && getProcessTimesAddr){ FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; sqlite3_int64 ftWallEnd = timeOfDay(); getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); - sputf(stdout, "Run Time: real %.3f user %f sys %f\n", + sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n", (ftWallEnd - ftWallBegin)*0.001, timeDiff(&ftUserBegin, &ftUserEnd), timeDiff(&ftKernelBegin, &ftKernelEnd)); @@ -1444,12 +765,12 @@ static void endTimer(void){ } #define BEGIN_TIMER beginTimer() -#define END_TIMER endTimer() +#define END_TIMER(X) endTimer(X) #define HAS_TIMER hasTimer() #else #define BEGIN_TIMER -#define END_TIMER +#define END_TIMER(X) /*no-op*/ #define HAS_TIMER 0 #endif @@ -1522,6 +843,14 @@ static char *shell_strncpy(char *dest, const char *src, size_t n){ return dest; } +/* +** strcpy() workalike to squelch an unwarranted link-time warning +** from OpenBSD. +*/ +static void shell_strcpy(char *dest, const char *src){ + while( (*(dest++) = *(src++))!=0 ){} +} + /* ** Optionally disable dynamic continuation prompt. ** Unless disabled, the continuation prompt shows open SQL lexemes if any, @@ -1587,7 +916,7 @@ static char *dynamicContinuePrompt(void){ size_t ncp = strlen(continuePrompt); size_t ndp = strlen(dynPrompt.zScannerAwaits); if( ndp > ncp-3 ) return continuePrompt; - strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits); + shell_strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits); while( ndp<3 ) dynPrompt.dynamicPrompt[ndp++] = ' '; shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, PROMPT_LEN_MAX-4); @@ -1642,37 +971,212 @@ static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){ va_start(ap, zFormat); z = sqlite3_vmprintf(zFormat, ap); va_end(ap); - sputf(iotrace, "%s", z); + sqlite3_fprintf(iotrace, "%s", z); sqlite3_free(z); } #endif +/* Lookup table to estimate the number of columns consumed by a Unicode +** character. +*/ +static const struct { + unsigned char w; /* Width of the character in columns */ + int iFirst; /* First character in a span having this width */ +} aUWidth[] = { + /* {1, 0x00000}, */ + {0, 0x00300}, {1, 0x00370}, {0, 0x00483}, {1, 0x00487}, {0, 0x00488}, + {1, 0x0048a}, {0, 0x00591}, {1, 0x005be}, {0, 0x005bf}, {1, 0x005c0}, + {0, 0x005c1}, {1, 0x005c3}, {0, 0x005c4}, {1, 0x005c6}, {0, 0x005c7}, + {1, 0x005c8}, {0, 0x00600}, {1, 0x00604}, {0, 0x00610}, {1, 0x00616}, + {0, 0x0064b}, {1, 0x0065f}, {0, 0x00670}, {1, 0x00671}, {0, 0x006d6}, + {1, 0x006e5}, {0, 0x006e7}, {1, 0x006e9}, {0, 0x006ea}, {1, 0x006ee}, + {0, 0x0070f}, {1, 0x00710}, {0, 0x00711}, {1, 0x00712}, {0, 0x00730}, + {1, 0x0074b}, {0, 0x007a6}, {1, 0x007b1}, {0, 0x007eb}, {1, 0x007f4}, + {0, 0x00901}, {1, 0x00903}, {0, 0x0093c}, {1, 0x0093d}, {0, 0x00941}, + {1, 0x00949}, {0, 0x0094d}, {1, 0x0094e}, {0, 0x00951}, {1, 0x00955}, + {0, 0x00962}, {1, 0x00964}, {0, 0x00981}, {1, 0x00982}, {0, 0x009bc}, + {1, 0x009bd}, {0, 0x009c1}, {1, 0x009c5}, {0, 0x009cd}, {1, 0x009ce}, + {0, 0x009e2}, {1, 0x009e4}, {0, 0x00a01}, {1, 0x00a03}, {0, 0x00a3c}, + {1, 0x00a3d}, {0, 0x00a41}, {1, 0x00a43}, {0, 0x00a47}, {1, 0x00a49}, + {0, 0x00a4b}, {1, 0x00a4e}, {0, 0x00a70}, {1, 0x00a72}, {0, 0x00a81}, + {1, 0x00a83}, {0, 0x00abc}, {1, 0x00abd}, {0, 0x00ac1}, {1, 0x00ac6}, + {0, 0x00ac7}, {1, 0x00ac9}, {0, 0x00acd}, {1, 0x00ace}, {0, 0x00ae2}, + {1, 0x00ae4}, {0, 0x00b01}, {1, 0x00b02}, {0, 0x00b3c}, {1, 0x00b3d}, + {0, 0x00b3f}, {1, 0x00b40}, {0, 0x00b41}, {1, 0x00b44}, {0, 0x00b4d}, + {1, 0x00b4e}, {0, 0x00b56}, {1, 0x00b57}, {0, 0x00b82}, {1, 0x00b83}, + {0, 0x00bc0}, {1, 0x00bc1}, {0, 0x00bcd}, {1, 0x00bce}, {0, 0x00c3e}, + {1, 0x00c41}, {0, 0x00c46}, {1, 0x00c49}, {0, 0x00c4a}, {1, 0x00c4e}, + {0, 0x00c55}, {1, 0x00c57}, {0, 0x00cbc}, {1, 0x00cbd}, {0, 0x00cbf}, + {1, 0x00cc0}, {0, 0x00cc6}, {1, 0x00cc7}, {0, 0x00ccc}, {1, 0x00cce}, + {0, 0x00ce2}, {1, 0x00ce4}, {0, 0x00d41}, {1, 0x00d44}, {0, 0x00d4d}, + {1, 0x00d4e}, {0, 0x00dca}, {1, 0x00dcb}, {0, 0x00dd2}, {1, 0x00dd5}, + {0, 0x00dd6}, {1, 0x00dd7}, {0, 0x00e31}, {1, 0x00e32}, {0, 0x00e34}, + {1, 0x00e3b}, {0, 0x00e47}, {1, 0x00e4f}, {0, 0x00eb1}, {1, 0x00eb2}, + {0, 0x00eb4}, {1, 0x00eba}, {0, 0x00ebb}, {1, 0x00ebd}, {0, 0x00ec8}, + {1, 0x00ece}, {0, 0x00f18}, {1, 0x00f1a}, {0, 0x00f35}, {1, 0x00f36}, + {0, 0x00f37}, {1, 0x00f38}, {0, 0x00f39}, {1, 0x00f3a}, {0, 0x00f71}, + {1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88}, + {0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6}, + {1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033}, + {0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058}, + {1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f}, + {1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735}, + {0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4}, + {1, 0x017b6}, {0, 0x017b7}, {1, 0x017be}, {0, 0x017c6}, {1, 0x017c7}, + {0, 0x017c9}, {1, 0x017d4}, {0, 0x017dd}, {1, 0x017de}, {0, 0x0180b}, + {1, 0x0180e}, {0, 0x018a9}, {1, 0x018aa}, {0, 0x01920}, {1, 0x01923}, + {0, 0x01927}, {1, 0x01929}, {0, 0x01932}, {1, 0x01933}, {0, 0x01939}, + {1, 0x0193c}, {0, 0x01a17}, {1, 0x01a19}, {0, 0x01b00}, {1, 0x01b04}, + {0, 0x01b34}, {1, 0x01b35}, {0, 0x01b36}, {1, 0x01b3b}, {0, 0x01b3c}, + {1, 0x01b3d}, {0, 0x01b42}, {1, 0x01b43}, {0, 0x01b6b}, {1, 0x01b74}, + {0, 0x01dc0}, {1, 0x01dcb}, {0, 0x01dfe}, {1, 0x01e00}, {0, 0x0200b}, + {1, 0x02010}, {0, 0x0202a}, {1, 0x0202f}, {0, 0x02060}, {1, 0x02064}, + {0, 0x0206a}, {1, 0x02070}, {0, 0x020d0}, {1, 0x020f0}, {2, 0x02329}, + {1, 0x0232b}, {2, 0x02e80}, {0, 0x0302a}, {2, 0x03030}, {1, 0x0303f}, + {2, 0x03040}, {0, 0x03099}, {2, 0x0309b}, {1, 0x0a4d0}, {0, 0x0a806}, + {1, 0x0a807}, {0, 0x0a80b}, {1, 0x0a80c}, {0, 0x0a825}, {1, 0x0a827}, + {2, 0x0ac00}, {1, 0x0d7a4}, {2, 0x0f900}, {1, 0x0fb00}, {0, 0x0fb1e}, + {1, 0x0fb1f}, {0, 0x0fe00}, {2, 0x0fe10}, {1, 0x0fe1a}, {0, 0x0fe20}, + {1, 0x0fe24}, {2, 0x0fe30}, {1, 0x0fe70}, {0, 0x0feff}, {2, 0x0ff00}, + {1, 0x0ff61}, {2, 0x0ffe0}, {1, 0x0ffe7}, {0, 0x0fff9}, {1, 0x0fffc}, + {0, 0x10a01}, {1, 0x10a04}, {0, 0x10a05}, {1, 0x10a07}, {0, 0x10a0c}, + {1, 0x10a10}, {0, 0x10a38}, {1, 0x10a3b}, {0, 0x10a3f}, {1, 0x10a40}, + {0, 0x1d167}, {1, 0x1d16a}, {0, 0x1d173}, {1, 0x1d183}, {0, 0x1d185}, + {1, 0x1d18c}, {0, 0x1d1aa}, {1, 0x1d1ae}, {0, 0x1d242}, {1, 0x1d245}, + {2, 0x20000}, {1, 0x2fffe}, {2, 0x30000}, {1, 0x3fffe}, {0, 0xe0001}, + {1, 0xe0002}, {0, 0xe0020}, {1, 0xe0080}, {0, 0xe0100}, {1, 0xe01f0} +}; + /* -** Output string zUtf to Out stream as w characters. If w is negative, +** Return an estimate of the width, in columns, for the single Unicode +** character c. For normal characters, the answer is always 1. But the +** estimate might be 0 or 2 for zero-width and double-width characters. +** +** Different display devices display unicode using different widths. So +** it is impossible to know that true display width with 100% accuracy. +** Inaccuracies in the width estimates might cause columns to be misaligned. +** Unfortunately, there is nothing we can do about that. +*/ +int cli_wcwidth(int c){ + int iFirst, iLast; + + /* Fast path for common characters */ + if( c<=0x300 ) return 1; + + /* The general case */ + iFirst = 0; + iLast = sizeof(aUWidth)/sizeof(aUWidth[0]) - 1; + while( iFirst c ){ + iLast = iMid - 1; + }else{ + return aUWidth[iMid].w; + } + } + if( aUWidth[iLast].iFirst > c ) return aUWidth[iFirst].w; + return aUWidth[iLast].w; +} + +/* +** Compute the value and length of a multi-byte UTF-8 character that +** begins at z[0]. Return the length. Write the Unicode value into *pU. +** +** This routine only works for *multi-byte* UTF-8 characters. +*/ +static int decodeUtf8(const unsigned char *z, int *pU){ + if( (z[0] & 0xe0)==0xc0 && (z[1] & 0xc0)==0x80 ){ + *pU = ((z[0] & 0x1f)<<6) | (z[1] & 0x3f); + return 2; + } + if( (z[0] & 0xf0)==0xe0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 ){ + *pU = ((z[0] & 0x0f)<<12) | ((z[1] & 0x3f)<<6) | (z[2] & 0x3f); + return 3; + } + if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 + && (z[3] & 0xc0)==0x80 + ){ + *pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6 + | (z[4] & 0x3f); + return 4; + } + *pU = 0; + return 1; +} + + +#if 0 /* NOT USED */ +/* +** Return the width, in display columns, of a UTF-8 string. +** +** Each normal character counts as 1. Zero-width characters count +** as zero, and double-width characters count as 2. +*/ +int cli_wcswidth(const char *z){ + const unsigned char *a = (const unsigned char*)z; + int n = 0; + int i = 0; + unsigned char c; + while( (c = a[i])!=0 ){ + if( c>=0xc0 ){ + int u; + int len = decodeUtf8(&a[i], &u); + i += len; + n += cli_wcwidth(u); + }else if( c>=' ' ){ + n++; + i++; + }else{ + i++; + } + } + return n; +} +#endif + +/* +** Output string zUtf to stdout as w characters. If w is negative, ** then right-justify the text. W is the width in UTF-8 characters, not ** in bytes. This is different from the %*.*s specification in printf ** since with %*.*s the width is measured in bytes, not characters. +** +** Take into account zero-width and double-width Unicode characters. +** In other words, a zero-width character does not count toward the +** the w limit. A double-width character counts as two. */ -static void utf8_width_print(int w, const char *zUtf){ - int i; - int n; +static void utf8_width_print(FILE *out, int w, const char *zUtf){ + const unsigned char *a = (const unsigned char*)zUtf; + unsigned char c; + int i = 0; + int n = 0; int aw = w<0 ? -w : w; if( zUtf==0 ) zUtf = ""; - for(i=n=0; zUtf[i]; i++){ - if( (zUtf[i]&0xc0)!=0x80 ){ - n++; - if( n==aw ){ - do{ i++; }while( (zUtf[i]&0xc0)==0x80 ); + while( (c = a[i])!=0 ){ + if( (c&0xc0)==0xc0 ){ + int u; + int len = decodeUtf8(a+i, &u); + int x = cli_wcwidth(u); + if( x+n>aw ){ break; } + i += len; + n += x; + }else if( n>=aw ){ + break; + }else{ + n++; + i++; } } if( n>=aw ){ - oputf("%.*s", i, zUtf); + sqlite3_fprintf(out, "%.*s", i, zUtf); }else if( w<0 ){ - oputf("%*s%s", aw-n, "", zUtf); + sqlite3_fprintf(out, "%*s%s", aw-n, "", zUtf); }else{ - oputf("%s%*s", zUtf, aw-n, ""); + sqlite3_fprintf(out, "%s%*s", zUtf, aw-n, ""); } } @@ -1738,7 +1242,7 @@ static FILE * openChrSource(const char *zFile){ /* On Windows, open first, then check the stream nature. This order ** is necessary because _stat() and sibs, when checking a named pipe, ** effectively break the pipe as its supplier sees it. */ - FILE *rv = fopen(zFile, "rb"); + FILE *rv = sqlite3_fopen(zFile, "rb"); if( rv==0 ) return 0; if( _fstat64(_fileno(rv), &x) != 0 || !STAT_CHR_SRC(x.st_mode)){ @@ -1752,7 +1256,7 @@ static FILE * openChrSource(const char *zFile){ # define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode)) if( rc!=0 ) return 0; if( STAT_CHR_SRC(x.st_mode) ){ - return fopen(zFile, "rb"); + return sqlite3_fopen(zFile, "rb"); }else{ return 0; } @@ -1779,7 +1283,7 @@ static char *local_getline(char *zLine, FILE *in){ zLine = realloc(zLine, nLine); shell_check_oom(zLine); } - if( fgets(&zLine[n], nLine - n, in)==0 ){ + if( sqlite3_fgets(&zLine[n], nLine - n, in)==0 ){ if( n==0 ){ free(zLine); return 0; @@ -2848,10 +2352,20 @@ int sqlite3PcacheTraceDeactivate(void){ ** Two SQL functions are implemented: ** ** sha3(X,SIZE) -** sha3_query(Y,SIZE) +** sha3_agg(Y,SIZE) +** sha3_query(Z,SIZE) ** ** The sha3(X) function computes the SHA3 hash of the input X, or NULL if -** X is NULL. +** X is NULL. If inputs X is text, the UTF-8 rendering of that text is +** used to compute the hash. If X is a BLOB, then the binary data of the +** blob is used to compute the hash. If X is an integer or real number, +** then that number if converted into UTF-8 text and the hash is computed +** over the text. +** +** The sha3_agg(Y) function computes the SHA3 hash of all Y inputs. Since +** order is important for the hash, it is recommended that the Y expression +** by followed by an ORDER BY clause to guarantee that the inputs occur +** in the desired order. ** ** The sha3_query(Y) function evaluates all queries in the SQL statements of Y ** and returns a hash of their results. @@ -2859,6 +2373,68 @@ int sqlite3PcacheTraceDeactivate(void){ ** The SIZE argument is optional. If omitted, the SHA3-256 hash algorithm ** is used. If SIZE is included it must be one of the integers 224, 256, ** 384, or 512, to determine SHA3 hash variant that is computed. +** +** Because the sha3_agg() and sha3_query() functions compute a hash over +** multiple values, the values are encode to use include type information. +** +** In sha3_agg(), the sequence of bytes that gets hashed for each input +** Y depends on the datatype of Y: +** +** typeof(Y)='null' A single "N" is hashed. (One byte) +** +** typeof(Y)='integer' The data hash is the character "I" followed +** by an 8-byte big-endian binary of the +** 64-bit signed integer. (Nine bytes total.) +** +** typeof(Y)='real' The character "F" followed by an 8-byte +** big-ending binary of the double. (Nine +** bytes total.) +** +** typeof(Y)='text' The hash is over prefix "Tnnn:" followed +** by the UTF8 encoding of the text. The "nnn" +** in the prefix is the minimum-length decimal +** representation of the octet_length of the text. +** Notice the ":" at the end of the prefix, which +** is needed to separate the prefix from the +** content in cases where the content starts +** with a digit. +** +** typeof(Y)='blob' The hash is taken over prefix "Bnnn:" followed +** by the binary content of the blob. The "nnn" +** in the prefix is the mimimum-length decimal +** representation of the byte-length of the blob. +** +** According to the rules above, all of the following SELECT statements +** should return TRUE: +** +** SELECT sha3(1) = sha3('1'); +** +** SELECT sha3('hello') = sha3(x'68656c6c6f'); +** +** WITH a(x) AS (VALUES('xyzzy')) +** SELECT sha3_agg(x) = sha3('T5:xyzzy') FROM a; +** +** WITH a(x) AS (VALUES(x'010203')) +** SELECT sha3_agg(x) = sha3(x'42333a010203') FROM a; +** +** WITH a(x) AS (VALUES(0x123456)) +** SELECT sha3_agg(x) = sha3(x'490000000000123456') FROM a; +** +** WITH a(x) AS (VALUES(100.015625)) +** SELECT sha3_agg(x) = sha3(x'464059010000000000') FROM a; +** +** WITH a(x) AS (VALUES(NULL)) +** SELECT sha3_agg(x) = sha3('N') FROM a; +** +** +** In sha3_query(), individual column values are encoded as with +** sha3_agg(), but with the addition that a single "R" character is +** inserted at the start of each row. +** +** Note that sha3_agg() hashes rows for which Y is NULL. Add a FILTER +** clause if NULL rows should be excluded: +** +** SELECT sha3_agg(x ORDER BY rowid) FILTER(WHERE x NOT NULL) FROM t1; */ /* #include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 @@ -2908,6 +2484,7 @@ struct SHA3Context { unsigned nRate; /* Bytes of input accepted per Keccak iteration */ unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */ unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */ + unsigned iSize; /* 224, 256, 358, or 512 */ }; /* @@ -3237,6 +2814,7 @@ static void KeccakF1600Step(SHA3Context *p){ */ static void SHA3Init(SHA3Context *p, int iSize){ memset(p, 0, sizeof(*p)); + p->iSize = iSize; if( iSize>=128 && iSize<=512 ){ p->nRate = (1600 - ((iSize + 31)&~31)*2)/8; }else{ @@ -3380,6 +2958,60 @@ static void sha3_step_vformat( SHA3Update(p, (unsigned char*)zBuf, n); } +/* +** Update a SHA3Context using a single sqlite3_value. +*/ +static void sha3UpdateFromValue(SHA3Context *p, sqlite3_value *pVal){ + switch( sqlite3_value_type(pVal) ){ + case SQLITE_NULL: { + SHA3Update(p, (const unsigned char*)"N",1); + break; + } + case SQLITE_INTEGER: { + sqlite3_uint64 u; + int j; + unsigned char x[9]; + sqlite3_int64 v = sqlite3_value_int64(pVal); + memcpy(&u, &v, 8); + for(j=8; j>=1; j--){ + x[j] = u & 0xff; + u >>= 8; + } + x[0] = 'I'; + SHA3Update(p, x, 9); + break; + } + case SQLITE_FLOAT: { + sqlite3_uint64 u; + int j; + unsigned char x[9]; + double r = sqlite3_value_double(pVal); + memcpy(&u, &r, 8); + for(j=8; j>=1; j--){ + x[j] = u & 0xff; + u >>= 8; + } + x[0] = 'F'; + SHA3Update(p,x,9); + break; + } + case SQLITE_TEXT: { + int n2 = sqlite3_value_bytes(pVal); + const unsigned char *z2 = sqlite3_value_text(pVal); + sha3_step_vformat(p,"T%d:",n2); + SHA3Update(p, z2, n2); + break; + } + case SQLITE_BLOB: { + int n2 = sqlite3_value_bytes(pVal); + const unsigned char *z2 = sqlite3_value_blob(pVal); + sha3_step_vformat(p,"B%d:",n2); + SHA3Update(p, z2, n2); + break; + } + } +} + /* ** Implementation of the sha3_query(SQL,SIZE) function. ** @@ -3469,54 +3101,7 @@ static void sha3QueryFunc( while( SQLITE_ROW==sqlite3_step(pStmt) ){ SHA3Update(&cx,(const unsigned char*)"R",1); for(i=0; i=1; j--){ - x[j] = u & 0xff; - u >>= 8; - } - x[0] = 'I'; - SHA3Update(&cx, x, 9); - break; - } - case SQLITE_FLOAT: { - sqlite3_uint64 u; - int j; - unsigned char x[9]; - double r = sqlite3_column_double(pStmt,i); - memcpy(&u, &r, 8); - for(j=8; j>=1; j--){ - x[j] = u & 0xff; - u >>= 8; - } - x[0] = 'F'; - SHA3Update(&cx,x,9); - break; - } - case SQLITE_TEXT: { - int n2 = sqlite3_column_bytes(pStmt, i); - const unsigned char *z2 = sqlite3_column_text(pStmt, i); - sha3_step_vformat(&cx,"T%d:",n2); - SHA3Update(&cx, z2, n2); - break; - } - case SQLITE_BLOB: { - int n2 = sqlite3_column_bytes(pStmt, i); - const unsigned char *z2 = sqlite3_column_blob(pStmt, i); - sha3_step_vformat(&cx,"B%d:",n2); - SHA3Update(&cx, z2, n2); - break; - } - } + sha3UpdateFromValue(&cx, sqlite3_column_value(pStmt,i)); } } sqlite3_finalize(pStmt); @@ -3524,6 +3109,44 @@ static void sha3QueryFunc( sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT); } +/* +** xStep function for sha3_agg(). +*/ +static void sha3AggStep( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + SHA3Context *p; + p = (SHA3Context*)sqlite3_aggregate_context(context, sizeof(*p)); + if( p==0 ) return; + if( p->nRate==0 ){ + int sz = 256; + if( argc==2 ){ + sz = sqlite3_value_int(argv[1]); + if( sz!=224 && sz!=384 && sz!=512 ){ + sz = 256; + } + } + SHA3Init(p, sz); + } + sha3UpdateFromValue(p, argv[0]); +} + + +/* +** xFinal function for sha3_agg(). +*/ +static void sha3AggFinal(sqlite3_context *context){ + SHA3Context *p; + p = (SHA3Context*)sqlite3_aggregate_context(context, sizeof(*p)); + if( p==0 ) return; + if( p->iSize ){ + sqlite3_result_blob(context, SHA3Final(p), p->iSize/8, SQLITE_TRANSIENT); + } +} + + #ifdef _WIN32 @@ -3544,6 +3167,16 @@ int sqlite3_shathree_init( SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, 0, sha3Func, 0, 0); } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "sha3_agg", 1, + SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, + 0, 0, sha3AggStep, sha3AggFinal); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "sha3_agg", 2, + SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, + 0, 0, sha3AggStep, sha3AggFinal); + } if( rc==SQLITE_OK ){ rc = sqlite3_create_function(db, "sha3_query", 1, SQLITE_UTF8 | SQLITE_DIRECTONLY, @@ -3558,6 +3191,418 @@ int sqlite3_shathree_init( } /************************* End ../ext/misc/shathree.c ********************/ +/************************* Begin ../ext/misc/sha1.c ******************/ +/* +** 2017-01-27 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This SQLite extension implements functions that compute SHA1 hashes. +** Two SQL functions are implemented: +** +** sha1(X) +** sha1_query(Y) +** +** The sha1(X) function computes the SHA1 hash of the input X, or NULL if +** X is NULL. +** +** The sha1_query(Y) function evalutes all queries in the SQL statements of Y +** and returns a hash of their results. +*/ +/* #include "sqlite3ext.h" */ +SQLITE_EXTENSION_INIT1 +#include +#include +#include + +/****************************************************************************** +** The Hash Engine +*/ +/* Context for the SHA1 hash */ +typedef struct SHA1Context SHA1Context; +struct SHA1Context { + unsigned int state[5]; + unsigned int count[2]; + unsigned char buffer[64]; +}; + +#define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r)) +#define rol(x,k) SHA_ROT(x,k,32-(k)) +#define ror(x,k) SHA_ROT(x,32-(k),k) + +#define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \ + |(rol(block[i],8)&0x00FF00FF)) +#define blk0be(i) block[i] +#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ + ^block[(i+2)&15]^block[i&15],1)) + +/* + * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 + * + * Rl0() for little-endian and Rb0() for big-endian. Endianness is + * determined at run-time. + */ +#define Rl0(v,w,x,y,z,i) \ + z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); +#define Rb0(v,w,x,y,z,i) \ + z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2); +#define R1(v,w,x,y,z,i) \ + z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2); +#define R2(v,w,x,y,z,i) \ + z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2); +#define R3(v,w,x,y,z,i) \ + z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2); +#define R4(v,w,x,y,z,i) \ + z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2); + +/* + * Hash a single 512-bit block. This is the core of the algorithm. + */ +static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]){ + unsigned int qq[5]; /* a, b, c, d, e; */ + static int one = 1; + unsigned int block[16]; + memcpy(block, buffer, 64); + memcpy(qq,state,5*sizeof(unsigned int)); + +#define a qq[0] +#define b qq[1] +#define c qq[2] +#define d qq[3] +#define e qq[4] + + /* Copy p->state[] to working vars */ + /* + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + */ + + /* 4 rounds of 20 operations each. Loop unrolled. */ + if( 1 == *(unsigned char*)&one ){ + Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); + Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); + Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); + Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); + }else{ + Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); + Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); + Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); + Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); + } + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + +#undef a +#undef b +#undef c +#undef d +#undef e +} + + +/* Initialize a SHA1 context */ +static void hash_init(SHA1Context *p){ + /* SHA1 initialization constants */ + p->state[0] = 0x67452301; + p->state[1] = 0xEFCDAB89; + p->state[2] = 0x98BADCFE; + p->state[3] = 0x10325476; + p->state[4] = 0xC3D2E1F0; + p->count[0] = p->count[1] = 0; +} + +/* Add new content to the SHA1 hash */ +static void hash_step( + SHA1Context *p, /* Add content to this context */ + const unsigned char *data, /* Data to be added */ + unsigned int len /* Number of bytes in data */ +){ + unsigned int i, j; + + j = p->count[0]; + if( (p->count[0] += len << 3) < j ){ + p->count[1] += (len>>29)+1; + } + j = (j >> 3) & 63; + if( (j + len) > 63 ){ + (void)memcpy(&p->buffer[j], data, (i = 64-j)); + SHA1Transform(p->state, p->buffer); + for(; i + 63 < len; i += 64){ + SHA1Transform(p->state, &data[i]); + } + j = 0; + }else{ + i = 0; + } + (void)memcpy(&p->buffer[j], &data[i], len - i); +} + +/* Compute a string using sqlite3_vsnprintf() and hash it */ +static void hash_step_vformat( + SHA1Context *p, /* Add content to this context */ + const char *zFormat, + ... +){ + va_list ap; + int n; + char zBuf[50]; + va_start(ap, zFormat); + sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap); + va_end(ap); + n = (int)strlen(zBuf); + hash_step(p, (unsigned char*)zBuf, n); +} + + +/* Add padding and compute the message digest. Render the +** message digest as lower-case hexadecimal and put it into +** zOut[]. zOut[] must be at least 41 bytes long. */ +static void hash_finish( + SHA1Context *p, /* The SHA1 context to finish and render */ + char *zOut, /* Store hex or binary hash here */ + int bAsBinary /* 1 for binary hash, 0 for hex hash */ +){ + unsigned int i; + unsigned char finalcount[8]; + unsigned char digest[20]; + static const char zEncode[] = "0123456789abcdef"; + + for (i = 0; i < 8; i++){ + finalcount[i] = (unsigned char)((p->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + hash_step(p, (const unsigned char *)"\200", 1); + while ((p->count[0] & 504) != 448){ + hash_step(p, (const unsigned char *)"\0", 1); + } + hash_step(p, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++){ + digest[i] = (unsigned char)((p->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + if( bAsBinary ){ + memcpy(zOut, digest, 20); + }else{ + for(i=0; i<20; i++){ + zOut[i*2] = zEncode[(digest[i]>>4)&0xf]; + zOut[i*2+1] = zEncode[digest[i] & 0xf]; + } + zOut[i*2]= 0; + } +} +/* End of the hashing logic +*****************************************************************************/ + +/* +** Implementation of the sha1(X) function. +** +** Return a lower-case hexadecimal rendering of the SHA1 hash of the +** argument X. If X is a BLOB, it is hashed as is. For all other +** types of input, X is converted into a UTF-8 string and the string +** is hash without the trailing 0x00 terminator. The hash of a NULL +** value is NULL. +*/ +static void sha1Func( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + SHA1Context cx; + int eType = sqlite3_value_type(argv[0]); + int nByte = sqlite3_value_bytes(argv[0]); + char zOut[44]; + + assert( argc==1 ); + if( eType==SQLITE_NULL ) return; + hash_init(&cx); + if( eType==SQLITE_BLOB ){ + hash_step(&cx, sqlite3_value_blob(argv[0]), nByte); + }else{ + hash_step(&cx, sqlite3_value_text(argv[0]), nByte); + } + if( sqlite3_user_data(context)!=0 ){ + hash_finish(&cx, zOut, 1); + sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT); + }else{ + hash_finish(&cx, zOut, 0); + sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT); + } +} + +/* +** Implementation of the sha1_query(SQL) function. +** +** This function compiles and runs the SQL statement(s) given in the +** argument. The results are hashed using SHA1 and that hash is returned. +** +** The original SQL text is included as part of the hash. +** +** The hash is not just a concatenation of the outputs. Each query +** is delimited and each row and value within the query is delimited, +** with all values being marked with their datatypes. +*/ +static void sha1QueryFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + sqlite3 *db = sqlite3_context_db_handle(context); + const char *zSql = (const char*)sqlite3_value_text(argv[0]); + sqlite3_stmt *pStmt = 0; + int nCol; /* Number of columns in the result set */ + int i; /* Loop counter */ + int rc; + int n; + const char *z; + SHA1Context cx; + char zOut[44]; + + assert( argc==1 ); + if( zSql==0 ) return; + hash_init(&cx); + while( zSql[0] ){ + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql); + if( rc ){ + char *zMsg = sqlite3_mprintf("error SQL statement [%s]: %s", + zSql, sqlite3_errmsg(db)); + sqlite3_finalize(pStmt); + sqlite3_result_error(context, zMsg, -1); + sqlite3_free(zMsg); + return; + } + if( !sqlite3_stmt_readonly(pStmt) ){ + char *zMsg = sqlite3_mprintf("non-query: [%s]", sqlite3_sql(pStmt)); + sqlite3_finalize(pStmt); + sqlite3_result_error(context, zMsg, -1); + sqlite3_free(zMsg); + return; + } + nCol = sqlite3_column_count(pStmt); + z = sqlite3_sql(pStmt); + n = (int)strlen(z); + hash_step_vformat(&cx,"S%d:",n); + hash_step(&cx,(unsigned char*)z,n); + + /* Compute a hash over the result of the query */ + while( SQLITE_ROW==sqlite3_step(pStmt) ){ + hash_step(&cx,(const unsigned char*)"R",1); + for(i=0; i=1; j--){ + x[j] = u & 0xff; + u >>= 8; + } + x[0] = 'I'; + hash_step(&cx, x, 9); + break; + } + case SQLITE_FLOAT: { + sqlite3_uint64 u; + int j; + unsigned char x[9]; + double r = sqlite3_column_double(pStmt,i); + memcpy(&u, &r, 8); + for(j=8; j>=1; j--){ + x[j] = u & 0xff; + u >>= 8; + } + x[0] = 'F'; + hash_step(&cx,x,9); + break; + } + case SQLITE_TEXT: { + int n2 = sqlite3_column_bytes(pStmt, i); + const unsigned char *z2 = sqlite3_column_text(pStmt, i); + hash_step_vformat(&cx,"T%d:",n2); + hash_step(&cx, z2, n2); + break; + } + case SQLITE_BLOB: { + int n2 = sqlite3_column_bytes(pStmt, i); + const unsigned char *z2 = sqlite3_column_blob(pStmt, i); + hash_step_vformat(&cx,"B%d:",n2); + hash_step(&cx, z2, n2); + break; + } + } + } + } + sqlite3_finalize(pStmt); + } + hash_finish(&cx, zOut, 0); + sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT); +} + + +#ifdef _WIN32 + +#endif +int sqlite3_sha_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc = SQLITE_OK; + static int one = 1; + SQLITE_EXTENSION_INIT2(pApi); + (void)pzErrMsg; /* Unused parameter */ + rc = sqlite3_create_function(db, "sha1", 1, + SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, + 0, sha1Func, 0, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "sha1b", 1, + SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC, + (void*)&one, sha1Func, 0, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "sha1_query", 1, + SQLITE_UTF8|SQLITE_DIRECTONLY, 0, + sha1QueryFunc, 0, 0); + } + return rc; +} + +/************************* End ../ext/misc/sha1.c ********************/ /************************* Begin ../ext/misc/uint.c ******************/ /* ** 2020-04-14 @@ -4540,6 +4585,512 @@ int sqlite3_decimal_init( } /************************* End ../ext/misc/decimal.c ********************/ +/************************* Begin ../ext/misc/percentile.c ******************/ +/* +** 2013-05-28 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains code to implement the percentile(Y,P) SQL function +** and similar as described below: +** +** (1) The percentile(Y,P) function is an aggregate function taking +** exactly two arguments. +** +** (2) If the P argument to percentile(Y,P) is not the same for every +** row in the aggregate then an error is thrown. The word "same" +** in the previous sentence means that the value differ by less +** than 0.001. +** +** (3) If the P argument to percentile(Y,P) evaluates to anything other +** than a number in the range of 0.0 to 100.0 inclusive then an +** error is thrown. +** +** (4) If any Y argument to percentile(Y,P) evaluates to a value that +** is not NULL and is not numeric then an error is thrown. +** +** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus +** infinity then an error is thrown. (SQLite always interprets NaN +** values as NULL.) +** +** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions, +** including CASE WHEN expressions. +** +** (7) The percentile(Y,P) aggregate is able to handle inputs of at least +** one million (1,000,000) rows. +** +** (8) If there are no non-NULL values for Y, then percentile(Y,P) +** returns NULL. +** +** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P) +** returns the one Y value. +** +** (10) If there N non-NULL values of Y where N is two or more and +** the Y values are ordered from least to greatest and a graph is +** drawn from 0 to N-1 such that the height of the graph at J is +** the J-th Y value and such that straight lines are drawn between +** adjacent Y values, then the percentile(Y,P) function returns +** the height of the graph at P*(N-1)/100. +** +** (11) The percentile(Y,P) function always returns either a floating +** point number or NULL. +** +** (12) The percentile(Y,P) is implemented as a single C99 source-code +** file that compiles into a shared-library or DLL that can be loaded +** into SQLite using the sqlite3_load_extension() interface. +** +** (13) A separate median(Y) function is the equivalent percentile(Y,50). +** +** (14) A separate percentile_cont(Y,P) function is equivalent to +** percentile(Y,P/100.0). In other words, the fraction value in +** the second argument is in the range of 0 to 1 instead of 0 to 100. +** +** (15) A separate percentile_disc(Y,P) function is like +** percentile_cont(Y,P) except that instead of returning the weighted +** average of the nearest two input values, it returns the next lower +** value. So the percentile_disc(Y,P) will always return a value +** that was one of the inputs. +** +** (16) All of median(), percentile(Y,P), percentile_cont(Y,P) and +** percentile_disc(Y,P) can be used as window functions. +** +** Differences from standard SQL: +** +** * The percentile_cont(X,P) function is equivalent to the following in +** standard SQL: +** +** (percentile_cont(P) WITHIN GROUP (ORDER BY X)) +** +** The SQLite syntax is much more compact. The standard SQL syntax +** is also supported if SQLite is compiled with the +** -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES option. +** +** * No median(X) function exists in the SQL standard. App developers +** are expected to write "percentile_cont(0.5)WITHIN GROUP(ORDER BY X)". +** +** * No percentile(Y,P) function exists in the SQL standard. Instead of +** percential(Y,P), developers must write this: +** "percentile_cont(P/100.0) WITHIN GROUP (ORDER BY Y)". Note that +** the fraction parameter to percentile() goes from 0 to 100 whereas +** the fraction parameter in SQL standard percentile_cont() goes from +** 0 to 1. +** +** Implementation notes as of 2024-08-31: +** +** * The regular aggregate-function versions of these routines work +** by accumulating all values in an array of doubles, then sorting +** that array using quicksort before computing the answer. Thus +** the runtime is O(NlogN) where N is the number of rows of input. +** +** * For the window-function versions of these routines, the array of +** inputs is sorted as soon as the first value is computed. Thereafter, +** the array is kept in sorted order using an insert-sort. This +** results in O(N*K) performance where K is the size of the window. +** One can imagine alternative implementations that give O(N*logN*logK) +** performance, but they require more complex logic and data structures. +** The developers have elected to keep the asymptotically slower +** algorithm for now, for simplicity, under the theory that window +** functions are seldom used and when they are, the window size K is +** often small. The developers might revisit that decision later, +** should the need arise. +*/ +#if defined(SQLITE3_H) + /* no-op */ +#elif defined(SQLITE_STATIC_PERCENTILE) +/* # include "sqlite3.h" */ +#else +/* # include "sqlite3ext.h" */ + SQLITE_EXTENSION_INIT1 +#endif +#include +#include +#include + +/* The following object is the group context for a single percentile() +** aggregate. Remember all input Y values until the very end. +** Those values are accumulated in the Percentile.a[] array. +*/ +typedef struct Percentile Percentile; +struct Percentile { + unsigned nAlloc; /* Number of slots allocated for a[] */ + unsigned nUsed; /* Number of slots actually used in a[] */ + char bSorted; /* True if a[] is already in sorted order */ + char bKeepSorted; /* True if advantageous to keep a[] sorted */ + char bPctValid; /* True if rPct is valid */ + double rPct; /* Fraction. 0.0 to 1.0 */ + double *a; /* Array of Y values */ +}; + +/* Details of each function in the percentile family */ +typedef struct PercentileFunc PercentileFunc; +struct PercentileFunc { + const char *zName; /* Function name */ + char nArg; /* Number of arguments */ + char mxFrac; /* Maximum value of the "fraction" input */ + char bDiscrete; /* True for percentile_disc() */ +}; +static const PercentileFunc aPercentFunc[] = { + { "median", 1, 1, 0 }, + { "percentile", 2, 100, 0 }, + { "percentile_cont", 2, 1, 0 }, + { "percentile_disc", 2, 1, 1 }, +}; + +/* +** Return TRUE if the input floating-point number is an infinity. +*/ +static int percentIsInfinity(double r){ + sqlite3_uint64 u; + assert( sizeof(u)==sizeof(r) ); + memcpy(&u, &r, sizeof(u)); + return ((u>>52)&0x7ff)==0x7ff; +} + +/* +** Return TRUE if two doubles differ by 0.001 or less. +*/ +static int percentSameValue(double a, double b){ + a -= b; + return a>=-0.001 && a<=0.001; +} + +/* +** Search p (which must have p->bSorted) looking for an entry with +** value y. Return the index of that entry. +** +** If bExact is true, return -1 if the entry is not found. +** +** If bExact is false, return the index at which a new entry with +** value y should be insert in order to keep the values in sorted +** order. The smallest return value in this case will be 0, and +** the largest return value will be p->nUsed. +*/ +static int percentBinarySearch(Percentile *p, double y, int bExact){ + int iFirst = 0; /* First element of search range */ + int iLast = p->nUsed - 1; /* Last element of search range */ + while( iLast>=iFirst ){ + int iMid = (iFirst+iLast)/2; + double x = p->a[iMid]; + if( xy ){ + iLast = iMid - 1; + }else{ + return iMid; + } + } + if( bExact ) return -1; + return iFirst; +} + +/* +** Generate an error for a percentile function. +** +** The error format string must have exactly one occurrance of "%%s()" +** (with two '%' characters). That substring will be replaced by the name +** of the function. +*/ +static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){ + PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx); + char *zMsg1; + char *zMsg2; + va_list ap; + + va_start(ap, zFormat); + zMsg1 = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + zMsg2 = zMsg1 ? sqlite3_mprintf(zMsg1, pFunc->zName) : 0; + sqlite3_result_error(pCtx, zMsg2, -1); + sqlite3_free(zMsg1); + sqlite3_free(zMsg2); +} + +/* +** The "step" function for percentile(Y,P) is called once for each +** input row. +*/ +static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){ + Percentile *p; + double rPct; + int eType; + double y; + assert( argc==2 || argc==1 ); + + if( argc==1 ){ + /* Requirement 13: median(Y) is the same as percentile(Y,50). */ + rPct = 0.5; + }else{ + /* Requirement 3: P must be a number between 0 and 100 */ + PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx); + eType = sqlite3_value_numeric_type(argv[1]); + rPct = sqlite3_value_double(argv[1])/(double)pFunc->mxFrac; + if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT) + || rPct<0.0 || rPct>1.0 + ){ + percentError(pCtx, "the fraction argument to %%s()" + " is not between 0.0 and %.1f", + (double)pFunc->mxFrac); + return; + } + } + + /* Allocate the session context. */ + p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + if( p==0 ) return; + + /* Remember the P value. Throw an error if the P value is different + ** from any prior row, per Requirement (2). */ + if( !p->bPctValid ){ + p->rPct = rPct; + p->bPctValid = 1; + }else if( !percentSameValue(p->rPct,rPct) ){ + percentError(pCtx, "the fraction argument to %%s()" + " is not the same for all input rows"); + return; + } + + /* Ignore rows for which Y is NULL */ + eType = sqlite3_value_type(argv[0]); + if( eType==SQLITE_NULL ) return; + + /* If not NULL, then Y must be numeric. Otherwise throw an error. + ** Requirement 4 */ + if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){ + percentError(pCtx, "input to %%s() is not numeric"); + return; + } + + /* Throw an error if the Y value is infinity or NaN */ + y = sqlite3_value_double(argv[0]); + if( percentIsInfinity(y) ){ + percentError(pCtx, "Inf input to %%s()"); + return; + } + + /* Allocate and store the Y */ + if( p->nUsed>=p->nAlloc ){ + unsigned n = p->nAlloc*2 + 250; + double *a = sqlite3_realloc64(p->a, sizeof(double)*n); + if( a==0 ){ + sqlite3_free(p->a); + memset(p, 0, sizeof(*p)); + sqlite3_result_error_nomem(pCtx); + return; + } + p->nAlloc = n; + p->a = a; + } + if( p->nUsed==0 ){ + p->a[p->nUsed++] = y; + p->bSorted = 1; + }else if( !p->bSorted || y>=p->a[p->nUsed-1] ){ + p->a[p->nUsed++] = y; + }else if( p->bKeepSorted ){ + int i; + i = percentBinarySearch(p, y, 0); + if( i<(int)p->nUsed ){ + memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0])); + } + p->a[i] = y; + p->nUsed++; + }else{ + p->a[p->nUsed++] = y; + p->bSorted = 0; + } +} + +/* +** Interchange two doubles. +*/ +#define SWAP_DOUBLE(X,Y) {double ttt=(X);(X)=(Y);(Y)=ttt;} + +/* +** Sort an array of doubles. +** +** Algorithm: quicksort +** +** This is implemented separately rather than using the qsort() routine +** from the standard library because: +** +** (1) To avoid a dependency on qsort() +** (2) To avoid the function call to the comparison routine for each +** comparison. +*/ +static void percentSort(double *a, unsigned int n){ + int iLt; /* Entries before a[iLt] are less than rPivot */ + int iGt; /* Entries at or after a[iGt] are greater than rPivot */ + int i; /* Loop counter */ + double rPivot; /* The pivot value */ + + assert( n>=2 ); + if( a[0]>a[n-1] ){ + SWAP_DOUBLE(a[0],a[n-1]) + } + if( n==2 ) return; + iGt = n-1; + i = n/2; + if( a[0]>a[i] ){ + SWAP_DOUBLE(a[0],a[i]) + }else if( a[i]>a[iGt] ){ + SWAP_DOUBLE(a[i],a[iGt]) + } + if( n==3 ) return; + rPivot = a[i]; + iLt = i = 1; + do{ + if( a[i]iLt ) SWAP_DOUBLE(a[i],a[iLt]) + iLt++; + i++; + }else if( a[i]>rPivot ){ + do{ + iGt--; + }while( iGt>i && a[iGt]>rPivot ); + SWAP_DOUBLE(a[i],a[iGt]) + }else{ + i++; + } + }while( i=2 ) percentSort(a, iLt); + if( n-iGt>=2 ) percentSort(a+iGt, n-iGt); + +/* Uncomment for testing */ +#if 0 + for(i=0; ibSorted==0 ){ + assert( p->nUsed>1 ); + percentSort(p->a, p->nUsed); + p->bSorted = 1; + } + p->bKeepSorted = 1; + + /* Find and remove the row */ + i = percentBinarySearch(p, y, 1); + if( i>=0 ){ + p->nUsed--; + if( i<(int)p->nUsed ){ + memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0])); + } + } +} + +/* +** Compute the final output of percentile(). Clean up all allocated +** memory if and only if bIsFinal is true. +*/ +static void percentCompute(sqlite3_context *pCtx, int bIsFinal){ + Percentile *p; + PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx); + unsigned i1, i2; + double v1, v2; + double ix, vx; + p = (Percentile*)sqlite3_aggregate_context(pCtx, 0); + if( p==0 ) return; + if( p->a==0 ) return; + if( p->nUsed ){ + if( p->bSorted==0 ){ + assert( p->nUsed>1 ); + percentSort(p->a, p->nUsed); + p->bSorted = 1; + } + ix = p->rPct*(p->nUsed-1); + i1 = (unsigned)ix; + if( pFunc->bDiscrete ){ + vx = p->a[i1]; + }else{ + i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1; + v1 = p->a[i1]; + v2 = p->a[i2]; + vx = v1 + (v2-v1)*(ix-i1); + } + sqlite3_result_double(pCtx, vx); + } + if( bIsFinal ){ + sqlite3_free(p->a); + memset(p, 0, sizeof(*p)); + }else{ + p->bKeepSorted = 1; + } +} +static void percentFinal(sqlite3_context *pCtx){ + percentCompute(pCtx, 1); +} +static void percentValue(sqlite3_context *pCtx){ + percentCompute(pCtx, 0); +} + +#if defined(_WIN32) && !defined(SQLITE3_H) && !defined(SQLITE_STATIC_PERCENTILE) + +#endif +int sqlite3_percentile_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc = SQLITE_OK; + unsigned int i; +#if defined(SQLITE3_H) || defined(SQLITE_STATIC_PERCENTILE) + (void)pApi; /* Unused parameter */ +#else + SQLITE_EXTENSION_INIT2(pApi); +#endif + (void)pzErrMsg; /* Unused parameter */ + for(i=0; i