/*
* * This is the amalgamated source code to the " sqlite3 " or " sqlite3 . exe "
* * command - line shell ( CLI ) for SQLite . This file is automatically
* * generated by the tool / mkshellc . tcl script from the following sources :
* *
* * ext / expert / sqlite3expert . c
* * ext / expert / sqlite3expert . h
* * ext / intck / sqlite3intck . c
* * ext / intck / sqlite3intck . h
* * ext / misc / appendvfs . c
* * ext / misc / base64 . c
* * ext / misc / base85 . c
* * ext / misc / completion . c
* * ext / misc / decimal . c
* * ext / misc / fileio . c
* * ext / misc / ieee754 . c
* * ext / misc / memtrace . c
* * ext / misc / pcachetrace . c
* * ext / misc / regexp . c
* * ext / misc / series . c
* * ext / misc / sha1 . c
* * ext / misc / shathree . c
* * ext / misc / sqlar . c
* * ext / misc / sqlite3_stdio . c
* * ext / misc / sqlite3_stdio . h
* * ext / misc / stmtrand . c
* * ext / misc / uint . c
* * ext / misc / vfstrace . c
* * ext / misc / windirent . h
* * ext / misc / zipfile . c
* * ext / qrf / qrf . c
* * ext / qrf / qrf . h
* * ext / recover / dbdata . c
* * ext / recover / sqlite3recover . c
* * ext / recover / sqlite3recover . h
* * src / shell . c . in
* *
* * To modify this program , get a copy of the canonical SQLite source tree ,
* * edit the src / shell . c . in file and / or some of the other files that are
* * listed above , then rerun the command " make shell . c " .
*/
/************************* Begin src/shell.c.in ******************/
/*
* * 2001 September 15
* *
* * The author disclaims copyright to this source code . In place of
* * a legal notice , here is a blessing :
* *
* * May you do good and not evil .
* * May you find forgiveness for yourself and forgive others .
* * May you share freely , never taking more than you give .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * This file contains code to implement the " sqlite3 " command line
* * utility for accessing SQLite databases .
*/
#if (defined (_WIN32) || defined (WIN32)) && !defined (_CRT_SECURE_NO_WARNINGS)
/* This needs to come before any includes for MSVC compiler */
#define _CRT_SECURE_NO_WARNINGS
#endif
typedef unsigned int u32;
typedef unsigned short int u16;
/*
* * Limit input nesting via . read or any other input redirect .
* * It ' s not too expensive , so a generous allowance can be made .
*/
#define MAX_INPUT_NESTING 25
/*
* * Used to prevent warnings about unused parameters
*/
#define UNUSED_PARAMETER(x) (void )(x)
/*
* * Number of elements in an array
*/
#define ArraySize(X) (int )(sizeof (X)/sizeof (X[0 ]))
/*
* * Optionally # include a user - defined header , whereby compilation options
* * may be set prior to where they take effect , but after platform setup .
* * If SQLITE_CUSTOM_INCLUDE = ? is defined , its value names the # include
* * file . Note that this macro has a like effect on sqlite3 . c compilation .
*/
# define SHELL_STRINGIFY_(f) #f
# define SHELL_STRINGIFY(f) SHELL_STRINGIFY_(f)
#ifdef SQLITE_CUSTOM_INCLUDE
# include SHELL_STRINGIFY(SQLITE_CUSTOM_INCLUDE)
#endif
/*
* * If SQLITE_SHELL_FIDDLE is defined then the shell is modified
* * somewhat for use as a WASM module in a web browser . This flag
* * should only be used when building the " fiddle " web application , as
* * the browser - mode build has much different user input requirements
* * and this build mode rewires the user input subsystem to account for
* * that .
*/
#if defined (SQLITE_SHELL_FIDDLE)
# undef SQLITE_OMIT_LOAD_EXTENSION
# define SQLITE_OMIT_LOAD_EXTENSION 1
#endif
/*
* * Warning pragmas copied from msvc . h in the core .
*/
#if defined (_MSC_VER)
#pragma warning(disable : 4054 )
#pragma warning(disable : 4055 )
#pragma warning(disable : 4100 )
#pragma warning(disable : 4127 )
#pragma warning(disable : 4130 )
#pragma warning(disable : 4152 )
#pragma warning(disable : 4189 )
#pragma warning(disable : 4206 )
#pragma warning(disable : 4210 )
#pragma warning(disable : 4232 )
#pragma warning(disable : 4244 )
#pragma warning(disable : 4305 )
#pragma warning(disable : 4306 )
#pragma warning(disable : 4702 )
#pragma warning(disable : 4706 )
#endif /* defined(_MSC_VER) */
/*
* * No support for loadable extensions in VxWorks .
*/
#if (defined (__RTP__) || defined (_WRS_KERNEL)) && !SQLITE_OMIT_LOAD_EXTENSION
# define SQLITE_OMIT_LOAD_EXTENSION 1
#endif
/*
* * Enable large - file support for fopen ( ) and friends on unix .
*/
#ifndef SQLITE_DISABLE_LFS
# define _LARGE_FILE 1
# ifndef _FILE_OFFSET_BITS
# define _FILE_OFFSET_BITS 64
# endif
# define _LARGEFILE_SOURCE 1
#endif
#if defined (SQLITE_SHELL_FIDDLE) && !defined (_POSIX_SOURCE)
/*
* * emcc requires _ POSIX_SOURCE ( or one of several similar defines )
* * to expose strdup ( ) .
*/
# define _POSIX_SOURCE
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <stdint.h>
#include "sqlite3.h"
typedef sqlite3_int64 i64;
typedef sqlite3_uint64 u64;
typedef unsigned char u8;
#include <ctype.h>
#include <stdarg.h>
#ifndef _WIN32
# include <sys/time.h>
# include <sys/ioctl.h>
#endif
#if !defined (_WIN32) && !defined (WIN32)
# include <signal.h>
# if !defined (__RTP__) && !defined (_WRS_KERNEL) && !defined (SQLITE_WASI)
# include <pwd.h>
# endif
#endif
#if (!defined (_WIN32) && !defined (WIN32)) || defined (__MINGW32__)
# include <unistd.h>
# include <dirent.h>
# define GETPID getpid
# if defined (__MINGW32__)
# define DIRENT dirent
# ifndef S_ISLNK
# define S_ISLNK(mode) (0 )
# endif
# endif
#else
# define GETPID (int )GetCurrentProcessId
#endif
#include <sys/types.h>
#include <sys/stat.h>
#if HAVE_READLINE
# include <readline/readline.h>
# include <readline/history.h>
#endif
#if HAVE_EDITLINE
# include <editline/readline.h>
#endif
#if HAVE_EDITLINE || HAVE_READLINE
# define shell_add_history(X) add_history(X)
# define shell_read_history(X) read_history(X)
# define shell_write_history(X) write_history(X)
# define shell_stifle_history(X) stifle_history(X)
# define shell_readline(X) readline(X)
#elif HAVE_LINENOISE
# include "linenoise.h"
# define shell_add_history(X) linenoiseHistoryAdd(X)
# define shell_read_history(X) linenoiseHistoryLoad(X)
# define shell_write_history(X) linenoiseHistorySave(X)
# define shell_stifle_history(X) linenoiseHistorySetMaxLen(X)
# define shell_readline(X) linenoise(X)
#else
# define shell_read_history(X)
# define shell_write_history(X)
# define shell_stifle_history(X)
# define SHELL_USE_LOCAL_GETLINE 1
#endif
#ifndef deliberate_fall_through
/* Quiet some compilers about some of our intentional code. */
# if defined (GCC_VERSION) && GCC_VERSION>=7000000
# define deliberate_fall_through __attribute__((fallthrough));
# else
# define deliberate_fall_through
# endif
#endif
#if defined (_WIN32) || defined (WIN32)
# include <io.h>
# include <fcntl.h>
# define isatty(h) _isatty(h)
# ifndef access
# define access(f,m) _access((f),(m))
# endif
# ifndef unlink
# define unlink _unlink
# endif
# ifndef strdup
# define strdup _strdup
# endif
# undef pclose
# define pclose _pclose
#else
/* Make sure isatty() has a prototype. */
extern int isatty(int );
# if !defined (__RTP__) && !defined (_WRS_KERNEL) && !defined (SQLITE_WASI)
/* popen and pclose are not C89 functions and so are
** sometimes omitted from the <stdio.h> header */
extern FILE *popen(const char *,const char *);
extern int pclose(FILE*);
# else
# define SQLITE_OMIT_POPEN 1
# endif
#endif
#if defined (_WIN32_WCE)
/* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty()
* thus we always assume that we have a console . That can be
* overridden with the - batch command line option .
*/
#define isatty(x) 1
#endif
/* ctype macros that work with signed characters */
#define IsSpace(X) isspace((unsigned char )X)
#define IsDigit(X) isdigit((unsigned char )X)
#define ToLower(X) (char )tolower((unsigned char )X)
#define IsAlnum(X) isalnum((unsigned char )X)
#define IsAlpha(X) isalpha((unsigned char )X)
#if defined (_WIN32) || defined (WIN32)
#undef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
/* string conversion routines only needed on Win32 */
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 <stdio.h>
#include <stdarg.h>
#include <windows.h>
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, ...);
int sqlite3_vfprintf(FILE *stream, const char *format, va_list);
void sqlite3_fsetmode(FILE *stream, int mode);
#else
/**** Definitions For All Other Platforms ****/
#include <stdio.h>
#define sqlite3_fopen fopen
#define sqlite3_popen popen
#define sqlite3_fgets fgets
#define sqlite3_fputs fputs
#define sqlite3_fprintf fprintf
#define sqlite3_vfprintf vfprintf
#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 <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
/* #include "sqlite3.h" */
#include <ctype.h>
#include <stdarg.h>
#include <io.h>
#include <fcntl.h>
/*
* * 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 .
* *
* * The SQLITE_USE_W32_FOR_CONSOLE_IO macro is also available . If
* * defined , it forces the use of Win32 APIs for all console I / O , both
* * input and output . This is necessary for some non - Microsoft run - times
* * that implement stdio differently from Microsoft / Visual - Studio .
*/
#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 = sqlite3_malloc64( (sz1+1 )*sizeof (b1[0 ]) );
b2 = sqlite3_malloc64( (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);
}
sqlite3_free(b1);
sqlite3_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 = sqlite3_malloc64( (sz1+1 )*sizeof (b1[0 ]) );
b2 = sqlite3_malloc64( (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);
}
sqlite3_free(b1);
sqlite3_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 = sqlite3_malloc64( sz*sizeof (wchar_t ) );
if ( b1==0 ) return 0 ;
#ifdef SQLITE_USE_W32_FOR_CONSOLE_IO
DWORD nRead = 0 ;
if ( IsConsole(in)
&& ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), b1, sz-1 , &nRead, 0 )
){
b1[nRead] = 0 ;
}else
#endif
{
_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<sz && b1[i]>=0 x80; 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<sz && b1[i]<0 x80; i++){
fwrite(&b1[i], 1 , 1 , out);
}
fflush(out);
_setmode(_fileno(out), _O_U8TEXT);
b1 += i;
sz -= i;
}
}
}
/*
* * Work - alike for fputs ( ) from the standard C library .
*/
int sqlite3_fputs(const char *z, FILE *out){
if ( !UseWtextForOutput(out) ){
/* Writing to a file or other destination, just write bytes without
** any translation. */
return fputs(z, out);
}else {
/* One must use UTF16 in order to get unicode support when writing
* * to the console on Windows .
*/
int sz = (int )strlen(z);
wchar_t *b1 = sqlite3_malloc64( (sz+1 )*sizeof (wchar_t ) );
if ( b1==0 ) return 0 ;
sz = MultiByteToWideChar(CP_UTF8, 0 , z, sz, b1, sz);
b1[sz] = 0 ;
#ifdef SQLITE_USE_W32_FOR_CONSOLE_IO
DWORD nWr = 0 ;
if ( IsConsole(out)
&& WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),b1,sz,&nWr,0 )
){
/* If writing to the console, then the WriteConsoleW() is all we
** need to do. */
}else
#endif
{
/* As long as SQLITE_USE_W32_FOR_CONSOLE_IO is not defined, or for
* * non - console I / O even if that macro is defined , write using the
** standard library. */
_setmode(_fileno(out), _O_U8TEXT);
if ( UseBinaryWText(out) ){
piecemealOutput(b1, sz, out);
}else {
fputws(b1, out);
}
}
sqlite3_free(b1);
return 0 ;
}
}
/*
* * Work - alikes for fprintf ( ) and vfprintf ( ) from the standard C library .
*/
int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
int rc;
if ( UseWtextForOutput(out) ){
/* When writing to the command-prompt in Windows, it is necessary
* * to use _ O_WTEXT input mode and write UTF - 16 characters .
*/
char *z;
va_list ap;
va_start(ap, zFormat);
z = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
sqlite3_fputs(z, out);
rc = (int )strlen(z);
sqlite3_free(z);
}else {
/* Writing to a file or other destination, just write bytes without
** any translation. */
va_list ap;
va_start(ap, zFormat);
rc = vfprintf(out, zFormat, ap);
va_end(ap);
}
return rc;
}
int sqlite3_vfprintf(FILE *out, const char *zFormat, va_list ap){
int rc;
if ( UseWtextForOutput(out) ){
/* When writing to the command-prompt in Windows, it is necessary
* * to use _ O_WTEXT input mode and write UTF - 16 characters .
*/
char *z;
z = sqlite3_vmprintf(zFormat, ap);
sqlite3_fputs(z, out);
rc = (int )strlen(z);
sqlite3_free(z);
}else {
/* Writing to a file or other destination, just write bytes without
** any translation. */
rc = vfprintf(out, zFormat, ap);
}
return rc;
}
/*
* * Set the mode for an output stream . mode argument is typically _ O_BINARY or
* * _ O_TEXT .
*/
void sqlite3_fsetmode(FILE *fp, int mode){
if ( !UseWtextForOutput(fp) ){
fflush(fp);
_setmode(_fileno(fp), mode);
}else if ( fp==stdout || fp==stderr ){
simBinaryStdout = (mode==_O_BINARY);
}else {
simBinaryOther = (mode==_O_BINARY);
}
}
#endif /* defined(_WIN32) */
/************************* End ext/misc/sqlite3_stdio.c ********************/
/************************* Begin ext/qrf/qrf.h ******************/
/*
* * 2025 - 10 - 20
* *
* * 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 .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * Header file for the Query Result - Format or " qrf " utility library for
* * SQLite . See the README . md documentation for additional information .
*/
#ifndef SQLITE_QRF_H
#define SQLITE_QRF_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
/* #include "sqlite3.h" */
/*
* * Specification used by clients to define the output format they want
*/
typedef struct sqlite3_qrf_spec sqlite3_qrf_spec;
struct sqlite3_qrf_spec {
unsigned char iVersion; /* Version number of this structure */
unsigned char eStyle; /* Formatting style. "box", "csv", etc... */
unsigned char eEsc; /* How to escape control characters in text */
unsigned char eText; /* Quoting style for text */
unsigned char eTitle; /* Quating style for the text of column names */
unsigned char eBlob; /* Quoting style for BLOBs */
unsigned char bTitles; /* True to show column names */
unsigned char bWordWrap; /* Try to wrap on word boundaries */
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
unsigned char eTitleAlign; /* Alignment for column headers */
unsigned char bSplitColumn; /* Wrap single-column output into many columns */
unsigned char bBorder; /* Show outer border in Box and Table styles */
short int nWrap; /* Wrap columns wider than this */
short int nScreenWidth; /* Maximum overall table width */
short int nLineLimit; /* Maximum number of lines for any row */
short int nTitleLimit; /* Maximum number of characters in a title */
unsigned int nMultiInsert; /* Add rows to one INSERT until size exceeds */
int nCharLimit; /* Maximum number of characters in a cell */
int nWidth; /* Number of entries in aWidth[] */
int nAlign; /* Number of entries in aAlignment[] */
short int *aWidth; /* Column widths */
unsigned char *aAlign; /* Column alignments */
char *zColumnSep; /* Alternative column separator */
char *zRowSep; /* Alternative row separator */
char *zTableName; /* Output table name */
char *zNull; /* Rendering of NULL */
char *(*xRender)(void *,sqlite3_value*); /* Render a value */
int (*xWrite)(void *,const char *,sqlite3_int64); /* Write output */
void *pRenderArg; /* First argument to the xRender callback */
void *pWriteArg; /* First argument to the xWrite callback */
char **pzOutput; /* Storage location for output string */
/* Additional fields may be added in the future */
};
/*
* * Interfaces
*/
int sqlite3_format_query_result(
sqlite3_stmt *pStmt, /* SQL statement to run */
const sqlite3_qrf_spec *pSpec, /* Result format specification */
char **pzErr /* OUT: Write error message here */
);
/*
* * Range of values for sqlite3_qrf_spec . aWidth [ ] entries and for
* * sqlite3_qrf_spec . mxColWidth and . nScreenWidth
*/
#define QRF_MAX_WIDTH 10000
#define QRF_MIN_WIDTH 0
/*
* * Output styles :
*/
#define QRF_STYLE_Auto 0 /* Choose a style automatically */
#define QRF_STYLE_Box 1 /* Unicode box-drawing characters */
#define QRF_STYLE_Column 2 /* One record per line in neat columns */
#define QRF_STYLE_Count 3 /* Output only a count of the rows of output */
#define QRF_STYLE_Csv 4 /* Comma-separated-value */
#define QRF_STYLE_Eqp 5 /* Format EXPLAIN QUERY PLAN output */
#define QRF_STYLE_Explain 6 /* EXPLAIN output */
#define QRF_STYLE_Html 7 /* Generate an XHTML table */
#define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */
#define QRF_STYLE_Json 9 /* Output is a list of JSON objects */
#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */
#define QRF_STYLE_Line 11 /* One column per line. */
#define QRF_STYLE_List 12 /* One record per line with a separator */
#define QRF_STYLE_Markdown 13 /* Markdown formatting */
#define QRF_STYLE_Off 14 /* No query output shown */
#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */
#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */
#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */
#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */
#define QRF_STYLE_Table 19 /* MySQL-style table formatting */
/*
* * Quoting styles for text .
* * Allowed values for sqlite3_qrf_spec . eText
*/
#define QRF_TEXT_Auto 0 /* Choose text encoding automatically */
#define QRF_TEXT_Plain 1 /* Literal text */
#define QRF_TEXT_Sql 2 /* Quote as an SQL literal */
#define QRF_TEXT_Csv 3 /* CSV-style quoting */
#define QRF_TEXT_Html 4 /* HTML-style quoting */
#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */
#define QRF_TEXT_Json 6 /* JSON quoting */
#define QRF_TEXT_Relaxed 7 /* Relaxed SQL quoting */
/*
* * Quoting styles for BLOBs
* * Allowed values for sqlite3_qrf_spec . eBlob
*/
#define QRF_BLOB_Auto 0 /* Determine BLOB quoting using eText */
#define QRF_BLOB_Text 1 /* Display content exactly as it is */
#define QRF_BLOB_Sql 2 /* Quote as an SQL literal */
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
#define QRF_BLOB_Tcl 4 /* "\000" notation */
#define QRF_BLOB_Json 5 /* A JSON string */
#define QRF_BLOB_Size 6 /* Display the blob size only */
/*
* * Control - character escape modes .
* * Allowed values for sqlite3_qrf_spec . eEsc
*/
#define QRF_ESC_Auto 0 /* Choose the ctrl-char escape automatically */
#define QRF_ESC_Off 1 /* Do not escape control characters */
#define QRF_ESC_Ascii 2 /* Unix-style escapes. Ex: U+0007 shows ^G */
#define QRF_ESC_Symbol 3 /* Unicode escapes. Ex: U+0007 shows U+2407 */
/*
* * Allowed values for " boolean " fields , such as " bColumnNames " , " bWordWrap " ,
* * and " bTextJsonb " . There is an extra " auto " variants so these are actually
* * tri - state settings , not booleans .
*/
#define QRF_SW_Auto 0 /* Let QRF choose the best value */
#define QRF_SW_Off 1 /* This setting is forced off */
#define QRF_SW_On 2 /* This setting is forced on */
#define QRF_Auto 0 /* Alternate spelling for QRF_*_Auto */
#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */
#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */
/*
* * Possible alignment values alignment settings
* *
* * Horizontal Vertial
** ---------- -------- */
#define QRF_ALIGN_Auto 0 /* auto auto */
#define QRF_ALIGN_Left 1 /* left auto */
#define QRF_ALIGN_Center 2 /* center auto */
#define QRF_ALIGN_Right 3 /* right auto */
#define QRF_ALIGN_Top 4 /* auto top */
#define QRF_ALIGN_NW 5 /* left top */
#define QRF_ALIGN_N 6 /* center top */
#define QRF_ALIGN_NE 7 /* right top */
#define QRF_ALIGN_Middle 8 /* auto middle */
#define QRF_ALIGN_W 9 /* left middle */
#define QRF_ALIGN_C 10 /* center middle */
#define QRF_ALIGN_E 11 /* right middle */
#define QRF_ALIGN_Bottom 12 /* auto bottom */
#define QRF_ALIGN_SW 13 /* left bottom */
#define QRF_ALIGN_S 14 /* center bottom */
#define QRF_ALIGN_SE 15 /* right bottom */
#define QRF_ALIGN_HMASK 3 /* Horizontal alignment mask */
#define QRF_ALIGN_VMASK 12 /* Vertical alignment mask */
/*
* * Auxiliary routines contined within this module that might be useful
* * in other contexts , and which are therefore exported .
*/
/*
* * 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 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 sqlite3_qrf_wcwidth(int c);
/*
* * Return an estimate of the number of display columns used by the
* * string in the argument . The width of individual characters is
* * determined as for sqlite3_qrf_wcwidth ( ) . VT100 escape code sequences
* * are assigned a width of zero .
*/
size_t sqlite3_qrf_wcswidth(const char *);
#ifdef __cplusplus
}
#endif
#endif /* !defined(SQLITE_QRF_H) */
/************************* End ext/qrf/qrf.h ********************/
/************************* Begin ext/qrf/qrf.c ******************/
/*
* * 2025 - 10 - 20
* *
* * 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 the Query Result - Format or " qrf " utility library for
* * SQLite . See the README . md documentation for additional information .
*/
#ifndef SQLITE_QRF_H
#include "qrf.h"
#endif
#include <string.h>
#include <assert.h>
#include <stdint.h>
#ifndef SQLITE_AMALGAMATION
/* typedef sqlite3_int64 i64; */
#endif
/* A single line in the EQP output */
typedef struct qrfEQPGraphRow qrfEQPGraphRow;
struct qrfEQPGraphRow {
int iEqpId; /* ID for this row */
int iParentId; /* ID of the parent row */
qrfEQPGraphRow *pNext; /* Next row in sequence */
char zText[1 ]; /* Text to display for this row */
};
/* All EQP output is collected into an instance of the following */
typedef struct qrfEQPGraph qrfEQPGraph;
struct qrfEQPGraph {
qrfEQPGraphRow *pRow; /* Linked list of all rows of the EQP output */
qrfEQPGraphRow *pLast; /* Last element of the pRow list */
int nWidth; /* Width of the graph */
char zPrefix[400 ]; /* Graph prefix */
};
/*
* * Private state information . Subject to change from one release to the
* * next .
*/
typedef struct Qrf Qrf;
struct Qrf {
sqlite3_stmt *pStmt; /* The statement whose output is to be rendered */
sqlite3 *db; /* The corresponding database connection */
sqlite3_stmt *pJTrans; /* JSONB to JSON translator statement */
char **pzErr; /* Write error message here, if not NULL */
sqlite3_str *pOut; /* Accumulated output */
int iErr; /* Error code */
int nCol; /* Number of output columns */
int expMode; /* Original sqlite3_stmt_isexplain() plus 1 */
int mxWidth; /* Screen width */
int mxHeight; /* nLineLimit */
union {
struct { /* Content for QRF_STYLE_Line */
int mxColWth; /* Maximum display width of any column */
char **azCol; /* Names of output columns (MODE_Line) */
} sLine;
qrfEQPGraph *pGraph; /* EQP graph (Eqp, Stats, and StatsEst) */
struct { /* Content for QRF_STYLE_Explain */
int nIndent; /* Slots allocated for aiIndent */
int iIndent; /* Current slot */
int *aiIndent; /* Indentation for each opcode */
} sExpln;
unsigned int nIns; /* Bytes used for current INSERT stmt */
} u;
sqlite3_int64 nRow; /* Number of rows handled so far */
int *actualWidth; /* Actual width of each column */
sqlite3_qrf_spec spec; /* Copy of the original spec */
};
/*
* * Data for substitute ctype . h functions . Used for x - platform
* * consistency and so that ' _ ' is counted as an alphabetic
* * character .
* *
* * 0 x01 - space
* * 0 x02 - digit
* * 0 x04 - alphabetic , including ' _ '
*/
static const char qrfCType[] = {
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 ,
4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 0 , 0 , 0 , 0 , 4 ,
0 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 ,
4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
};
#define qrfSpace(x) ((qrfCType[(unsigned char )x]&1 )!=0 )
#define qrfDigit(x) ((qrfCType[(unsigned char )x]&2 )!=0 )
#define qrfAlpha(x) ((qrfCType[(unsigned char )x]&4 )!=0 )
#define qrfAlnum(x) ((qrfCType[(unsigned char )x]&6 )!=0 )
#ifndef deliberate_fall_through
/* Quiet some compilers about some of our intentional code. */
# if defined (GCC_VERSION) && GCC_VERSION>=7000000
# define deliberate_fall_through __attribute__((fallthrough));
# else
# define deliberate_fall_through
# endif
#endif
/*
* * Set an error code and error message .
*/
static void qrfError(
Qrf *p, /* Query result state */
int iCode, /* Error code */
const char *zFormat, /* Message format (or NULL) */
...
){
p->iErr = iCode;
if ( p->pzErr!=0 ){
sqlite3_free(*p->pzErr);
*p->pzErr = 0 ;
if ( zFormat ){
va_list ap;
va_start(ap, zFormat);
*p->pzErr = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
}
}
}
/*
* * Out - of - memory error .
*/
static void qrfOom(Qrf *p){
qrfError(p, SQLITE_NOMEM, "out of memory" );
}
/*
* * Transfer any error in pStr over into p .
*/
static void qrfStrErr(Qrf *p, sqlite3_str *pStr){
int rc = pStr ? sqlite3_str_errcode(pStr) : 0 ;
if ( rc ){
qrfError(p, rc, sqlite3_errstr(rc));
}
}
/*
* * Add a new entry to the EXPLAIN QUERY PLAN data
*/
static void qrfEqpAppend(Qrf *p, int iEqpId, int p2, const char *zText){
qrfEQPGraphRow *pNew;
sqlite3_int64 nText;
if ( zText==0 ) return ;
if ( p->u.pGraph==0 ){
p->u.pGraph = sqlite3_malloc64( sizeof (qrfEQPGraph) );
if ( p->u.pGraph==0 ){
qrfOom(p);
return ;
}
memset(p->u.pGraph, 0 , sizeof (qrfEQPGraph) );
}
nText = strlen(zText);
pNew = sqlite3_malloc64( sizeof (*pNew) + nText );
if ( pNew==0 ){
qrfOom(p);
return ;
}
pNew->iEqpId = iEqpId;
pNew->iParentId = p2;
memcpy(pNew->zText, zText, nText+1 );
pNew->pNext = 0 ;
if ( p->u.pGraph->pLast ){
p->u.pGraph->pLast->pNext = pNew;
}else {
p->u.pGraph->pRow = pNew;
}
p->u.pGraph->pLast = pNew;
}
/*
* * Free and reset the EXPLAIN QUERY PLAN data that has been collected
* * in p - > u . pGraph .
*/
static void qrfEqpReset(Qrf *p){
qrfEQPGraphRow *pRow, *pNext;
if ( p->u.pGraph ){
for (pRow = p->u.pGraph->pRow; pRow; pRow = pNext){
pNext = pRow->pNext;
sqlite3_free(pRow);
}
sqlite3_free(p->u.pGraph);
p->u.pGraph = 0 ;
}
}
/* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after
* * pOld , or return the first such line if pOld is NULL
*/
static qrfEQPGraphRow *qrfEqpNextRow(Qrf *p, int iEqpId, qrfEQPGraphRow *pOld){
qrfEQPGraphRow *pRow = pOld ? pOld->pNext : p->u.pGraph->pRow;
while ( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext;
return pRow;
}
/* Render a single level of the graph that has iEqpId as its parent. Called
* * recursively to render sublevels .
*/
static void qrfEqpRenderLevel(Qrf *p, int iEqpId){
qrfEQPGraphRow *pRow, *pNext;
i64 n = strlen(p->u.pGraph->zPrefix);
char *z;
for (pRow = qrfEqpNextRow(p, iEqpId, 0 ); pRow; pRow = pNext){
pNext = qrfEqpNextRow(p, iEqpId, pRow);
z = pRow->zText;
sqlite3_str_appendf(p->pOut, "%s%s%s\n" , p->u.pGraph->zPrefix,
pNext ? "|--" : "`--" , z);
if ( n<(i64)sizeof (p->u.pGraph->zPrefix)-7 ){
memcpy(&p->u.pGraph->zPrefix[n], pNext ? "| " : " " , 4 );
qrfEqpRenderLevel(p, pRow->iEqpId);
p->u.pGraph->zPrefix[n] = 0 ;
}
}
}
/*
* * Render the 64 - bit value N in a more human - readable format into
* * pOut .
* *
* * + Only show the first three significant digits .
* * + Append suffixes K , M , G , T , P , and E for 1 e3 , 1 e6 , . . . 1 e18
*/
static void qrfApproxInt64(sqlite3_str *pOut, i64 N){
static const char aSuffix[] = { 'K' , 'M' , 'G' , 'T' , 'P' , 'E' };
int i;
if ( N<0 ){
N = N==INT64_MIN ? INT64_MAX : -N;
sqlite3_str_append(pOut, "-" , 1 );
}
if ( N<10000 ){
sqlite3_str_appendf(pOut, "%4lld " , N);
return ;
}
for (i=1 ; i<=18 ; i++){
N = (N+5 )/10 ;
if ( N<10000 ){
int n = (int )N;
switch ( i%3 ){
case 0 :
sqlite3_str_appendf(pOut, "%d.%02d" , n/1000 , (n%1000 )/10 );
break ;
case 1 :
sqlite3_str_appendf(pOut, "%2d.%d" , n/100 , (n%100 )/10 );
break ;
case 2 :
sqlite3_str_appendf(pOut, "%4d" , n/10 );
break ;
}
sqlite3_str_append(pOut, &aSuffix[i/3 ], 1 );
break ;
}
}
}
/*
* * Display and reset the EXPLAIN QUERY PLAN data
*/
static void qrfEqpRender(Qrf *p, i64 nCycle){
qrfEQPGraphRow *pRow;
if ( p->u.pGraph!=0 && (pRow = p->u.pGraph->pRow)!=0 ){
if ( pRow->zText[0 ]=='-' ){
if ( pRow->pNext==0 ){
qrfEqpReset(p);
return ;
}
sqlite3_str_appendf(p->pOut, "%s\n" , pRow->zText+3 );
p->u.pGraph->pRow = pRow->pNext;
sqlite3_free(pRow);
}else if ( nCycle>0 ){
int nSp = p->u.pGraph->nWidth - 2 ;
if ( p->spec.eStyle==QRF_STYLE_StatsEst ){
sqlite3_str_appendchar(p->pOut, nSp, ' ' );
sqlite3_str_appendall(p->pOut,
"Cycles Loops (est) Rows (est)\n" );
sqlite3_str_appendchar(p->pOut, nSp, ' ' );
sqlite3_str_appendall(p->pOut,
"---------- ------------ ------------\n" );
}else {
sqlite3_str_appendchar(p->pOut, nSp, ' ' );
sqlite3_str_appendall(p->pOut,
"Cycles Loops Rows \n" );
sqlite3_str_appendchar(p->pOut, nSp, ' ' );
sqlite3_str_appendall(p->pOut,
"---------- ----- -----\n" );
}
sqlite3_str_appendall(p->pOut, "QUERY PLAN" );
sqlite3_str_appendchar(p->pOut, nSp - 10 , ' ' );
qrfApproxInt64(p->pOut, nCycle);
sqlite3_str_appendall(p->pOut, " 100%\n" );
}else {
sqlite3_str_appendall(p->pOut, "QUERY PLAN\n" );
}
p->u.pGraph->zPrefix[0 ] = 0 ;
qrfEqpRenderLevel(p, 0 );
qrfEqpReset(p);
}
}
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
/*
* * Helper function for qrfExpStats ( ) .
* *
*/
static int qrfStatsHeight(sqlite3_stmt *p, int iEntry){
int iPid = 0 ;
int ret = 1 ;
sqlite3_stmt_scanstatus_v2(p, iEntry,
SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void *)&iPid
);
while ( iPid!=0 ){
int ii;
for (ii=0 ; 1 ; ii++){
int iId;
int res;
res = sqlite3_stmt_scanstatus_v2(p, ii,
SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void *)&iId
);
if ( res ) break ;
if ( iId==iPid ){
sqlite3_stmt_scanstatus_v2(p, ii,
SQLITE_SCANSTAT_PARENTID, SQLITE_SCANSTAT_COMPLEX, (void *)&iPid
);
}
}
ret++;
}
return ret;
}
#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */
/*
* * Generate " . scanstatus est " style of EQP output .
*/
static void qrfEqpStats(Qrf *p){
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
qrfError(p, SQLITE_ERROR, "not available in this build" );
#else
static const int f = SQLITE_SCANSTAT_COMPLEX;
sqlite3_stmt *pS = p->pStmt;
int i = 0 ;
i64 nTotal = 0 ;
int nWidth = 0 ;
int prevPid = -1 ; /* Previous iPid */
double rEstCum = 1 .0 ; /* Cumulative row estimate */
sqlite3_str *pLine = sqlite3_str_new(p->db);
sqlite3_str *pStats = sqlite3_str_new(p->db);
qrfEqpReset(p);
for (i=0 ; 1 ; i++){
const char *z = 0 ;
int n = 0 ;
if ( sqlite3_stmt_scanstatus_v2(pS,i,SQLITE_SCANSTAT_EXPLAIN,f,(void *)&z) ){
break ;
}
n = (int )strlen(z) + qrfStatsHeight(pS,i)*3 ;
if ( n>nWidth ) nWidth = n;
}
nWidth += 2 ;
sqlite3_stmt_scanstatus_v2(pS,-1 , SQLITE_SCANSTAT_NCYCLE, f, (void *)&nTotal);
for (i=0 ; 1 ; i++){
i64 nLoop = 0 ;
i64 nRow = 0 ;
i64 nCycle = 0 ;
int iId = 0 ;
int iPid = 0 ;
const char *zo = 0 ;
const char *zName = 0 ;
double rEst = 0 .0 ;
if ( sqlite3_stmt_scanstatus_v2(pS,i,SQLITE_SCANSTAT_EXPLAIN,f,(void *)&zo) ){
break ;
}
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_PARENTID,f,(void *)&iPid);
if ( iPid!=prevPid ){
prevPid = iPid;
rEstCum = 1 .0 ;
}
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_EST,f,(void *)&rEst);
rEstCum *= rEst;
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NLOOP,f,(void *)&nLoop);
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NVISIT,f,(void *)&nRow);
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NCYCLE,f,(void *)&nCycle);
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_SELECTID,f,(void *)&iId);
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NAME,f,(void *)&zName);
if ( nCycle>=0 || nLoop>=0 || nRow>=0 ){
int nSp = 0 ;
sqlite3_str_reset(pStats);
if ( nCycle>=0 && nTotal>0 ){
qrfApproxInt64(pStats, nCycle);
sqlite3_str_appendf(pStats, " %3d%%" ,
((nCycle*100 )+nTotal/2 ) / nTotal
);
nSp = 2 ;
}
if ( nLoop>=0 ){
if ( nSp ) sqlite3_str_appendchar(pStats, nSp, ' ' );
qrfApproxInt64(pStats, nLoop);
nSp = 2 ;
if ( p->spec.eStyle==QRF_STYLE_StatsEst ){
sqlite3_str_appendf(pStats, " " );
qrfApproxInt64(pStats, (i64)(rEstCum/rEst));
}
}
if ( nRow>=0 ){
if ( nSp ) sqlite3_str_appendchar(pStats, nSp, ' ' );
qrfApproxInt64(pStats, nRow);
nSp = 2 ;
if ( p->spec.eStyle==QRF_STYLE_StatsEst ){
sqlite3_str_appendf(pStats, " " );
qrfApproxInt64(pStats, (i64)rEstCum);
}
}
sqlite3_str_appendf(pLine,
"% *s %s" , -1 *(nWidth-qrfStatsHeight(pS,i)*3 ), zo,
sqlite3_str_value(pStats)
);
sqlite3_str_reset(pStats);
qrfEqpAppend(p, iId, iPid, sqlite3_str_value(pLine));
sqlite3_str_reset(pLine);
}else {
qrfEqpAppend(p, iId, iPid, zo);
}
}
if ( p->u.pGraph ) p->u.pGraph->nWidth = nWidth;
qrfStrErr(p, pLine);
sqlite3_free(sqlite3_str_finish(pLine));
qrfStrErr(p, pStats);
sqlite3_free(sqlite3_str_finish(pStats));
#endif
}
/*
* * Reset the prepared statement .
*/
static void qrfResetStmt(Qrf *p){
int rc = sqlite3_reset(p->pStmt);
if ( rc!=SQLITE_OK && p->iErr==SQLITE_OK ){
qrfError(p, rc, "%s" , sqlite3_errmsg(p->db));
}
}
/*
* * If xWrite is defined , send all content of pOut to xWrite and
* * reset pOut .
*/
static void qrfWrite(Qrf *p){
int n;
if ( p->spec.xWrite && (n = sqlite3_str_length(p->pOut))>0 ){
int rc = p->spec.xWrite(p->spec.pWriteArg,
sqlite3_str_value(p->pOut),
(sqlite3_int64)n);
sqlite3_str_reset(p->pOut);
if ( rc ){
qrfError(p, rc, "Failed to write %d bytes of output" , n);
}
}
}
/* 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 */
} aQrfUWidth[] = {
/* {1, 0x00000}, */
{0 , 0 x00300}, {1 , 0 x00370}, {0 , 0 x00483}, {1 , 0 x00487}, {0 , 0 x00488},
{1 , 0 x0048a}, {0 , 0 x00591}, {1 , 0 x005be}, {0 , 0 x005bf}, {1 , 0 x005c0},
{0 , 0 x005c1}, {1 , 0 x005c3}, {0 , 0 x005c4}, {1 , 0 x005c6}, {0 , 0 x005c7},
{1 , 0 x005c8}, {0 , 0 x00600}, {1 , 0 x00604}, {0 , 0 x00610}, {1 , 0 x00616},
{0 , 0 x0064b}, {1 , 0 x0065f}, {0 , 0 x00670}, {1 , 0 x00671}, {0 , 0 x006d6},
{1 , 0 x006e5}, {0 , 0 x006e7}, {1 , 0 x006e9}, {0 , 0 x006ea}, {1 , 0 x006ee},
{0 , 0 x0070f}, {1 , 0 x00710}, {0 , 0 x00711}, {1 , 0 x00712}, {0 , 0 x00730},
{1 , 0 x0074b}, {0 , 0 x007a6}, {1 , 0 x007b1}, {0 , 0 x007eb}, {1 , 0 x007f4},
{0 , 0 x00901}, {1 , 0 x00903}, {0 , 0 x0093c}, {1 , 0 x0093d}, {0 , 0 x00941},
{1 , 0 x00949}, {0 , 0 x0094d}, {1 , 0 x0094e}, {0 , 0 x00951}, {1 , 0 x00955},
{0 , 0 x00962}, {1 , 0 x00964}, {0 , 0 x00981}, {1 , 0 x00982}, {0 , 0 x009bc},
{1 , 0 x009bd}, {0 , 0 x009c1}, {1 , 0 x009c5}, {0 , 0 x009cd}, {1 , 0 x009ce},
{0 , 0 x009e2}, {1 , 0 x009e4}, {0 , 0 x00a01}, {1 , 0 x00a03}, {0 , 0 x00a3c},
{1 , 0 x00a3d}, {0 , 0 x00a41}, {1 , 0 x00a43}, {0 , 0 x00a47}, {1 , 0 x00a49},
{0 , 0 x00a4b}, {1 , 0 x00a4e}, {0 , 0 x00a70}, {1 , 0 x00a72}, {0 , 0 x00a81},
{1 , 0 x00a83}, {0 , 0 x00abc}, {1 , 0 x00abd}, {0 , 0 x00ac1}, {1 , 0 x00ac6},
{0 , 0 x00ac7}, {1 , 0 x00ac9}, {0 , 0 x00acd}, {1 , 0 x00ace}, {0 , 0 x00ae2},
{1 , 0 x00ae4}, {0 , 0 x00b01}, {1 , 0 x00b02}, {0 , 0 x00b3c}, {1 , 0 x00b3d},
{0 , 0 x00b3f}, {1 , 0 x00b40}, {0 , 0 x00b41}, {1 , 0 x00b44}, {0 , 0 x00b4d},
{1 , 0 x00b4e}, {0 , 0 x00b56}, {1 , 0 x00b57}, {0 , 0 x00b82}, {1 , 0 x00b83},
{0 , 0 x00bc0}, {1 , 0 x00bc1}, {0 , 0 x00bcd}, {1 , 0 x00bce}, {0 , 0 x00c3e},
{1 , 0 x00c41}, {0 , 0 x00c46}, {1 , 0 x00c49}, {0 , 0 x00c4a}, {1 , 0 x00c4e},
{0 , 0 x00c55}, {1 , 0 x00c57}, {0 , 0 x00cbc}, {1 , 0 x00cbd}, {0 , 0 x00cbf},
{1 , 0 x00cc0}, {0 , 0 x00cc6}, {1 , 0 x00cc7}, {0 , 0 x00ccc}, {1 , 0 x00cce},
{0 , 0 x00ce2}, {1 , 0 x00ce4}, {0 , 0 x00d41}, {1 , 0 x00d44}, {0 , 0 x00d4d},
{1 , 0 x00d4e}, {0 , 0 x00dca}, {1 , 0 x00dcb}, {0 , 0 x00dd2}, {1 , 0 x00dd5},
{0 , 0 x00dd6}, {1 , 0 x00dd7}, {0 , 0 x00e31}, {1 , 0 x00e32}, {0 , 0 x00e34},
{1 , 0 x00e3b}, {0 , 0 x00e47}, {1 , 0 x00e4f}, {0 , 0 x00eb1}, {1 , 0 x00eb2},
{0 , 0 x00eb4}, {1 , 0 x00eba}, {0 , 0 x00ebb}, {1 , 0 x00ebd}, {0 , 0 x00ec8},
{1 , 0 x00ece}, {0 , 0 x00f18}, {1 , 0 x00f1a}, {0 , 0 x00f35}, {1 , 0 x00f36},
{0 , 0 x00f37}, {1 , 0 x00f38}, {0 , 0 x00f39}, {1 , 0 x00f3a}, {0 , 0 x00f71},
{1 , 0 x00f7f}, {0 , 0 x00f80}, {1 , 0 x00f85}, {0 , 0 x00f86}, {1 , 0 x00f88},
{0 , 0 x00f90}, {1 , 0 x00f98}, {0 , 0 x00f99}, {1 , 0 x00fbd}, {0 , 0 x00fc6},
{1 , 0 x00fc7}, {0 , 0 x0102d}, {1 , 0 x01031}, {0 , 0 x01032}, {1 , 0 x01033},
{0 , 0 x01036}, {1 , 0 x0103b}, {0 , 0 x01058},
{1 , 0 x0105a}, {2 , 0 x01100}, {0 , 0 x01160}, {1 , 0 x01200}, {0 , 0 x0135f},
{1 , 0 x01360}, {0 , 0 x01712}, {1 , 0 x01715}, {0 , 0 x01732}, {1 , 0 x01735},
{0 , 0 x01752}, {1 , 0 x01754}, {0 , 0 x01772}, {1 , 0 x01774}, {0 , 0 x017b4},
{1 , 0 x017b6}, {0 , 0 x017b7}, {1 , 0 x017be}, {0 , 0 x017c6}, {1 , 0 x017c7},
{0 , 0 x017c9}, {1 , 0 x017d4}, {0 , 0 x017dd}, {1 , 0 x017de}, {0 , 0 x0180b},
{1 , 0 x0180e}, {0 , 0 x018a9}, {1 , 0 x018aa}, {0 , 0 x01920}, {1 , 0 x01923},
{0 , 0 x01927}, {1 , 0 x01929}, {0 , 0 x01932}, {1 , 0 x01933}, {0 , 0 x01939},
{1 , 0 x0193c}, {0 , 0 x01a17}, {1 , 0 x01a19}, {0 , 0 x01b00}, {1 , 0 x01b04},
{0 , 0 x01b34}, {1 , 0 x01b35}, {0 , 0 x01b36}, {1 , 0 x01b3b}, {0 , 0 x01b3c},
{1 , 0 x01b3d}, {0 , 0 x01b42}, {1 , 0 x01b43}, {0 , 0 x01b6b}, {1 , 0 x01b74},
{0 , 0 x01dc0}, {1 , 0 x01dcb}, {0 , 0 x01dfe}, {1 , 0 x01e00}, {0 , 0 x0200b},
{1 , 0 x02010}, {0 , 0 x0202a}, {1 , 0 x0202f}, {0 , 0 x02060}, {1 , 0 x02064},
{0 , 0 x0206a}, {1 , 0 x02070}, {0 , 0 x020d0}, {1 , 0 x020f0}, {2 , 0 x02329},
{1 , 0 x0232b}, {2 , 0 x02e80}, {0 , 0 x0302a}, {2 , 0 x03030}, {1 , 0 x0303f},
{2 , 0 x03040}, {0 , 0 x03099}, {2 , 0 x0309b}, {1 , 0 x0a4d0}, {0 , 0 x0a806},
{1 , 0 x0a807}, {0 , 0 x0a80b}, {1 , 0 x0a80c}, {0 , 0 x0a825}, {1 , 0 x0a827},
{2 , 0 x0ac00}, {1 , 0 x0d7a4}, {2 , 0 x0f900}, {1 , 0 x0fb00}, {0 , 0 x0fb1e},
{1 , 0 x0fb1f}, {0 , 0 x0fe00}, {2 , 0 x0fe10}, {1 , 0 x0fe1a}, {0 , 0 x0fe20},
{1 , 0 x0fe24}, {2 , 0 x0fe30}, {1 , 0 x0fe70}, {0 , 0 x0feff}, {2 , 0 x0ff00},
{1 , 0 x0ff61}, {2 , 0 x0ffe0}, {1 , 0 x0ffe7}, {0 , 0 x0fff9}, {1 , 0 x0fffc},
{0 , 0 x10a01}, {1 , 0 x10a04}, {0 , 0 x10a05}, {1 , 0 x10a07}, {0 , 0 x10a0c},
{1 , 0 x10a10}, {0 , 0 x10a38}, {1 , 0 x10a3b}, {0 , 0 x10a3f}, {1 , 0 x10a40},
{0 , 0 x1d167}, {1 , 0 x1d16a}, {0 , 0 x1d173}, {1 , 0 x1d183}, {0 , 0 x1d185},
{1 , 0 x1d18c}, {0 , 0 x1d1aa}, {1 , 0 x1d1ae}, {0 , 0 x1d242}, {1 , 0 x1d245},
{2 , 0 x20000}, {1 , 0 x2fffe}, {2 , 0 x30000}, {1 , 0 x3fffe}, {0 , 0 xe0001},
{1 , 0 xe0002}, {0 , 0 xe0020}, {1 , 0 xe0080}, {0 , 0 xe0100}, {1 , 0 xe01f0}
};
/*
* * 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 sqlite3_qrf_wcwidth(int c){
int iFirst, iLast;
/* Fast path for common characters */
if ( c<0 x300 ) return 1 ;
/* The general case */
iFirst = 0 ;
iLast = sizeof (aQrfUWidth)/sizeof (aQrfUWidth[0 ]) - 1 ;
while ( iFirst<iLast-1 ){
int iMid = (iFirst+iLast)/2 ;
int cMid = aQrfUWidth[iMid].iFirst;
if ( cMid < c ){
iFirst = iMid;
}else if ( cMid > c ){
iLast = iMid - 1 ;
}else {
return aQrfUWidth[iMid].w;
}
}
if ( aQrfUWidth[iLast].iFirst > c ) return aQrfUWidth[iFirst].w;
return aQrfUWidth[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 . It does
* * not attempt to detect illegal characters .
*/
int sqlite3_qrf_decode_utf8(const unsigned char *z, int *pU){
if ( (z[0 ] & 0 xe0)==0 xc0 && (z[1 ] & 0 xc0)==0 x80 ){
*pU = ((z[0 ] & 0 x1f)<<6 ) | (z[1 ] & 0 x3f);
return 2 ;
}
if ( (z[0 ] & 0 xf0)==0 xe0 && (z[1 ] & 0 xc0)==0 x80 && (z[2 ] & 0 xc0)==0 x80 ){
*pU = ((z[0 ] & 0 x0f)<<12 ) | ((z[1 ] & 0 x3f)<<6 ) | (z[2 ] & 0 x3f);
return 3 ;
}
if ( (z[0 ] & 0 xf8)==0 xf0 && (z[1 ] & 0 xc0)==0 x80 && (z[2 ] & 0 xc0)==0 x80
&& (z[3 ] & 0 xc0)==0 x80
){
*pU = ((z[0 ] & 0 x0f)<<18 ) | ((z[1 ] & 0 x3f)<<12 ) | ((z[2 ] & 0 x3f))<<6
| (z[3 ] & 0 x3f);
return 4 ;
}
*pU = 0 ;
return 1 ;
}
/*
* * Check to see if z [ ] is a valid VT100 escape . If it is , then
* * return the number of bytes in the escape sequence . Return 0 if
* * z [ ] is not a VT100 escape .
* *
* * This routine assumes that z [ 0 ] is \ 033 ( ESC ) .
*/
static int qrfIsVt100(const unsigned char *z){
int i;
if ( z[1 ]!='[' ) return 0 ;
i = 2 ;
while ( z[i]>=0 x30 && z[i]<=0 x3f ){ i++; }
while ( z[i]>=0 x20 && z[i]<=0 x2f ){ i++; }
if ( z[i]<0 x40 || z[i]>0 x7e ) return 0 ;
return i+1 ;
}
/*
* * Return the length of a string in display characters .
* *
* * Most characters of the input string count as 1 , including
* * multi - byte UTF8 characters . However , zero - width unicode
* * characters and VT100 escape sequences count as zero , and
* * double - width characters count as two .
* *
* * The definition of " zero - width " and " double - width " characters
* * is not precise . It depends on the output device , to some extent ,
* * and it varies according to the Unicode version . This routine
* * makes the best guess that it can .
*/
size_t sqlite3_qrf_wcswidth(const char *zIn){
const unsigned char *z = (const unsigned char *)zIn;
size_t n = 0 ;
while ( *z ){
if ( z[0 ]<' ' ){
int k;
if ( z[0 ]=='\033' && (k = qrfIsVt100(z))>0 ){
z += k;
}else {
z++;
}
}else if ( (0 x80&z[0 ])==0 ){
n++;
z++;
}else {
int u = 0 ;
int len = sqlite3_qrf_decode_utf8(z, &u);
z += len;
n += sqlite3_qrf_wcwidth(u);
}
}
return n;
}
/*
* * Return the display width of the longest line of text
* * in the ( possibly ) multi - line input string zIn [ 0 . . nByte ] .
* * zIn [ ] is not necessarily zero - terminated . Take
* * into account tab characters , zero - and double - width
* * characters , CR and NL , and VT100 escape codes .
* *
* * Write the number of newlines into * pnNL . So , * pnNL will
* * return 0 if everything fits on one line , or positive it
* * it will need to be split .
*/
static int qrfDisplayWidth(const char *zIn, sqlite3_int64 nByte, int *pnNL){
const unsigned char *z;
const unsigned char *zEnd;
int mx = 0 ;
int n = 0 ;
int nNL = 0 ;
if ( zIn==0 ) zIn = "" ;
z = (const unsigned char *)zIn;
zEnd = &z[nByte];
while ( z<zEnd ){
if ( z[0 ]<' ' ){
int k;
if ( z[0 ]=='\033' && (k = qrfIsVt100(z))>0 ){
z += k;
}else {
if ( z[0 ]=='\t' ){
n = (n+8 )&~7 ;
}else if ( z[0 ]=='\n' || z[0 ]=='\r' ){
nNL++;
if ( n>mx ) mx = n;
n = 0 ;
}
z++;
}
}else if ( (0 x80&z[0 ])==0 ){
n++;
z++;
}else {
int u = 0 ;
int len = sqlite3_qrf_decode_utf8(z, &u);
z += len;
n += sqlite3_qrf_wcwidth(u);
}
}
if ( mx>n ) n = mx;
if ( pnNL ) *pnNL = nNL;
return n;
}
/*
* * Escape the input string if it is needed and in accordance with
* * eEsc , which is either QRF_ESC_Ascii or QRF_ESC_Symbol .
* *
* * Escaping is needed if the string contains any control characters
* * other than \ t , \ n , and \ r \ n
* *
* * If no escaping is needed ( the common case ) then set * ppOut to NULL
* * and return 0 . If escaping is needed , write the escaped string into
* * memory obtained from sqlite3_malloc64 ( ) and make * ppOut point to that
* * memory and return 0 . If an error occurs , return non - zero .
* *
* * The caller is responsible for freeing * ppFree if it is non - NULL in order
* * to reclaim memory .
*/
static void qrfEscape(
int eEsc, /* QRF_ESC_Ascii or QRF_ESC_Symbol */
sqlite3_str *pStr, /* String to be escaped */
int iStart /* Begin escapding on this byte of pStr */
){
sqlite3_int64 i, j; /* Loop counters */
sqlite3_int64 sz; /* Size of the string prior to escaping */
sqlite3_int64 nCtrl = 0 ;/* Number of control characters to escape */
unsigned char *zIn; /* Text to be escaped */
unsigned char c; /* A single character of the text */
unsigned char *zOut; /* Where to write the results */
/* Find the text to be escaped */
zIn = (unsigned char *)sqlite3_str_value(pStr);
if ( zIn==0 ) return ;
zIn += iStart;
/* Count the control characters */
for (i=0 ; (c = zIn[i])!=0 ; i++){
if ( c<=0 x1f
&& c!='\t'
&& c!='\n'
&& (c!='\r' || zIn[i+1 ]!='\n' )
){
nCtrl++;
}
}
if ( nCtrl==0 ) return ; /* Early out if no control characters */
/* Make space to hold the escapes. Copy the original text to the end
** of the available space. */
sz = sqlite3_str_length(pStr) - iStart;
if ( eEsc==QRF_ESC_Symbol ) nCtrl *= 2 ;
sqlite3_str_appendchar(pStr, nCtrl, ' ' );
zOut = (unsigned char *)sqlite3_str_value(pStr);
if ( zOut==0 ) return ;
zOut += iStart;
zIn = zOut + nCtrl;
memmove(zIn,zOut,sz);
/* Convert the control characters */
for (i=j=0 ; (c = zIn[i])!=0 ; i++){
if ( c>0 x1f
|| c=='\t'
|| c=='\n'
|| (c=='\r' && zIn[i+1 ]=='\n' )
){
continue ;
}
if ( i>0 ){
memmove(&zOut[j], zIn, i);
j += i;
}
zIn += i+1 ;
i = -1 ;
if ( eEsc==QRF_ESC_Symbol ){
zOut[j++] = 0 xe2;
zOut[j++] = 0 x90;
zOut[j++] = 0 x80+c;
}else {
zOut[j++] = '^' ;
zOut[j++] = 0 x40+c;
}
}
}
/*
* * Determine if the string z [ ] can be shown as plain text . Return true
* * if z [ ] is unambiguously text . Return false if z [ ] needs to be
* * quoted .
* *
* * All of the following must be true in order for z [ ] to be relaxable :
* *
* * ( 1 ) z [ ] does not begin or end with ' or whitespace
* * ( 2 ) z [ ] is not the same as the NULL rendering
* * ( 3 ) z [ ] does not looks like a numeric literal
*/
static int qrfRelaxable(Qrf *p, const char *z){
size_t i, n;
if ( z[0 ]=='\' ' || qrfSpace(z[0]) ) return 0;
if ( z[0 ]==0 ){
return (p->spec.zNull!=0 && p->spec.zNull[0 ]!=0 );
}
n = strlen(z);
if ( n==0 || z[n-1 ]=='\' ' || qrfSpace(z[n-1]) ) return 0;
if ( p->spec.zNull && strcmp(p->spec.zNull,z)==0 ) return 0 ;
i = (z[0 ]=='-' || z[0 ]=='+' );
if ( strcmp(z+i,"Inf" )==0 ) return 0 ;
if ( !qrfDigit(z[i]) ) return 1 ;
i++;
while ( qrfDigit(z[i]) ){ i++; }
if ( z[i]==0 ) return 0 ;
if ( z[i]=='.' ){
i++;
while ( qrfDigit(z[i]) ){ i++; }
if ( z[i]==0 ) return 0 ;
}
if ( z[i]=='e' || z[i]=='E' ){
i++;
if ( z[i]=='+' || z[i]=='-' ){ i++; }
if ( !qrfDigit(z[i]) ) return 1 ;
i++;
while ( qrfDigit(z[i]) ){ i++; }
}
return z[i]!=0 ;
}
/*
* * If a field contains any character identified by a 1 in the following
* * array , then the string must be quoted for CSV .
*/
static const char qrfCsvQuote[] = {
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
};
/*
* * Encode text appropriately and append it to pOut .
*/
static void qrfEncodeText(Qrf *p, sqlite3_str *pOut, const char *zTxt){
int iStart = sqlite3_str_length(pOut);
switch ( p->spec.eText ){
case QRF_TEXT_Relaxed:
if ( qrfRelaxable(p, zTxt) ){
sqlite3_str_appendall(pOut, zTxt);
break ;
}
deliberate_fall_through; /* FALLTHRU */
case QRF_TEXT_Sql: {
if ( p->spec.eEsc==QRF_ESC_Off ){
sqlite3_str_appendf(pOut, "%Q" , zTxt);
}else {
sqlite3_str_appendf(pOut, "%#Q" , zTxt);
}
break ;
}
case QRF_TEXT_Csv: {
unsigned int i;
for (i=0 ; zTxt[i]; i++){
if ( qrfCsvQuote[((const unsigned char *)zTxt)[i]] ){
i = 0 ;
break ;
}
}
if ( i==0 || strstr(zTxt, p->spec.zColumnSep)!=0 ){
sqlite3_str_appendf(pOut, "\" %w\"" , zTxt);
}else {
sqlite3_str_appendall(pOut, zTxt);
}
break ;
}
case QRF_TEXT_Html: {
const unsigned char *z = (const unsigned char *)zTxt;
while ( *z ){
unsigned int i = 0 ;
unsigned char c;
while ( (c=z[i])>'>'
|| (c && c!='<' && c!='>' && c!='&' && c!='\"' && c!='\' ')
){
i++;
}
if ( i>0 ){
sqlite3_str_append(pOut, (const char *)z, i);
}
switch ( z[i] ){
case '>' : sqlite3_str_append(pOut, "<" , 4 ); break ;
case '&' : sqlite3_str_append(pOut, "&" , 5 ); break ;
case '<' : sqlite3_str_append(pOut, "<" , 4 ); break ;
case '"' : sqlite3_str_append(pOut, """ , 6 ); break ;
case '\' ': sqlite3_str_append(pOut, "'", 5); break;
default : i--;
}
z += i + 1 ;
}
break ;
}
case QRF_TEXT_Tcl:
case QRF_TEXT_Json: {
const unsigned char *z = (const unsigned char *)zTxt;
sqlite3_str_append(pOut, "\" ", 1);
while ( *z ){
unsigned int i;
for (i=0 ; z[i]>=0 x20 && z[i]!='\\' && z[i]!='"' ; i++){}
if ( i>0 ){
sqlite3_str_append(pOut, (const char *)z, i);
}
if ( z[i]==0 ) break ;
switch ( z[i] ){
case '"' : sqlite3_str_append(pOut, "\\\" ", 2); break;
case '\\' : sqlite3_str_append(pOut, "\\\\" , 2 ); break ;
case '\b' : sqlite3_str_append(pOut, "\\b" , 2 ); break ;
case '\f' : sqlite3_str_append(pOut, "\\f" , 2 ); break ;
case '\n' : sqlite3_str_append(pOut, "\\n" , 2 ); break ;
case '\r' : sqlite3_str_append(pOut, "\\r" , 2 ); break ;
case '\t' : sqlite3_str_append(pOut, "\\t" , 2 ); break ;
default : {
if ( p->spec.eText==QRF_TEXT_Json ){
sqlite3_str_appendf(pOut, "\\u%04x" , z[i]);
}else {
sqlite3_str_appendf(pOut, "\\%03o" , z[i]);
}
break ;
}
}
z += i + 1 ;
}
sqlite3_str_append(pOut, "\" ", 1);
break ;
}
default : {
sqlite3_str_appendall(pOut, zTxt);
break ;
}
}
if ( p->spec.eEsc!=QRF_ESC_Off ){
qrfEscape(p->spec.eEsc, pOut, iStart);
}
}
/*
* * Do a quick sanity check to see aBlob [ 0 . . nBlob - 1 ] is valid JSONB
* * return true if it is and false if it is not .
* *
* * False positives are possible , but not false negatives .
*/
static int qrfJsonbQuickCheck(unsigned char *aBlob, int nBlob){
unsigned char x; /* Payload size half-byte */
int i; /* Loop counter */
int n; /* Bytes in the payload size integer */
sqlite3_uint64 sz; /* value of the payload size integer */
if ( nBlob==0 ) return 0 ;
x = aBlob[0 ]>>4 ;
if ( x<=11 ) return nBlob==(1 +x);
n = x<14 ? x-11 : 4 *(x-13 );
if ( nBlob<1 +n ) return 0 ;
sz = aBlob[1 ];
for (i=1 ; i<n; i++) sz = (sz<<8 ) + aBlob[i+1 ];
return sz+n+1 ==(sqlite3_uint64)nBlob;
}
/*
* * The current iCol - th column of p - > pStmt is known to be a BLOB . Check
* * to see if that BLOB is really a JSONB blob . If it is , then translate
* * it into a text JSON representation and return a pointer to that text JSON .
* * If the BLOB is not JSONB , then return a NULL pointer .
* *
* * The memory used to hold the JSON text is managed internally by the
* * " p " object and is overwritten and / or deallocated upon the next call
* * to this routine ( with the same p argument ) or when the p object is
* * finailized .
*/
static const char *qrfJsonbToJson(Qrf *p, int iCol){
int nByte;
const void *pBlob;
int rc;
nByte = sqlite3_column_bytes(p->pStmt, iCol);
pBlob = sqlite3_column_blob(p->pStmt, iCol);
if ( qrfJsonbQuickCheck((unsigned char *)pBlob, nByte)==0 ){
return 0 ;
}
if ( p->pJTrans==0 ){
sqlite3 *db;
rc = sqlite3_open(":memory:" ,&db);
if ( rc ){
sqlite3_close(db);
return 0 ;
}
rc = sqlite3_prepare_v2(db, "SELECT json(?1)" , -1 , &p->pJTrans, 0 );
if ( rc ){
sqlite3_finalize(p->pJTrans);
p->pJTrans = 0 ;
sqlite3_close(db);
return 0 ;
}
}else {
sqlite3_reset(p->pJTrans);
}
sqlite3_bind_blob(p->pJTrans, 1 , (void *)pBlob, nByte, SQLITE_STATIC);
rc = sqlite3_step(p->pJTrans);
if ( rc==SQLITE_ROW ){
return (const char *)sqlite3_column_text(p->pJTrans, 0 );
}else {
return 0 ;
}
}
/*
* * Adjust the input string zIn [ ] such that it is no more than N display
* * characters wide . If it is wider than that , then truncate and add
* * ellipsis . Or if zIn [ ] contains a \ r or \ n , truncate at that point ,
* * adding ellipsis . Embedded tabs in zIn [ ] are converted into ordinary
* * spaces .
* *
* * Return this display width of the modified title string .
*/
static int qrfTitleLimit(char *zIn, int N){
unsigned char *z = (unsigned char *)zIn;
int n = 0 ;
unsigned char *zEllipsis = 0 ;
while ( 1 /*exit-by-break*/ ){
if ( z[0 ]<' ' ){
int k;
if ( z[0 ]==0 ){
zEllipsis = 0 ;
break ;
}else if ( z[0 ]=='\033' && (k = qrfIsVt100(z))>0 ){
z += k;
}else if ( z[0 ]=='\t' ){
z[0 ] = ' ' ;
}else if ( z[0 ]=='\n' || z[0 ]=='\r' ){
z[0 ] = ' ' ;
}else {
z++;
}
}else if ( (0 x80&z[0 ])==0 ){
if ( n>=(N-3 ) && zEllipsis==0 ) zEllipsis = z;
if ( n==N ){ z[0 ] = 0 ; break ; }
n++;
z++;
}else {
int u = 0 ;
int len = sqlite3_qrf_decode_utf8(z, &u);
if ( n+len>(N-3 ) && zEllipsis==0 ) zEllipsis = z;
if ( n+len>N ){ z[0 ] = 0 ; break ; }
z += len;
n += sqlite3_qrf_wcwidth(u);
}
}
if ( zEllipsis && N>=3 ) memcpy(zEllipsis,"..." ,4 );
return n;
}
/*
* * Render value pVal into pOut
*/
static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){
#if SQLITE_VERSION_NUMBER>=3052000
int iStartLen = sqlite3_str_length(pOut);
#endif
if ( p->spec.xRender ){
sqlite3_value *pVal;
char *z;
pVal = sqlite3_value_dup(sqlite3_column_value(p->pStmt,iCol));
z = p->spec.xRender(p->spec.pRenderArg, pVal);
sqlite3_value_free(pVal);
if ( z ){
sqlite3_str_appendall(pOut, z);
sqlite3_free(z);
return ;
}
}
switch ( sqlite3_column_type(p->pStmt,iCol) ){
case SQLITE_INTEGER: {
sqlite3_str_appendf(pOut, "%lld" , sqlite3_column_int64(p->pStmt,iCol));
break ;
}
case SQLITE_FLOAT: {
const char *zTxt = (const char *)sqlite3_column_text(p->pStmt,iCol);
sqlite3_str_appendall(pOut, zTxt);
break ;
}
case SQLITE_BLOB: {
if ( p->spec.bTextJsonb==QRF_Yes ){
const char *zJson = qrfJsonbToJson(p, iCol);
if ( zJson ){
if ( p->spec.eText==QRF_TEXT_Sql ){
sqlite3_str_append(pOut,"jsonb(" ,6 );
qrfEncodeText(p, pOut, zJson);
sqlite3_str_append(pOut,")" ,1 );
}else {
qrfEncodeText(p, pOut, zJson);
}
break ;
}
}
switch ( p->spec.eBlob ){
case QRF_BLOB_Hex:
case QRF_BLOB_Sql: {
int iStart;
int nBlob = sqlite3_column_bytes(p->pStmt,iCol);
int i, j;
char *zVal;
const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol);
if ( p->spec.eBlob==QRF_BLOB_Sql ){
sqlite3_str_append(pOut, "x'" , 2 );
}
iStart = sqlite3_str_length(pOut);
sqlite3_str_appendchar(pOut, nBlob, ' ' );
sqlite3_str_appendchar(pOut, nBlob, ' ' );
if ( p->spec.eBlob==QRF_BLOB_Sql ){
sqlite3_str_appendchar(pOut, 1 , '\' ');
}
if ( sqlite3_str_errcode(pOut) ) return ;
zVal = sqlite3_str_value(pOut);
for (i=0 , j=iStart; i<nBlob; i++, j+=2 ){
unsigned char c = a[i];
zVal[j] = "0123456789abcdef" [(c>>4 )&0 xf];
zVal[j+1 ] = "0123456789abcdef" [(c)&0 xf];
}
break ;
}
case QRF_BLOB_Tcl:
case QRF_BLOB_Json: {
int iStart;
int nBlob = sqlite3_column_bytes(p->pStmt,iCol);
int i, j;
char *zVal;
const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol);
int szC = p->spec.eBlob==QRF_BLOB_Json ? 6 : 4 ;
sqlite3_str_append(pOut, "\" ", 1);
iStart = sqlite3_str_length(pOut);
for (i=szC; i>0 ; i--){
sqlite3_str_appendchar(pOut, nBlob, ' ' );
}
sqlite3_str_appendchar(pOut, 1 , '"' );
if ( sqlite3_str_errcode(pOut) ) return ;
zVal = sqlite3_str_value(pOut);
for (i=0 , j=iStart; i<nBlob; i++, j+=szC){
unsigned char c = a[i];
zVal[j] = '\\' ;
if ( szC==4 ){
zVal[j+1 ] = '0' + ((c>>6 )&3 );
zVal[j+2 ] = '0' + ((c>>3 )&7 );
zVal[j+3 ] = '0' + (c&7 );
}else {
zVal[j+1 ] = 'u' ;
zVal[j+2 ] = '0' ;
zVal[j+3 ] = '0' ;
zVal[j+4 ] = "0123456789abcdef" [(c>>4 )&0 xf];
zVal[j+5 ] = "0123456789abcdef" [(c)&0 xf];
}
}
break ;
}
case QRF_BLOB_Size: {
int nBlob = sqlite3_column_bytes(p->pStmt,iCol);
sqlite3_str_appendf(pOut, "(%d-byte blob)" , nBlob);
break ;
}
default : {
const char *zTxt = (const char *)sqlite3_column_text(p->pStmt,iCol);
qrfEncodeText(p, pOut, zTxt);
}
}
break ;
}
case SQLITE_NULL: {
sqlite3_str_appendall(pOut, p->spec.zNull);
break ;
}
case SQLITE_TEXT: {
const char *zTxt = (const char *)sqlite3_column_text(p->pStmt,iCol);
qrfEncodeText(p, pOut, zTxt);
break ;
}
}
#if SQLITE_VERSION_NUMBER>=3052000
if ( p->spec.nCharLimit>0
&& (sqlite3_str_length(pOut) - iStartLen) > p->spec.nCharLimit
){
const unsigned char *z;
int ii = 0 , w = 0 , limit = p->spec.nCharLimit;
z = (const unsigned char *)sqlite3_str_value(pOut) + iStartLen;
if ( limit<4 ) limit = 4 ;
while ( 1 ){
if ( z[ii]<' ' ){
int k;
if ( z[ii]=='\033' && (k = qrfIsVt100(z+ii))>0 ){
ii += k;
}else if ( z[ii]==0 ){
break ;
}else {
ii++;
}
}else if ( (0 x80&z[ii])==0 ){
w++;
if ( w>limit ) break ;
ii++;
}else {
int u = 0 ;
int len = sqlite3_qrf_decode_utf8(&z[ii], &u);
w += sqlite3_qrf_wcwidth(u);
if ( w>limit ) break ;
ii += len;
}
}
if ( w>limit ){
sqlite3_str_truncate(pOut, iStartLen+ii);
sqlite3_str_append(pOut, "..." , 3 );
}
}
#endif
}
/* Trim spaces of the end if pOut
*/
static void qrfRTrim(sqlite3_str *pOut){
#if SQLITE_VERSION_NUMBER>=3052000
int nByte = sqlite3_str_length(pOut);
const char *zOut = sqlite3_str_value(pOut);
while ( nByte>0 && zOut[nByte-1 ]==' ' ){ nByte--; }
sqlite3_str_truncate(pOut, nByte);
#endif
}
/*
* * Store string zUtf to pOut as w characters . If w is negative ,
* * then right - justify the text . W is the width in display characters , not
* * in bytes . Double - width unicode characters count as two characters .
* * VT100 escape sequences count as zero . And so forth .
*/
static void qrfWidthPrint(Qrf *p, sqlite3_str *pOut, int w, const char *zUtf){
const unsigned char *a = (const unsigned char *)zUtf;
static const int mxW = 10000000 ;
unsigned char c;
int i = 0 ;
int n = 0 ;
int k;
int aw;
(void )p;
if ( w<-mxW ){
w = -mxW;
}else if ( w>mxW ){
w= mxW;
}
aw = w<0 ? -w : w;
if ( a==0 ) a = (const unsigned char *)"" ;
while ( (c = a[i])!=0 ){
if ( (c&0 xc0)==0 xc0 ){
int u;
int len = sqlite3_qrf_decode_utf8(a+i, &u);
int x = sqlite3_qrf_wcwidth(u);
if ( x+n>aw ){
break ;
}
i += len;
n += x;
}else if ( c==0 x1b && (k = qrfIsVt100(&a[i]))>0 ){
i += k;
}else if ( n>=aw ){
break ;
}else {
n++;
i++;
}
}
if ( n>=aw ){
sqlite3_str_append(pOut, zUtf, i);
}else if ( w<0 ){
if ( aw>n ) sqlite3_str_appendchar(pOut, aw-n, ' ' );
sqlite3_str_append(pOut, zUtf, i);
}else {
sqlite3_str_append(pOut, zUtf, i);
if ( aw>n ) sqlite3_str_appendchar(pOut, aw-n, ' ' );
}
}
/*
* * ( * pz ) [ ] is a line of text that is to be displayed the box or table or
* * similar tabular formats . z [ ] contain newlines or might be too wide
* * to fit in the columns so will need to be split into multiple line .
* *
* * This routine determines :
* *
* * * How many bytes of z [ ] should be shown on the current line .
* * * How many character positions those bytes will cover .
* * * The byte offset to the start of the next line .
*/
static void qrfWrapLine(
const char *zIn, /* Input text to be displayed */
int w, /* Column width in characters (not bytes) */
int bWrap, /* True if we should do word-wrapping */
int *pnThis, /* OUT: How many bytes of z[] for the current line */
int *pnWide, /* OUT: How wide is the text of this line */
int *piNext /* OUT: Offset into z[] to start of the next line */
){
int i; /* Input bytes consumed */
int k; /* Bytes in a VT100 code */
int n; /* Output column number */
const unsigned char *z = (const unsigned char *)zIn;
unsigned char c = 0 ;
if ( z[0 ]==0 ){
*pnThis = 0 ;
*pnWide = 0 ;
*piNext = 0 ;
return ;
}
n = 0 ;
for (i=0 ; n<=w; i++){
c = z[i];
if ( c>=0 xc0 ){
int u;
int len = sqlite3_qrf_decode_utf8(&z[i], &u);
int wcw = sqlite3_qrf_wcwidth(u);
if ( wcw+n>w ) break ;
i += len-1 ;
n += wcw;
continue ;
}
if ( c>=' ' ){
if ( n==w ) break ;
n++;
continue ;
}
if ( c==0 || c=='\n' ) break ;
if ( c=='\r' && z[i+1 ]=='\n' ){ c = z[++i]; break ; }
if ( c=='\t' ){
int wcw = 8 - (n&7 );
if ( n+wcw>w ) break ;
n += wcw;
continue ;
}
if ( c==0 x1b && (k = qrfIsVt100(&z[i]))>0 ){
i += k-1 ;
}else if ( n==w ){
break ;
}else {
n++;
}
}
if ( c==0 ){
*pnThis = i;
*pnWide = n;
*piNext = i;
return ;
}
if ( c=='\n' ){
*pnThis = i;
*pnWide = n;
*piNext = i+1 ;
return ;
}
/* If we get this far, that means the current line will end at some
* * point that is neither a " \ n " or a 0 x00 . Figure out where that
* * split should occur
*/
if ( bWrap && z[i]!=0 && !qrfSpace(z[i]) && qrfAlnum(c)==qrfAlnum(z[i]) ){
/* Perhaps try to back up to a better place to break the line */
for (k=i-1 ; k>=i/2 ; k--){
if ( qrfSpace(z[k]) ) break ;
}
if ( k<i/2 ){
for (k=i; k>=i/2 ; k--){
if ( qrfAlnum(z[k-1 ])!=qrfAlnum(z[k]) && (z[k]&0 xc0)!=0 x80 ) break ;
}
}
if ( k>=i/2 ){
i = k;
n = qrfDisplayWidth((const char *)z, k, 0 );
}
}
*pnThis = i;
*pnWide = n;
while ( zIn[i]==' ' || zIn[i]=='\t' || zIn[i]=='\r' ){ i++; }
*piNext = i;
}
/*
* * Append nVal bytes of text from zVal onto the end of pOut .
* * Convert tab characters in zVal to the appropriate number of
* * spaces .
*/
static void qrfAppendWithTabs(
sqlite3_str *pOut, /* Append text here */
const char *zVal, /* Text to append */
int nVal /* Use only the first nVal bytes of zVal[] */
){
int i = 0 ;
unsigned int col = 0 ;
unsigned char *z = (unsigned char *)zVal;
while ( i<nVal ){
unsigned char c = z[i];
if ( c<' ' ){
int k;
sqlite3_str_append(pOut, (const char *)z, i);
nVal -= i;
z += i;
i = 0 ;
if ( c=='\033' && (k = qrfIsVt100(z))>0 ){
sqlite3_str_append(pOut, (const char *)z, k);
z += k;
nVal -= k;
}else if ( c=='\t' ){
k = 8 - (col&7 );
sqlite3_str_appendchar(pOut, k, ' ' );
col += k;
z++;
nVal--;
}else if ( c=='\r' && nVal==1 ){
z++;
nVal--;
}else {
char zCtrlPik[4 ];
col++;
zCtrlPik[0 ] = 0 xe2;
zCtrlPik[1 ] = 0 x90;
zCtrlPik[2 ] = 0 x80+c;
sqlite3_str_append(pOut, zCtrlPik, 3 );
z++;
nVal--;
}
}else if ( (0 x80&c)==0 ){
i++;
col++;
}else {
int u = 0 ;
int len = sqlite3_qrf_decode_utf8(&z[i], &u);
i += len;
col += sqlite3_qrf_wcwidth(u);
}
}
sqlite3_str_append(pOut, (const char *)z, i);
}
/*
* * GCC does not define the offsetof ( ) macro so we ' ll have to do it
* * ourselves .
*/
#ifndef offsetof
# define offsetof(ST,M) ((size_t)((char *)&((ST*)0 )->M - (char *)0 ))
#endif
/*
* * Data for columnar layout , collected into a single object so
* * that it can be more easily passed into subroutines .
*/
typedef struct qrfColData qrfColData;
struct qrfColData {
Qrf *p; /* The QRF instance */
int nCol; /* Number of columns in the table */
unsigned char bMultiRow; /* One or more cells will span multiple lines */
unsigned char nMargin; /* Width of column margins */
sqlite3_int64 nRow; /* Number of rows */
sqlite3_int64 nAlloc; /* Number of cells allocated */
sqlite3_int64 n; /* Number of cells. nCol*nRow */
char **az; /* Content of all cells */
int *aiWth; /* Width of each cell */
unsigned char *abNum; /* True for each numeric cell */
struct qrfPerCol { /* Per-column data */
char *z; /* Cache of text for current row */
int w; /* Computed width of this column */
int mxW; /* Maximum natural (unwrapped) width */
unsigned char e; /* Alignment */
unsigned char fx; /* Width is fixed */
unsigned char bNum; /* True if is numeric */
} *a; /* One per column */
};
/*
* * Output horizontally justified text into pOut . The text is the
* * first nVal bytes of zVal . Include nWS bytes of whitespace , either
* * split between both sides , or on the left , or on the right , depending
* * on eAlign .
*/
static void qrfPrintAligned(
sqlite3_str *pOut, /* Append text here */
struct qrfPerCol *pCol, /* Information about the text to print */
int nVal, /* Use only the first nVal bytes of zVal[] */
int nWS /* Whitespace for horizonal alignment */
){
unsigned char eAlign = pCol->e & QRF_ALIGN_HMASK;
if ( eAlign==QRF_Auto && pCol->bNum ) eAlign = QRF_ALIGN_Right;
if ( eAlign==QRF_ALIGN_Center ){
/* Center the text */
sqlite3_str_appendchar(pOut, nWS/2 , ' ' );
qrfAppendWithTabs(pOut, pCol->z, nVal);
sqlite3_str_appendchar(pOut, nWS - nWS/2 , ' ' );
}else if ( eAlign==QRF_ALIGN_Right ){
/* Right justify the text */
sqlite3_str_appendchar(pOut, nWS, ' ' );
qrfAppendWithTabs(pOut, pCol->z, nVal);
}else {
/* Left justify the text */
qrfAppendWithTabs(pOut, pCol->z, nVal);
sqlite3_str_appendchar(pOut, nWS, ' ' );
}
}
/*
* * Free all the memory allocates in the qrfColData object
*/
static void qrfColDataFree(qrfColData *p){
sqlite3_int64 i;
for (i=0 ; i<p->n; i++) sqlite3_free(p->az[i]);
sqlite3_free(p->az);
sqlite3_free(p->aiWth);
sqlite3_free(p->abNum);
sqlite3_free(p->a);
memset(p, 0 , sizeof (*p));
}
/*
* * Allocate space for more cells in the qrfColData object .
* * Return non - zero if a memory allocation fails .
*/
static int qrfColDataEnlarge(qrfColData *p){
char **azData;
int *aiWth;
unsigned char *abNum;
p->nAlloc = 2 *p->nAlloc + 10 *p->nCol;
azData = sqlite3_realloc64(p->az, p->nAlloc*sizeof (char *));
if ( azData==0 ){
qrfOom(p->p);
qrfColDataFree(p);
return 1 ;
}
p->az = azData;
aiWth = sqlite3_realloc64(p->aiWth, p->nAlloc*sizeof (int ));
if ( aiWth==0 ){
qrfOom(p->p);
qrfColDataFree(p);
return 1 ;
}
p->aiWth = aiWth;
abNum = sqlite3_realloc64(p->abNum, p->nAlloc);
if ( abNum==0 ){
qrfOom(p->p);
qrfColDataFree(p);
return 1 ;
}
p->abNum = abNum;
return 0 ;
}
/*
* * Print a markdown or table - style row separator using ascii - art
*/
static void qrfRowSeparator(sqlite3_str *pOut, qrfColData *p, char cSep){
int i;
if ( p->nCol>0 ){
int useBorder = p->p->spec.bBorder!=QRF_No;
if ( useBorder ){
sqlite3_str_append(pOut, &cSep, 1 );
}
sqlite3_str_appendchar(pOut, p->a[0 ].w+p->nMargin, '-' );
for (i=1 ; i<p->nCol; i++){
sqlite3_str_append(pOut, &cSep, 1 );
sqlite3_str_appendchar(pOut, p->a[i].w+p->nMargin, '-' );
}
if ( useBorder ){
sqlite3_str_append(pOut, &cSep, 1 );
}
}
sqlite3_str_append(pOut, "\n" , 1 );
}
/*
* * UTF8 box - drawing characters . Imagine box lines like this :
* *
* * 1
* * |
* * 4 - - + - - 2
* * |
* * 3
* *
* * Each box characters has between 2 and 4 of the lines leading from
* * the center . The characters are here identified by the numbers of
* * their corresponding lines .
*/
#define BOX_24 "\342\224\200" /* U+2500 --- */
#define BOX_13 "\342\224\202" /* U+2502 | */
#define BOX_23 "\342\224\214" /* U+250c ,- */
#define BOX_34 "\342\224\220" /* U+2510 -, */
#define BOX_12 "\342\224\224" /* U+2514 '- */
#define BOX_14 "\342\224\230" /* U+2518 -' */
#define BOX_123 "\342\224\234" /* U+251c |- */
#define BOX_134 "\342\224\244" /* U+2524 -| */
#define BOX_234 "\342\224\254" /* U+252c -,- */
#define BOX_124 "\342\224\264" /* U+2534 -'- */
#define BOX_1234 "\342\224\274" /* U+253c -|- */
/* Rounded corners: */
#define BOX_R12 "\342\225\260" /* U+2570 '- */
#define BOX_R23 "\342\225\255" /* U+256d ,- */
#define BOX_R34 "\342\225\256" /* U+256e -, */
#define BOX_R14 "\342\225\257" /* U+256f -' */
/* Doubled horizontal lines: */
#define DBL_24 "\342\225\220" /* U+2550 === */
#define DBL_123 "\342\225\236" /* U+255e |= */
#define DBL_134 "\342\225\241" /* U+2561 =| */
#define DBL_1234 "\342\225\252" /* U+256a =|= */
/* Draw horizontal line N characters long using unicode box
* * characters
*/
static void qrfBoxLine(sqlite3_str *pOut, int N, int bDbl){
const char *azDash[2 ] = {
BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24,
DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24
};/* 0 1 2 3 4 5 6 7 8 9 */
const int nDash = 30 ;
N *= 3 ;
while ( N>nDash ){
sqlite3_str_append(pOut, azDash[bDbl], nDash);
N -= nDash;
}
sqlite3_str_append(pOut, azDash[bDbl], N);
}
/*
* * Draw a horizontal separator for a QRF_STYLE_Box table .
*/
static void qrfBoxSeparator(
sqlite3_str *pOut,
qrfColData *p,
const char *zSep1,
const char *zSep2,
const char *zSep3,
int bDbl
){
int i;
if ( p->nCol>0 ){
int useBorder = p->p->spec.bBorder!=QRF_No;
if ( useBorder ){
sqlite3_str_appendall(pOut, zSep1);
}
qrfBoxLine(pOut, p->a[0 ].w+p->nMargin, bDbl);
for (i=1 ; i<p->nCol; i++){
sqlite3_str_appendall(pOut, zSep2);
qrfBoxLine(pOut, p->a[i].w+p->nMargin, bDbl);
}
if ( useBorder ){
sqlite3_str_appendall(pOut, zSep3);
}
}
sqlite3_str_append(pOut, "\n" , 1 );
}
/*
* * Load into pData the default alignment for the body of a table .
*/
static void qrfLoadAlignment(qrfColData *pData, Qrf *p){
sqlite3_int64 i;
for (i=0 ; i<pData->nCol; i++){
pData->a[i].e = p->spec.eDfltAlign;
if ( i<p->spec.nAlign ){
unsigned char ax = p->spec.aAlign[i];
if ( (ax & QRF_ALIGN_HMASK)!=0 ){
pData->a[i].e = (ax & QRF_ALIGN_HMASK) |
(pData->a[i].e & QRF_ALIGN_VMASK);
}
}else if ( i<p->spec.nWidth ){
if ( p->spec.aWidth[i]<0 ){
pData->a[i].e = QRF_ALIGN_Right |
(pData->a[i].e & QRF_ALIGN_VMASK);
}
}
}
}
/*
* * If the single column in pData - > a [ ] with pData - > n entries can be
* * laid out as nCol columns with a 2 - space gap between each such
* * that all columns fit within nSW , then return a pointer to an array
* * of integers which is the width of each column from left to right .
* *
* * If the layout is not possible , return a NULL pointer .
* *
* * Space to hold the returned array is from sqlite_malloc64 ( ) .
*/
static int *qrfValidLayout(
qrfColData *pData, /* Collected query results */
Qrf *p, /* On which to report an OOM */
int nCol, /* Attempt this many columns */
int nSW /* Screen width */
){
int i; /* Loop counter */
int nr; /* Number of rows */
int w = 0 ; /* Width of the current column */
int t; /* Total width of all columns */
int *aw; /* Array of individual column widths */
aw = sqlite3_malloc64( sizeof (int )*nCol );
if ( aw==0 ){
qrfOom(p);
return 0 ;
}
nr = (pData->n + nCol - 1 )/nCol;
for (i=0 ; i<pData->n; i++){
if ( (i%nr)==0 ){
if ( i>0 ) aw[i/nr-1 ] = w;
w = pData->aiWth[i];
}else if ( pData->aiWth[i]>w ){
w = pData->aiWth[i];
}
}
aw[nCol-1 ] = w;
for (t=i=0 ; i<nCol; i++) t += aw[i];
t += 2 *(nCol-1 );
if ( t>nSW ){
sqlite3_free(aw);
return 0 ;
}
return aw;
}
/*
* * The output is single - column and the bSplitColumn flag is set .
* * Check to see if the single - column output can be split into multiple
* * columns that appear side - by - side . Adjust pData appropriately .
*/
static void qrfSplitColumn(qrfColData *pData, Qrf *p){
int nCol = 1 ;
int *aw = 0 ;
char **az = 0 ;
int *aiWth = 0 ;
unsigned char *abNum = 0 ;
int nColNext = 2 ;
int w;
struct qrfPerCol *a = 0 ;
sqlite3_int64 nRow = 1 ;
sqlite3_int64 i;
while ( 1 /*exit-by-break*/ ){
int *awNew = qrfValidLayout(pData, p, nColNext, p->spec.nScreenWidth);
if ( awNew==0 ) break ;
sqlite3_free(aw);
aw = awNew;
nCol = nColNext;
nRow = (pData->n + nCol - 1 )/nCol;
if ( nRow==1 ) break ;
nColNext++;
while ( (pData->n + nColNext - 1 )/nColNext == nRow ) nColNext++;
}
if ( nCol==1 ){
sqlite3_free(aw);
return ; /* Cannot do better than 1 column */
}
az = sqlite3_malloc64( nRow*nCol*sizeof (char *) );
if ( az==0 ){
qrfOom(p);
return ;
}
aiWth = sqlite3_malloc64( nRow*nCol*sizeof (int ) );
if ( aiWth==0 ){
sqlite3_free(az);
qrfOom(p);
return ;
}
a = sqlite3_malloc64( nCol*sizeof (struct qrfPerCol) );
if ( a==0 ){
sqlite3_free(az);
sqlite3_free(aiWth);
qrfOom(p);
return ;
}
abNum = sqlite3_malloc64( nRow*nCol );
if ( abNum==0 ){
sqlite3_free(az);
sqlite3_free(aiWth);
sqlite3_free(a);
qrfOom(p);
return ;
}
for (i=0 ; i<pData->n; i++){
sqlite3_int64 j = (i%nRow)*nCol + (i/nRow);
az[j] = pData->az[i];
abNum[j]= pData->abNum[i];
pData->az[i] = 0 ;
aiWth[j] = pData->aiWth[i];
}
while ( i<nRow*nCol ){
sqlite3_int64 j = (i%nRow)*nCol + (i/nRow);
az[j] = sqlite3_mprintf("" );
if ( az[j]==0 ) qrfOom(p);
aiWth[j] = 0 ;
abNum[j] = 0 ;
i++;
}
for (i=0 ; i<nCol; i++){
a[i].fx = a[i].mxW = a[i].w = aw[i];
a[i].e = pData->a[0 ].e;
}
sqlite3_free(pData->az);
sqlite3_free(pData->aiWth);
sqlite3_free(pData->a);
sqlite3_free(pData->abNum);
sqlite3_free(aw);
pData->az = az;
pData->aiWth = aiWth;
pData->a = a;
pData->abNum = abNum;
pData->nCol = nCol;
pData->n = pData->nAlloc = nRow*nCol;
for (i=w=0 ; i<nCol; i++) w += a[i].w;
pData->nMargin = (p->spec.nScreenWidth - w)/(nCol - 1 );
if ( pData->nMargin>5 ) pData->nMargin = 5 ;
}
/*
* * Adjust the layout for the screen width restriction
*/
static void qrfRestrictScreenWidth(qrfColData *pData, Qrf *p){
int sepW; /* Width of all box separators and margins */
int sumW; /* Total width of data area over all columns */
int targetW; /* Desired total data area */
int i; /* Loop counters */
int nCol; /* Number of columns */
pData->nMargin = 2 ; /* Default to normal margins */
if ( p->spec.nScreenWidth==0 ) return ;
if ( p->spec.eStyle==QRF_STYLE_Column ){
sepW = pData->nCol*2 - 2 ;
}else {
sepW = pData->nCol*3 + 1 ;
if ( p->spec.bBorder==QRF_No ) sepW -= 2 ;
}
nCol = pData->nCol;
for (i=sumW=0 ; i<nCol; i++) sumW += pData->a[i].w;
if ( p->spec.nScreenWidth >= sumW+sepW ) return ;
/* First thing to do is reduce the separation between columns */
pData->nMargin = 0 ;
if ( p->spec.eStyle==QRF_STYLE_Column ){
sepW = pData->nCol - 1 ;
}else {
sepW = pData->nCol + 1 ;
if ( p->spec.bBorder==QRF_No ) sepW -= 2 ;
}
targetW = p->spec.nScreenWidth - sepW;
#define MIN_SQUOZE 8
#define MIN_EX_SQUOZE 16
/* Reduce the width of the widest eligible column. A column is
* * eligible for narrowing if :
* *
* * * It is not a fixed - width column ( a [ 0 ] . fx is false )
* * * The current width is more than MIN_SQUOZE
* * * Either :
* * + The current width is more then MIN_EX_SQUOZE , or
* * + The current width is more than half the max width ( a [ ] . mxW )
* *
* * Keep making reductions until either no more reductions are
* * possible or until the size target is reached .
*/
while ( sumW > targetW ){
int gain, w;
int ix = -1 ;
int mx = 0 ;
for (i=0 ; i<nCol; i++){
if ( pData->a[i].fx==0
&& (w = pData->a[i].w)>mx
&& w>MIN_SQUOZE
&& (w>MIN_EX_SQUOZE || w*2 >pData->a[i].mxW)
){
ix = i;
mx = w;
}
}
if ( ix<0 ) break ;
if ( mx>=MIN_SQUOZE*2 ){
gain = mx/2 ;
}else {
gain = mx - MIN_SQUOZE;
}
if ( sumW - gain < targetW ){
gain = sumW - targetW;
}
sumW -= gain;
pData->a[ix].w -= gain;
pData->bMultiRow = 1 ;
}
}
/*
* * Columnar modes require that the entire query be evaluated first , with
* * results written into memory , so that we can compute appropriate column
* * widths .
*/
static void qrfColumnar(Qrf *p){
sqlite3_int64 i, j; /* Loop counters */
const char *colSep = 0 ; /* Column separator text */
const char *rowSep = 0 ; /* Row terminator text */
const char *rowStart = 0 ; /* Row start text */
int szColSep, szRowSep, szRowStart; /* Size in bytes of previous 3 */
int rc; /* Result code */
int nColumn = p->nCol; /* Number of columns */
int bWW; /* True to do word-wrap */
sqlite3_str *pStr; /* Temporary rendering */
qrfColData data; /* Columnar layout data */
int bRTrim; /* Trim trailing space */
rc = sqlite3_step(p->pStmt);
if ( rc!=SQLITE_ROW || nColumn==0 ){
return ; /* No output */
}
/* Initialize the data container */
memset(&data, 0 , sizeof (data));
data.nCol = p->nCol;
data.p = p;
data.a = sqlite3_malloc64( nColumn*sizeof (struct qrfPerCol) );
if ( data.a==0 ){
qrfOom(p);
return ;
}
memset(data.a, 0 , nColumn*sizeof (struct qrfPerCol) );
if ( qrfColDataEnlarge(&data) ) return ;
assert( data.az!=0 );
/* Load the column header names and all cell content into data */
if ( p->spec.bTitles==QRF_Yes ){
unsigned char saved_eText = p->spec.eText;
p->spec.eText = p->spec.eTitle;
memset(data.abNum, 0 , nColumn);
for (i=0 ; i<nColumn; i++){
const char *z = (const char *)sqlite3_column_name(p->pStmt,i);
int nNL = 0 ;
int n, w;
pStr = sqlite3_str_new(p->db);
qrfEncodeText(p, pStr, z ? z : "" );
n = sqlite3_str_length(pStr);
qrfStrErr(p, pStr);
z = data.az[data.n] = sqlite3_str_finish(pStr);
if ( p->spec.nTitleLimit ){
nNL = 0 ;
data.aiWth[data.n] = w = qrfTitleLimit(data.az[data.n],
p->spec.nTitleLimit );
}else {
data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL);
}
data.n++;
if ( w>data.a[i].mxW ) data.a[i].mxW = w;
if ( nNL ) data.bMultiRow = 1 ;
}
p->spec.eText = saved_eText;
p->nRow++;
}
do {
if ( data.n+nColumn > data.nAlloc ){
if ( qrfColDataEnlarge(&data) ) return ;
}
for (i=0 ; i<nColumn; i++){
char *z;
int nNL = 0 ;
int n, w;
int eType = sqlite3_column_type(p->pStmt,i);
pStr = sqlite3_str_new(p->db);
qrfRenderValue(p, pStr, i);
n = sqlite3_str_length(pStr);
qrfStrErr(p, pStr);
z = data.az[data.n] = sqlite3_str_finish(pStr);
data.abNum[data.n] = eType==SQLITE_INTEGER || eType==SQLITE_FLOAT;
data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL);
data.n++;
if ( w>data.a[i].mxW ) data.a[i].mxW = w;
if ( nNL ) data.bMultiRow = 1 ;
}
p->nRow++;
}while ( sqlite3_step(p->pStmt)==SQLITE_ROW && p->iErr==SQLITE_OK );
if ( p->iErr ){
qrfColDataFree(&data);
return ;
}
/* Compute the width and alignment of every column */
if ( p->spec.bTitles==QRF_No ){
qrfLoadAlignment(&data, p);
}else {
unsigned char e;
if ( p->spec.eTitleAlign==QRF_Auto ){
e = QRF_ALIGN_Center;
}else {
e = p->spec.eTitleAlign;
}
for (i=0 ; i<nColumn; i++) data.a[i].e = e;
}
for (i=0 ; i<nColumn; i++){
int w = 0 ;
if ( i<p->spec.nWidth ){
w = p->spec.aWidth[i];
if ( w==(-32768 ) ){
w = 0 ;
if ( p->spec.nAlign>i && (p->spec.aAlign[i] & QRF_ALIGN_HMASK)==0 ){
data.a[i].e |= QRF_ALIGN_Right;
}
}else if ( w<0 ){
w = -w;
if ( p->spec.nAlign>i && (p->spec.aAlign[i] & QRF_ALIGN_HMASK)==0 ){
data.a[i].e |= QRF_ALIGN_Right;
}
}
if ( w ) data.a[i].fx = 1 ;
}
if ( w==0 ){
w = data.a[i].mxW;
if ( p->spec.nWrap>0 && w>p->spec.nWrap ){
w = p->spec.nWrap;
data.bMultiRow = 1 ;
}
}else if ( (data.bMultiRow==0 || w==1 ) && data.a[i].mxW>w ){
data.bMultiRow = 1 ;
if ( w==1 ){
/* If aiWth[j] is 2 or more, then there might be a double-wide
** character somewhere. So make the column width at least 2. */
w = 2 ;
}
}
data.a[i].w = w;
}
if ( nColumn==1
&& data.n>1
&& p->spec.bSplitColumn==QRF_Yes
&& p->spec.eStyle==QRF_STYLE_Column
&& p->spec.bTitles==QRF_No
&& p->spec.nScreenWidth>data.a[0 ].w+3
){
/* Attempt to convert single-column tables into multi-column by
* * verticle wrapping , if the screen is wide enough and if the
** bSplitColumn flag is set. */
qrfSplitColumn(&data, p);
nColumn = data.nCol;
}else {
/* Adjust the column widths due to screen width restrictions */
qrfRestrictScreenWidth(&data, p);
}
/* Draw the line across the top of the table. Also initialize
** the row boundary and column separator texts. */
switch ( p->spec.eStyle ){
case QRF_STYLE_Box:
if ( data.nMargin ){
rowStart = BOX_13 " " ;
colSep = " " BOX_13 " " ;
rowSep = " " BOX_13 "\n" ;
}else {
rowStart = BOX_13;
colSep = BOX_13;
rowSep = BOX_13 "\n" ;
}
if ( p->spec.bBorder==QRF_No){
rowStart += 3 ;
rowSep = "\n" ;
}else {
qrfBoxSeparator(p->pOut, &data, BOX_R23, BOX_234, BOX_R34, 0 );
}
break ;
case QRF_STYLE_Table:
if ( data.nMargin ){
rowStart = "| " ;
colSep = " | " ;
rowSep = " |\n" ;
}else {
rowStart = "|" ;
colSep = "|" ;
rowSep = "|\n" ;
}
if ( p->spec.bBorder==QRF_No ){
rowStart += 1 ;
rowSep = "\n" ;
}else {
qrfRowSeparator(p->pOut, &data, '+' );
}
break ;
case QRF_STYLE_Column: {
static const char zSpace[] = " " ;
rowStart = "" ;
if ( data.nMargin<2 ){
colSep = " " ;
}else if ( data.nMargin<=5 ){
colSep = &zSpace[5 -data.nMargin];
}else {
colSep = zSpace;
}
rowSep = "\n" ;
break ;
}
default : /*case QRF_STYLE_Markdown:*/
if ( data.nMargin ){
rowStart = "| " ;
colSep = " | " ;
rowSep = " |\n" ;
}else {
rowStart = "|" ;
colSep = "|" ;
rowSep = "|\n" ;
}
break ;
}
szRowStart = (int )strlen(rowStart);
szRowSep = (int )strlen(rowSep);
szColSep = (int )strlen(colSep);
bWW = (p->spec.bWordWrap==QRF_Yes && data.bMultiRow);
if ( p->spec.eStyle==QRF_STYLE_Column
|| (p->spec.bBorder==QRF_No
&& (p->spec.eStyle==QRF_STYLE_Box || p->spec.eStyle==QRF_STYLE_Table)
)
){
bRTrim = 1 ;
}else {
bRTrim = 0 ;
}
for (i=0 ; i<data.n && sqlite3_str_errcode(p->pOut)==SQLITE_OK; i+=nColumn){
int bMore;
int nRow = 0 ;
/* Draw a single row of the table. This might be the title line
* * ( if there is a title line ) or a row in the body of the table .
* * The column number will be j . The row number is i / nColumn .
*/
for (j=0 ; j<nColumn; j++){
data.a[j].z = data.az[i+j];
if ( data.a[j].z==0 ) data.a[j].z = "" ;
data.a[j].bNum = data.abNum[i+j];
}
do {
sqlite3_str_append(p->pOut, rowStart, szRowStart);
bMore = 0 ;
for (j=0 ; j<nColumn; j++){
int nThis = 0 ;
int nWide = 0 ;
int iNext = 0 ;
int nWS;
qrfWrapLine(data.a[j].z, data.a[j].w, bWW, &nThis, &nWide, &iNext);
nWS = data.a[j].w - nWide;
qrfPrintAligned(p->pOut, &data.a[j], nThis, nWS);
data.a[j].z += iNext;
if ( data.a[j].z[0 ]!=0 ){
bMore = 1 ;
}
if ( j<nColumn-1 ){
sqlite3_str_append(p->pOut, colSep, szColSep);
}else {
if ( bRTrim ) qrfRTrim(p->pOut);
sqlite3_str_append(p->pOut, rowSep, szRowSep);
}
}
}while ( bMore && ++nRow < p->mxHeight );
if ( bMore ){
/* This row was terminated by nLineLimit. Show ellipsis. */
sqlite3_str_append(p->pOut, rowStart, szRowStart);
for (j=0 ; j<nColumn; j++){
if ( data.a[j].z[0 ]==0 ){
sqlite3_str_appendchar(p->pOut, data.a[j].w, ' ' );
}else {
int nE = 3 ;
if ( nE>data.a[j].w ) nE = data.a[j].w;
data.a[j].z = "..." ;
qrfPrintAligned(p->pOut, &data.a[j], nE, data.a[j].w-nE);
}
if ( j<nColumn-1 ){
sqlite3_str_append(p->pOut, colSep, szColSep);
}else {
if ( bRTrim ) qrfRTrim(p->pOut);
sqlite3_str_append(p->pOut, rowSep, szRowSep);
}
}
}
/* Draw either (1) the separator between the title line and the body
* * of the table , or ( 2 ) separators between individual rows of the table
* * body . isTitleDataSeparator will be true if we are doing ( 1 ) .
*/
if ( (i==0 || data.bMultiRow) && i+nColumn<data.n ){
int isTitleDataSeparator = (i==0 && p->spec.bTitles==QRF_Yes);
if ( isTitleDataSeparator ){
qrfLoadAlignment(&data, p);
}
switch ( p->spec.eStyle ){
case QRF_STYLE_Table: {
if ( isTitleDataSeparator || data.bMultiRow ){
qrfRowSeparator(p->pOut, &data, '+' );
}
break ;
}
case QRF_STYLE_Box: {
if ( isTitleDataSeparator ){
qrfBoxSeparator(p->pOut, &data, DBL_123, DBL_1234, DBL_134, 1 );
}else if ( data.bMultiRow ){
qrfBoxSeparator(p->pOut, &data, BOX_123, BOX_1234, BOX_134, 0 );
}
break ;
}
case QRF_STYLE_Markdown: {
if ( isTitleDataSeparator ){
qrfRowSeparator(p->pOut, &data, '|' );
}
break ;
}
case QRF_STYLE_Column: {
if ( isTitleDataSeparator ){
for (j=0 ; j<nColumn; j++){
sqlite3_str_appendchar(p->pOut, data.a[j].w, '-' );
if ( j<nColumn-1 ){
sqlite3_str_append(p->pOut, colSep, szColSep);
}else {
qrfRTrim(p->pOut);
sqlite3_str_append(p->pOut, rowSep, szRowSep);
}
}
}else if ( data.bMultiRow ){
qrfRTrim(p->pOut);
sqlite3_str_append(p->pOut, "\n" , 1 );
}
break ;
}
}
}
}
/* Draw the line across the bottom of the table */
if ( p->spec.bBorder!=QRF_No ){
switch ( p->spec.eStyle ){
case QRF_STYLE_Box:
qrfBoxSeparator(p->pOut, &data, BOX_R12, BOX_124, BOX_R14, 0 );
break ;
case QRF_STYLE_Table:
qrfRowSeparator(p->pOut, &data, '+' );
break ;
}
}
qrfWrite(p);
qrfColDataFree(&data);
return ;
}
/*
* * Parameter azArray points to a zero - terminated array of strings . zStr
* * points to a single nul - terminated string . Return non - zero if zStr
* * is equal , according to strcmp ( ) , to any of the strings in the array .
* * Otherwise , return zero .
*/
static int qrfStringInArray(const char *zStr, const char **azArray){
int i;
if ( zStr==0 ) return 0 ;
for (i=0 ; azArray[i]; i++){
if ( 0 ==strcmp(zStr, azArray[i]) ) return 1 ;
}
return 0 ;
}
/*
* * Print out an EXPLAIN with indentation . This is a two - pass algorithm .
* *
* * On the first pass , we compute aiIndent [ iOp ] which is the amount of
* * indentation to apply to the iOp - th opcode . The output actually occurs
* * on the second pass .
* *
* * The indenting rules are :
* *
* * * For each " Next " , " Prev " , " VNext " or " VPrev " instruction , indent
* * all opcodes that occur between the p2 jump destination and the opcode
* * itself by 2 spaces .
* *
* * * Do the previous for " Return " instructions for when P2 is positive .
* * See tag - 20220407 a in wherecode . c and vdbe . c .
* *
* * * For each " Goto " , if the jump destination is earlier in the program
* * and ends on one of :
* * Yield SeekGt SeekLt RowSetRead Rewind
* * or if the P1 parameter is one instead of zero ,
* * then indent all opcodes between the earlier instruction
* * and " Goto " by 2 spaces .
*/
static void qrfExplain(Qrf *p){
int *abYield = 0 ; /* abYield[iOp] is rue if opcode iOp is an OP_Yield */
int *aiIndent = 0 ; /* Indent the iOp-th opcode by aiIndent[iOp] */
i64 nAlloc = 0 ; /* Allocated size of aiIndent[], abYield */
int nIndent = 0 ; /* Number of entries in aiIndent[] */
int iOp; /* Opcode number */
int i; /* Column loop counter */
const char *azNext[] = { "Next" , "Prev" , "VPrev" , "VNext" , "SorterNext" ,
"Return" , 0 };
const char *azYield[] = { "Yield" , "SeekLT" , "SeekGT" , "RowSetRead" ,
"Rewind" , 0 };
const char *azGoto[] = { "Goto" , 0 };
/* The caller guarantees that the leftmost 4 columns of the statement
* * passed to this function are equivalent to the leftmost 4 columns
* * of EXPLAIN statement output . In practice the statement may be
** an EXPLAIN, or it may be a query on the bytecode() virtual table. */
assert( sqlite3_column_count(p->pStmt)>=4 );
assert( 0 ==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 0 ), "addr" ) );
assert( 0 ==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 1 ), "opcode" ) );
assert( 0 ==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 2 ), "p1" ) );
assert( 0 ==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 3 ), "p2" ) );
for (iOp=0 ; SQLITE_ROW==sqlite3_step(p->pStmt) && !p->iErr; iOp++){
int iAddr = sqlite3_column_int(p->pStmt, 0 );
const char *zOp = (const char *)sqlite3_column_text(p->pStmt, 1 );
int p1 = sqlite3_column_int(p->pStmt, 2 );
int p2 = sqlite3_column_int(p->pStmt, 3 );
/* Assuming that p2 is an instruction address, set variable p2op to the
* * index of that instruction in the aiIndent [ ] array . p2 and p2op may be
* * different if the current instruction is part of a sub - program generated
** by an SQL trigger or foreign key. */
int p2op = (p2 + (iOp-iAddr));
/* Grow the aiIndent array as required */
if ( iOp>=nAlloc ){
nAlloc += 100 ;
aiIndent = (int *)sqlite3_realloc64(aiIndent, nAlloc*sizeof (int ));
abYield = (int *)sqlite3_realloc64(abYield, nAlloc*sizeof (int ));
if ( aiIndent==0 || abYield==0 ){
qrfOom(p);
sqlite3_free(aiIndent);
sqlite3_free(abYield);
return ;
}
}
abYield[iOp] = qrfStringInArray(zOp, azYield);
aiIndent[iOp] = 0 ;
nIndent = iOp+1 ;
if ( qrfStringInArray(zOp, azNext) && p2op>0 ){
for (i=p2op; i<iOp; i++) aiIndent[i] += 2 ;
}
if ( qrfStringInArray(zOp, azGoto) && p2op<iOp && (abYield[p2op] || p1) ){
for (i=p2op; i<iOp; i++) aiIndent[i] += 2 ;
}
}
sqlite3_free(abYield);
/* Second pass. Actually generate output */
sqlite3_reset(p->pStmt);
if ( p->iErr==SQLITE_OK ){
static const int aExplainWidth[] = {4 , 13 , 4 , 4 , 4 , 13 , 2 , 13 };
static const int aExplainMap[] = {0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 };
static const int aScanExpWidth[] = {4 ,15 , 6 , 13 , 4 , 4 , 4 , 13 , 2 , 13 };
static const int aScanExpMap[] = {0 , 9 , 8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 };
const int *aWidth = aExplainWidth;
const int *aMap = aExplainMap;
int nWidth = sizeof (aExplainWidth)/sizeof (int );
int iIndent = 1 ;
int nArg = p->nCol;
if ( p->spec.eStyle==QRF_STYLE_StatsVm ){
aWidth = aScanExpWidth;
aMap = aScanExpMap;
nWidth = sizeof (aScanExpWidth)/sizeof (int );
iIndent = 3 ;
}
if ( nArg>nWidth ) nArg = nWidth;
for (iOp=0 ; sqlite3_step(p->pStmt)==SQLITE_ROW && !p->iErr; iOp++){
/* If this is the first row seen, print out the headers */
if ( iOp==0 ){
for (i=0 ; i<nArg; i++){
const char *zCol = sqlite3_column_name(p->pStmt, aMap[i]);
qrfWidthPrint(p,p->pOut, aWidth[i], zCol);
if ( i==nArg-1 ){
sqlite3_str_append(p->pOut, "\n" , 1 );
}else {
sqlite3_str_append(p->pOut, " " , 2 );
}
}
for (i=0 ; i<nArg; i++){
sqlite3_str_appendf(p->pOut, "%.*c" , aWidth[i], '-' );
if ( i==nArg-1 ){
sqlite3_str_append(p->pOut, "\n" , 1 );
}else {
sqlite3_str_append(p->pOut, " " , 2 );
}
}
}
for (i=0 ; i<nArg; i++){
const char *zSep = " " ;
int w = aWidth[i];
const char *zVal = (const char *)sqlite3_column_text(p->pStmt, aMap[i]);
int len;
if ( i==nArg-1 ) w = 0 ;
if ( zVal==0 ) zVal = "" ;
len = (int )sqlite3_qrf_wcswidth(zVal);
if ( len>w ){
w = len;
zSep = " " ;
}
if ( i==iIndent && aiIndent && iOp<nIndent ){
sqlite3_str_appendchar(p->pOut, aiIndent[iOp], ' ' );
}
qrfWidthPrint(p, p->pOut, w, zVal);
if ( i==nArg-1 ){
sqlite3_str_append(p->pOut, "\n" , 1 );
}else {
sqlite3_str_appendall(p->pOut, zSep);
}
}
p->nRow++;
}
qrfWrite(p);
}
sqlite3_free(aiIndent);
}
/*
* * Do a " scanstatus vm " style EXPLAIN listing on p - > pStmt .
* *
* * p - > pStmt is probably not an EXPLAIN query . Instead , construct a
* * new query that is a bytecode ( ) rendering of p - > pStmt with extra
* * columns for the " scanstatus vm " outputs , and run the results of
* * that new query through the normal EXPLAIN formatting .
*/
static void qrfScanStatusVm(Qrf *p){
sqlite3_stmt *pOrigStmt = p->pStmt;
sqlite3_stmt *pExplain;
int rc;
static const char *zSql =
" SELECT addr, opcode, p1, p2, p3, p4, p5, comment, nexec,"
" format('% 6s (%.2f%%)',"
" CASE WHEN ncycle<100_000 THEN ncycle || ' '"
" WHEN ncycle<100_000_000 THEN (ncycle/1_000) || 'K'"
" WHEN ncycle<100_000_000_000 THEN (ncycle/1_000_000) || 'M'"
" ELSE (ncycle/1000_000_000) || 'G' END,"
" ncycle*100.0/(sum(ncycle) OVER ())"
" ) AS cycles"
" FROM bytecode(?1)" ;
rc = sqlite3_prepare_v2(p->db, zSql, -1 , &pExplain, 0 );
if ( rc ){
qrfError(p, rc, "%s" , sqlite3_errmsg(p->db));
sqlite3_finalize(pExplain);
return ;
}
sqlite3_bind_pointer(pExplain, 1 , pOrigStmt, "stmt-pointer" , 0 );
p->pStmt = pExplain;
p->nCol = 10 ;
qrfExplain(p);
sqlite3_finalize(pExplain);
p->pStmt = pOrigStmt;
}
/*
* * Attempt to determine if identifier zName needs to be quoted , either
* * because it contains non - alphanumeric characters , or because it is an
* * SQLite keyword . Be conservative in this estimate : When in doubt assume
* * that quoting is required .
* *
* * Return 1 if quoting is required . Return 0 if no quoting is required .
*/
static int qrf_need_quote(const char *zName){
int i;
const unsigned char *z = (const unsigned char *)zName;
if ( z==0 ) return 1 ;
if ( !qrfAlpha(z[0 ]) ) return 1 ;
for (i=0 ; z[i]; i++){
if ( !qrfAlnum(z[i]) ) return 1 ;
}
return sqlite3_keyword_check(zName, i)!=0 ;
}
/*
* * Helper function for QRF_STYLE_Json and QRF_STYLE_JObject .
* * The initial " { " for a JSON object that will contain row content
* * has been output . Now output all the content .
*/
static void qrfOneJsonRow(Qrf *p){
int i, nItem;
for (nItem=i=0 ; i<p->nCol; i++){
const char *zCName;
zCName = sqlite3_column_name(p->pStmt, i);
if ( nItem>0 ) sqlite3_str_append(p->pOut, "," , 1 );
nItem++;
qrfEncodeText(p, p->pOut, zCName);
sqlite3_str_append(p->pOut, ":" , 1 );
qrfRenderValue(p, p->pOut, i);
}
qrfWrite(p);
}
/*
* * Render a single row of output for non - columnar styles - any
* * style that lets us render row by row as the content is received
* * from the query .
*/
static void qrfOneSimpleRow(Qrf *p){
int i;
switch ( p->spec.eStyle ){
case QRF_STYLE_Off:
case QRF_STYLE_Count: {
/* No-op */
break ;
}
case QRF_STYLE_Json: {
if ( p->nRow==0 ){
sqlite3_str_append(p->pOut, "[{" , 2 );
}else {
sqlite3_str_append(p->pOut, "},\n{" , 4 );
}
qrfOneJsonRow(p);
break ;
}
case QRF_STYLE_JObject: {
if ( p->nRow==0 ){
sqlite3_str_append(p->pOut, "{" , 1 );
}else {
sqlite3_str_append(p->pOut, "}\n{" , 3 );
}
qrfOneJsonRow(p);
break ;
}
case QRF_STYLE_Html: {
if ( p->nRow==0 && p->spec.bTitles==QRF_Yes ){
sqlite3_str_append(p->pOut, "<TR>" , 4 );
for (i=0 ; i<p->nCol; i++){
const char *zCName = sqlite3_column_name(p->pStmt, i);
sqlite3_str_append(p->pOut, "\n<TH>" , 5 );
qrfEncodeText(p, p->pOut, zCName);
}
sqlite3_str_append(p->pOut, "\n</TR>\n" , 7 );
}
sqlite3_str_append(p->pOut, "<TR>" , 4 );
for (i=0 ; i<p->nCol; i++){
sqlite3_str_append(p->pOut, "\n<TD>" , 5 );
qrfRenderValue(p, p->pOut, i);
}
sqlite3_str_append(p->pOut, "\n</TR>\n" , 7 );
qrfWrite(p);
break ;
}
case QRF_STYLE_Insert: {
unsigned int mxIns = p->spec.nMultiInsert;
int szStart = sqlite3_str_length(p->pOut);
if ( p->u.nIns==0 || p->u.nIns>=mxIns ){
if ( p->u.nIns ){
sqlite3_str_append(p->pOut, ";\n" , 2 );
p->u.nIns = 0 ;
}
if ( qrf_need_quote(p->spec.zTableName) ){
sqlite3_str_appendf(p->pOut,"INSERT INTO \" %w\"" ,p->spec.zTableName);
}else {
sqlite3_str_appendf(p->pOut,"INSERT INTO %s" ,p->spec.zTableName);
}
if ( p->spec.bTitles==QRF_Yes ){
for (i=0 ; i<p->nCol; i++){
const char *zCName = sqlite3_column_name(p->pStmt, i);
if ( qrf_need_quote(zCName) ){
sqlite3_str_appendf(p->pOut, "%c\" %w\"" ,
i==0 ? '(' : ',' , zCName);
}else {
sqlite3_str_appendf(p->pOut, "%c%s" ,
i==0 ? '(' : ',' , zCName);
}
}
sqlite3_str_append(p->pOut, ")" , 1 );
}
sqlite3_str_append(p->pOut," VALUES(" , 8 );
}else {
sqlite3_str_append(p->pOut,",\n (" , 5 );
}
for (i=0 ; i<p->nCol; i++){
if ( i>0 ) sqlite3_str_append(p->pOut, "," , 1 );
qrfRenderValue(p, p->pOut, i);
}
p->u.nIns += sqlite3_str_length(p->pOut) + 2 - szStart;
if ( p->u.nIns>=mxIns ){
sqlite3_str_append(p->pOut, ");\n" , 3 );
p->u.nIns = 0 ;
}else {
sqlite3_str_append(p->pOut, ")" , 1 );
}
qrfWrite(p);
break ;
}
case QRF_STYLE_Line: {
sqlite3_str *pVal;
int mxW;
int bWW;
int nSep;
if ( p->u.sLine.azCol==0 ){
p->u.sLine.azCol = sqlite3_malloc64( p->nCol*sizeof (char *) );
if ( p->u.sLine.azCol==0 ){
qrfOom(p);
break ;
}
p->u.sLine.mxColWth = 0 ;
for (i=0 ; i<p->nCol; i++){
int sz;
const char *zCName = sqlite3_column_name(p->pStmt, i);
if ( zCName==0 ) zCName = "unknown" ;
p->u.sLine.azCol[i] = sqlite3_mprintf("%s" , zCName);
if ( p->spec.nTitleLimit>0 ){
(void )qrfTitleLimit(p->u.sLine.azCol[i], p->spec.nTitleLimit);
}
sz = (int )sqlite3_qrf_wcswidth(p->u.sLine.azCol[i]);
if ( sz > p->u.sLine.mxColWth ) p->u.sLine.mxColWth = sz;
}
}
if ( p->nRow ) sqlite3_str_append(p->pOut, "\n" , 1 );
pVal = sqlite3_str_new(p->db);
nSep = (int )strlen(p->spec.zColumnSep);
mxW = p->mxWidth - (nSep + p->u.sLine.mxColWth);
bWW = p->spec.bWordWrap==QRF_Yes;
for (i=0 ; i<p->nCol; i++){
const char *zVal;
int cnt = 0 ;
qrfWidthPrint(p, p->pOut, -p->u.sLine.mxColWth, p->u.sLine.azCol[i]);
sqlite3_str_append(p->pOut, p->spec.zColumnSep, nSep);
qrfRenderValue(p, pVal, i);
zVal = sqlite3_str_value(pVal);
if ( zVal==0 ) zVal = "" ;
do {
int nThis, nWide, iNext;
qrfWrapLine(zVal, mxW, bWW, &nThis, &nWide, &iNext);
if ( cnt ){
sqlite3_str_appendchar(p->pOut,p->u.sLine.mxColWth+nSep,' ' );
}
cnt++;
if ( cnt>p->mxHeight ){
zVal = "..." ;
nThis = iNext = 3 ;
}
sqlite3_str_append(p->pOut, zVal, nThis);
sqlite3_str_append(p->pOut, "\n" , 1 );
zVal += iNext;
}while ( zVal[0 ] );
sqlite3_str_reset(pVal);
}
qrfStrErr(p, pVal);
sqlite3_free(sqlite3_str_finish(pVal));
qrfWrite(p);
break ;
}
case QRF_STYLE_Eqp: {
const char *zEqpLine = (const char *)sqlite3_column_text(p->pStmt,3 );
int iEqpId = sqlite3_column_int(p->pStmt, 0 );
int iParentId = sqlite3_column_int(p->pStmt, 1 );
if ( zEqpLine==0 ) zEqpLine = "" ;
if ( zEqpLine[0 ]=='-' ) qrfEqpRender(p, 0 );
qrfEqpAppend(p, iEqpId, iParentId, zEqpLine);
break ;
}
default : { /* QRF_STYLE_List */
if ( p->nRow==0 && p->spec.bTitles==QRF_Yes ){
int saved_eText = p->spec.eText;
p->spec.eText = p->spec.eTitle;
for (i=0 ; i<p->nCol; i++){
const char *zCName = sqlite3_column_name(p->pStmt, i);
if ( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep);
qrfEncodeText(p, p->pOut, zCName);
}
sqlite3_str_appendall(p->pOut, p->spec.zRowSep);
qrfWrite(p);
p->spec.eText = saved_eText;
}
for (i=0 ; i<p->nCol; i++){
if ( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep);
qrfRenderValue(p, p->pOut, i);
}
sqlite3_str_appendall(p->pOut, p->spec.zRowSep);
qrfWrite(p);
break ;
}
}
p->nRow++;
}
/*
* * Initialize the internal Qrf object .
*/
static void qrfInitialize(
Qrf *p, /* State object to be initialized */
sqlite3_stmt *pStmt, /* Query whose output to be formatted */
const sqlite3_qrf_spec *pSpec, /* Format specification */
char **pzErr /* Write errors here */
){
size_t sz; /* Size of pSpec[], based on pSpec->iVersion */
memset(p, 0 , sizeof (*p));
p->pzErr = pzErr;
if ( pSpec->iVersion>1 ){
qrfError(p, SQLITE_ERROR,
"unusable sqlite3_qrf_spec.iVersion (%d)" ,
pSpec->iVersion);
return ;
}
p->pStmt = pStmt;
p->db = sqlite3_db_handle(pStmt);
p->pOut = sqlite3_str_new(p->db);
if ( p->pOut==0 ){
qrfOom(p);
return ;
}
p->iErr = SQLITE_OK;
p->nCol = sqlite3_column_count(p->pStmt);
p->nRow = 0 ;
sz = sizeof (sqlite3_qrf_spec);
memcpy(&p->spec, pSpec, sz);
if ( p->spec.zNull==0 ) p->spec.zNull = "" ;
p->mxWidth = p->spec.nScreenWidth;
if ( p->mxWidth<=0 ) p->mxWidth = QRF_MAX_WIDTH;
p->mxHeight = p->spec.nLineLimit;
if ( p->mxHeight<=0 ) p->mxHeight = 2147483647 ;
if ( p->spec.eStyle>QRF_STYLE_Table ) p->spec.eStyle = QRF_Auto;
if ( p->spec.eEsc>QRF_ESC_Symbol ) p->spec.eEsc = QRF_Auto;
if ( p->spec.eText>QRF_TEXT_Relaxed ) p->spec.eText = QRF_Auto;
if ( p->spec.eTitle>QRF_TEXT_Relaxed ) p->spec.eTitle = QRF_Auto;
if ( p->spec.eBlob>QRF_BLOB_Size ) p->spec.eBlob = QRF_Auto;
qrf_reinit:
switch ( p->spec.eStyle ){
case QRF_Auto: {
switch ( sqlite3_stmt_isexplain(pStmt) ){
case 0 : p->spec.eStyle = QRF_STYLE_Box; break ;
case 1 : p->spec.eStyle = QRF_STYLE_Explain; break ;
default : p->spec.eStyle = QRF_STYLE_Eqp; break ;
}
goto qrf_reinit;
}
case QRF_STYLE_List: {
if ( p->spec.zColumnSep==0 ) p->spec.zColumnSep = "|" ;
if ( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n" ;
break ;
}
case QRF_STYLE_JObject:
case QRF_STYLE_Json: {
p->spec.eText = QRF_TEXT_Json;
p->spec.zNull = "null" ;
break ;
}
case QRF_STYLE_Html: {
p->spec.eText = QRF_TEXT_Html;
p->spec.zNull = "null" ;
break ;
}
case QRF_STYLE_Insert: {
p->spec.eText = QRF_TEXT_Sql;
p->spec.zNull = "NULL" ;
if ( p->spec.zTableName==0 || p->spec.zTableName[0 ]==0 ){
p->spec.zTableName = "tab" ;
}
p->u.nIns = 0 ;
break ;
}
case QRF_STYLE_Line: {
if ( p->spec.zColumnSep==0 ){
p->spec.zColumnSep = ": " ;
}
break ;
}
case QRF_STYLE_Csv: {
p->spec.eStyle = QRF_STYLE_List;
p->spec.eText = QRF_TEXT_Csv;
p->spec.zColumnSep = "," ;
p->spec.zRowSep = "\r\n" ;
p->spec.zNull = "" ;
break ;
}
case QRF_STYLE_Quote: {
p->spec.eText = QRF_TEXT_Sql;
p->spec.zNull = "NULL" ;
p->spec.zColumnSep = "," ;
p->spec.zRowSep = "\n" ;
break ;
}
case QRF_STYLE_Eqp: {
int expMode = sqlite3_stmt_isexplain(p->pStmt);
if ( expMode!=2 ){
sqlite3_stmt_explain(p->pStmt, 2 );
p->expMode = expMode+1 ;
}
break ;
}
case QRF_STYLE_Explain: {
int expMode = sqlite3_stmt_isexplain(p->pStmt);
if ( expMode!=1 ){
sqlite3_stmt_explain(p->pStmt, 1 );
p->expMode = expMode+1 ;
}
break ;
}
}
if ( p->spec.eEsc==QRF_Auto ){
p->spec.eEsc = QRF_ESC_Ascii;
}
if ( p->spec.eText==QRF_Auto ){
p->spec.eText = QRF_TEXT_Plain;
}
if ( p->spec.eTitle==QRF_Auto ){
switch ( p->spec.eStyle ){
case QRF_STYLE_Box:
case QRF_STYLE_Column:
case QRF_STYLE_Table:
p->spec.eTitle = QRF_TEXT_Plain;
break ;
default :
p->spec.eTitle = p->spec.eText;
break ;
}
}
if ( p->spec.eBlob==QRF_Auto ){
switch ( p->spec.eText ){
case QRF_TEXT_Sql: p->spec.eBlob = QRF_BLOB_Sql; break ;
case QRF_TEXT_Csv: p->spec.eBlob = QRF_BLOB_Tcl; break ;
case QRF_TEXT_Tcl: p->spec.eBlob = QRF_BLOB_Tcl; break ;
case QRF_TEXT_Json: p->spec.eBlob = QRF_BLOB_Json; break ;
default : p->spec.eBlob = QRF_BLOB_Text; break ;
}
}
if ( p->spec.bTitles==QRF_Auto ){
switch ( p->spec.eStyle ){
case QRF_STYLE_Box:
case QRF_STYLE_Csv:
case QRF_STYLE_Column:
case QRF_STYLE_Table:
case QRF_STYLE_Markdown:
p->spec.bTitles = QRF_Yes;
break ;
default :
p->spec.bTitles = QRF_No;
break ;
}
}
if ( p->spec.bWordWrap==QRF_Auto ){
p->spec.bWordWrap = QRF_Yes;
}
if ( p->spec.bTextJsonb==QRF_Auto ){
p->spec.bTextJsonb = QRF_No;
}
if ( p->spec.zColumnSep==0 ) p->spec.zColumnSep = "," ;
if ( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n" ;
}
/*
* * Finish rendering the results
*/
static void qrfFinalize(Qrf *p){
switch ( p->spec.eStyle ){
case QRF_STYLE_Count: {
sqlite3_str_appendf(p->pOut, "%lld\n" , p->nRow);
break ;
}
case QRF_STYLE_Json: {
if ( p->nRow>0 ){
sqlite3_str_append(p->pOut, "}]\n" , 3 );
}
break ;
}
case QRF_STYLE_JObject: {
if ( p->nRow>0 ){
sqlite3_str_append(p->pOut, "}\n" , 2 );
}
break ;
}
case QRF_STYLE_Insert: {
if ( p->u.nIns ){
sqlite3_str_append(p->pOut, ";\n" , 2 );
}
break ;
}
case QRF_STYLE_Line: {
if ( p->u.sLine.azCol ){
int i;
for (i=0 ; i<p->nCol; i++) sqlite3_free(p->u.sLine.azCol[i]);
sqlite3_free(p->u.sLine.azCol);
}
break ;
}
case QRF_STYLE_Stats:
case QRF_STYLE_StatsEst: {
i64 nCycle = 0 ;
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
sqlite3_stmt_scanstatus_v2(p->pStmt, -1 , SQLITE_SCANSTAT_NCYCLE,
SQLITE_SCANSTAT_COMPLEX, (void *)&nCycle);
#endif
qrfEqpRender(p, nCycle);
break ;
}
case QRF_STYLE_Eqp: {
qrfEqpRender(p, 0 );
break ;
}
}
qrfWrite(p);
qrfStrErr(p, p->pOut);
if ( p->spec.pzOutput ){
if ( p->spec.pzOutput[0 ] ){
sqlite3_int64 n, sz;
char *zCombined;
sz = strlen(p->spec.pzOutput[0 ]);
n = sqlite3_str_length(p->pOut);
zCombined = sqlite3_realloc64(p->spec.pzOutput[0 ], sz+n+1 );
if ( zCombined==0 ){
sqlite3_free(p->spec.pzOutput[0 ]);
p->spec.pzOutput[0 ] = 0 ;
qrfOom(p);
}else {
p->spec.pzOutput[0 ] = zCombined;
memcpy(zCombined+sz, sqlite3_str_value(p->pOut), n+1 );
}
sqlite3_free(sqlite3_str_finish(p->pOut));
}else {
p->spec.pzOutput[0 ] = sqlite3_str_finish(p->pOut);
}
}else if ( p->pOut ){
sqlite3_free(sqlite3_str_finish(p->pOut));
}
if ( p->expMode>0 ){
sqlite3_stmt_explain(p->pStmt, p->expMode-1 );
}
if ( p->actualWidth ){
sqlite3_free(p->actualWidth);
}
if ( p->pJTrans ){
sqlite3 *db = sqlite3_db_handle(p->pJTrans);
sqlite3_finalize(p->pJTrans);
sqlite3_close(db);
}
}
/*
* * Run the prepared statement pStmt and format the results according
* * to the specification provided in pSpec . Return an error code .
* * If pzErr is not NULL and if an error occurs , write an error message
* * into * pzErr .
*/
int sqlite3_format_query_result(
sqlite3_stmt *pStmt, /* Statement to evaluate */
const sqlite3_qrf_spec *pSpec, /* Format specification */
char **pzErr /* Write error message here */
){
Qrf qrf; /* The new Qrf being created */
if ( pStmt==0 ) return SQLITE_OK; /* No-op */
if ( pSpec==0 ) return SQLITE_MISUSE;
qrfInitialize(&qrf, pStmt, pSpec, pzErr);
switch ( qrf.spec.eStyle ){
case QRF_STYLE_Box:
case QRF_STYLE_Column:
case QRF_STYLE_Markdown:
case QRF_STYLE_Table: {
/* Columnar modes require that the entire query be evaluated and the
** results stored in memory, so that we can compute column widths */
qrfColumnar(&qrf);
break ;
}
case QRF_STYLE_Explain: {
qrfExplain(&qrf);
break ;
}
case QRF_STYLE_StatsVm: {
qrfScanStatusVm(&qrf);
break ;
}
case QRF_STYLE_Stats:
case QRF_STYLE_StatsEst: {
qrfEqpStats(&qrf);
break ;
}
default : {
/* Non-columnar modes where the output can occur after each row
** of result is received */
while ( qrf.iErr==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
qrfOneSimpleRow(&qrf);
}
break ;
}
}
qrfResetStmt(&qrf);
qrfFinalize(&qrf);
return qrf.iErr;
}
/************************* End ext/qrf/qrf.c ********************/
/* Use console I/O package as a direct INCLUDE. */
#define SQLITE_INTERNAL_LINKAGE static
#ifdef SQLITE_SHELL_FIDDLE
/* Deselect most features from the console I/O package for Fiddle. */
# define SQLITE_CIO_NO_REDIRECT
# define SQLITE_CIO_NO_CLASSIFY
# define SQLITE_CIO_NO_TRANSLATE
# define SQLITE_CIO_NO_SETMODE
# define SQLITE_CIO_NO_FLUSH
#endif
/*
* * The source code for several run - time loadable extensions is inserted
* * below by the . . / tool / mkshellc . tcl script . Before processing that included
* * code , we need to override some macros to make the included program code
* * work here in the middle of this regular program .
*/
#define SQLITE_EXTENSION_INIT1
#define SQLITE_EXTENSION_INIT2(X) (void )(X)
/************************* Begin ext/misc/windirent.h ******************/
/*
* * 2025 - 06 - 05
* *
* * 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 .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* * An implementation of opendir ( ) , readdir ( ) , and closedir ( ) for Windows ,
* * based on the FindFirstFile ( ) , FindNextFile ( ) , and FindClose ( ) APIs
* * of Win32 .
* *
* * # include this file inside any C - code module that needs to use
* * opendir ( ) / readdir ( ) / closedir ( ) . This file is a no - op on non - Windows
* * machines . On Windows , static functions are defined that implement
* * those standard interfaces .
*/
#if defined (_WIN32) && defined (_MSC_VER) && !defined (SQLITE_WINDIRENT_H)
#define SQLITE_WINDIRENT_H
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#ifndef FILENAME_MAX
# define FILENAME_MAX (260 )
#endif
#ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#ifndef S_ISLNK
#define S_ISLNK(m) (0 )
#endif
typedef unsigned short mode_t;
/* The dirent object for Windows is abbreviated. The only field really
* * usable by applications is d_name [ ] .
*/
struct dirent {
int d_ino; /* Inode number (synthesized) */
unsigned d_attributes; /* File attributes */
char d_name[FILENAME_MAX]; /* Null-terminated filename */
};
/* The internals of DIR are opaque according to standards. So it
** does not matter what we put here. */
typedef struct DIR DIR;
struct DIR {
intptr_t d_handle; /* Handle for findfirst()/findnext() */
struct dirent cur; /* Current entry */
};
/* Ignore hidden and system files */
#define WindowsFileToIgnore(a) \
((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
/*
* * Close a previously opened directory
*/
static int closedir(DIR *pDir){
int rc = 0 ;
if ( pDir==0 ){
return EINVAL;
}
if ( pDir->d_handle!=0 && pDir->d_handle!=(-1 ) ){
rc = _findclose(pDir->d_handle);
}
sqlite3_free(pDir);
return rc;
}
/*
* * Open a new directory . The directory name should be UTF - 8 encoded .
* * appropriate translations happen automatically .
*/
static DIR *opendir(const char *zDirName){
DIR *pDir;
wchar_t *b1;
sqlite3_int64 sz;
struct _wfinddata_t data;
pDir = sqlite3_malloc64( sizeof (DIR) );
if ( pDir==0 ) return 0 ;
memset(pDir, 0 , sizeof (DIR));
memset(&data, 0 , sizeof (data));
sz = strlen(zDirName);
b1 = sqlite3_malloc64( (sz+3 )*sizeof (b1[0 ]) );
if ( b1==0 ){
closedir(pDir);
return NULL;
}
sz = MultiByteToWideChar(CP_UTF8, 0 , zDirName, sz, b1, sz);
b1[sz++] = '\\' ;
b1[sz++] = '*' ;
b1[sz] = 0 ;
if ( sz+1 >sizeof (data.name)/sizeof (data.name[0 ]) ){
closedir(pDir);
sqlite3_free(b1);
return NULL;
}
memcpy(data.name, b1, (sz+1 )*sizeof (b1[0 ]));
sqlite3_free(b1);
pDir->d_handle = _wfindfirst(data.name, &data);
if ( pDir->d_handle<0 ){
closedir(pDir);
return NULL;
}
while ( WindowsFileToIgnore(data) ){
memset(&data, 0 , sizeof (data));
if ( _wfindnext(pDir->d_handle, &data)==-1 ){
closedir(pDir);
return NULL;
}
}
pDir->cur.d_ino = 0 ;
pDir->cur.d_attributes = data.attrib;
WideCharToMultiByte(CP_UTF8, 0 , data.name, -1 ,
pDir->cur.d_name, FILENAME_MAX, 0 , 0 );
return pDir;
}
/*
* * Read the next entry from a directory .
* *
* * The returned struct - dirent object is managed by DIR . It is only
* * valid until the next readdir ( ) or closedir ( ) call . Only the
* * d_name [ ] field is meaningful . The d_name [ ] value has been
* * translated into UTF8 .
*/
static struct dirent *readdir(DIR *pDir){
struct _wfinddata_t data;
if ( pDir==0 ) return 0 ;
if ( (pDir->cur.d_ino++)==0 ){
return &pDir->cur;
}
do {
memset(&data, 0 , sizeof (data));
if ( _wfindnext(pDir->d_handle, &data)==-1 ){
return NULL;
}
}while ( WindowsFileToIgnore(data) );
pDir->cur.d_attributes = data.attrib;
WideCharToMultiByte(CP_UTF8, 0 , data.name, -1 ,
pDir->cur.d_name, FILENAME_MAX, 0 , 0 );
return &pDir->cur;
}
#endif /* defined(_WIN32) && defined(_MSC_VER) */
/************************* End ext/misc/windirent.h ********************/
/************************* Begin ext/misc/memtrace.c ******************/
/*
* * 2019 - 01 - 21
* *
* * The author disclaims copyright to this source code . In place of
* * a legal notice , here is a blessing :
* *
* * May you do good and not evil .
* * May you find forgiveness for yourself and forgive others .
* * May you share freely , never taking more than you give .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* * This file implements an extension that uses the SQLITE_CONFIG_MALLOC
* * mechanism to add a tracing layer on top of SQLite . If this extension
* * is registered prior to sqlite3_initialize ( ) , it will cause all memory
* * allocation activities to be logged on standard output , or to some other
* * FILE specified by the initializer .
* *
* * This file needs to be compiled into the application that uses it .
* *
* * This extension is used to implement the - - memtrace option of the
* * command - line shell .
*/
#include <assert.h>
#include <string.h>
#include <stdio.h>
/* The original memory allocation routines */
static sqlite3_mem_methods memtraceBase;
static FILE *memtraceOut;
/* Methods that trace memory allocations */
static void *memtraceMalloc(int n){
if ( memtraceOut ){
fprintf(memtraceOut, "MEMTRACE: allocate %d bytes\n" ,
memtraceBase.xRoundup(n));
}
return memtraceBase.xMalloc(n);
}
static void memtraceFree(void *p){
if ( p==0 ) return ;
if ( memtraceOut ){
fprintf(memtraceOut, "MEMTRACE: free %d bytes\n" , memtraceBase.xSize(p));
}
memtraceBase.xFree(p);
}
static void *memtraceRealloc(void *p, int n){
if ( p==0 ) return memtraceMalloc(n);
if ( n==0 ){
memtraceFree(p);
return 0 ;
}
if ( memtraceOut ){
fprintf(memtraceOut, "MEMTRACE: resize %d -> %d bytes\n" ,
memtraceBase.xSize(p), memtraceBase.xRoundup(n));
}
return memtraceBase.xRealloc(p, n);
}
static int memtraceSize(void *p){
return memtraceBase.xSize(p);
}
static int memtraceRoundup(int n){
return memtraceBase.xRoundup(n);
}
static int memtraceInit(void *p){
return memtraceBase.xInit(p);
}
static void memtraceShutdown(void *p){
memtraceBase.xShutdown(p);
}
/* The substitute memory allocator */
static sqlite3_mem_methods ersaztMethods = {
memtraceMalloc,
memtraceFree,
memtraceRealloc,
memtraceSize,
memtraceRoundup,
memtraceInit,
memtraceShutdown,
0
};
/* Begin tracing memory allocations to out. */
int sqlite3MemTraceActivate(FILE *out){
int rc = SQLITE_OK;
if ( memtraceBase.xMalloc==0 ){
rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memtraceBase);
if ( rc==SQLITE_OK ){
rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &ersaztMethods);
}
}
memtraceOut = out;
return rc;
}
/* Deactivate memory tracing */
int sqlite3MemTraceDeactivate(void ){
int rc = SQLITE_OK;
if ( memtraceBase.xMalloc!=0 ){
rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memtraceBase);
if ( rc==SQLITE_OK ){
memset(&memtraceBase, 0 , sizeof (memtraceBase));
}
}
memtraceOut = 0 ;
return rc;
}
/************************* End ext/misc/memtrace.c ********************/
/************************* Begin ext/misc/pcachetrace.c ******************/
/*
* * 2023 - 06 - 21
* *
* * The author disclaims copyright to this source code . In place of
* * a legal notice , here is a blessing :
* *
* * May you do good and not evil .
* * May you find forgiveness for yourself and forgive others .
* * May you share freely , never taking more than you give .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* * This file implements an extension that uses the SQLITE_CONFIG_PCACHE2
* * mechanism to add a tracing layer on top of pluggable page cache of
* * SQLite . If this extension is registered prior to sqlite3_initialize ( ) ,
* * it will cause all page cache activities to be logged on standard output ,
* * or to some other FILE specified by the initializer .
* *
* * This file needs to be compiled into the application that uses it .
* *
* * This extension is used to implement the - - pcachetrace option of the
* * command - line shell .
*/
#include <assert.h>
#include <string.h>
#include <stdio.h>
/* The original page cache routines */
static sqlite3_pcache_methods2 pcacheBase;
static FILE *pcachetraceOut;
/* Methods that trace pcache activity */
static int pcachetraceInit(void *pArg){
int nRes;
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xInit(%p)\n" , pArg);
}
nRes = pcacheBase.xInit(pArg);
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xInit(%p) -> %d\n" , pArg, nRes);
}
return nRes;
}
static void pcachetraceShutdown(void *pArg){
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xShutdown(%p)\n" , pArg);
}
pcacheBase.xShutdown(pArg);
}
static sqlite3_pcache *pcachetraceCreate(int szPage, int szExtra, int bPurge){
sqlite3_pcache *pRes;
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xCreate(%d,%d,%d)\n" ,
szPage, szExtra, bPurge);
}
pRes = pcacheBase.xCreate(szPage, szExtra, bPurge);
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xCreate(%d,%d,%d) -> %p\n" ,
szPage, szExtra, bPurge, pRes);
}
return pRes;
}
static void pcachetraceCachesize(sqlite3_pcache *p, int nCachesize){
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xCachesize(%p, %d)\n" , p, nCachesize);
}
pcacheBase.xCachesize(p, nCachesize);
}
static int pcachetracePagecount(sqlite3_pcache *p){
int nRes;
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xPagecount(%p)\n" , p);
}
nRes = pcacheBase.xPagecount(p);
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xPagecount(%p) -> %d\n" , p, nRes);
}
return nRes;
}
static sqlite3_pcache_page *pcachetraceFetch(
sqlite3_pcache *p,
unsigned key,
int crFg
){
sqlite3_pcache_page *pRes;
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xFetch(%p,%u,%d)\n" , p, key, crFg);
}
pRes = pcacheBase.xFetch(p, key, crFg);
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xFetch(%p,%u,%d) -> %p\n" ,
p, key, crFg, pRes);
}
return pRes;
}
static void pcachetraceUnpin(
sqlite3_pcache *p,
sqlite3_pcache_page *pPg,
int bDiscard
){
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xUnpin(%p, %p, %d)\n" ,
p, pPg, bDiscard);
}
pcacheBase.xUnpin(p, pPg, bDiscard);
}
static void pcachetraceRekey(
sqlite3_pcache *p,
sqlite3_pcache_page *pPg,
unsigned oldKey,
unsigned newKey
){
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xRekey(%p, %p, %u, %u)\n" ,
p, pPg, oldKey, newKey);
}
pcacheBase.xRekey(p, pPg, oldKey, newKey);
}
static void pcachetraceTruncate(sqlite3_pcache *p, unsigned n){
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xTruncate(%p, %u)\n" , p, n);
}
pcacheBase.xTruncate(p, n);
}
static void pcachetraceDestroy(sqlite3_pcache *p){
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xDestroy(%p)\n" , p);
}
pcacheBase.xDestroy(p);
}
static void pcachetraceShrink(sqlite3_pcache *p){
if ( pcachetraceOut ){
fprintf(pcachetraceOut, "PCACHETRACE: xShrink(%p)\n" , p);
}
pcacheBase.xShrink(p);
}
/* The substitute pcache methods */
static sqlite3_pcache_methods2 ersaztPcacheMethods = {
0 ,
0 ,
pcachetraceInit,
pcachetraceShutdown,
pcachetraceCreate,
pcachetraceCachesize,
pcachetracePagecount,
pcachetraceFetch,
pcachetraceUnpin,
pcachetraceRekey,
pcachetraceTruncate,
pcachetraceDestroy,
pcachetraceShrink
};
/* Begin tracing memory allocations to out. */
int sqlite3PcacheTraceActivate(FILE *out){
int rc = SQLITE_OK;
if ( pcacheBase.xFetch==0 ){
rc = sqlite3_config(SQLITE_CONFIG_GETPCACHE2, &pcacheBase);
if ( rc==SQLITE_OK ){
rc = sqlite3_config(SQLITE_CONFIG_PCACHE2, &ersaztPcacheMethods);
}
}
pcachetraceOut = out;
return rc;
}
/* Deactivate memory tracing */
int sqlite3PcacheTraceDeactivate(void ){
int rc = SQLITE_OK;
if ( pcacheBase.xFetch!=0 ){
rc = sqlite3_config(SQLITE_CONFIG_PCACHE2, &pcacheBase);
if ( rc==SQLITE_OK ){
memset(&pcacheBase, 0 , sizeof (pcacheBase));
}
}
pcachetraceOut = 0 ;
return rc;
}
/************************* End ext/misc/pcachetrace.c ********************/
/************************* Begin ext/misc/shathree.c ******************/
/*
* * 2017 - 03 - 08
* *
* * 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 SHA3 hashes
* * in the way described by the ( U . S . ) NIST FIPS 202 SHA - 3 Standard .
* * Three SQL functions are implemented :
* *
* * sha3 ( X , 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 . 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 .
* *
* * 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 minimum - 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 ' 68656 c6c6f ' ) ;
* *
* * 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 ' 42333 a010203 ' ) FROM a ;
* *
* * WITH a ( x ) AS ( VALUES ( 0 x123456 ) )
* * 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
#include <assert.h>
#include <string.h>
#include <stdarg.h>
#ifndef SQLITE_AMALGAMATION
/* typedef sqlite3_uint64 u64; */
#endif /* SQLITE_AMALGAMATION */
/******************************************************************************
* * The Hash Engine
*/
/*
* * Macros to determine whether the machine is big or little endian ,
* * and whether or not that determination is run - time or compile - time .
* *
* * For best performance , an attempt is made to guess at the byte - order
* * using C - preprocessor macros . If that is unsuccessful , or if
* * - DSHA3_BYTEORDER = 0 is set , then byte - order is determined
* * at run - time .
*/
#ifndef SHA3_BYTEORDER
# if defined (i386) || defined (__i386__) || defined (_M_IX86) || \
defined (__x86_64) || defined (__x86_64__) || defined (_M_X64) || \
defined (_M_AMD64) || defined (_M_ARM) || defined (__x86) || \
defined (__arm__)
# define SHA3_BYTEORDER 1234
# elif defined (sparc) || defined (__ppc__)
# define SHA3_BYTEORDER 4321
# else
# define SHA3_BYTEORDER 0
# endif
#endif
/*
* * State structure for a SHA3 hash in progress
*/
typedef struct SHA3Context SHA3Context;
struct SHA3Context {
union {
u64 s[25 ]; /* Keccak state. 5x5 lines of 64 bits each */
unsigned char x[1600 ]; /* ... or 1600 bytes */
} u;
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 */
};
/*
* * A single step of the Keccak mixing function for a 1600 - bit state
*/
static void KeccakF1600Step(SHA3Context *p){
int i;
u64 b0, b1, b2, b3, b4;
u64 c0, c1, c2, c3, c4;
u64 d0, d1, d2, d3, d4;
static const u64 RC[] = {
0 x0000000000000001ULL, 0 x0000000000008082ULL,
0 x800000000000808aULL, 0 x8000000080008000ULL,
0 x000000000000808bULL, 0 x0000000080000001ULL,
0 x8000000080008081ULL, 0 x8000000000008009ULL,
0 x000000000000008aULL, 0 x0000000000000088ULL,
0 x0000000080008009ULL, 0 x000000008000000aULL,
0 x000000008000808bULL, 0 x800000000000008bULL,
0 x8000000000008089ULL, 0 x8000000000008003ULL,
0 x8000000000008002ULL, 0 x8000000000000080ULL,
0 x000000000000800aULL, 0 x800000008000000aULL,
0 x8000000080008081ULL, 0 x8000000000008080ULL,
0 x0000000080000001ULL, 0 x8000000080008008ULL
};
# define a00 (p->u.s[0 ])
# define a01 (p->u.s[1 ])
# define a02 (p->u.s[2 ])
# define a03 (p->u.s[3 ])
# define a04 (p->u.s[4 ])
# define a10 (p->u.s[5 ])
# define a11 (p->u.s[6 ])
# define a12 (p->u.s[7 ])
# define a13 (p->u.s[8 ])
# define a14 (p->u.s[9 ])
# define a20 (p->u.s[10 ])
# define a21 (p->u.s[11 ])
# define a22 (p->u.s[12 ])
# define a23 (p->u.s[13 ])
# define a24 (p->u.s[14 ])
# define a30 (p->u.s[15 ])
# define a31 (p->u.s[16 ])
# define a32 (p->u.s[17 ])
# define a33 (p->u.s[18 ])
# define a34 (p->u.s[19 ])
# define a40 (p->u.s[20 ])
# define a41 (p->u.s[21 ])
# define a42 (p->u.s[22 ])
# define a43 (p->u.s[23 ])
# define a44 (p->u.s[24 ])
# define ROL64(a,x) ((a<<x)|(a>>(64 -x)))
for (i=0 ; i<24 ; i+=4 ){
c0 = a00^a10^a20^a30^a40;
c1 = a01^a11^a21^a31^a41;
c2 = a02^a12^a22^a32^a42;
c3 = a03^a13^a23^a33^a43;
c4 = a04^a14^a24^a34^a44;
d0 = c4^ROL64(c1, 1 );
d1 = c0^ROL64(c2, 1 );
d2 = c1^ROL64(c3, 1 );
d3 = c2^ROL64(c4, 1 );
d4 = c3^ROL64(c0, 1 );
b0 = (a00^d0);
b1 = ROL64((a11^d1), 44 );
b2 = ROL64((a22^d2), 43 );
b3 = ROL64((a33^d3), 21 );
b4 = ROL64((a44^d4), 14 );
a00 = b0 ^((~b1)& b2 );
a00 ^= RC[i];
a11 = b1 ^((~b2)& b3 );
a22 = b2 ^((~b3)& b4 );
a33 = b3 ^((~b4)& b0 );
a44 = b4 ^((~b0)& b1 );
b2 = ROL64((a20^d0), 3 );
b3 = ROL64((a31^d1), 45 );
b4 = ROL64((a42^d2), 61 );
b0 = ROL64((a03^d3), 28 );
b1 = ROL64((a14^d4), 20 );
a20 = b0 ^((~b1)& b2 );
a31 = b1 ^((~b2)& b3 );
a42 = b2 ^((~b3)& b4 );
a03 = b3 ^((~b4)& b0 );
a14 = b4 ^((~b0)& b1 );
b4 = ROL64((a40^d0), 18 );
b0 = ROL64((a01^d1), 1 );
b1 = ROL64((a12^d2), 6 );
b2 = ROL64((a23^d3), 25 );
b3 = ROL64((a34^d4), 8 );
a40 = b0 ^((~b1)& b2 );
a01 = b1 ^((~b2)& b3 );
a12 = b2 ^((~b3)& b4 );
a23 = b3 ^((~b4)& b0 );
a34 = b4 ^((~b0)& b1 );
b1 = ROL64((a10^d0), 36 );
b2 = ROL64((a21^d1), 10 );
b3 = ROL64((a32^d2), 15 );
b4 = ROL64((a43^d3), 56 );
b0 = ROL64((a04^d4), 27 );
a10 = b0 ^((~b1)& b2 );
a21 = b1 ^((~b2)& b3 );
a32 = b2 ^((~b3)& b4 );
a43 = b3 ^((~b4)& b0 );
a04 = b4 ^((~b0)& b1 );
b3 = ROL64((a30^d0), 41 );
b4 = ROL64((a41^d1), 2 );
b0 = ROL64((a02^d2), 62 );
b1 = ROL64((a13^d3), 55 );
b2 = ROL64((a24^d4), 39 );
a30 = b0 ^((~b1)& b2 );
a41 = b1 ^((~b2)& b3 );
a02 = b2 ^((~b3)& b4 );
a13 = b3 ^((~b4)& b0 );
a24 = b4 ^((~b0)& b1 );
c0 = a00^a20^a40^a10^a30;
c1 = a11^a31^a01^a21^a41;
c2 = a22^a42^a12^a32^a02;
c3 = a33^a03^a23^a43^a13;
c4 = a44^a14^a34^a04^a24;
d0 = c4^ROL64(c1, 1 );
d1 = c0^ROL64(c2, 1 );
d2 = c1^ROL64(c3, 1 );
d3 = c2^ROL64(c4, 1 );
d4 = c3^ROL64(c0, 1 );
b0 = (a00^d0);
b1 = ROL64((a31^d1), 44 );
b2 = ROL64((a12^d2), 43 );
b3 = ROL64((a43^d3), 21 );
b4 = ROL64((a24^d4), 14 );
a00 = b0 ^((~b1)& b2 );
a00 ^= RC[i+1 ];
a31 = b1 ^((~b2)& b3 );
a12 = b2 ^((~b3)& b4 );
a43 = b3 ^((~b4)& b0 );
a24 = b4 ^((~b0)& b1 );
b2 = ROL64((a40^d0), 3 );
b3 = ROL64((a21^d1), 45 );
b4 = ROL64((a02^d2), 61 );
b0 = ROL64((a33^d3), 28 );
b1 = ROL64((a14^d4), 20 );
a40 = b0 ^((~b1)& b2 );
a21 = b1 ^((~b2)& b3 );
a02 = b2 ^((~b3)& b4 );
a33 = b3 ^((~b4)& b0 );
a14 = b4 ^((~b0)& b1 );
b4 = ROL64((a30^d0), 18 );
b0 = ROL64((a11^d1), 1 );
b1 = ROL64((a42^d2), 6 );
b2 = ROL64((a23^d3), 25 );
b3 = ROL64((a04^d4), 8 );
a30 = b0 ^((~b1)& b2 );
a11 = b1 ^((~b2)& b3 );
a42 = b2 ^((~b3)& b4 );
a23 = b3 ^((~b4)& b0 );
a04 = b4 ^((~b0)& b1 );
b1 = ROL64((a20^d0), 36 );
b2 = ROL64((a01^d1), 10 );
b3 = ROL64((a32^d2), 15 );
b4 = ROL64((a13^d3), 56 );
b0 = ROL64((a44^d4), 27 );
a20 = b0 ^((~b1)& b2 );
a01 = b1 ^((~b2)& b3 );
a32 = b2 ^((~b3)& b4 );
a13 = b3 ^((~b4)& b0 );
a44 = b4 ^((~b0)& b1 );
b3 = ROL64((a10^d0), 41 );
b4 = ROL64((a41^d1), 2 );
b0 = ROL64((a22^d2), 62 );
b1 = ROL64((a03^d3), 55 );
b2 = ROL64((a34^d4), 39 );
a10 = b0 ^((~b1)& b2 );
a41 = b1 ^((~b2)& b3 );
a22 = b2 ^((~b3)& b4 );
a03 = b3 ^((~b4)& b0 );
a34 = b4 ^((~b0)& b1 );
c0 = a00^a40^a30^a20^a10;
c1 = a31^a21^a11^a01^a41;
c2 = a12^a02^a42^a32^a22;
c3 = a43^a33^a23^a13^a03;
c4 = a24^a14^a04^a44^a34;
d0 = c4^ROL64(c1, 1 );
d1 = c0^ROL64(c2, 1 );
d2 = c1^ROL64(c3, 1 );
d3 = c2^ROL64(c4, 1 );
d4 = c3^ROL64(c0, 1 );
b0 = (a00^d0);
b1 = ROL64((a21^d1), 44 );
b2 = ROL64((a42^d2), 43 );
b3 = ROL64((a13^d3), 21 );
b4 = ROL64((a34^d4), 14 );
a00 = b0 ^((~b1)& b2 );
a00 ^= RC[i+2 ];
a21 = b1 ^((~b2)& b3 );
a42 = b2 ^((~b3)& b4 );
a13 = b3 ^((~b4)& b0 );
a34 = b4 ^((~b0)& b1 );
b2 = ROL64((a30^d0), 3 );
b3 = ROL64((a01^d1), 45 );
b4 = ROL64((a22^d2), 61 );
b0 = ROL64((a43^d3), 28 );
b1 = ROL64((a14^d4), 20 );
a30 = b0 ^((~b1)& b2 );
a01 = b1 ^((~b2)& b3 );
a22 = b2 ^((~b3)& b4 );
a43 = b3 ^((~b4)& b0 );
a14 = b4 ^((~b0)& b1 );
b4 = ROL64((a10^d0), 18 );
b0 = ROL64((a31^d1), 1 );
b1 = ROL64((a02^d2), 6 );
b2 = ROL64((a23^d3), 25 );
b3 = ROL64((a44^d4), 8 );
a10 = b0 ^((~b1)& b2 );
a31 = b1 ^((~b2)& b3 );
a02 = b2 ^((~b3)& b4 );
a23 = b3 ^((~b4)& b0 );
a44 = b4 ^((~b0)& b1 );
b1 = ROL64((a40^d0), 36 );
b2 = ROL64((a11^d1), 10 );
b3 = ROL64((a32^d2), 15 );
b4 = ROL64((a03^d3), 56 );
b0 = ROL64((a24^d4), 27 );
a40 = b0 ^((~b1)& b2 );
a11 = b1 ^((~b2)& b3 );
a32 = b2 ^((~b3)& b4 );
a03 = b3 ^((~b4)& b0 );
a24 = b4 ^((~b0)& b1 );
b3 = ROL64((a20^d0), 41 );
b4 = ROL64((a41^d1), 2 );
b0 = ROL64((a12^d2), 62 );
b1 = ROL64((a33^d3), 55 );
b2 = ROL64((a04^d4), 39 );
a20 = b0 ^((~b1)& b2 );
a41 = b1 ^((~b2)& b3 );
a12 = b2 ^((~b3)& b4 );
a33 = b3 ^((~b4)& b0 );
a04 = b4 ^((~b0)& b1 );
c0 = a00^a30^a10^a40^a20;
c1 = a21^a01^a31^a11^a41;
c2 = a42^a22^a02^a32^a12;
c3 = a13^a43^a23^a03^a33;
c4 = a34^a14^a44^a24^a04;
d0 = c4^ROL64(c1, 1 );
d1 = c0^ROL64(c2, 1 );
d2 = c1^ROL64(c3, 1 );
d3 = c2^ROL64(c4, 1 );
d4 = c3^ROL64(c0, 1 );
b0 = (a00^d0);
b1 = ROL64((a01^d1), 44 );
b2 = ROL64((a02^d2), 43 );
b3 = ROL64((a03^d3), 21 );
b4 = ROL64((a04^d4), 14 );
a00 = b0 ^((~b1)& b2 );
a00 ^= RC[i+3 ];
a01 = b1 ^((~b2)& b3 );
a02 = b2 ^((~b3)& b4 );
a03 = b3 ^((~b4)& b0 );
a04 = b4 ^((~b0)& b1 );
b2 = ROL64((a10^d0), 3 );
b3 = ROL64((a11^d1), 45 );
b4 = ROL64((a12^d2), 61 );
b0 = ROL64((a13^d3), 28 );
b1 = ROL64((a14^d4), 20 );
a10 = b0 ^((~b1)& b2 );
a11 = b1 ^((~b2)& b3 );
a12 = b2 ^((~b3)& b4 );
a13 = b3 ^((~b4)& b0 );
a14 = b4 ^((~b0)& b1 );
b4 = ROL64((a20^d0), 18 );
b0 = ROL64((a21^d1), 1 );
b1 = ROL64((a22^d2), 6 );
b2 = ROL64((a23^d3), 25 );
b3 = ROL64((a24^d4), 8 );
a20 = b0 ^((~b1)& b2 );
a21 = b1 ^((~b2)& b3 );
a22 = b2 ^((~b3)& b4 );
a23 = b3 ^((~b4)& b0 );
a24 = b4 ^((~b0)& b1 );
b1 = ROL64((a30^d0), 36 );
b2 = ROL64((a31^d1), 10 );
b3 = ROL64((a32^d2), 15 );
b4 = ROL64((a33^d3), 56 );
b0 = ROL64((a34^d4), 27 );
a30 = b0 ^((~b1)& b2 );
a31 = b1 ^((~b2)& b3 );
a32 = b2 ^((~b3)& b4 );
a33 = b3 ^((~b4)& b0 );
a34 = b4 ^((~b0)& b1 );
b3 = ROL64((a40^d0), 41 );
b4 = ROL64((a41^d1), 2 );
b0 = ROL64((a42^d2), 62 );
b1 = ROL64((a43^d3), 55 );
b2 = ROL64((a44^d4), 39 );
a40 = b0 ^((~b1)& b2 );
a41 = b1 ^((~b2)& b3 );
a42 = b2 ^((~b3)& b4 );
a43 = b3 ^((~b4)& b0 );
a44 = b4 ^((~b0)& b1 );
}
}
/*
* * Initialize a new hash . iSize determines the size of the hash
* * in bits and should be one of 224 , 256 , 384 , or 512 . Or iSize
* * can be zero to use the default hash size of 256 bits .
*/
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 {
p->nRate = (1600 - 2 *256 )/8 ;
}
#if SHA3_BYTEORDER==1234
/* Known to be little-endian at compile-time. No-op */
#elif SHA3_BYTEORDER==4321
p->ixMask = 7 ; /* Big-endian */
#else
{
static unsigned int one = 1 ;
if ( 1 ==*(unsigned char *)&one ){
/* Little endian. No byte swapping. */
p->ixMask = 0 ;
}else {
/* Big endian. Byte swap. */
p->ixMask = 7 ;
}
}
#endif
}
/*
* * Make consecutive calls to the SHA3Update function to add new content
* * to the hash
*/
static void SHA3Update(
SHA3Context *p,
const unsigned char *aData,
unsigned int nData
){
unsigned int i = 0 ;
if ( aData==0 ) return ;
#if SHA3_BYTEORDER==1234
if ( (p->nLoaded % 8 )==0 && ((aData - (const unsigned char *)0 )&7 )==0 ){
for (; i+7 <nData; i+=8 ){
p->u.s[p->nLoaded/8 ] ^= *(u64*)&aData[i];
p->nLoaded += 8 ;
if ( p->nLoaded>=p->nRate ){
KeccakF1600Step(p);
p->nLoaded = 0 ;
}
}
}
#endif
for (; i<nData; i++){
#if SHA3_BYTEORDER==1234
p->u.x[p->nLoaded] ^= aData[i];
#elif SHA3_BYTEORDER==4321
p->u.x[p->nLoaded^0 x07] ^= aData[i];
#else
p->u.x[p->nLoaded^p->ixMask] ^= aData[i];
#endif
p->nLoaded++;
if ( p->nLoaded==p->nRate ){
KeccakF1600Step(p);
p->nLoaded = 0 ;
}
}
}
/*
* * After all content has been added , invoke SHA3Final ( ) to compute
* * the final hash . The function returns a pointer to the binary
* * hash value .
*/
static unsigned char *SHA3Final(SHA3Context *p){
unsigned int i;
if ( p->nLoaded==p->nRate-1 ){
const unsigned char c1 = 0 x86;
SHA3Update(p, &c1, 1 );
}else {
const unsigned char c2 = 0 x06;
const unsigned char c3 = 0 x80;
SHA3Update(p, &c2, 1 );
p->nLoaded = p->nRate - 1 ;
SHA3Update(p, &c3, 1 );
}
for (i=0 ; i<p->nRate; i++){
p->u.x[i+p->nRate] = p->u.x[i^p->ixMask];
}
return &p->u.x[p->nRate];
}
/* End of the hashing logic
*****************************************************************************/
/*
* * Implementation of the sha3 ( X , SIZE ) function .
* *
* * Return a BLOB which is the SIZE - bit SHA3 hash of X . The default
* * size is 256 . If X is a BLOB , it is hashed as is .
* * For all other non - NULL types of input , X is converted into a UTF - 8 string
* * and the string is hashed without the trailing 0 x00 terminator . The hash
* * of a NULL value is NULL .
*/
static void sha3Func(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
SHA3Context cx;
int eType = sqlite3_value_type(argv[0 ]);
int nByte = sqlite3_value_bytes(argv[0 ]);
int iSize;
if ( argc==1 ){
iSize = 256 ;
}else {
iSize = sqlite3_value_int(argv[1 ]);
if ( iSize!=224 && iSize!=256 && iSize!=384 && iSize!=512 ){
sqlite3_result_error(context, "SHA3 size should be one of: 224 256 "
"384 512" , -1 );
return ;
}
}
if ( eType==SQLITE_NULL ) return ;
SHA3Init(&cx, iSize);
if ( eType==SQLITE_BLOB ){
SHA3Update(&cx, sqlite3_value_blob(argv[0 ]), nByte);
}else {
SHA3Update(&cx, sqlite3_value_text(argv[0 ]), nByte);
}
sqlite3_result_blob(context, SHA3Final(&cx), iSize/8 , SQLITE_TRANSIENT);
}
/* Compute a string using sqlite3_vsnprintf() with a maximum length
* * of 50 bytes and add it to the hash .
*/
static void sha3_step_vformat(
SHA3Context *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);
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 & 0 xff;
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 & 0 xff;
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 .
* *
* * This function compiles and runs the SQL statement ( s ) given in the
* * argument . The results are hashed using a SIZE - bit SHA3 . The default
* * size is 256 .
* *
* * The format of the byte stream that is hashed is summarized as follows :
* *
* * S < n > : < sql >
* * R
* * N
* * I < int >
* * F < ieee - float >
* * B < size > : < bytes >
* * T < size > : < text >
* *
* * < sql > is the original SQL text for each statement run and < n > is
* * the size of that text . The SQL text is UTF - 8 . A single R character
* * occurs before the start of each row . N means a NULL value .
* * I mean an 8 - byte little - endian integer < int > . F is a floating point
* * number with an 8 - byte little - endian IEEE floating point value < ieee - float > .
* * B means blobs of < size > bytes . T means text rendered as < size >
* * bytes of UTF - 8 . The < n > and < size > values are expressed as an ASCII
* * text integers .
* *
* * For each SQL statement in the X input , there is one S segment . Each
* * S segment is followed by zero or more R segments , one for each row in the
* * result set . After each R , there are one or more N , I , F , B , or T segments ,
* * one for each column in the result set . Segments are concatentated directly
* * with no delimiters of any kind .
*/
static void sha3QueryFunc(
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;
SHA3Context cx;
int iSize;
if ( argc==1 ){
iSize = 256 ;
}else {
iSize = sqlite3_value_int(argv[1 ]);
if ( iSize!=224 && iSize!=256 && iSize!=384 && iSize!=512 ){
sqlite3_result_error(context, "SHA3 size should be one of: 224 256 "
"384 512" , -1 );
return ;
}
}
if ( zSql==0 ) return ;
SHA3Init(&cx, iSize);
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);
if ( z ){
n = (int )strlen(z);
sha3_step_vformat(&cx,"S%d:" ,n);
SHA3Update(&cx,(unsigned char *)z,n);
}
/* Compute a hash over the result of the query */
while ( SQLITE_ROW==sqlite3_step(pStmt) ){
SHA3Update(&cx,(const unsigned char *)"R" ,1 );
for (i=0 ; i<nCol; i++){
sha3UpdateFromValue(&cx, sqlite3_column_value(pStmt,i));
}
}
sqlite3_finalize(pStmt);
}
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
#endif
int sqlite3_shathree_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void )pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "sha3" , 1 ,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
0 , sha3Func, 0 , 0 );
if ( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sha3" , 2 ,
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,
0 , sha3QueryFunc, 0 , 0 );
}
if ( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sha3_query" , 2 ,
SQLITE_UTF8 | SQLITE_DIRECTONLY,
0 , sha3QueryFunc, 0 , 0 );
}
return rc;
}
/************************* 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 <assert.h>
#include <string.h>
#include <stdarg.h>
/******************************************************************************
* * 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 )&0 xFF00FF00) \
|(rol(block[i],8 )&0 x00FF00FF))
#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)+0 x5A827999+rol(v,5 );w=ror(w,2 );
#define Rb0(v,w,x,y,z,i) \
z+=((w&(x^y))^y)+blk0be(i)+0 x5A827999+rol(v,5 );w=ror(w,2 );
#define R1(v,w,x,y,z,i) \
z+=((w&(x^y))^y)+blk(i)+0 x5A827999+rol(v,5 );w=ror(w,2 );
#define R2(v,w,x,y,z,i) \
z+=(w^x^y)+blk(i)+0 x6ED9EBA1+rol(v,5 );w=ror(w,2 );
#define R3(v,w,x,y,z,i) \
z+=(((w|x)&y)|(w&x))+blk(i)+0 x8F1BBCDC+rol(v,5 );w=ror(w,2 );
#define R4(v,w,x,y,z,i) \
z+=(w^x^y)+blk(i)+0 xCA62C1D6+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 ] = 0 x67452301;
p->state[1 ] = 0 xEFCDAB89;
p->state[2 ] = 0 x98BADCFE;
p->state[3 ] = 0 x10325476;
p->state[4 ] = 0 xC3D2E1F0;
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 )&0 xf];
zOut[i*2 +1 ] = zEncode[digest[i] & 0 xf];
}
zOut[i*2 ]= 0 ;
}
}
/* End of the hashing logic
*****************************************************************************/
/*
* * Two SQL functions : sha1 ( X ) and sha1b ( X ) .
* *
* * sha1 ( X ) returns 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 hashed without the trailing 0 x00 terminator . The hash of a NULL
* * value is NULL .
* *
* * sha1b ( X ) is the same except that it returns a 20 - byte BLOB containing
* * the binary hash instead of a hexadecimal string .
*/
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 ]);
const unsigned char *pData;
char zOut[44 ];
assert( argc==1 );
if ( eType==SQLITE_NULL ) return ;
hash_init(&cx);
if ( eType==SQLITE_BLOB ){
pData = (const unsigned char *)sqlite3_value_blob(argv[0 ]);
}else {
pData = (const unsigned char *)sqlite3_value_text(argv[0 ]);
}
if ( pData==0 ) return ;
hash_step(&cx, pData, nByte);
if ( sqlite3_user_data(context)!=0 ){
/* sha1b() - binary result */
hash_finish(&cx, zOut, 1 );
sqlite3_result_blob(context, zOut, 20 , SQLITE_TRANSIENT);
}else {
/* sha1() - hexadecimal text result */
hash_finish(&cx, zOut, 0 );
sqlite3_result_text(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);
if ( z==0 ) z = "" ;
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<nCol; i++){
switch ( sqlite3_column_type(pStmt,i) ){
case SQLITE_NULL: {
hash_step(&cx, (const unsigned char *)"N" ,1 );
break ;
}
case SQLITE_INTEGER: {
sqlite3_uint64 u;
int j;
unsigned char x[9 ];
sqlite3_int64 v = sqlite3_column_int64(pStmt,i);
memcpy(&u, &v, 8 );
for (j=8 ; j>=1 ; j--){
x[j] = u & 0 xff;
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 & 0 xff;
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
* *
* * 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 the UINT collating sequence .
* *
* * UINT works like BINARY for text , except that embedded strings
* * of digits compare in numeric order .
* *
* * * Leading zeros are handled properly , in the sense that
* * they do not mess of the magnitude comparison of embedded
* * strings of digits . " x00123y " is equal to " x123y " .
* *
* * * Only unsigned integers are recognized . Plus and minus
* * signs are ignored . Decimal points and exponential notation
* * are ignored .
* *
* * * Embedded integers can be of arbitrary length . Comparison
* * is * not * limited integers that can be expressed as a
* * 64 - bit machine integer .
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <ctype.h>
/*
* * Compare text in lexicographic order , except strings of digits
* * compare in numeric order .
*/
static int uintCollFunc(
void *notUsed,
int nKey1, const void *pKey1,
int nKey2, const void *pKey2
){
const unsigned char *zA = (const unsigned char *)pKey1;
const unsigned char *zB = (const unsigned char *)pKey2;
int i=0 , j=0 , x;
(void )notUsed;
while ( i<nKey1 && j<nKey2 ){
x = zA[i] - zB[j];
if ( isdigit(zA[i]) ){
int k;
if ( !isdigit(zB[j]) ) return x;
while ( i<nKey1 && zA[i]=='0' ){ i++; }
while ( j<nKey2 && zB[j]=='0' ){ j++; }
k = 0 ;
while ( i+k<nKey1 && isdigit(zA[i+k])
&& j+k<nKey2 && isdigit(zB[j+k]) ){
k++;
}
if ( i+k<nKey1 && isdigit(zA[i+k]) ){
return +1 ;
}else if ( j+k<nKey2 && isdigit(zB[j+k]) ){
return -1 ;
}else {
x = memcmp(zA+i, zB+j, k);
if ( x ) return x;
i += k;
j += k;
}
}else if ( x ){
return x;
}else {
i++;
j++;
}
}
return (nKey1 - i) - (nKey2 - j);
}
#ifdef _WIN32
#endif
int sqlite3_uint_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
SQLITE_EXTENSION_INIT2(pApi);
(void )pzErrMsg; /* Unused parameter */
return sqlite3_create_collation(db, "uint" , SQLITE_UTF8, 0 , uintCollFunc);
}
/************************* End ext/misc/uint.c ********************/
/************************* Begin ext/misc/decimal.c ******************/
/*
* * 2020 - 06 - 22
* *
* * 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 .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* * Routines to implement arbitrary - precision decimal math .
* *
* * The focus here is on simplicity and correctness , not performance .
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
/* Mark a function parameter as unused, to suppress nuisance compiler
** warnings. */
#ifndef UNUSED_PARAMETER
# define UNUSED_PARAMETER(X) (void )(X)
#endif
#ifndef IsSpace
#define IsSpace(X) isspace((unsigned char )X)
#endif
#ifndef SQLITE_DECIMAL_MAX_DIGIT
# define SQLITE_DECIMAL_MAX_DIGIT 10000000
#endif
/* A decimal object */
typedef struct Decimal Decimal;
struct Decimal {
char sign; /* 0 for positive, 1 for negative */
char oom; /* True if an OOM is encountered */
char isNull; /* True if holds a NULL rather than a number */
char isInit; /* True upon initialization */
int nDigit; /* Total number of digits */
int nFrac; /* Number of digits to the right of the decimal point */
signed char *a; /* Array of digits. Most significant first. */
};
/*
* * Release memory held by a Decimal , but do not free the object itself .
*/
static void decimal_clear(Decimal *p){
sqlite3_free(p->a);
}
/*
* * Destroy a Decimal object
*/
static void decimal_free(Decimal *p){
if ( p ){
decimal_clear(p);
sqlite3_free(p);
}
}
/*
* * Allocate a new Decimal object initialized to the text in zIn [ ] .
* * Return NULL if any kind of error occurs .
*/
static Decimal *decimalNewFromText(const char *zIn, int n){
Decimal *p = 0 ;
int i;
int iExp = 0 ;
if ( zIn==0 ) goto new_from_text_failed;
p = sqlite3_malloc64( sizeof (*p) );
if ( p==0 ) goto new_from_text_failed;
p->sign = 0 ;
p->oom = 0 ;
p->isInit = 1 ;
p->isNull = 0 ;
p->nDigit = 0 ;
p->nFrac = 0 ;
p->a = sqlite3_malloc64( n+1 );
if ( p->a==0 ) goto new_from_text_failed;
for (i=0 ; IsSpace(zIn[i]); i++){}
if ( zIn[i]=='-' ){
p->sign = 1 ;
i++;
}else if ( zIn[i]=='+' ){
i++;
}
while ( i<n && zIn[i]=='0' ) i++;
while ( i<n ){
char c = zIn[i];
if ( c>='0' && c<='9' ){
p->a[p->nDigit++] = c - '0' ;
}else if ( c=='.' ){
p->nFrac = p->nDigit + 1 ;
}else if ( c=='e' || c=='E' ){
int j = i+1 ;
int neg = 0 ;
if ( j>=n ) break ;
if ( zIn[j]=='-' ){
neg = 1 ;
j++;
}else if ( zIn[j]=='+' ){
j++;
}
while ( j<n && iExp<1000000 ){
if ( zIn[j]>='0' && zIn[j]<='9' ){
iExp = iExp*10 + zIn[j] - '0' ;
}
j++;
}
if ( neg ) iExp = -iExp;
break ;
}
i++;
}
if ( p->nFrac ){
p->nFrac = p->nDigit - (p->nFrac - 1 );
}
if ( iExp>0 ){
if ( p->nFrac>0 ){
if ( iExp<=p->nFrac ){
p->nFrac -= iExp;
iExp = 0 ;
}else {
iExp -= p->nFrac;
p->nFrac = 0 ;
}
}
if ( iExp>0 ){
signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
+ (sqlite3_int64)iExp + 1 );
if ( a==0 ) goto new_from_text_failed;
p->a = a;
memset(p->a+p->nDigit, 0 , iExp);
p->nDigit += iExp;
}
}else if ( iExp<0 ){
int nExtra;
iExp = -iExp;
nExtra = p->nDigit - p->nFrac - 1 ;
if ( nExtra ){
if ( nExtra>=iExp ){
p->nFrac += iExp;
iExp = 0 ;
}else {
iExp -= nExtra;
p->nFrac = p->nDigit - 1 ;
}
}
if ( iExp>0 ){
signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
+ (sqlite3_int64)iExp + 1 );
if ( a==0 ) goto new_from_text_failed;
p->a = a;
memmove(p->a+iExp, p->a, p->nDigit);
memset(p->a, 0 , iExp);
p->nDigit += iExp;
p->nFrac += iExp;
}
}
if ( p->sign ){
for (i=0 ; i<p->nDigit && p->a[i]==0 ; i++){}
if ( i>=p->nDigit ) p->sign = 0 ;
}
if ( p->nDigit>SQLITE_DECIMAL_MAX_DIGIT ) goto new_from_text_failed;
return p;
new_from_text_failed:
if ( p ){
if ( p->a ) sqlite3_free(p->a);
sqlite3_free(p);
}
return 0 ;
}
/* Forward reference */
static Decimal *decimalFromDouble(double );
/*
* * Allocate a new Decimal object from an sqlite3_value . Return a pointer
* * to the new object , or NULL if there is an error . If the pCtx argument
* * is not NULL , then errors are reported on it as well .
* *
* * If the pIn argument is SQLITE_TEXT or SQLITE_INTEGER , it is converted
* * directly into a Decimal . For SQLITE_FLOAT or for SQLITE_BLOB of length
* * 8 bytes , the resulting double value is expanded into its decimal equivalent .
* * If pIn is NULL or if it is a BLOB that is not exactly 8 bytes in length ,
* * then NULL is returned .
*/
static Decimal *decimal_new(
sqlite3_context *pCtx, /* Report error here, if not null */
sqlite3_value *pIn, /* Construct the decimal object from this */
int bTextOnly /* Always interpret pIn as text if true */
){
Decimal *p = 0 ;
int eType = sqlite3_value_type(pIn);
if ( bTextOnly && (eType==SQLITE_FLOAT || eType==SQLITE_BLOB) ){
eType = SQLITE_TEXT;
}
switch ( eType ){
case SQLITE_TEXT:
case SQLITE_INTEGER: {
const char *zIn = (const char *)sqlite3_value_text(pIn);
int n = sqlite3_value_bytes(pIn);
p = decimalNewFromText(zIn, n);
if ( p==0 ) goto new_failed;
break ;
}
case SQLITE_FLOAT: {
p = decimalFromDouble(sqlite3_value_double(pIn));
break ;
}
case SQLITE_BLOB: {
const unsigned char *x;
unsigned int i;
sqlite3_uint64 v = 0 ;
double r;
if ( sqlite3_value_bytes(pIn)!=sizeof (r) ) break ;
x = sqlite3_value_blob(pIn);
for (i=0 ; i<sizeof (r); i++){
v = (v<<8 ) | x[i];
}
memcpy(&r, &v, sizeof (r));
p = decimalFromDouble(r);
break ;
}
case SQLITE_NULL: {
break ;
}
}
return p;
new_failed:
if ( pCtx ) sqlite3_result_error_nomem(pCtx);
sqlite3_free(p);
return 0 ;
}
/*
* * Make the given Decimal the result .
*/
static void decimal_result(sqlite3_context *pCtx, Decimal *p){
char *z;
int i, j;
int n;
if ( p==0 || p->oom ){
sqlite3_result_error_nomem(pCtx);
return ;
}
if ( p->isNull ){
sqlite3_result_null(pCtx);
return ;
}
z = sqlite3_malloc64( (sqlite3_int64)p->nDigit+4 );
if ( z==0 ){
sqlite3_result_error_nomem(pCtx);
return ;
}
i = 0 ;
if ( p->nDigit==0 || (p->nDigit==1 && p->a[0 ]==0 ) ){
p->sign = 0 ;
}
if ( p->sign ){
z[0 ] = '-' ;
i = 1 ;
}
n = p->nDigit - p->nFrac;
if ( n<=0 ){
z[i++] = '0' ;
}
j = 0 ;
while ( n>1 && p->a[j]==0 ){
j++;
n--;
}
while ( n>0 ){
z[i++] = p->a[j] + '0' ;
j++;
n--;
}
if ( p->nFrac ){
z[i++] = '.' ;
do {
z[i++] = p->a[j] + '0' ;
j++;
}while ( j<p->nDigit );
}
z[i] = 0 ;
sqlite3_result_text(pCtx, z, i, sqlite3_free);
}
/*
* * Round a decimal value to N significant digits . N must be positive .
*/
static void decimal_round(Decimal *p, int N){
int i;
int nZero;
if ( N<1 ) return ;
if ( p==0 ) return ;
if ( p->nDigit<=N ) return ;
for (nZero=0 ; nZero<p->nDigit && p->a[nZero]==0 ; nZero++){}
N += nZero;
if ( p->nDigit<=N ) return ;
if ( p->a[N]>4 ){
p->a[N-1 ]++;
for (i=N-1 ; i>0 && p->a[i]>9 ; i--){
p->a[i] = 0 ;
p->a[i-1 ]++;
}
if ( p->a[0 ]>9 ){
p->a[0 ] = 1 ;
p->nFrac--;
}
}
memset(&p->a[N], 0 , p->nDigit - N);
}
/*
* * Make the given Decimal the result in an format similar to ' % + # e ' .
* * In other words , show exponential notation with leading and trailing
* * zeros omitted .
*/
static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p, int N){
char *z; /* The output buffer */
int i; /* Loop counter */
int nZero; /* Number of leading zeros */
int nDigit; /* Number of digits not counting trailing zeros */
int nFrac; /* Digits to the right of the decimal point */
int exp; /* Exponent value */
signed char zero; /* Zero value */
signed char *a; /* Array of digits */
if ( p==0 || p->oom ){
sqlite3_result_error_nomem(pCtx);
return ;
}
if ( p->isNull ){
sqlite3_result_null(pCtx);
return ;
}
if ( N<1 ) N = 0 ;
for (nDigit=p->nDigit; nDigit>N && p->a[nDigit-1 ]==0 ; nDigit--){}
for (nZero=0 ; nZero<nDigit && p->a[nZero]==0 ; nZero++){}
nFrac = p->nFrac + (nDigit - p->nDigit);
nDigit -= nZero;
z = sqlite3_malloc64( (sqlite3_int64)nDigit+20 );
if ( z==0 ){
sqlite3_result_error_nomem(pCtx);
return ;
}
if ( nDigit==0 ){
zero = 0 ;
a = &zero;
nDigit = 1 ;
nFrac = 0 ;
}else {
a = &p->a[nZero];
}
if ( p->sign && nDigit>0 ){
z[0 ] = '-' ;
}else {
z[0 ] = '+' ;
}
z[1 ] = a[0 ]+'0' ;
z[2 ] = '.' ;
if ( nDigit==1 ){
z[3 ] = '0' ;
i = 4 ;
}else {
for (i=1 ; i<nDigit; i++){
z[2 +i] = a[i]+'0' ;
}
i = nDigit+2 ;
}
exp = nDigit - nFrac - 1 ;
sqlite3_snprintf(nDigit+20 -i, &z[i], "e%+03d" , exp);
sqlite3_result_text(pCtx, z, -1 , sqlite3_free);
}
/*
* * Compare to Decimal objects . Return negative , 0 , or positive if the
* * first object is less than , equal to , or greater than the second .
* *
* * Preconditions for this routine :
* *
* * pA ! = 0
* * pA - > isNull = = 0
* * pB ! = 0
* * pB - > isNull = = 0
*/
static int decimal_cmp(Decimal *pA, Decimal *pB){
int nASig, nBSig, rc, n;
while ( pA->nFrac>0 && pA->a[pA->nDigit-1 ]==0 ){
pA->nDigit--;
pA->nFrac--;
}
while ( pB->nFrac>0 && pB->a[pB->nDigit-1 ]==0 ){
pB->nDigit--;
pB->nFrac--;
}
if ( pA->sign!=pB->sign ){
return pA->sign ? -1 : +1 ;
}
if ( pA->sign ){
Decimal *pTemp = pA;
pA = pB;
pB = pTemp;
}
nASig = pA->nDigit - pA->nFrac;
nBSig = pB->nDigit - pB->nFrac;
if ( nASig!=nBSig ){
return nASig - nBSig;
}
n = pA->nDigit;
if ( n>pB->nDigit ) n = pB->nDigit;
rc = memcmp(pA->a, pB->a, n);
if ( rc==0 ){
rc = pA->nDigit - pB->nDigit;
}
return rc;
}
/*
* * SQL Function : decimal_cmp ( X , Y )
* *
* * Return negative , zero , or positive if X is less then , equal to , or
* * greater than Y .
*/
static void decimalCmpFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *pA = 0 , *pB = 0 ;
int rc;
UNUSED_PARAMETER(argc);
pA = decimal_new(context, argv[0 ], 1 );
if ( pA==0 || pA->isNull ) goto cmp_done;
pB = decimal_new(context, argv[1 ], 1 );
if ( pB==0 || pB->isNull ) goto cmp_done;
rc = decimal_cmp(pA, pB);
if ( rc<0 ) rc = -1 ;
else if ( rc>0 ) rc = +1 ;
sqlite3_result_int(context, rc);
cmp_done:
decimal_free(pA);
decimal_free(pB);
}
/*
* * Expand the Decimal so that it has a least nDigit digits and nFrac
* * digits to the right of the decimal point .
*/
static void decimal_expand(Decimal *p, int nDigit, int nFrac){
int nAddSig;
int nAddFrac;
signed char *a;
if ( p==0 ) return ;
nAddFrac = nFrac - p->nFrac;
nAddSig = (nDigit - p->nDigit) - nAddFrac;
if ( nAddFrac==0 && nAddSig==0 ) return ;
if ( nDigit+1 >SQLITE_DECIMAL_MAX_DIGIT ){ p->oom = 1 ; return ; }
a = sqlite3_realloc64(p->a, nDigit+1 );
if ( a==0 ){
p->oom = 1 ;
return ;
}
p->a = a;
if ( nAddSig ){
memmove(p->a+nAddSig, p->a, p->nDigit);
memset(p->a, 0 , nAddSig);
p->nDigit += nAddSig;
}
if ( nAddFrac ){
memset(p->a+p->nDigit, 0 , nAddFrac);
p->nDigit += nAddFrac;
p->nFrac += nAddFrac;
}
}
/*
* * Add the value pB into pA . A : = A + B .
* *
* * Both pA and pB might become denormalized by this routine .
*/
static void decimal_add(Decimal *pA, Decimal *pB){
int nSig, nFrac, nDigit;
int i, rc;
if ( pA==0 ){
return ;
}
if ( pA->oom || pB==0 || pB->oom ){
pA->oom = 1 ;
return ;
}
if ( pA->isNull || pB->isNull ){
pA->isNull = 1 ;
return ;
}
nSig = pA->nDigit - pA->nFrac;
if ( nSig && pA->a[0 ]==0 ) nSig--;
if ( nSig<pB->nDigit-pB->nFrac ){
nSig = pB->nDigit - pB->nFrac;
}
nFrac = pA->nFrac;
if ( nFrac<pB->nFrac ) nFrac = pB->nFrac;
nDigit = nSig + nFrac + 1 ;
decimal_expand(pA, nDigit, nFrac);
decimal_expand(pB, nDigit, nFrac);
if ( pA->oom || pB->oom ){
pA->oom = 1 ;
}else {
if ( pA->sign==pB->sign ){
int carry = 0 ;
for (i=nDigit-1 ; i>=0 ; i--){
int x = pA->a[i] + pB->a[i] + carry;
if ( x>=10 ){
carry = 1 ;
pA->a[i] = x - 10 ;
}else {
carry = 0 ;
pA->a[i] = x;
}
}
}else {
signed char *aA, *aB;
int borrow = 0 ;
rc = memcmp(pA->a, pB->a, nDigit);
if ( rc<0 ){
aA = pB->a;
aB = pA->a;
pA->sign = !pA->sign;
}else {
aA = pA->a;
aB = pB->a;
}
for (i=nDigit-1 ; i>=0 ; i--){
int x = aA[i] - aB[i] - borrow;
if ( x<0 ){
pA->a[i] = x+10 ;
borrow = 1 ;
}else {
pA->a[i] = x;
borrow = 0 ;
}
}
}
}
}
/*
* * Multiply A by B . A : = A * B
* *
* * All significant digits after the decimal point are retained .
* * Trailing zeros after the decimal point are omitted as long as
* * the number of digits after the decimal point is no less than
* * either the number of digits in either input .
*/
static void decimalMul(Decimal *pA, Decimal *pB){
signed char *acc = 0 ;
int i, j, k;
int minFrac;
sqlite3_int64 sumDigit;
if ( pA==0 || pA->oom || pA->isNull
|| pB==0 || pB->oom || pB->isNull
){
goto mul_end;
}
sumDigit = pA->nDigit;
sumDigit += pB->nDigit;
sumDigit += 2 ;
if ( sumDigit>SQLITE_DECIMAL_MAX_DIGIT ){ pA->oom = 1 ; return ; }
acc = sqlite3_malloc64( sumDigit );
if ( acc==0 ){
pA->oom = 1 ;
goto mul_end;
}
memset(acc, 0 , pA->nDigit + pB->nDigit + 2 );
minFrac = pA->nFrac;
if ( pB->nFrac<minFrac ) minFrac = pB->nFrac;
for (i=pA->nDigit-1 ; i>=0 ; i--){
signed char f = pA->a[i];
int carry = 0 , x;
for (j=pB->nDigit-1 , k=i+j+3 ; j>=0 ; j--, k--){
x = acc[k] + f*pB->a[j] + carry;
acc[k] = x%10 ;
carry = x/10 ;
}
x = acc[k] + carry;
acc[k] = x%10 ;
acc[k-1 ] += x/10 ;
}
sqlite3_free(pA->a);
pA->a = acc;
acc = 0 ;
pA->nDigit += pB->nDigit + 2 ;
pA->nFrac += pB->nFrac;
pA->sign ^= pB->sign;
while ( pA->nFrac>minFrac && pA->a[pA->nDigit-1 ]==0 ){
pA->nFrac--;
pA->nDigit--;
}
mul_end:
sqlite3_free(acc);
}
/*
* * Create a new Decimal object that contains an integer power of 2 .
*/
static Decimal *decimalPow2(int N){
Decimal *pA = 0 ; /* The result to be returned */
Decimal *pX = 0 ; /* Multiplier */
if ( N<-20000 || N>20000 ) goto pow2_fault;
pA = decimalNewFromText("1.0" , 3 );
if ( pA==0 || pA->oom ) goto pow2_fault;
if ( N==0 ) return pA;
if ( N>0 ){
pX = decimalNewFromText("2.0" , 3 );
}else {
N = -N;
pX = decimalNewFromText("0.5" , 3 );
}
if ( pX==0 || pX->oom ) goto pow2_fault;
while ( 1 /* Exit by break */ ){
if ( N & 1 ){
decimalMul(pA, pX);
if ( pA->oom ) goto pow2_fault;
}
N >>= 1 ;
if ( N==0 ) break ;
decimalMul(pX, pX);
}
decimal_free(pX);
return pA;
pow2_fault:
decimal_free(pA);
decimal_free(pX);
return 0 ;
}
/*
* * Use an IEEE754 binary64 ( " double " ) to generate a new Decimal object .
*/
static Decimal *decimalFromDouble(double r){
sqlite3_int64 m, a;
int e;
int isNeg;
Decimal *pA;
Decimal *pX;
char zNum[100 ];
if ( r<0 .0 ){
isNeg = 1 ;
r = -r;
}else {
isNeg = 0 ;
}
memcpy(&a,&r,sizeof (a));
if ( a==0 || a==(sqlite3_int64)0 x8000000000000000LL){
e = 0 ;
m = 0 ;
}else {
e = a>>52 ;
m = a & ((((sqlite3_int64)1 )<<52 )-1 );
if ( e==0 ){
m <<= 1 ;
}else {
m |= ((sqlite3_int64)1 )<<52 ;
}
while ( e<1075 && m>0 && (m&1 )==0 ){
m >>= 1 ;
e++;
}
if ( isNeg ) m = -m;
e = e - 1075 ;
if ( e>971 ){
return 0 ; /* A NaN or an Infinity */
}
}
/* At this point m is the integer significand and e is the exponent */
sqlite3_snprintf(sizeof (zNum), zNum, "%lld" , m);
pA = decimalNewFromText(zNum, (int )strlen(zNum));
pX = decimalPow2(e);
decimalMul(pA, pX);
decimal_free(pX);
return pA;
}
/*
* * SQL Function : decimal ( X )
* * OR : decimal_exp ( X )
* *
* * Convert input X into decimal and then back into text .
* *
* * If X is originally a float , then a full decimal expansion of that floating
* * point value is done . Or if X is an 8 - byte blob , it is interpreted
* * as a float and similarly expanded .
* *
* * The decimal_exp ( X ) function returns the result in exponential notation .
* * decimal ( X ) returns a complete decimal , without the e + NNN at the end .
*/
static void decimalFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *p = decimal_new(context, argv[0 ], 0 );
int N;
if ( argc==2 ){
N = sqlite3_value_int(argv[1 ]);
if ( N>0 ) decimal_round(p, N);
}else {
N = 0 ;
}
if ( p ){
if ( sqlite3_user_data(context)!=0 ){
decimal_result_sci(context, p, N);
}else {
decimal_result(context, p);
}
decimal_free(p);
}
}
/*
* * Compare text in decimal order .
*/
static int decimalCollFunc(
void *notUsed,
int nKey1, const void *pKey1,
int nKey2, const void *pKey2
){
const unsigned char *zA = (const unsigned char *)pKey1;
const unsigned char *zB = (const unsigned char *)pKey2;
Decimal *pA = decimalNewFromText((const char *)zA, nKey1);
Decimal *pB = decimalNewFromText((const char *)zB, nKey2);
int rc;
UNUSED_PARAMETER(notUsed);
if ( pA==0 || pB==0 ){
rc = 0 ;
}else {
rc = decimal_cmp(pA, pB);
}
decimal_free(pA);
decimal_free(pB);
return rc;
}
/*
* * SQL Function : decimal_add ( X , Y )
* * decimal_sub ( X , Y )
* *
* * Return the sum or difference of X and Y .
*/
static void decimalAddFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *pA = decimal_new(context, argv[0 ], 1 );
Decimal *pB = decimal_new(context, argv[1 ], 1 );
UNUSED_PARAMETER(argc);
decimal_add(pA, pB);
decimal_result(context, pA);
decimal_free(pA);
decimal_free(pB);
}
static void decimalSubFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *pA = decimal_new(context, argv[0 ], 1 );
Decimal *pB = decimal_new(context, argv[1 ], 1 );
UNUSED_PARAMETER(argc);
if ( pB ){
pB->sign = !pB->sign;
decimal_add(pA, pB);
decimal_result(context, pA);
}
decimal_free(pA);
decimal_free(pB);
}
/* Aggregate function: decimal_sum(X)
* *
* * Works like sum ( ) except that it uses decimal arithmetic for unlimited
* * precision .
*/
static void decimalSumStep(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *p;
Decimal *pArg;
UNUSED_PARAMETER(argc);
p = sqlite3_aggregate_context(context, sizeof (*p));
if ( p==0 ) return ;
if ( !p->isInit ){
p->isInit = 1 ;
p->a = sqlite3_malloc64(2 );
if ( p->a==0 ){
p->oom = 1 ;
}else {
p->a[0 ] = 0 ;
}
p->nDigit = 1 ;
p->nFrac = 0 ;
}
if ( sqlite3_value_type(argv[0 ])==SQLITE_NULL ) return ;
pArg = decimal_new(context, argv[0 ], 1 );
decimal_add(p, pArg);
decimal_free(pArg);
}
static void decimalSumInverse(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *p;
Decimal *pArg;
UNUSED_PARAMETER(argc);
p = sqlite3_aggregate_context(context, sizeof (*p));
if ( p==0 ) return ;
if ( sqlite3_value_type(argv[0 ])==SQLITE_NULL ) return ;
pArg = decimal_new(context, argv[0 ], 1 );
if ( pArg ) pArg->sign = !pArg->sign;
decimal_add(p, pArg);
decimal_free(pArg);
}
static void decimalSumValue(sqlite3_context *context){
Decimal *p = sqlite3_aggregate_context(context, 0 );
if ( p==0 ) return ;
decimal_result(context, p);
}
static void decimalSumFinalize(sqlite3_context *context){
Decimal *p = sqlite3_aggregate_context(context, 0 );
if ( p==0 ) return ;
decimal_result(context, p);
decimal_clear(p);
}
/*
* * SQL Function : decimal_mul ( X , Y )
* *
* * Return the product of X and Y .
*/
static void decimalMulFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Decimal *pA = decimal_new(context, argv[0 ], 1 );
Decimal *pB = decimal_new(context, argv[1 ], 1 );
UNUSED_PARAMETER(argc);
if ( pA==0 || pA->oom || pA->isNull
|| pB==0 || pB->oom || pB->isNull
){
goto mul_end;
}
decimalMul(pA, pB);
if ( pA->oom ){
goto mul_end;
}
decimal_result(context, pA);
mul_end:
decimal_free(pA);
decimal_free(pB);
}
/*
* * SQL Function : decimal_pow2 ( N )
* *
* * Return the N - th power of 2 . N must be an integer .
*/
static void decimalPow2Func(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if ( sqlite3_value_type(argv[0 ])==SQLITE_INTEGER ){
Decimal *pA = decimalPow2(sqlite3_value_int(argv[0 ]));
decimal_result_sci(context, pA, 0 );
decimal_free(pA);
}
}
#ifdef _WIN32
#endif
int sqlite3_decimal_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
static const struct {
const char *zFuncName;
int nArg;
int iArg;
void (*xFunc)(sqlite3_context*,int ,sqlite3_value**);
} aFunc[] = {
{ "decimal" , 1 , 0 , decimalFunc },
{ "decimal" , 2 , 0 , decimalFunc },
{ "decimal_exp" , 1 , 1 , decimalFunc },
{ "decimal_exp" , 2 , 1 , decimalFunc },
{ "decimal_cmp" , 2 , 0 , decimalCmpFunc },
{ "decimal_add" , 2 , 0 , decimalAddFunc },
{ "decimal_sub" , 2 , 0 , decimalSubFunc },
{ "decimal_mul" , 2 , 0 , decimalMulFunc },
{ "decimal_pow2" , 1 , 0 , decimalPow2Func },
};
unsigned int i;
(void )pzErrMsg; /* Unused parameter */
SQLITE_EXTENSION_INIT2(pApi);
for (i=0 ; i<(int )(sizeof (aFunc)/sizeof (aFunc[0 ])) && rc==SQLITE_OK; i++){
rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
aFunc[i].iArg ? db : 0 , aFunc[i].xFunc, 0 , 0 );
}
if ( rc==SQLITE_OK ){
rc = sqlite3_create_window_function(db, "decimal_sum" , 1 ,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, 0 ,
decimalSumStep, decimalSumFinalize,
decimalSumValue, decimalSumInverse, 0 );
}
if ( rc==SQLITE_OK ){
rc = sqlite3_create_collation(db, "decimal" , SQLITE_UTF8,
0 , decimalCollFunc);
}
return rc;
}
/************************* End ext/misc/decimal.c ********************/
/************************* Begin ext/misc/base64.c ******************/
/*
* * 2022 - 11 - 18
* *
* * 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 is a SQLite extension for converting in either direction
* * between a ( binary ) blob and base64 text . Base64 can transit a
* * sane USASCII channel unmolested . It also plays nicely in CSV or
* * written as TCL brace - enclosed literals or SQL string literals ,
* * and can be used unmodified in XML - like documents .
* *
* * This is an independent implementation of conversions specified in
* * RFC 4648 , done on the above date by the author ( Larry Brasfield )
* * who thereby has the right to put this into the public domain .
* *
* * The conversions meet RFC 4648 requirements , provided that this
* * C source specifies that line - feeds are included in the encoded
* * data to limit visible line lengths to 72 characters and to
* * terminate any encoded blob having non - zero length .
* *
* * Length limitations are not imposed except that the runtime
* * SQLite string or blob length limits are respected . Otherwise ,
* * any length binary sequence can be represented and recovered .
* * Generated base64 sequences , with their line - feeds included ,
* * can be concatenated ; the result converted back to binary will
* * be the concatenation of the represented binary sequences .
* *
* * This SQLite3 extension creates a function , base64 ( x ) , which
* * either : converts text x containing base64 to a returned blob ;
* * or converts a blob x to returned text containing base64 . An
* * error will be thrown for other input argument types .
* *
* * This code relies on UTF - 8 encoding only with respect to the
* * meaning of the first 128 ( 7 - bit ) codes matching that of USASCII .
* * It will fail miserably if somehow made to try to convert EBCDIC .
* * Because it is table - driven , it could be enhanced to handle that ,
* * but the world and SQLite have moved on from that anachronism .
* *
* * To build the extension :
* * Set shell variable SQDIR = < your favorite SQLite checkout directory >
* * * Nix : gcc - O2 - shared - I $ SQDIR - fPIC - o base64 . so base64 . c
* * OSX : gcc - O2 - dynamiclib - fPIC - I $ SQDIR - o base64 . dylib base64 . c
* * Win32 : gcc - O2 - shared - I % SQDIR % - o base64 . dll base64 . c
* * Win32 : cl / Os - I % SQDIR % base64 . c - link - dll - out : base64 . dll
*/
#include <assert.h>
/* #include "sqlite3ext.h" */
#ifndef deliberate_fall_through
/* Quiet some compilers about some of our intentional code. */
# if GCC_VERSION>=7000000
# define deliberate_fall_through __attribute__((fallthrough));
# else
# define deliberate_fall_through
# endif
#endif
SQLITE_EXTENSION_INIT1;
#define PC 0 x80 /* pad character */
#define WS 0 x81 /* whitespace */
#define ND 0 x82 /* Not above or digit-value */
#define PAD_CHAR '='
#ifndef U8_TYPEDEF
/* typedef unsigned char u8; */
#define U8_TYPEDEF
#endif
/* Decoding table, ASCII (7-bit) value to base 64 digit value or other */
static const u8 b64DigitValues[128 ] = {
/* HT LF VT FF CR */
ND,ND,ND,ND, ND,ND,ND,ND, ND,WS,WS,WS, WS,WS,ND,ND,
/* US */
ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND,
/*sp + / */
WS,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,62 , ND,ND,ND,63 ,
/* 0 1 5 9 = */
52 ,53 ,54 ,55 , 56 ,57 ,58 ,59 , 60 ,61 ,ND,ND, ND,PC,ND,ND,
/* A O */
ND, 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ,10 , 11 ,12 ,13 ,14 ,
/* P Z */
15 ,16 ,17 ,18 , 19 ,20 ,21 ,22 , 23 ,24 ,25 ,ND, ND,ND,ND,ND,
/* a o */
ND,26 ,27 ,28 , 29 ,30 ,31 ,32 , 33 ,34 ,35 ,36 , 37 ,38 ,39 ,40 ,
/* p z */
41 ,42 ,43 ,44 , 45 ,46 ,47 ,48 , 49 ,50 ,51 ,ND, ND,ND,ND,ND
};
static const char b64Numerals[64 +1 ]
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;
#define BX_DV_PROTO(c) \
((((u8)(c))<0 x80)? (u8)(b64DigitValues[(u8)(c)]) : 0 x80)
#define IS_BX_DIGIT(bdp) (((u8)(bdp))<0 x80)
#define IS_BX_WS(bdp) ((bdp)==WS)
#define IS_BX_PAD(bdp) ((bdp)==PC)
#define BX_NUMERAL(dv) (b64Numerals[(u8)(dv)])
/* Width of base64 lines. Should be an integer multiple of 4. */
#define B64_DARK_MAX 72
/* Encode a byte buffer into base64 text with linefeeds appended to limit
* * encoded group lengths to B64_DARK_MAX or to terminate the last group .
*/
static char * toBase64( u8 *pIn, int nbIn, char *pOut ){
int nCol = 0 ;
while ( nbIn >= 3 ){
/* Do the bit-shuffle, exploiting unsigned input to avoid masking. */
pOut[0 ] = BX_NUMERAL(pIn[0 ]>>2 );
pOut[1 ] = BX_NUMERAL(((pIn[0 ]<<4 )|(pIn[1 ]>>4 ))&0 x3f);
pOut[2 ] = BX_NUMERAL(((pIn[1 ]&0 xf)<<2 )|(pIn[2 ]>>6 ));
pOut[3 ] = BX_NUMERAL(pIn[2 ]&0 x3f);
pOut += 4 ;
nbIn -= 3 ;
pIn += 3 ;
if ( (nCol += 4 )>=B64_DARK_MAX || nbIn<=0 ){
*pOut++ = '\n' ;
nCol = 0 ;
}
}
if ( nbIn > 0 ){
signed char nco = nbIn+1 ;
int nbe;
unsigned long qv = *pIn++;
for ( nbe=1 ; nbe<3 ; ++nbe ){
qv <<= 8 ;
if ( nbe<nbIn ) qv |= *pIn++;
}
for ( nbe=3 ; nbe>=0 ; --nbe ){
char ce = (nbe<nco)? BX_NUMERAL((u8)(qv & 0 x3f)) : PAD_CHAR;
qv >>= 6 ;
pOut[nbe] = ce;
}
pOut += 4 ;
*pOut++ = '\n' ;
}
*pOut = 0 ;
return pOut;
}
/* Skip over text which is not base64 numeral(s). */
static char * skipNonB64( char *s, int nc ){
char c;
while ( nc-- > 0 && (c = *s) && !IS_BX_DIGIT(BX_DV_PROTO(c)) ) ++s;
return s;
}
/* Decode base64 text into a byte buffer. */
static u8* fromBase64( char *pIn, int ncIn, u8 *pOut ){
if ( ncIn>0 && pIn[ncIn-1 ]=='\n' ) --ncIn;
while ( ncIn>0 && *pIn!=PAD_CHAR ){
static signed char nboi[] = { 0 , 0 , 1 , 2 , 3 };
char *pUse = skipNonB64(pIn, ncIn);
unsigned long qv = 0 L;
int nti, nbo, nac;
ncIn -= (pUse - pIn);
pIn = pUse;
nti = (ncIn>4 )? 4 : ncIn;
ncIn -= nti;
nbo = nboi[nti];
if ( nbo==0 ) break ;
for ( nac=0 ; nac<4 ; ++nac ){
char c = (nac<nti)? *pIn++ : b64Numerals[0 ];
u8 bdp = BX_DV_PROTO(c);
switch ( bdp ){
case ND:
/* Treat dark non-digits as pad, but they terminate decode too. */
ncIn = 0 ;
deliberate_fall_through; /* FALLTHRU */
case WS:
/* Treat whitespace as pad and terminate this group.*/
nti = nac;
deliberate_fall_through; /* FALLTHRU */
case PC:
bdp = 0 ;
--nbo;
deliberate_fall_through; /* FALLTHRU */
default : /* bdp is the digit value. */
qv = qv<<6 | bdp;
break ;
}
}
switch ( nbo ){
case 3 :
pOut[2 ] = (qv) & 0 xff;
deliberate_fall_through; /* FALLTHRU */
case 2 :
pOut[1 ] = (qv>>8 ) & 0 xff;
deliberate_fall_through; /* FALLTHRU */
case 1 :
pOut[0 ] = (qv>>16 ) & 0 xff;
break ;
}
pOut += nbo;
}
return pOut;
}
/* This function does the work for the SQLite base64(x) UDF. */
static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_int64 nb;
sqlite3_int64 nv = sqlite3_value_bytes(av[0 ]);
sqlite3_int64 nc;
int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
SQLITE_LIMIT_LENGTH, -1 );
char *cBuf;
u8 *bBuf;
assert(na==1 );
switch ( sqlite3_value_type(av[0 ]) ){
case SQLITE_BLOB:
nb = nv;
nc = 4 *((nv+2 )/3 ); /* quads needed */
nc += (nc+(B64_DARK_MAX-1 ))/B64_DARK_MAX + 1 ; /* LFs and a 0-terminator */
if ( nvMax < nc ){
sqlite3_result_error(context, "blob expanded to base64 too big" , -1 );
return ;
}
bBuf = (u8*)sqlite3_value_blob(av[0 ]);
if ( !bBuf ){
if ( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){
goto memFail;
}
sqlite3_result_text(context,"" ,-1 ,SQLITE_STATIC);
break ;
}
cBuf = sqlite3_malloc64(nc);
if ( !cBuf ) goto memFail;
nc = (int )(toBase64(bBuf, nb, cBuf) - cBuf);
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
break ;
case SQLITE_TEXT:
nc = nv;
nb = 3 *((nv+3 )/4 ); /* may overestimate due to LF and padding */
if ( nvMax < nb ){
sqlite3_result_error(context, "blob from base64 may be too big" , -1 );
return ;
}else if ( nb<1 ){
nb = 1 ;
}
cBuf = (char *)sqlite3_value_text(av[0 ]);
if ( !cBuf ){
if ( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){
goto memFail;
}
sqlite3_result_zeroblob(context, 0 );
break ;
}
bBuf = sqlite3_malloc64(nb);
if ( !bBuf ) goto memFail;
nb = (int )(fromBase64(cBuf, nc, bBuf) - bBuf);
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
break ;
default :
sqlite3_result_error(context, "base64 accepts only blob or text" , -1 );
return ;
}
return ;
memFail:
sqlite3_result_error(context, "base64 OOM" , -1 );
}
/*
* * Establish linkage to running SQLite library .
*/
#ifndef SQLITE_SHELL_EXTFUNCS
#ifdef _WIN32
#endif
int sqlite3_base64_init
#else
static int sqlite3_base64_init
#endif
(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){
SQLITE_EXTENSION_INIT2(pApi);
(void )pzErr;
return sqlite3_create_function
(db, "base64" , 1 ,
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8,
0 , base64, 0 , 0 );
}
/*
* * Define some macros to allow this extension to be built into the shell
* * conveniently , in conjunction with use of SQLITE_SHELL_EXTFUNCS . This
* * allows shell . c , as distributed , to have this extension built in .
*/
#define BASE64_INIT(db) sqlite3_base64_init(db, 0 , 0 )
#define BASE64_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */
/************************* End ext/misc/base64.c ********************/
/************************* Begin ext/misc/base85.c ******************/
/*
* * 2022 - 11 - 16
* *
* * The author disclaims copyright to this source code . In place of
* * a legal notice , here is a blessing :
* *
* * May you do good and not evil .
* * May you find forgiveness for yourself and forgive others .
* * May you share freely , never taking more than you give .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* * This is a utility for converting binary to base85 or vice - versa .
* * It can be built as a standalone program or an SQLite3 extension .
* *
* * Much like base64 representations , base85 can be sent through a
* * sane USASCII channel unmolested . It also plays nicely in CSV or
* * written as TCL brace - enclosed literals or SQL string literals .
* * It is not suited for unmodified use in XML - like documents .
* *
* * The encoding used resembles Ascii85 , but was devised by the author
* * ( Larry Brasfield ) before Mozilla , Adobe , ZMODEM or other Ascii85
* * variant sources existed , in the 1984 timeframe on a VAX mainframe .
* * Further , this is an independent implementation of a base85 system .
* * Hence , the author has rightfully put this into the public domain .
* *
* * Base85 numerals are taken from the set of 7 - bit USASCII codes ,
* * excluding control characters and Space ! " ' ( ) { | } ~ Del
* * in code order representing digit values 0 to 84 ( base 10 . )
* *
* * Groups of 4 bytes , interpreted as big - endian 32 - bit values ,
* * are represented as 5 - digit base85 numbers with MS to LS digit
* * order . Groups of 1 - 3 bytes are represented with 2 - 4 digits ,
* * still big - endian but 8 - 24 bit values . ( Using big - endian yields
* * the simplest transition to byte groups smaller than 4 bytes .
* * These byte groups can also be considered base - 256 numbers . )
* * Groups of 0 bytes are represented with 0 digits and vice - versa .
* * No pad characters are used ; Encoded base85 numeral sequence
* * ( aka " group " ) length maps 1 - to - 1 to the decoded binary length .
* *
* * Any character not in the base85 numeral set delimits groups .
* * When base85 is streamed or stored in containers of indefinite
* * size , newline is used to separate it into sub - sequences of no
* * more than 80 digits so that fgets ( ) can be used to read it .
* *
* * Length limitations are not imposed except that the runtime
* * SQLite string or blob length limits are respected . Otherwise ,
* * any length binary sequence can be represented and recovered .
* * Base85 sequences can be concatenated by separating them with
* * a non - base85 character ; the conversion to binary will then
* * be the concatenation of the represented binary sequences .
* * The standalone program either converts base85 on stdin to create
* * a binary file or converts a binary file to base85 on stdout .
* * Read or make it blurt its help for invocation details .
* *
* * The SQLite3 extension creates a function , base85 ( x ) , which will
* * either convert text base85 to a blob or a blob to text base85
* * and return the result ( or throw an error for other types . )
* * Unless built with OMIT_BASE85_CHECKER defined , it also creates a
* * function , is_base85 ( t ) , which returns 1 iff the text t contains
* * nothing other than base85 numerals and whitespace , or 0 otherwise .
* *
* * To build the extension :
* * Set shell variable SQDIR = < your favorite SQLite checkout directory >
* * and variable OPTS to - DOMIT_BASE85_CHECKER if is_base85 ( ) unwanted .
* * * Nix : gcc - O2 - shared - I $ SQDIR $ OPTS - fPIC - o base85 . so base85 . c
* * OSX : gcc - O2 - dynamiclib - fPIC - I $ SQDIR $ OPTS - o base85 . dylib base85 . c
* * Win32 : gcc - O2 - shared - I % SQDIR % % OPTS % - o base85 . dll base85 . c
* * Win32 : cl / Os - I % SQDIR % % OPTS % base85 . c - link - dll - out : base85 . dll
* *
* * To build the standalone program , define PP symbol BASE85_STANDALONE . Eg .
* * * Nix or OSX : gcc - O2 - DBASE85_STANDALONE base85 . c - o base85
* * Win32 : gcc - O2 - DBASE85_STANDALONE - o base85 . exe base85 . c
* * Win32 : cl / Os / MD - DBASE85_STANDALONE base85 . c
*/
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <assert.h>
#ifndef OMIT_BASE85_CHECKER
# include <ctype.h>
#endif
#ifndef BASE85_STANDALONE
/* # include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1;
#else
# ifdef _WIN32
# include <io.h>
# include <fcntl.h>
# else
# define setmode(fd,m)
# endif
static char *zHelp =
"Usage: base85 <dirFlag> <binFile>\n"
" <dirFlag> is either -r to read or -w to write <binFile>,\n"
" content to be converted to/from base85 on stdout/stdin.\n"
" <binFile> names a binary file to be rendered or created.\n"
" Or, the name '-' refers to the stdin or stdout stream.\n"
;
static void sayHelp(){
printf("%s" , zHelp);
}
#endif
#ifndef U8_TYPEDEF
/* typedef unsigned char u8; */
#define U8_TYPEDEF
#endif
/* Classify c according to interval within USASCII set w.r.t. base85
* Values of 1 and 3 are base85 numerals . Values of 0 , 2 , or 4 are not .
*/
#define B85_CLASS( c ) (((c)>='#' )+((c)>'&' )+((c)>='*' )+((c)>'z' ))
/* Provide digitValue to b85Numeral offset as a function of above class. */
static u8 b85_cOffset[] = { 0 , '#' , 0 , '*' -4 , 0 };
#define B85_DNOS( c ) b85_cOffset[B85_CLASS(c)]
/* Say whether c is a base85 numeral. */
#define IS_B85( c ) (B85_CLASS(c) & 1 )
#if 0 /* Not used, */
static u8 base85DigitValue( char c ){
u8 dv = (u8)(c - '#' );
if ( dv>87 ) return 0 xff;
return (dv > 3 )? dv-3 : dv;
}
#endif
/* Width of base64 lines. Should be an integer multiple of 5. */
#define B85_DARK_MAX 80
static char * skipNonB85( char *s, int nc ){
char c;
while ( nc-- > 0 && (c = *s) && !IS_B85(c) ) ++s;
return s;
}
/* Convert small integer, known to be in 0..84 inclusive, to base85 numeral.
* Do not use the macro form with argument expression having a side-effect.*/
#if 0
static char base85Numeral( u8 b ){
return (b < 4 )? (char )(b + '#' ) : (char )(b - 4 + '*' );
}
#else
# define base85Numeral( dn )\
((char )(((dn) < 4 )? (char )((dn) + '#' ) : (char )((dn) - 4 + '*' )))
#endif
static char *putcs(char *pc, char *s){
char c;
while ( (c = *s++)!=0 ) *pc++ = c;
return pc;
}
/* Encode a byte buffer into base85 text. If pSep!=0, it's a C string
* * to be appended to encoded groups to limit their length to B85_DARK_MAX
* * or to terminate the last group ( to aid concatenation . )
*/
static char * toBase85( u8 *pIn, int nbIn, char *pOut, char *pSep ){
int nCol = 0 ;
while ( nbIn >= 4 ){
int nco = 5 ;
unsigned long qbv = (((unsigned long )pIn[0 ])<<24 ) |
(pIn[1 ]<<16 ) | (pIn[2 ]<<8 ) | pIn[3 ];
while ( nco > 0 ){
unsigned nqv = (unsigned )(qbv/85 UL);
unsigned char dv = qbv - 85 UL*nqv;
qbv = nqv;
pOut[--nco] = base85Numeral(dv);
}
nbIn -= 4 ;
pIn += 4 ;
pOut += 5 ;
if ( pSep && (nCol += 5 )>=B85_DARK_MAX ){
pOut = putcs(pOut, pSep);
nCol = 0 ;
}
}
if ( nbIn > 0 ){
int nco = nbIn + 1 ;
unsigned long qv = *pIn++;
int nbe = 1 ;
while ( nbe++ < nbIn ){
qv = (qv<<8 ) | *pIn++;
}
nCol += nco;
while ( nco > 0 ){
u8 dv = (u8)(qv % 85 );
qv /= 85 ;
pOut[--nco] = base85Numeral(dv);
}
pOut += (nbIn+1 );
}
if ( pSep && nCol>0 ) pOut = putcs(pOut, pSep);
*pOut = 0 ;
return pOut;
}
/* Decode base85 text into a byte buffer. */
static u8* fromBase85( char *pIn, int ncIn, u8 *pOut ){
if ( ncIn>0 && pIn[ncIn-1 ]=='\n' ) --ncIn;
while ( ncIn>0 ){
static signed char nboi[] = { 0 , 0 , 1 , 2 , 3 , 4 };
char *pUse = skipNonB85(pIn, ncIn);
unsigned long qv = 0 L;
int nti, nbo;
ncIn -= (pUse - pIn);
pIn = pUse;
nti = (ncIn>5 )? 5 : ncIn;
nbo = nboi[nti];
if ( nbo==0 ) break ;
while ( nti>0 ){
char c = *pIn++;
u8 cdo = B85_DNOS(c);
--ncIn;
if ( cdo==0 ) break ;
qv = 85 * qv + (c - cdo);
--nti;
}
nbo -= nti; /* Adjust for early (non-digit) end of group. */
switch ( nbo ){
case 4 :
*pOut++ = (qv >> 24 )&0 xff;
/* FALLTHRU */
case 3 :
*pOut++ = (qv >> 16 )&0 xff;
/* FALLTHRU */
case 2 :
*pOut++ = (qv >> 8 )&0 xff;
/* FALLTHRU */
case 1 :
*pOut++ = qv&0 xff;
/* FALLTHRU */
case 0 :
break ;
}
}
return pOut;
}
#ifndef OMIT_BASE85_CHECKER
/* Say whether input char sequence is all (base85 and/or whitespace).*/
static int allBase85( char *p, int len ){
char c;
while ( len-- > 0 && (c = *p++) != 0 ){
if ( !IS_B85(c) && !isspace(c) ) return 0 ;
}
return 1 ;
}
#endif
#ifndef BASE85_STANDALONE
#ifndef OMIT_BASE85_CHECKER
/* This function does the work for the SQLite is_base85(t) UDF. */
static void is_base85(sqlite3_context *context, int na, sqlite3_value *av[]){
assert(na==1 );
switch ( sqlite3_value_type(av[0 ]) ){
case SQLITE_TEXT:
{
int rv = allBase85( (char *)sqlite3_value_text(av[0 ]),
sqlite3_value_bytes(av[0 ]) );
sqlite3_result_int(context, rv);
}
break ;
case SQLITE_NULL:
sqlite3_result_null(context);
break ;
default :
sqlite3_result_error(context, "is_base85 accepts only text or NULL" , -1 );
return ;
}
}
#endif
/* This function does the work for the SQLite base85(x) UDF. */
static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_int64 nb, nc, nv = sqlite3_value_bytes(av[0 ]);
int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
SQLITE_LIMIT_LENGTH, -1 );
char *cBuf;
u8 *bBuf;
assert(na==1 );
switch ( sqlite3_value_type(av[0 ]) ){
case SQLITE_BLOB:
nb = nv;
/* ulongs tail newlines tailenc+nul*/
nc = 5 *(nv/4 ) + nv%4 + nv/64 +1 + 2 ;
if ( nvMax < nc ){
sqlite3_result_error(context, "blob expanded to base85 too big" , -1 );
return ;
}
bBuf = (u8*)sqlite3_value_blob(av[0 ]);
if ( !bBuf ){
if ( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){
goto memFail;
}
sqlite3_result_text(context,"" ,-1 ,SQLITE_STATIC);
break ;
}
cBuf = sqlite3_malloc64(nc);
if ( !cBuf ) goto memFail;
nc = (int )(toBase85(bBuf, nb, cBuf, "\n" ) - cBuf);
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
break ;
case SQLITE_TEXT:
nc = nv;
nb = 4 *(nv/5 ) + nv%5 ; /* may overestimate */
if ( nvMax < nb ){
sqlite3_result_error(context, "blob from base85 may be too big" , -1 );
return ;
}else if ( nb<1 ){
nb = 1 ;
}
cBuf = (char *)sqlite3_value_text(av[0 ]);
if ( !cBuf ){
if ( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){
goto memFail;
}
sqlite3_result_zeroblob(context, 0 );
break ;
}
bBuf = sqlite3_malloc64(nb);
if ( !bBuf ) goto memFail;
nb = (int )(fromBase85(cBuf, nc, bBuf) - bBuf);
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
break ;
default :
sqlite3_result_error(context, "base85 accepts only blob or text." , -1 );
return ;
}
return ;
memFail:
sqlite3_result_error(context, "base85 OOM" , -1 );
}
/*
* * Establish linkage to running SQLite library .
*/
#ifndef SQLITE_SHELL_EXTFUNCS
#ifdef _WIN32
#endif
int sqlite3_base85_init
#else
static int sqlite3_base85_init
#endif
(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){
SQLITE_EXTENSION_INIT2(pApi);
(void )pzErr;
#ifndef OMIT_BASE85_CHECKER
{
int rc = sqlite3_create_function
(db, "is_base85" , 1 ,
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_UTF8,
0 , is_base85, 0 , 0 );
if ( rc!=SQLITE_OK ) return rc;
}
#endif
return sqlite3_create_function
(db, "base85" , 1 ,
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8,
0 , base85, 0 , 0 );
}
/*
* * Define some macros to allow this extension to be built into the shell
* * conveniently , in conjunction with use of SQLITE_SHELL_EXTFUNCS . This
* * allows shell . c , as distributed , to have this extension built in .
*/
# define BASE85_INIT(db) sqlite3_base85_init(db, 0 , 0 )
# define BASE85_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */
#else /* standalone program */
int main(int na, char *av[]){
int cin;
int rc = 0 ;
u8 bBuf[4 *(B85_DARK_MAX/5 )];
char cBuf[5 *(sizeof (bBuf)/4 )+2 ];
size_t nio;
# ifndef OMIT_BASE85_CHECKER
int b85Clean = 1 ;
# endif
char rw;
FILE *fb = 0 , *foc = 0 ;
char fmode[3 ] = "xb" ;
if ( na < 3 || av[1 ][0 ]!='-' || (rw = av[1 ][1 ])==0 || (rw!='r' && rw!='w' ) ){
sayHelp();
return 0 ;
}
fmode[0 ] = rw;
if ( av[2 ][0 ]=='-' && av[2 ][1 ]==0 ){
switch ( rw ){
case 'r' :
fb = stdin;
setmode(fileno(stdin), O_BINARY);
break ;
case 'w' :
fb = stdout;
setmode(fileno(stdout), O_BINARY);
break ;
}
}else {
fb = fopen(av[2 ], fmode);
foc = fb;
}
if ( !fb ){
fprintf(stderr, "Cannot open %s for %c\n" , av[2 ], rw);
rc = 1 ;
}else {
switch ( rw ){
case 'r' :
while ( (nio = fread( bBuf, 1 , sizeof (bBuf), fb))>0 ){
toBase85( bBuf, (int )nio, cBuf, 0 );
fprintf(stdout, "%s\n" , cBuf);
}
break ;
case 'w' :
while ( 0 != fgets(cBuf, sizeof (cBuf), stdin) ){
int nc = strlen(cBuf);
size_t nbo = fromBase85( cBuf, nc, bBuf ) - bBuf;
if ( 1 != fwrite(bBuf, nbo, 1 , fb) ) rc = 1 ;
#ifndef OMIT_BASE85_CHECKER
b85Clean &= allBase85( cBuf, nc );
#endif
}
break ;
default :
sayHelp();
rc = 1 ;
}
if ( foc ) fclose(foc);
}
# ifndef OMIT_BASE85_CHECKER
if ( !b85Clean ){
fprintf(stderr, "Base85 input had non-base85 dark or control content.\n" );
}
# endif
return rc;
}
#endif
/************************* End ext/misc/base85.c ********************/
/************************* Begin ext/misc/ieee754.c ******************/
/*
* * 2013 - 04 - 17
* *
* * 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 for the exact display
* * and input of IEEE754 Binary64 floating - point numbers .
* *
* * ieee754 ( X )
* * ieee754 ( Y , Z )
* *
* * In the first form , the value X should be a floating - point number .
* * The function will return a string of the form ' ieee754 ( Y , Z ) ' where
* * Y and Z are integers such that X = = Y * pow ( 2 , Z ) .
* *
* * In the second form , Y and Z are integers which are the mantissa and
* * base - 2 exponent of a new floating point number . The function returns
* * a floating - point value equal to Y * pow ( 2 , Z ) .
* *
* * Examples :
* *
* * ieee754 ( 2 . 0 ) - > ' ieee754 ( 2 , 0 ) '
* * ieee754 ( 45 . 25 ) - > ' ieee754 ( 181 , - 2 ) '
* * ieee754 ( 2 , 0 ) - > 2 . 0
* * ieee754 ( 181 , - 2 ) - > 45 . 25
* *
* * Two additional functions break apart the one - argument ieee754 ( )
* * result into separate integer values :
* *
* * ieee754_mantissa ( 45 . 25 ) - > 181
* * ieee754_exponent ( 45 . 25 ) - > - 2
* *
* * These functions convert binary64 numbers into blobs and back again .
* *
* * ieee754_from_blob ( x ' 3 ff0000000000000 ' ) - > 1 . 0
* * ieee754_to_blob ( 1 . 0 ) - > x ' 3 ff0000000000000 '
* *
* * In all single - argument functions , if the argument is an 8 - byte blob
* * then that blob is interpreted as a big - endian binary64 value .
* *
* *
* * EXACT DECIMAL REPRESENTATION OF BINARY64 VALUES
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
* * This extension in combination with the separate ' decimal ' extension
* * can be used to compute the exact decimal representation of binary64
* * values . To begin , first compute a table of exponent values :
* *
* * CREATE TABLE pow2 ( x INTEGER PRIMARY KEY , v TEXT ) ;
* * WITH RECURSIVE c ( x , v ) AS (
* * VALUES ( 0 , ' 1 ' )
* * UNION ALL
* * SELECT x + 1 , decimal_mul ( v , ' 2 ' ) FROM c WHERE x + 1 < = 971
* * ) INSERT INTO pow2 ( x , v ) SELECT x , v FROM c ;
* * WITH RECURSIVE c ( x , v ) AS (
* * VALUES ( - 1 , ' 0 . 5 ' )
* * UNION ALL
* * SELECT x - 1 , decimal_mul ( v , ' 0 . 5 ' ) FROM c WHERE x - 1 > = - 1075
* * ) INSERT INTO pow2 ( x , v ) SELECT x , v FROM c ;
* *
* * Then , to compute the exact decimal representation of a floating
* * point value ( the value 47 . 49 is used in the example ) do :
* *
* * WITH c ( n ) AS ( VALUES ( 47 . 49 ) )
* * - - - - - - - - - - - - - - - ^ ^ ^ ^ ^ - - - - Replace with whatever you want
* * SELECT decimal_mul ( ieee754_mantissa ( c . n ) , pow2 . v )
* * FROM pow2 , c WHERE pow2 . x = ieee754_exponent ( c . n ) ;
* *
* * Here is a query to show various boundry values for the binary64
* * number format :
* *
* * WITH c ( name , bin ) AS ( VALUES
* * ( ' minimum positive value ' , x ' 0000000000000001 ' ) ,
* * ( ' maximum subnormal value ' , x ' 000 fffffffffffff ' ) ,
* * ( ' minimum positive normal value ' , x ' 0010000000000000 ' ) ,
* * ( ' maximum value ' , x ' 7 fefffffffffffff ' ) )
* * SELECT c . name , decimal_mul ( ieee754_mantissa ( c . bin ) , pow2 . v )
* * FROM pow2 , c WHERE pow2 . x = ieee754_exponent ( c . bin ) ;
* *
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
/* Mark a function parameter as unused, to suppress nuisance compiler
** warnings. */
#ifndef UNUSED_PARAMETER
# define UNUSED_PARAMETER(X) (void )(X)
#endif
/*
* * Implementation of the ieee754 ( ) function
*/
static void ieee754func(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
if ( argc==1 ){
sqlite3_int64 m, a;
double r;
int e;
int isNeg;
char zResult[100 ];
assert( sizeof (m)==sizeof (r) );
if ( sqlite3_value_type(argv[0 ])==SQLITE_BLOB
&& sqlite3_value_bytes(argv[0 ])==sizeof (r)
){
const unsigned char *x = sqlite3_value_blob(argv[0 ]);
unsigned int i;
sqlite3_uint64 v = 0 ;
for (i=0 ; i<sizeof (r); i++){
v = (v<<8 ) | x[i];
}
memcpy(&r, &v, sizeof (r));
}else {
r = sqlite3_value_double(argv[0 ]);
}
if ( r<0 .0 ){
isNeg = 1 ;
r = -r;
}else {
isNeg = 0 ;
}
memcpy(&a,&r,sizeof (a));
if ( a==0 ){
e = 0 ;
m = 0 ;
}else if ( a==(sqlite3_int64)0 x8000000000000000LL ){
e = -1996 ;
m = -1 ;
}else {
e = a>>52 ;
m = a & ((((sqlite3_int64)1 )<<52 )-1 );
if ( e==0 ){
m <<= 1 ;
}else {
m |= ((sqlite3_int64)1 )<<52 ;
}
while ( e<1075 && m>0 && (m&1 )==0 ){
m >>= 1 ;
e++;
}
if ( isNeg ) m = -m;
}
switch ( *(int *)sqlite3_user_data(context) ){
case 0 :
sqlite3_snprintf(sizeof (zResult), zResult, "ieee754(%lld,%d)" ,
m, e-1075 );
sqlite3_result_text(context, zResult, -1 , SQLITE_TRANSIENT);
break ;
case 1 :
sqlite3_result_int64(context, m);
break ;
case 2 :
sqlite3_result_int(context, e-1075 );
break ;
}
}else {
sqlite3_int64 m, e, a;
double r;
int isNeg = 0 ;
m = sqlite3_value_int64(argv[0 ]);
e = sqlite3_value_int64(argv[1 ]);
/* Limit the range of e. Ticket 22dea1cfdb9151e4 2021-03-02 */
if ( e>10000 ){
e = 10000 ;
}else if ( e<-10000 ){
e = -10000 ;
}
if ( m<0 ){
if ( m<(-9223372036854775807 LL) ) return ;
isNeg = 1 ;
m = -m;
}else if ( m==0 && e>-1000 && e<1000 ){
sqlite3_result_double(context, 0 .0 );
return ;
}
while ( (m>>32 )&0 xffe00000 ){
m >>= 1 ;
e++;
}
while ( m!=0 && ((m>>32 )&0 xfff00000)==0 ){
m <<= 1 ;
e--;
}
e += 1075 ;
if ( e<=0 ){
/* Subnormal */
if ( 1 -e >= 64 ){
m = 0 ;
}else {
m >>= 1 -e;
}
e = 0 ;
}else if ( e>0 x7ff ){
e = 0 x7ff;
}
a = m & ((((sqlite3_int64)1 )<<52 )-1 );
a |= e<<52 ;
if ( isNeg ) a |= ((sqlite3_uint64)1 )<<63 ;
memcpy(&r, &a, sizeof (r));
sqlite3_result_double(context, r);
}
}
/*
* * Functions to convert between blobs and floats .
*/
static void ieee754func_from_blob(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if ( sqlite3_value_type(argv[0 ])==SQLITE_BLOB
&& sqlite3_value_bytes(argv[0 ])==sizeof (double )
){
double r;
const unsigned char *x = sqlite3_value_blob(argv[0 ]);
unsigned int i;
sqlite3_uint64 v = 0 ;
for (i=0 ; i<sizeof (r); i++){
v = (v<<8 ) | x[i];
}
memcpy(&r, &v, sizeof (r));
sqlite3_result_double(context, r);
}
}
static void ieee754func_to_blob(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if ( sqlite3_value_type(argv[0 ])==SQLITE_FLOAT
|| sqlite3_value_type(argv[0 ])==SQLITE_INTEGER
){
double r = sqlite3_value_double(argv[0 ]);
sqlite3_uint64 v;
unsigned char a[sizeof (r)];
unsigned int i;
memcpy(&v, &r, sizeof (r));
for (i=1 ; i<=sizeof (r); i++){
a[sizeof (r)-i] = v&0 xff;
v >>= 8 ;
}
sqlite3_result_blob(context, a, sizeof (r), SQLITE_TRANSIENT);
}
}
/*
* * Functions to convert between 64 - bit integers and floats .
* *
* * The bit patterns are copied . The numeric values are different .
*/
static void ieee754func_from_int(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if ( sqlite3_value_type(argv[0 ])==SQLITE_INTEGER ){
double r;
sqlite3_int64 v = sqlite3_value_int64(argv[0 ]);
memcpy(&r, &v, sizeof (r));
sqlite3_result_double(context, r);
}
}
static void ieee754func_to_int(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
if ( sqlite3_value_type(argv[0 ])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[0 ]);
sqlite3_uint64 v;
memcpy(&v, &r, sizeof (v));
sqlite3_result_int64(context, v);
}
}
/*
* * SQL Function : ieee754_inc ( r , N )
* *
* * Move the floating point value r by N quantums and return the new
* * values .
* *
* * Behind the scenes : this routine merely casts r into a 64 - bit unsigned
* * integer , adds N , then casts the value back into float .
* *
* * Example : To find the smallest positive number :
* *
* * SELECT ieee754_inc ( 0 . 0 , + 1 ) ;
*/
static void ieee754inc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
double r;
sqlite3_int64 N;
sqlite3_uint64 m1, m2;
double r2;
UNUSED_PARAMETER(argc);
r = sqlite3_value_double(argv[0 ]);
N = sqlite3_value_int64(argv[1 ]);
memcpy(&m1, &r, 8 );
m2 = m1 + N;
memcpy(&r2, &m2, 8 );
sqlite3_result_double(context, r2);
}
#ifdef _WIN32
#endif
int sqlite3_ieee_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
static const struct {
char *zFName;
int nArg;
int iAux;
void (*xFunc)(sqlite3_context*,int ,sqlite3_value**);
} aFunc[] = {
{ "ieee754" , 1 , 0 , ieee754func },
{ "ieee754" , 2 , 0 , ieee754func },
{ "ieee754_mantissa" , 1 , 1 , ieee754func },
{ "ieee754_exponent" , 1 , 2 , ieee754func },
{ "ieee754_to_blob" , 1 , 0 , ieee754func_to_blob },
{ "ieee754_from_blob" , 1 , 0 , ieee754func_from_blob },
{ "ieee754_to_int" , 1 , 0 , ieee754func_to_int },
{ "ieee754_from_int" , 1 , 0 , ieee754func_from_int },
{ "ieee754_inc" , 2 , 0 , ieee754inc },
};
unsigned int i;
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void )pzErrMsg; /* Unused parameter */
for (i=0 ; i<sizeof (aFunc)/sizeof (aFunc[0 ]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_function(db, aFunc[i].zFName, aFunc[i].nArg,
SQLITE_UTF8|SQLITE_INNOCUOUS,
(void *)&aFunc[i].iAux,
aFunc[i].xFunc, 0 , 0 );
}
return rc;
}
/************************* End ext/misc/ieee754.c ********************/
/************************* Begin ext/misc/series.c ******************/
/*
* * 2015 - 08 - 18 , 2023 - 04 - 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 demonstrates how to create a table - valued - function using
* * a virtual table . This demo implements the generate_series ( ) function
* * which gives the same results as the eponymous function in PostgreSQL ,
* * within the limitation that its arguments are signed 64 - bit integers .
* *
* * Considering its equivalents to generate_series ( start , stop , step ) : A
* * value V [ n ] sequence is produced for integer n ascending from 0 where
* * ( V [ n ] = = start + n * step & & sgn ( V [ n ] - stop ) * sgn ( step ) > = 0 )
* * for each produced value ( independent of production time ordering . )
* *
* * All parameters must be either integer or convertable to integer .
* * The start parameter is required .
* * The stop parameter defaults to ( 1 < < 32 ) - 1 ( aka 4294967295 or 0 xffffffff )
* * The step parameter defaults to 1 and 0 is treated as 1 .
* *
* * Examples :
* *
* * SELECT * FROM generate_series ( 0 , 100 , 5 ) ;
* *
* * The query above returns integers from 0 through 100 counting by steps
* * of 5 . In other words , 0 , 5 , 10 , 15 , . . . , 90 , 95 , 100 . There are a total
* * of 21 rows .
* *
* * SELECT * FROM generate_series ( 0 , 100 ) ;
* *
* * Integers from 0 through 100 with a step size of 1 . 101 rows .
* *
* * SELECT * FROM generate_series ( 20 ) LIMIT 10 ;
* *
* * Integers 20 through 29 . 10 rows .
* *
* * SELECT * FROM generate_series ( 0 , - 100 , - 5 ) ;
* *
* * Integers 0 - 5 - 10 . . . - 100 . 21 rows .
* *
* * SELECT * FROM generate_series ( 0 , - 1 ) ;
* *
* * Empty sequence .
* *
* * HOW IT WORKS
* *
* * The generate_series " function " is really a virtual table with the
* * following schema :
* *
* * CREATE TABLE generate_series (
* * value ,
* * start HIDDEN ,
* * stop HIDDEN ,
* * step HIDDEN
* * ) ;
* *
* * The virtual table also has a rowid which is an alias for the value .
* *
* * Function arguments in queries against this virtual table are translated
* * into equality constraints against successive hidden columns . In other
* * words , the following pairs of queries are equivalent to each other :
* *
* * SELECT * FROM generate_series ( 0 , 100 , 5 ) ;
* * SELECT * FROM generate_series WHERE start = 0 AND stop = 100 AND step = 5 ;
* *
* * SELECT * FROM generate_series ( 0 , 100 ) ;
* * SELECT * FROM generate_series WHERE start = 0 AND stop = 100 ;
* *
* * SELECT * FROM generate_series ( 20 ) LIMIT 10 ;
* * SELECT * FROM generate_series WHERE start = 20 LIMIT 10 ;
* *
* * The generate_series virtual table implementation leaves the xCreate method
* * set to NULL . This means that it is not possible to do a CREATE VIRTUAL
* * TABLE command with " generate_series " as the USING argument . Instead , there
* * is a single generate_series virtual table that is always available without
* * having to be created first .
* *
* * The xBestIndex method looks for equality constraints against the hidden
* * start , stop , and step columns , and if present , it uses those constraints
* * to bound the sequence of generated values . If the equality constraints
* * are missing , it uses 0 for start , 4294967295 for stop , and 1 for step .
* * xBestIndex returns a small cost when both start and stop are available ,
* * and a very large cost if either start or stop are unavailable . This
* * encourages the query planner to order joins such that the bounds of the
* * series are well - defined .
* *
* * Update on 2024 - 08 - 22 :
* * xBestIndex now also looks for equality and inequality constraints against
* * the value column and uses those constraints as additional bounds against
* * the sequence range . Thus , a query like this :
* *
* * SELECT value FROM generate_series ( $ SA , $ EA )
* * WHERE value BETWEEN $ SB AND $ EB ;
* *
* * Is logically the same as :
* *
* * SELECT value FROM generate_series ( max ( $ SA , $ SB ) , min ( $ EA , $ EB ) ) ;
* *
* * Constraints on the value column can server as substitutes for constraints
* * on the hidden start and stop columns . So , the following two queries
* * are equivalent :
* *
* * SELECT value FROM generate_series ( $ S , $ E ) ;
* * SELECT value FROM generate_series WHERE value BETWEEN $ S and $ E ;
* *
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* series_cursor is a subclass of sqlite3_vtab_cursor which will
* * serve as the underlying representation of a cursor that scans
* * over rows of the result .
* *
* * iOBase , iOTerm , and iOStep are the original values of the
* * start = , stop = , and step = constraints on the query . These are
* * the values reported by the start , stop , and step columns of the
* * virtual table .
* *
* * iBase , iTerm , iStep , and bDescp are the actual values used to generate
* * the sequence . These might be different from the iOxxxx values .
* * For example in
* *
* * SELECT value FROM generate_series ( 1 , 11 , 2 )
* * WHERE value BETWEEN 4 AND 8 ;
* *
* * The iOBase is 1 , but the iBase is 5 . iOTerm is 11 but iTerm is 7 .
* * Another example :
* *
* * SELECT value FROM generate_series ( 1 , 15 , 3 ) ORDER BY value DESC ;
* *
* * The cursor initialization for the above query is :
* *
* * iOBase = 1 iBase = 13
* * iOTerm = 15 iTerm = 1
* * iOStep = 3 iStep = 3 bDesc = 1
* *
* * The actual step size is unsigned so that can have a value of
* * + 9223372036854775808 which is needed for querys like this :
* *
* * SELECT value
* * FROM generate_series ( 9223372036854775807 ,
* * - 9223372036854775808 ,
* * - 9223372036854775808 )
* * ORDER BY value ASC ;
* *
* * The setup for the previous query will be :
* *
* * iOBase = 9223372036854775807 iBase = - 1
* * iOTerm = - 9223372036854775808 iTerm = 9223372036854775807
* * iOStep = - 9223372036854775808 iStep = 9223372036854775808 bDesc = 0
*/
/* typedef unsigned char u8; */
typedef struct series_cursor series_cursor;
struct series_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3_int64 iOBase; /* Original starting value ("start") */
sqlite3_int64 iOTerm; /* Original terminal value ("stop") */
sqlite3_int64 iOStep; /* Original step value */
sqlite3_int64 iBase; /* Starting value to actually use */
sqlite3_int64 iTerm; /* Terminal value to actually use */
sqlite3_uint64 iStep; /* The step size */
sqlite3_int64 iValue; /* Current value */
u8 bDesc; /* iStep is really negative */
u8 bDone; /* True if stepped past last element */
};
/*
* * Computed the difference between two 64 - bit signed integers using a
* * convoluted computation designed to work around the silly restriction
* * against signed integer overflow in C .
*/
static sqlite3_uint64 span64(sqlite3_int64 a, sqlite3_int64 b){
assert( a>=b );
return (*(sqlite3_uint64*)&a) - (*(sqlite3_uint64*)&b);
}
/*
* * Add or substract an unsigned 64 - bit integer from a signed 64 - bit integer
* * and return the new signed 64 - bit integer .
*/
static sqlite3_int64 add64(sqlite3_int64 a, sqlite3_uint64 b){
sqlite3_uint64 x = *(sqlite3_uint64*)&a;
x += b;
return *(sqlite3_int64*)&x;
}
static sqlite3_int64 sub64(sqlite3_int64 a, sqlite3_uint64 b){
sqlite3_uint64 x = *(sqlite3_uint64*)&a;
x -= b;
return *(sqlite3_int64*)&x;
}
/*
* * The seriesConnect ( ) method is invoked to create a new
* * series_vtab that describes the generate_series virtual table .
* *
* * Think of this routine as the constructor for series_vtab objects .
* *
* * All this routine needs to do is :
* *
* * ( 1 ) Allocate the series_vtab object and initialize all fields .
* *
* * ( 2 ) Tell SQLite ( via the sqlite3_declare_vtab ( ) interface ) what the
* * result set of queries against generate_series will look like .
*/
static int seriesConnect(
sqlite3 *db,
void *pUnused,
int argcUnused, const char *const *argvUnused,
sqlite3_vtab **ppVtab,
char **pzErrUnused
){
sqlite3_vtab *pNew;
int rc;
/* Column numbers */
#define SERIES_COLUMN_ROWID (-1 )
#define SERIES_COLUMN_VALUE 0
#define SERIES_COLUMN_START 1
#define SERIES_COLUMN_STOP 2
#define SERIES_COLUMN_STEP 3
(void )pUnused;
(void )argcUnused;
(void )argvUnused;
(void )pzErrUnused;
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)" );
if ( rc==SQLITE_OK ){
pNew = *ppVtab = sqlite3_malloc64( sizeof (*pNew) );
if ( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0 , sizeof (*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
return rc;
}
/*
* * This method is the destructor for series_cursor objects .
*/
static int seriesDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
/*
* * Constructor for a new series_cursor object .
*/
static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){
series_cursor *pCur;
(void )pUnused;
pCur = sqlite3_malloc64( sizeof (*pCur) );
if ( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0 , sizeof (*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
* * Destructor for a series_cursor .
*/
static int seriesClose(sqlite3_vtab_cursor *cur){
sqlite3_free(cur);
return SQLITE_OK;
}
/*
* * Advance a series_cursor to its next row of output .
*/
static int seriesNext(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
if ( pCur->iValue==pCur->iTerm ){
pCur->bDone = 1 ;
}else if ( pCur->bDesc ){
pCur->iValue = sub64(pCur->iValue, pCur->iStep);
assert( pCur->iValue>=pCur->iTerm );
}else {
pCur->iValue = add64(pCur->iValue, pCur->iStep);
assert( pCur->iValue<=pCur->iTerm );
}
return SQLITE_OK;
}
/*
* * Return values of columns for the row at which the series_cursor
* * is currently pointing .
*/
static int seriesColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
series_cursor *pCur = (series_cursor*)cur;
sqlite3_int64 x = 0 ;
switch ( i ){
case SERIES_COLUMN_START: x = pCur->iOBase; break ;
case SERIES_COLUMN_STOP: x = pCur->iOTerm; break ;
case SERIES_COLUMN_STEP: x = pCur->iOStep; break ;
default : x = pCur->iValue; break ;
}
sqlite3_result_int64(ctx, x);
return SQLITE_OK;
}
#ifndef LARGEST_UINT64
#define LARGEST_INT64 ((sqlite3_int64)0 x7fffffffffffffffLL)
#define LARGEST_UINT64 ((sqlite3_uint64)0 xffffffffffffffffULL)
#define SMALLEST_INT64 ((sqlite3_int64)0 x8000000000000000LL)
#endif
/*
* * The rowid is the same as the value .
*/
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
series_cursor *pCur = (series_cursor*)cur;
*pRowid = pCur->iValue;
return SQLITE_OK;
}
/*
* * Return TRUE if the cursor has been moved off of the last
* * row of output .
*/
static int seriesEof(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
return pCur->bDone;
}
/* True to cause run-time checking of the start=, stop=, and/or step=
* * parameters . The only reason to do this is for testing the
* * constraint checking logic for virtual tables in the SQLite core .
*/
#ifndef SQLITE_SERIES_CONSTRAINT_VERIFY
# define SQLITE_SERIES_CONSTRAINT_VERIFY 0
#endif
/*
* * Return the number of steps between pCur - > iBase and pCur - > iTerm if
* * the step width is pCur - > iStep .
*/
static sqlite3_uint64 seriesSteps(series_cursor *pCur){
if ( pCur->bDesc ){
assert( pCur->iBase >= pCur->iTerm );
return span64(pCur->iBase, pCur->iTerm)/pCur->iStep;
}else {
assert( pCur->iBase <= pCur->iTerm );
return span64(pCur->iTerm, pCur->iBase)/pCur->iStep;
}
}
#if defined (SQLITE_ENABLE_MATH_FUNCTIONS) || defined (_WIN32)
/*
* * Case 1 ( the most common case ) :
* * The standard math library is available so use ceil ( ) and floor ( ) from there .
*/
static double seriesCeil(double r){ return ceil(r); }
static double seriesFloor(double r){ return floor(r); }
#elif defined (__GNUC__) && !defined (SQLITE_DISABLE_INTRINSIC)
/*
* * Case 2 ( 2 nd most common ) : Use GCC / Clang builtins
*/
static double seriesCeil(double r){ return __builtin_ceil(r); }
static double seriesFloor(double r){ return __builtin_floor(r); }
#else
/*
* * Case 3 ( rarely happens ) : Use home - grown ceil ( ) and floor ( ) routines .
*/
static double seriesCeil(double r){
sqlite3_int64 x;
if ( r!=r ) return r;
if ( r<=(-4503599627370496 .0 ) ) return r;
if ( r>=(+4503599627370496 .0 ) ) return r;
x = (sqlite3_int64)r;
if ( r==(double )x ) return r;
if ( r>(double )x ) x++;
return (double )x;
}
static double seriesFloor(double r){
sqlite3_int64 x;
if ( r!=r ) return r;
if ( r<=(-4503599627370496 .0 ) ) return r;
if ( r>=(+4503599627370496 .0 ) ) return r;
x = (sqlite3_int64)r;
if ( r==(double )x ) return r;
if ( r<(double )x ) x--;
return (double )x;
}
#endif
/*
* * This method is called to " rewind " the series_cursor object back
* * to the first row of output . This method is always called at least
* * once prior to any call to seriesColumn ( ) or seriesRowid ( ) or
* * seriesEof ( ) .
* *
* * The query plan selected by seriesBestIndex is passed in the idxNum
* * parameter . ( idxStr is not used in this implementation . ) idxNum
* * is a bitmask showing which constraints are available :
* *
* * 0 x0001 : start = VALUE
* * 0 x0002 : stop = VALUE
* * 0 x0004 : step = VALUE
* * 0 x0008 : descending order
* * 0 x0010 : ascending order
* * 0 x0020 : LIMIT VALUE
* * 0 x0040 : OFFSET VALUE
* * 0 x0080 : value = VALUE
* * 0 x0100 : value > = VALUE
* * 0 x0200 : value > VALUE
* * 0 x1000 : value < = VALUE
* * 0 x2000 : value < VALUE
* *
* * This routine should initialize the cursor and position it so that it
* * is pointing at the first row , or pointing off the end of the table
* * ( so that seriesEof ( ) will return true ) if the table is empty .
*/
static int seriesFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStrUnused,
int argc, sqlite3_value **argv
){
series_cursor *pCur = (series_cursor *)pVtabCursor;
int iArg = 0 ; /* Arguments used so far */
int i; /* Loop counter */
sqlite3_int64 iMin = SMALLEST_INT64; /* Smallest allowed output value */
sqlite3_int64 iMax = LARGEST_INT64; /* Largest allowed output value */
sqlite3_int64 iLimit = 0 ; /* if >0, the value of the LIMIT */
sqlite3_int64 iOffset = 0 ; /* if >0, the value of the OFFSET */
(void )idxStrUnused;
/* If any constraints have a NULL value, then return no rows.
* * See ticket https : //sqlite.org/src/info/fac496b61722daf2
*/
for (i=0 ; i<argc; i++){
if ( sqlite3_value_type(argv[i])==SQLITE_NULL ){
goto series_no_rows;
}
}
/* Capture the three HIDDEN parameters to the virtual table and insert
* * default values for any parameters that are omitted .
*/
if ( idxNum & 0 x01 ){
pCur->iOBase = sqlite3_value_int64(argv[iArg++]);
}else {
pCur->iOBase = 0 ;
}
if ( idxNum & 0 x02 ){
pCur->iOTerm = sqlite3_value_int64(argv[iArg++]);
}else {
pCur->iOTerm = 0 xffffffff;
}
if ( idxNum & 0 x04 ){
pCur->iOStep = sqlite3_value_int64(argv[iArg++]);
if ( pCur->iOStep==0 ) pCur->iOStep = 1 ;
}else {
pCur->iOStep = 1 ;
}
/* If there are constraints on the value column but there are
* * no constraints on the start , stop , and step columns , then
* * initialize the default range to be the entire range of 64 - bit signed
* * integers . This range will contracted by the value column constraints
* * further below .
*/
if ( (idxNum & 0 x05)==0 && (idxNum & 0 x0380)!=0 ){
pCur->iOBase = SMALLEST_INT64;
}
if ( (idxNum & 0 x06)==0 && (idxNum & 0 x3080)!=0 ){
pCur->iOTerm = LARGEST_INT64;
}
pCur->iBase = pCur->iOBase;
pCur->iTerm = pCur->iOTerm;
if ( pCur->iOStep>0 ){
pCur->iStep = pCur->iOStep;
}else if ( pCur->iOStep>SMALLEST_INT64 ){
pCur->iStep = -pCur->iOStep;
}else {
pCur->iStep = LARGEST_INT64;
pCur->iStep++;
}
pCur->bDesc = pCur->iOStep<0 ;
if ( pCur->bDesc==0 && pCur->iBase>pCur->iTerm ){
goto series_no_rows;
}
if ( pCur->bDesc!=0 && pCur->iBase<pCur->iTerm ){
goto series_no_rows;
}
/* Extract the LIMIT and OFFSET values, but do not apply them yet.
* * The range must first be constrained by the limits on value .
*/
if ( idxNum & 0 x20 ){
iLimit = sqlite3_value_int64(argv[iArg++]);
if ( idxNum & 0 x40 ){
iOffset = sqlite3_value_int64(argv[iArg++]);
}
}
/* Narrow the range of iMin and iMax (the minimum and maximum outputs)
* * based on equality and inequality constraints on the " value " column .
*/
if ( idxNum & 0 x3380 ){
if ( idxNum & 0 x0080 ){ /* value=X */
if ( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[iArg++]);
if ( r==seriesCeil(r)
&& r>=(double )SMALLEST_INT64
&& r<=(double )LARGEST_INT64
){
iMin = iMax = (sqlite3_int64)r;
}else {
goto series_no_rows;
}
}else {
iMin = iMax = sqlite3_value_int64(argv[iArg++]);
}
}else {
if ( idxNum & 0 x0300 ){ /* value>X or value>=X */
if ( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[iArg++]);
if ( r<(double )SMALLEST_INT64 ){
iMin = SMALLEST_INT64;
}else if ( (idxNum & 0 x0200)!=0 && r==seriesCeil(r) ){
iMin = (sqlite3_int64)seriesCeil(r+1 .0 );
}else {
iMin = (sqlite3_int64)seriesCeil(r);
}
}else {
iMin = sqlite3_value_int64(argv[iArg++]);
if ( (idxNum & 0 x0200)!=0 ){
if ( iMin==LARGEST_INT64 ){
goto series_no_rows;
}else {
iMin++;
}
}
}
}
if ( idxNum & 0 x3000 ){ /* value<X or value<=X */
if ( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[iArg++]);
if ( r>(double )LARGEST_INT64 ){
iMax = LARGEST_INT64;
}else if ( (idxNum & 0 x2000)!=0 && r==seriesFloor(r) ){
iMax = (sqlite3_int64)(r-1 .0 );
}else {
iMax = (sqlite3_int64)seriesFloor(r);
}
}else {
iMax = sqlite3_value_int64(argv[iArg++]);
if ( idxNum & 0 x2000 ){
if ( iMax==SMALLEST_INT64 ){
goto series_no_rows;
}else {
iMax--;
}
}
}
}
if ( iMin>iMax ){
goto series_no_rows;
}
}
/* Try to reduce the range of values to be generated based on
* * constraints on the " value " column .
*/
if ( pCur->bDesc==0 ){
if ( pCur->iBase<iMin ){
sqlite3_uint64 span = span64(iMin,pCur->iBase);
pCur->iBase = add64(pCur->iBase, (span/pCur->iStep)*pCur->iStep);
if ( pCur->iBase<iMin ){
if ( pCur->iBase > sub64(LARGEST_INT64, pCur->iStep) ){
goto series_no_rows;
}
pCur->iBase = add64(pCur->iBase, pCur->iStep);
}
}
if ( pCur->iTerm>iMax ){
pCur->iTerm = iMax;
}
}else {
if ( pCur->iBase>iMax ){
sqlite3_uint64 span = span64(pCur->iBase,iMax);
pCur->iBase = sub64(pCur->iBase, (span/pCur->iStep)*pCur->iStep);
if ( pCur->iBase>iMax ){
if ( pCur->iBase < add64(SMALLEST_INT64, pCur->iStep) ){
goto series_no_rows;
}
pCur->iBase = sub64(pCur->iBase, pCur->iStep);
}
}
if ( pCur->iTerm<iMin ){
pCur->iTerm = iMin;
}
}
}
/* Adjust iTerm so that it is exactly the last value of the series.
*/
if ( pCur->bDesc==0 ){
if ( pCur->iBase>pCur->iTerm ){
goto series_no_rows;
}
pCur->iTerm = sub64(pCur->iTerm,
span64(pCur->iTerm,pCur->iBase) % pCur->iStep);
}else {
if ( pCur->iBase<pCur->iTerm ){
goto series_no_rows;
}
pCur->iTerm = add64(pCur->iTerm,
span64(pCur->iBase,pCur->iTerm) % pCur->iStep);
}
/* Transform the series generator to output values in the requested
* * order .
*/
if ( ((idxNum & 0 x0008)!=0 && pCur->bDesc==0 )
|| ((idxNum & 0 x0010)!=0 && pCur->bDesc!=0 )
){
sqlite3_int64 tmp = pCur->iBase;
pCur->iBase = pCur->iTerm;
pCur->iTerm = tmp;
pCur->bDesc = !pCur->bDesc;
}
/* Apply LIMIT and OFFSET constraints, if any */
assert( pCur->iStep!=0 );
if ( idxNum & 0 x20 ){
if ( iOffset>0 ){
if ( seriesSteps(pCur) < (sqlite3_uint64)iOffset ){
goto series_no_rows;
}else if ( pCur->bDesc ){
pCur->iBase = sub64(pCur->iBase, pCur->iStep*iOffset);
}else {
pCur->iBase = add64(pCur->iBase, pCur->iStep*iOffset);
}
}
if ( iLimit>=0 && seriesSteps(pCur) > (sqlite3_uint64)iLimit ){
pCur->iTerm = add64(pCur->iBase, (iLimit - 1 )*pCur->iStep);
}
}
pCur->iValue = pCur->iBase;
pCur->bDone = 0 ;
return SQLITE_OK;
series_no_rows:
pCur->iBase = 0 ;
pCur->iTerm = 0 ;
pCur->iStep = 1 ;
pCur->bDesc = 0 ;
pCur->bDone = 1 ;
return SQLITE_OK;
}
/*
* * SQLite will invoke this method one or more times while planning a query
* * that uses the generate_series virtual table . This routine needs to create
* * a query plan for each invocation and compute an estimated cost for that
* * plan .
* *
* * In this implementation idxNum is used to represent the
* * query plan . idxStr is unused .
* *
* * The query plan is represented by bits in idxNum :
* *
* * 0 x0001 start = $ num
* * 0 x0002 stop = $ num
* * 0 x0004 step = $ num
* * 0 x0008 output is in descending order
* * 0 x0010 output is in ascending order
* * 0 x0020 LIMIT $ num
* * 0 x0040 OFFSET $ num
* * 0 x0080 value = $ num
* * 0 x0100 value > = $ num
* * 0 x0200 value > $ num
* * 0 x1000 value < = $ num
* * 0 x2000 value < $ num
* *
* * Only one of 0 x0100 or 0 x0200 will be returned . Similarly , only
* * one of 0 x1000 or 0 x2000 will be returned . If the 0 x0080 is set , then
* * none of the 0 xff00 bits will be set .
* *
* * The order of parameters passed to xFilter is as follows :
* *
* * * The argument to start = if bit 0 x0001 is in the idxNum mask
* * * The argument to stop = if bit 0 x0002 is in the idxNum mask
* * * The argument to step = if bit 0 x0004 is in the idxNum mask
* * * The argument to LIMIT if bit 0 x0020 is in the idxNum mask
* * * The argument to OFFSET if bit 0 x0040 is in the idxNum mask
* * * The argument to value = , or value > = or value > if any of
* * bits 0 x0380 are in the idxNum mask
* * * The argument to value < = or value < if either of bits 0 x3000
* * are in the mask
* *
*/
static int seriesBestIndex(
sqlite3_vtab *pVTab,
sqlite3_index_info *pIdxInfo
){
int i, j; /* Loop over constraints */
int idxNum = 0 ; /* The query plan bitmask */
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
int bStartSeen = 0 ; /* EQ constraint seen on the START column */
#endif
int unusableMask = 0 ; /* Mask of unusable constraints */
int nArg = 0 ; /* Number of arguments that seriesFilter() expects */
int aIdx[7 ]; /* Constraints on start, stop, step, LIMIT, OFFSET,
* * and value . aIdx [ 5 ] covers value = , value > = , and
** value>, aIdx[6] covers value<= and value< */
const struct sqlite3_index_constraint *pConstraint;
/* This implementation assumes that the start, stop, and step columns
** are the last three columns in the virtual table. */
assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
aIdx[0 ] = aIdx[1 ] = aIdx[2 ] = aIdx[3 ] = aIdx[4 ] = aIdx[5 ] = aIdx[6 ] = -1 ;
pConstraint = pIdxInfo->aConstraint;
for (i=0 ; i<pIdxInfo->nConstraint; i++, pConstraint++){
int iCol; /* 0 for start, 1 for stop, 2 for step */
int iMask; /* bitmask for those column */
int op = pConstraint->op;
if ( op>=SQLITE_INDEX_CONSTRAINT_LIMIT
&& op<=SQLITE_INDEX_CONSTRAINT_OFFSET
){
if ( pConstraint->usable==0 ){
/* do nothing */
}else if ( op==SQLITE_INDEX_CONSTRAINT_LIMIT ){
aIdx[3 ] = i;
idxNum |= 0 x20;
}else {
assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET );
aIdx[4 ] = i;
idxNum |= 0 x40;
}
continue ;
}
if ( pConstraint->iColumn<SERIES_COLUMN_START ){
if ( (pConstraint->iColumn==SERIES_COLUMN_VALUE ||
pConstraint->iColumn==SERIES_COLUMN_ROWID)
&& pConstraint->usable
){
switch ( op ){
case SQLITE_INDEX_CONSTRAINT_EQ:
case SQLITE_INDEX_CONSTRAINT_IS: {
idxNum |= 0 x0080;
idxNum &= ~0 x3300;
aIdx[5 ] = i;
aIdx[6 ] = -1 ;
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
bStartSeen = 1 ;
#endif
break ;
}
case SQLITE_INDEX_CONSTRAINT_GE: {
if ( idxNum & 0 x0080 ) break ;
idxNum |= 0 x0100;
idxNum &= ~0 x0200;
aIdx[5 ] = i;
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
bStartSeen = 1 ;
#endif
break ;
}
case SQLITE_INDEX_CONSTRAINT_GT: {
if ( idxNum & 0 x0080 ) break ;
idxNum |= 0 x0200;
idxNum &= ~0 x0100;
aIdx[5 ] = i;
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
bStartSeen = 1 ;
#endif
break ;
}
case SQLITE_INDEX_CONSTRAINT_LE: {
if ( idxNum & 0 x0080 ) break ;
idxNum |= 0 x1000;
idxNum &= ~0 x2000;
aIdx[6 ] = i;
break ;
}
case SQLITE_INDEX_CONSTRAINT_LT: {
if ( idxNum & 0 x0080 ) break ;
idxNum |= 0 x2000;
idxNum &= ~0 x1000;
aIdx[6 ] = i;
break ;
}
}
}
continue ;
}
iCol = pConstraint->iColumn - SERIES_COLUMN_START;
assert( iCol>=0 && iCol<=2 );
iMask = 1 << iCol;
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
if ( iCol==0 && op==SQLITE_INDEX_CONSTRAINT_EQ ){
bStartSeen = 1 ;
}
#endif
if ( pConstraint->usable==0 ){
unusableMask |= iMask;
continue ;
}else if ( op==SQLITE_INDEX_CONSTRAINT_EQ ){
idxNum |= iMask;
aIdx[iCol] = i;
}
}
if ( aIdx[3 ]==0 ){
/* Ignore OFFSET if LIMIT is omitted */
idxNum &= ~0 x60;
aIdx[4 ] = 0 ;
}
for (i=0 ; i<7 ; i++){
if ( (j = aIdx[i])>=0 ){
pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[j].omit =
!SQLITE_SERIES_CONSTRAINT_VERIFY || i>=3 ;
}
}
/* The current generate_column() implementation requires at least one
* * argument ( the START value ) . Legacy versions assumed START = 0 if the
* * first argument was omitted . Compile with - DZERO_ARGUMENT_GENERATE_SERIES
** to obtain the legacy behavior */
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
if ( !bStartSeen ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf(
"first argument to \" generate_series()\" missing or unusable" );
return SQLITE_ERROR;
}
#endif
if ( (unusableMask & ~idxNum)!=0 ){
/* The start, stop, and step columns are inputs. Therefore if there
* * are unusable constraints on any of start , stop , or step then
** this plan is unusable */
return SQLITE_CONSTRAINT;
}
if ( (idxNum & 0 x03)==0 x03 ){
/* Both start= and stop= boundaries are available. This is the
** the preferred case */
pIdxInfo->estimatedCost = (double )(2 - ((idxNum&4 )!=0 ));
pIdxInfo->estimatedRows = 1000 ;
if ( pIdxInfo->nOrderBy>=1 && pIdxInfo->aOrderBy[0 ].iColumn==0 ){
if ( pIdxInfo->aOrderBy[0 ].desc ){
idxNum |= 0 x08;
}else {
idxNum |= 0 x10;
}
pIdxInfo->orderByConsumed = 1 ;
}
}else if ( (idxNum & 0 x21)==0 x21 ){
/* We have start= and LIMIT */
pIdxInfo->estimatedRows = 2500 ;
}else {
/* If either boundary is missing, we have to generate a huge span
* * of numbers . Make this case very expensive so that the query
** planner will work hard to avoid it. */
pIdxInfo->estimatedRows = 2147483647 ;
}
pIdxInfo->idxNum = idxNum;
#ifdef SQLITE_INDEX_SCAN_HEX
pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_HEX;
#endif
return SQLITE_OK;
}
/*
* * This following structure defines all the methods for the
* * generate_series virtual table .
*/
static sqlite3_module seriesModule = {
0 , /* iVersion */
0 , /* xCreate */
seriesConnect, /* xConnect */
seriesBestIndex, /* xBestIndex */
seriesDisconnect, /* xDisconnect */
0 , /* xDestroy */
seriesOpen, /* xOpen - open a cursor */
seriesClose, /* xClose - close a cursor */
seriesFilter, /* xFilter - configure scan constraints */
seriesNext, /* xNext - advance a cursor */
seriesEof, /* xEof - check for end of scan */
seriesColumn, /* xColumn - read data */
seriesRowid, /* xRowid - read data */
0 , /* xUpdate */
0 , /* xBegin */
0 , /* xSync */
0 , /* xCommit */
0 , /* xRollback */
0 , /* xFindMethod */
0 , /* xRename */
0 , /* xSavepoint */
0 , /* xRelease */
0 , /* xRollbackTo */
0 , /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifdef _WIN32
#endif
int sqlite3_series_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
#ifndef SQLITE_OMIT_VIRTUALTABLE
if ( sqlite3_libversion_number()<3008012 && pzErrMsg!=0 ){
*pzErrMsg = sqlite3_mprintf(
"generate_series() requires SQLite 3.8.12 or later" );
return SQLITE_ERROR;
}
rc = sqlite3_create_module(db, "generate_series" , &seriesModule, 0 );
#endif
return rc;
}
/************************* End ext/misc/series.c ********************/
/************************* Begin ext/misc/regexp.c ******************/
/*
* * 2012 - 11 - 13
* *
* * 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 .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* * The code in this file implements a compact but reasonably
* * efficient regular - expression matcher for posix extended regular
* * expressions against UTF8 text .
* *
* * This file is an SQLite extension . It registers a single function
* * named " regexp ( A , B ) " where A is the regular expression and B is the
* * string to be matched . By registering this function , SQLite will also
* * then implement the " B regexp A " operator . Note that with the function
* * the regular expression comes first , but with the operator it comes
* * second .
* *
* * The following regular expression syntax is supported :
* *
* * X * zero or more occurrences of X
* * X + one or more occurrences of X
* * X ? zero or one occurrences of X
* * X { p , q } between p and q occurrences of X
* * ( X ) match X
* * X | Y X or Y
* * ^ X X occurring at the beginning of the string
* * X $ X occurring at the end of the string
* * . Match any single character
* * \ c Character c where c is one of \ { } ( ) [ ] | * + ? - .
* * \ c C - language escapes for c in afnrtv . ex : \ t or \ n
* * \ uXXXX Where XXXX is exactly 4 hex digits , unicode value XXXX
* * \ xXX Where XX is exactly 2 hex digits , unicode value XX
* * [ abc ] Any single character from the set abc
* * [ ^ abc ] Any single character not in the set abc
* * [ a - z ] Any single character in the range a - z
* * [ ^ a - z ] Any single character not in the range a - z
* * \ b Word boundary
* * \ w Word character . [ A - Za - z0 - 9 _ ]
* * \ W Non - word character
* * \ d Digit
* * \ D Non - digit
* * \ s Whitespace character
* * \ S Non - whitespace character
* *
* * A nondeterministic finite automaton ( NFA ) is used for matching , so the
* * performance is bounded by O ( N * M ) where N is the size of the regular
* * expression and M is the size of the input string . The matcher never
* * exhibits exponential behavior . Note that the X { p , q } operator expands
* * to p copies of X following by q - p copies of X ? and that the size of the
* * regular expression in the O ( N * M ) performance bound is computed after
* * this expansion .
* *
* * To help prevent DoS attacks , the maximum size of the NFA is restricted .
*/
#include <string.h>
#include <stdlib.h>
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
/*
* * The following # defines change the names of some functions implemented in
* * this file to prevent name collisions with C - library functions of the
* * same name .
*/
#define re_match sqlite3re_match
#define re_compile sqlite3re_compile
#define re_free sqlite3re_free
/* The end-of-input character */
#define RE_EOF 0 /* End of input */
#define RE_START 0 xfffffff /* Start of input - larger than an UTF-8 */
/* The NFA is implemented as sequence of opcodes taken from the following
* * set . Each opcode has a single integer argument .
*/
#define RE_OP_MATCH 1 /* Match the one character in the argument */
#define RE_OP_ANY 2 /* Match any one character. (Implements ".") */
#define RE_OP_ANYSTAR 3 /* Special optimized version of .* */
#define RE_OP_FORK 4 /* Continue to both next and opcode at iArg */
#define RE_OP_GOTO 5 /* Jump to opcode at iArg */
#define RE_OP_ACCEPT 6 /* Halt and indicate a successful match */
#define RE_OP_CC_INC 7 /* Beginning of a [...] character class */
#define RE_OP_CC_EXC 8 /* Beginning of a [^...] character class */
#define RE_OP_CC_VALUE 9 /* Single value in a character class */
#define RE_OP_CC_RANGE 10 /* Range of values in a character class */
#define RE_OP_WORD 11 /* Perl word character [A-Za-z0-9_] */
#define RE_OP_NOTWORD 12 /* Not a perl word character */
#define RE_OP_DIGIT 13 /* digit: [0-9] */
#define RE_OP_NOTDIGIT 14 /* Not a digit */
#define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */
#define RE_OP_NOTSPACE 16 /* Not a digit */
#define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
#define RE_OP_ATSTART 18 /* Currently at the start of the string */
/* Each opcode is a "state" in the NFA */
typedef unsigned short ReStateNumber;
/* Because this is an NFA and not a DFA, multiple states can be active at
* * once . An instance of the following object records all active states in
* * the NFA . The implementation is optimized for the common case where the
* * number of actives states is small .
*/
typedef struct ReStateSet {
unsigned nState; /* Number of current states */
ReStateNumber *aState; /* Current states */
} ReStateSet;
/* An input string read one character at a time.
*/
typedef struct ReInput ReInput;
struct ReInput {
const unsigned char *z; /* All text */
int i; /* Next byte to read */
int mx; /* EOF when i>=mx */
};
/* A compiled NFA (or an NFA that is in the process of being compiled) is
* * an instance of the following object .
*/
typedef struct ReCompiled ReCompiled;
struct ReCompiled {
ReInput sIn; /* Regular expression text */
const char *zErr; /* Error message to return */
char *aOp; /* Operators for the virtual machine */
int *aArg; /* Arguments to each operator */
unsigned (*xNextChar)(ReInput*); /* Next character function */
unsigned char zInit[12 ]; /* Initial text to match */
int nInit; /* Number of bytes in zInit */
unsigned nState; /* Number of entries in aOp[] and aArg[] */
unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
unsigned mxAlloc; /* Complexity limit */
};
/* Add a state to the given state set if it is not already there */
static void re_add_state(ReStateSet *pSet, int newState){
unsigned i;
for (i=0 ; i<pSet->nState; i++) if ( pSet->aState[i]==newState ) return ;
pSet->aState[pSet->nState++] = (ReStateNumber)newState;
}
/* Extract the next unicode character from *pzIn and return it. Advance
* * * pzIn to the first byte past the end of the character returned . To
* * be clear : this routine converts utf8 to unicode . This routine is
* * optimized for the common case where the next character is a single byte .
*/
static unsigned re_next_char(ReInput *p){
unsigned c;
if ( p->i>=p->mx ) return 0 ;
c = p->z[p->i++];
if ( c>=0 x80 ){
if ( (c&0 xe0)==0 xc0 && p->i<p->mx && (p->z[p->i]&0 xc0)==0 x80 ){
c = (c&0 x1f)<<6 | (p->z[p->i++]&0 x3f);
if ( c<0 x80 ) c = 0 xfffd;
}else if ( (c&0 xf0)==0 xe0 && p->i+1 <p->mx && (p->z[p->i]&0 xc0)==0 x80
&& (p->z[p->i+1 ]&0 xc0)==0 x80 ){
c = (c&0 x0f)<<12 | ((p->z[p->i]&0 x3f)<<6 ) | (p->z[p->i+1 ]&0 x3f);
p->i += 2 ;
if ( c<=0 x7ff || (c>=0 xd800 && c<=0 xdfff) ) c = 0 xfffd;
}else if ( (c&0 xf8)==0 xf0 && p->i+2 <p->mx && (p->z[p->i]&0 xc0)==0 x80
&& (p->z[p->i+1 ]&0 xc0)==0 x80 && (p->z[p->i+2 ]&0 xc0)==0 x80 ){
c = (c&0 x07)<<18 | ((p->z[p->i]&0 x3f)<<12 ) | ((p->z[p->i+1 ]&0 x3f)<<6 )
| (p->z[p->i+2 ]&0 x3f);
p->i += 3 ;
if ( c<=0 xffff || c>0 x10ffff ) c = 0 xfffd;
}else {
c = 0 xfffd;
}
}
return c;
}
static unsigned re_next_char_nocase(ReInput *p){
unsigned c = re_next_char(p);
if ( c>='A' && c<='Z' ) c += 'a' - 'A' ;
return c;
}
/* Return true if c is a perl "word" character: [A-Za-z0-9_] */
static int re_word_char(int c){
return (c>='0' && c<='9' ) || (c>='a' && c<='z' )
|| (c>='A' && c<='Z' ) || c=='_' ;
}
/* Return true if c is a "digit" character: [0-9] */
static int re_digit_char(int c){
return (c>='0' && c<='9' );
}
/* Return true if c is a perl "space" character: [ \t\r\n\v\f] */
static int re_space_char(int c){
return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f' ;
}
/* Run a compiled regular expression on the zero-terminated input
* * string zIn [ ] . Return true on a match and false if there is no match .
*/
static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
ReStateSet aStateSet[2 ], *pThis, *pNext;
ReStateNumber aSpace[100 ];
ReStateNumber *pToFree;
unsigned int i = 0 ;
unsigned int iSwap = 0 ;
int c = RE_START;
int cPrev = 0 ;
int rc = 0 ;
ReInput in;
in.z = zIn;
in.i = 0 ;
in.mx = nIn>=0 ? nIn : (int )strlen((char const *)zIn);
/* Look for the initial prefix match, if there is one. */
if ( pRe->nInit ){
unsigned char x = pRe->zInit[0 ];
while ( in.i+pRe->nInit<=in.mx
&& (zIn[in.i]!=x ||
strncmp((const char *)zIn+in.i, (const char *)pRe->zInit, pRe->nInit)!=0 )
){
in.i++;
}
if ( in.i+pRe->nInit>in.mx ) return 0 ;
c = RE_START-1 ;
}
if ( pRe->nState<=(sizeof (aSpace)/(sizeof (aSpace[0 ])*2 )) ){
pToFree = 0 ;
aStateSet[0 ].aState = aSpace;
}else {
pToFree = sqlite3_malloc64( sizeof (ReStateNumber)*2 *pRe->nState );
if ( pToFree==0 ) return -1 ;
aStateSet[0 ].aState = pToFree;
}
aStateSet[1 ].aState = &aStateSet[0 ].aState[pRe->nState];
pNext = &aStateSet[1 ];
pNext->nState = 0 ;
re_add_state(pNext, 0 );
while ( c!=RE_EOF && pNext->nState>0 ){
cPrev = c;
c = pRe->xNextChar(&in);
pThis = pNext;
pNext = &aStateSet[iSwap];
iSwap = 1 - iSwap;
pNext->nState = 0 ;
for (i=0 ; i<pThis->nState; i++){
int x = pThis->aState[i];
switch ( pRe->aOp[x] ){
case RE_OP_MATCH: {
if ( pRe->aArg[x]==c ) re_add_state(pNext, x+1 );
break ;
}
case RE_OP_ATSTART: {
if ( cPrev==RE_START ) re_add_state(pThis, x+1 );
break ;
}
case RE_OP_ANY: {
if ( c!=0 ) re_add_state(pNext, x+1 );
break ;
}
case RE_OP_WORD: {
if ( re_word_char(c) ) re_add_state(pNext, x+1 );
break ;
}
case RE_OP_NOTWORD: {
if ( !re_word_char(c) && c!=0 ) re_add_state(pNext, x+1 );
break ;
}
case RE_OP_DIGIT: {
if ( re_digit_char(c) ) re_add_state(pNext, x+1 );
break ;
}
case RE_OP_NOTDIGIT: {
if ( !re_digit_char(c) && c!=0 ) re_add_state(pNext, x+1 );
break ;
}
case RE_OP_SPACE: {
if ( re_space_char(c) ) re_add_state(pNext, x+1 );
break ;
}
case RE_OP_NOTSPACE: {
if ( !re_space_char(c) && c!=0 ) re_add_state(pNext, x+1 );
break ;
}
case RE_OP_BOUNDARY: {
if ( re_word_char(c)!=re_word_char(cPrev) ) re_add_state(pThis, x+1 );
break ;
}
case RE_OP_ANYSTAR: {
re_add_state(pNext, x);
re_add_state(pThis, x+1 );
break ;
}
case RE_OP_FORK: {
re_add_state(pThis, x+pRe->aArg[x]);
re_add_state(pThis, x+1 );
break ;
}
case RE_OP_GOTO: {
re_add_state(pThis, x+pRe->aArg[x]);
break ;
}
case RE_OP_ACCEPT: {
rc = 1 ;
goto re_match_end;
}
case RE_OP_CC_EXC: {
if ( c==0 ) break ;
/* fall-through */ goto re_op_cc_inc;
}
case RE_OP_CC_INC: re_op_cc_inc: {
int j = 1 ;
int n = pRe->aArg[x];
int hit = 0 ;
for (j=1 ; j>0 && j<n; j++){
if ( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
if ( pRe->aArg[x+j]==c ){
hit = 1 ;
j = -1 ;
}
}else {
if ( pRe->aArg[x+j]<=c && pRe->aArg[x+j+1 ]>=c ){
hit = 1 ;
j = -1 ;
}else {
j++;
}
}
}
if ( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit;
if ( hit ) re_add_state(pNext, x+n);
break ;
}
}
}
}
for (i=0 ; i<pNext->nState; i++){
int x = pNext->aState[i];
while ( pRe->aOp[x]==RE_OP_GOTO ) x += pRe->aArg[x];
if ( pRe->aOp[x]==RE_OP_ACCEPT ){ rc = 1 ; break ; }
}
re_match_end:
sqlite3_free(pToFree);
return rc;
}
/* Resize the opcode and argument arrays for an RE under construction.
*/
static int re_resize(ReCompiled *p, unsigned int N){
char *aOp;
int *aArg;
if ( N>p->mxAlloc ){ p->zErr = "REGEXP pattern too big" ; return 1 ; }
aOp = sqlite3_realloc64(p->aOp, N*sizeof (p->aOp[0 ]));
if ( aOp==0 ){ p->zErr = "out of memory" ; return 1 ; }
p->aOp = aOp;
aArg = sqlite3_realloc64(p->aArg, N*sizeof (p->aArg[0 ]));
if ( aArg==0 ){ p->zErr = "out of memory" ; return 1 ; }
p->aArg = aArg;
p->nAlloc = N;
return 0 ;
}
/* Insert a new opcode and argument into an RE under construction. The
* * insertion point is just prior to existing opcode iBefore .
*/
static int re_insert(ReCompiled *p, int iBefore, int op, int arg){
int i;
if ( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2 ) ) return 0 ;
for (i=p->nState; i>iBefore; i--){
p->aOp[i] = p->aOp[i-1 ];
p->aArg[i] = p->aArg[i-1 ];
}
p->nState++;
p->aOp[iBefore] = (char )op;
p->aArg[iBefore] = arg;
return iBefore;
}
/* Append a new opcode and argument to the end of the RE under construction.
*/
static int re_append(ReCompiled *p, int op, int arg){
return re_insert(p, p->nState, op, arg);
}
/* Make a copy of N opcodes starting at iStart onto the end of the RE
* * under construction .
*/
static void re_copy(ReCompiled *p, int iStart, unsigned int N){
if ( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2 +N) ) return ;
memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof (p->aOp[0 ]));
memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof (p->aArg[0 ]));
p->nState += N;
}
/* Return true if c is a hexadecimal digit character: [0-9a-fA-F]
* * If c is a hex digit , also set * pV = ( * pV ) * 16 + valueof ( c ) . If
* * c is not a hex digit * pV is unchanged .
*/
static int re_hex(int c, int *pV){
if ( c>='0' && c<='9' ){
c -= '0' ;
}else if ( c>='a' && c<='f' ){
c -= 'a' - 10 ;
}else if ( c>='A' && c<='F' ){
c -= 'A' - 10 ;
}else {
return 0 ;
}
*pV = (*pV)*16 + (c & 0 xff);
return 1 ;
}
/* A backslash character has been seen, read the next character and
* * return its interpretation .
*/
static unsigned re_esc_char(ReCompiled *p){
static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]-" ;
static const char zTrans[] = "\a\f\n\r\t\v" ;
int i, v = 0 ;
char c;
if ( p->sIn.i>=p->sIn.mx ) return 0 ;
c = p->sIn.z[p->sIn.i];
if ( c=='u' && p->sIn.i+4 <p->sIn.mx ){
const unsigned char *zIn = p->sIn.z + p->sIn.i;
if ( re_hex(zIn[1 ],&v)
&& re_hex(zIn[2 ],&v)
&& re_hex(zIn[3 ],&v)
&& re_hex(zIn[4 ],&v)
){
p->sIn.i += 5 ;
return v;
}
}
if ( c=='x' && p->sIn.i+2 <p->sIn.mx ){
const unsigned char *zIn = p->sIn.z + p->sIn.i;
if ( re_hex(zIn[1 ],&v)
&& re_hex(zIn[2 ],&v)
){
p->sIn.i += 3 ;
return v;
}
}
for (i=0 ; zEsc[i] && zEsc[i]!=c; i++){}
if ( zEsc[i] ){
if ( i<6 ) c = zTrans[i];
p->sIn.i++;
}else {
p->zErr = "unknown \\ escape" ;
}
return c;
}
/* Forward declaration */
static const char *re_subcompile_string(ReCompiled*);
/* Peek at the next byte of input */
static unsigned char rePeek(ReCompiled *p){
return p->sIn.i<p->sIn.mx ? p->sIn.z[p->sIn.i] : 0 ;
}
/* Compile RE text into a sequence of opcodes. Continue up to the
* * first unmatched " ) " character , then return . If an error is found ,
* * return a pointer to the error message string .
*/
static const char *re_subcompile_re(ReCompiled *p){
const char *zErr;
int iStart, iEnd, iGoto;
iStart = p->nState;
zErr = re_subcompile_string(p);
if ( zErr ) return zErr;
while ( rePeek(p)=='|' ){
iEnd = p->nState;
re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart);
iGoto = re_append(p, RE_OP_GOTO, 0 );
p->sIn.i++;
zErr = re_subcompile_string(p);
if ( zErr ) return zErr;
p->aArg[iGoto] = p->nState - iGoto;
}
return 0 ;
}
/* Compile an element of regular expression text (anything that can be
* * an operand to the " | " operator ) . Return NULL on success or a pointer
* * to the error message if there is a problem .
*/
static const char *re_subcompile_string(ReCompiled *p){
int iPrev = -1 ;
int iStart;
unsigned c;
const char *zErr;
while ( (c = p->xNextChar(&p->sIn))!=0 ){
iStart = p->nState;
switch ( c ){
case '|' :
case ')' : {
p->sIn.i--;
return 0 ;
}
case '(' : {
zErr = re_subcompile_re(p);
if ( zErr ) return zErr;
if ( rePeek(p)!=')' ) return "unmatched '('" ;
p->sIn.i++;
break ;
}
case '.' : {
if ( rePeek(p)=='*' ){
re_append(p, RE_OP_ANYSTAR, 0 );
p->sIn.i++;
}else {
re_append(p, RE_OP_ANY, 0 );
}
break ;
}
case '*' : {
if ( iPrev<0 ) return "'*' without operand" ;
re_insert(p, iPrev, RE_OP_GOTO, p->nState - iPrev + 1 );
re_append(p, RE_OP_FORK, iPrev - p->nState + 1 );
break ;
}
case '+' : {
if ( iPrev<0 ) return "'+' without operand" ;
re_append(p, RE_OP_FORK, iPrev - p->nState);
break ;
}
case '?' : {
if ( iPrev<0 ) return "'?' without operand" ;
re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1 );
break ;
}
case '$' : {
re_append(p, RE_OP_MATCH, RE_EOF);
break ;
}
case '^' : {
re_append(p, RE_OP_ATSTART, 0 );
break ;
}
case '{' : {
unsigned int m = 0 , n = 0 ;
unsigned int sz, j;
if ( iPrev<0 ) return "'{m,n}' without operand" ;
while ( (c=rePeek(p))>='0' && c<='9' ){
m = m*10 + c - '0' ;
if ( m*2 >p->mxAlloc ) return "REGEXP pattern too big" ;
p->sIn.i++;
}
n = m;
if ( c==',' ){
p->sIn.i++;
n = 0 ;
while ( (c=rePeek(p))>='0' && c<='9' ){
n = n*10 + c-'0' ;
if ( n*2 >p->mxAlloc ) return "REGEXP pattern too big" ;
p->sIn.i++;
}
}
if ( c!='}' ) return "unmatched '{'" ;
if ( n<m ) return "n less than m in '{m,n}'" ;
p->sIn.i++;
sz = p->nState - iPrev;
if ( m==0 ){
if ( n==0 ) return "both m and n are zero in '{m,n}'" ;
re_insert(p, iPrev, RE_OP_FORK, sz+1 );
iPrev++;
n--;
}else {
for (j=1 ; j<m; j++) re_copy(p, iPrev, sz);
}
for (j=m; j<n; j++){
re_append(p, RE_OP_FORK, sz+1 );
re_copy(p, iPrev, sz);
}
if ( n==0 && m>0 ){
re_append(p, RE_OP_FORK, -(int )sz);
}
break ;
}
case '[' : {
unsigned int iFirst = p->nState;
if ( rePeek(p)=='^' ){
re_append(p, RE_OP_CC_EXC, 0 );
p->sIn.i++;
}else {
re_append(p, RE_OP_CC_INC, 0 );
}
while ( (c = p->xNextChar(&p->sIn))!=0 ){
if ( c=='[' && rePeek(p)==':' ){
return "POSIX character classes not supported" ;
}
if ( c=='\\' ) c = re_esc_char(p);
if ( rePeek(p)=='-' ){
re_append(p, RE_OP_CC_RANGE, c);
p->sIn.i++;
c = p->xNextChar(&p->sIn);
if ( c=='\\' ) c = re_esc_char(p);
re_append(p, RE_OP_CC_RANGE, c);
}else {
re_append(p, RE_OP_CC_VALUE, c);
}
if ( rePeek(p)==']' ){ p->sIn.i++; break ; }
}
if ( c==0 ) return "unclosed '['" ;
if ( p->nState>iFirst ) p->aArg[iFirst] = p->nState - iFirst;
break ;
}
case '\\' : {
int specialOp = 0 ;
switch ( rePeek(p) ){
case 'b' : specialOp = RE_OP_BOUNDARY; break ;
case 'd' : specialOp = RE_OP_DIGIT; break ;
case 'D' : specialOp = RE_OP_NOTDIGIT; break ;
case 's' : specialOp = RE_OP_SPACE; break ;
case 'S' : specialOp = RE_OP_NOTSPACE; break ;
case 'w' : specialOp = RE_OP_WORD; break ;
case 'W' : specialOp = RE_OP_NOTWORD; break ;
}
if ( specialOp ){
p->sIn.i++;
re_append(p, specialOp, 0 );
}else {
c = re_esc_char(p);
re_append(p, RE_OP_MATCH, c);
}
break ;
}
default : {
re_append(p, RE_OP_MATCH, c);
break ;
}
}
iPrev = iStart;
}
return 0 ;
}
/* Free and reclaim all the memory used by a previously compiled
* * regular expression . Applications should invoke this routine once
* * for every call to re_compile ( ) to avoid memory leaks .
*/
static void re_free(ReCompiled *pRe){
if ( pRe ){
sqlite3_free(pRe->aOp);
sqlite3_free(pRe->aArg);
sqlite3_free(pRe);
}
}
/*
* * Version of re_free ( ) that accepts a pointer of type ( void * ) . Required
* * to satisfy sanitizers when the re_free ( ) function is called via a
* * function pointer .
*/
static void re_free_voidptr(void *p){
re_free((ReCompiled*)p);
}
/*
* * Compile a textual regular expression in zIn [ ] into a compiled regular
* * expression suitable for us by re_match ( ) and return a pointer to the
* * compiled regular expression in * ppRe . Return NULL on success or an
* * error message if something goes wrong .
*/
static const char *re_compile(
ReCompiled **ppRe, /* OUT: write compiled NFA here */
const char *zIn, /* Input regular expression */
int mxRe, /* Complexity limit */
int noCase /* True for caseless comparisons */
){
ReCompiled *pRe;
const char *zErr;
int i, j;
*ppRe = 0 ;
pRe = sqlite3_malloc64( sizeof (*pRe) );
if ( pRe==0 ){
return "out of memory" ;
}
memset(pRe, 0 , sizeof (*pRe));
pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
pRe->mxAlloc = mxRe;
if ( re_resize(pRe, 30 ) ){
zErr = pRe->zErr;
re_free(pRe);
return zErr;
}
if ( zIn[0 ]=='^' ){
zIn++;
}else {
re_append(pRe, RE_OP_ANYSTAR, 0 );
}
pRe->sIn.z = (unsigned char *)zIn;
pRe->sIn.i = 0 ;
pRe->sIn.mx = (int )strlen(zIn);
zErr = re_subcompile_re(pRe);
if ( zErr ){
re_free(pRe);
return zErr;
}
if ( pRe->sIn.i>=pRe->sIn.mx ){
re_append(pRe, RE_OP_ACCEPT, 0 );
*ppRe = pRe;
}else {
re_free(pRe);
return "unrecognized character" ;
}
/* The following is a performance optimization. If the regex begins with
* * " . * " ( if the input regex lacks an initial " ^ " ) and afterwards there are
* * one or more matching characters , enter those matching characters into
* * zInit [ ] . The re_match ( ) routine can then search ahead in the input
* * string looking for the initial match without having to run the whole
* * regex engine over the string . Do not worry about trying to match
* * unicode characters beyond plane 0 - those are very rare and this is
** just an optimization. */
if ( pRe->aOp[0 ]==RE_OP_ANYSTAR && !noCase ){
for (j=0 , i=1 ; j<(int )sizeof (pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
unsigned x = pRe->aArg[i];
if ( x<=0 x7f ){
pRe->zInit[j++] = (unsigned char )x;
}else if ( x<=0 x7ff ){
pRe->zInit[j++] = (unsigned char )(0 xc0 | (x>>6 ));
pRe->zInit[j++] = 0 x80 | (x&0 x3f);
}else if ( x<=0 xffff ){
pRe->zInit[j++] = (unsigned char )(0 xe0 | (x>>12 ));
pRe->zInit[j++] = 0 x80 | ((x>>6 )&0 x3f);
pRe->zInit[j++] = 0 x80 | (x&0 x3f);
}else {
break ;
}
}
if ( j>0 && pRe->zInit[j-1 ]==0 ) j--;
pRe->nInit = j;
}
return pRe->zErr;
}
/*
* * The value of LIMIT_MAX_PATTERN_LENGTH .
*/
static int re_maxlen(sqlite3_context *context){
sqlite3 *db = sqlite3_context_db_handle(context);
return sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1 );
}
/*
* * Maximum NFA size given a maximum pattern length .
*/
static int re_maxnfa(int mxlen){
return 75 +mxlen/2 ;
}
/*
* * Implementation of the regexp ( ) SQL function . This function implements
* * the build - in REGEXP operator . The first argument to the function is the
* * pattern and the second argument is the string . So , the SQL statements :
* *
* * A REGEXP B
* *
* * is implemented as regexp ( B , A ) .
*/
static void re_sql_func(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
ReCompiled *pRe; /* Compiled regular expression */
const char *zPattern; /* The regular expression */
const unsigned char *zStr;/* String being searched */
const char *zErr; /* Compile error message */
int setAux = 0 ; /* True to invoke sqlite3_set_auxdata() */
(void )argc; /* Unused */
pRe = sqlite3_get_auxdata(context, 0 );
if ( pRe==0 ){
int mxLen = re_maxlen(context);
int nPattern;
zPattern = (const char *)sqlite3_value_text(argv[0 ]);
if ( zPattern==0 ) return ;
nPattern = sqlite3_value_bytes(argv[0 ]);
if ( nPattern>mxLen ){
zErr = "REGEXP pattern too big" ;
}else {
zErr = re_compile(&pRe, zPattern, re_maxnfa(mxLen),
sqlite3_user_data(context)!=0 );
}
if ( zErr ){
re_free(pRe);
sqlite3_result_error(context, zErr, -1 );
return ;
}
if ( pRe==0 ){
sqlite3_result_error_nomem(context);
return ;
}
setAux = 1 ;
}
zStr = (const unsigned char *)sqlite3_value_text(argv[1 ]);
if ( zStr!=0 ){
sqlite3_result_int(context, re_match(pRe, zStr, -1 ));
}
if ( setAux ){
sqlite3_set_auxdata(context, 0 , pRe, re_free_voidptr);
}
}
#if defined (SQLITE_DEBUG)
/*
* * This function is used for testing and debugging only . It is only available
* * if the SQLITE_DEBUG compile - time option is used .
* *
* * Compile a regular expression and then convert the compiled expression into
* * text and return that text .
*/
static void re_bytecode_func(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zPattern;
const char *zErr;
ReCompiled *pRe;
sqlite3_str *pStr;
int i;
int n;
char *z;
static const char *ReOpName[] = {
"EOF" ,
"MATCH" ,
"ANY" ,
"ANYSTAR" ,
"FORK" ,
"GOTO" ,
"ACCEPT" ,
"CC_INC" ,
"CC_EXC" ,
"CC_VALUE" ,
"CC_RANGE" ,
"WORD" ,
"NOTWORD" ,
"DIGIT" ,
"NOTDIGIT" ,
"SPACE" ,
"NOTSPACE" ,
"BOUNDARY" ,
"ATSTART" ,
};
(void )argc;
zPattern = (const char *)sqlite3_value_text(argv[0 ]);
if ( zPattern==0 ) return ;
zErr = re_compile(&pRe, zPattern, re_maxnfa(re_maxlen(context)),
sqlite3_user_data(context)!=0 );
if ( zErr ){
re_free(pRe);
sqlite3_result_error(context, zErr, -1 );
return ;
}
if ( pRe==0 ){
sqlite3_result_error_nomem(context);
return ;
}
pStr = sqlite3_str_new(0 );
if ( pStr==0 ) goto re_bytecode_func_err;
if ( pRe->nInit>0 ){
sqlite3_str_appendf(pStr, "INIT " );
for (i=0 ; i<pRe->nInit; i++){
sqlite3_str_appendf(pStr, "%02x" , pRe->zInit[i]);
}
sqlite3_str_appendf(pStr, "\n" );
}
for (i=0 ; (unsigned )i<pRe->nState; i++){
sqlite3_str_appendf(pStr, "%-8s %4d\n" ,
ReOpName[(unsigned char )pRe->aOp[i]], pRe->aArg[i]);
}
n = sqlite3_str_length(pStr);
z = sqlite3_str_finish(pStr);
if ( n==0 ){
sqlite3_free(z);
}else {
sqlite3_result_text(context, z, n-1 , sqlite3_free);
}
re_bytecode_func_err:
re_free(pRe);
}
#endif /* SQLITE_DEBUG */
/*
* * Invoke this routine to register the regexp ( ) function with the
* * SQLite database connection .
*/
#ifdef _WIN32
#endif
int sqlite3_regexp_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void )pzErrMsg; /* Unused */
rc = sqlite3_create_function(db, "regexp" , 2 ,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
0 , re_sql_func, 0 , 0 );
if ( rc==SQLITE_OK ){
/* The regexpi(PATTERN,STRING) function is a case-insensitive version
** of regexp(PATTERN,STRING). */
rc = sqlite3_create_function(db, "regexpi" , 2 ,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
(void *)db, re_sql_func, 0 , 0 );
#if defined (SQLITE_DEBUG)
if ( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "regexp_bytecode" , 1 ,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
0 , re_bytecode_func, 0 , 0 );
}
#endif /* SQLITE_DEBUG */
}
return rc;
}
/************************* End ext/misc/regexp.c ********************/
#ifndef SQLITE_SHELL_FIDDLE
/************************* Begin ext/misc/fileio.c ******************/
/*
* * 2014 - 06 - 13
* *
* * 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 SQL functions readfile ( ) and
* * writefile ( ) , and eponymous virtual type " fsdir " .
* *
* * WRITEFILE ( FILE , DATA [ , MODE [ , MTIME ] ] ) :
* *
* * If neither of the optional arguments is present , then this UDF
* * function writes blob DATA to file FILE . If successful , the number
* * of bytes written is returned . If an error occurs , NULL is returned .
* *
* * If the first option argument - MODE - is present , then it must
* * be passed an integer value that corresponds to a POSIX mode
* * value ( file type + permissions , as returned in the stat . st_mode
* * field by the stat ( ) system call ) . Three types of files may
* * be written / created :
* *
* * regular files : ( mode & 0170000 ) = = 0100000
* * symbolic links : ( mode & 0170000 ) = = 0120000
* * directories : ( mode & 0170000 ) = = 0040000
* *
* * For a directory , the DATA is ignored . For a symbolic link , it is
* * interpreted as text and used as the target of the link . For a
* * regular file , it is interpreted as a blob and written into the
* * named file . Regardless of the type of file , its permissions are
* * set to ( mode & 0777 ) before returning .
* *
* * If the optional MTIME argument is present , then it is interpreted
* * as an integer - the number of seconds since the unix epoch . The
* * modification - time of the target file is set to this value before
* * returning .
* *
* * If five or more arguments are passed to this function and an
* * error is encountered , an exception is raised .
* *
* * READFILE ( FILE ) :
* *
* * Read and return the contents of file FILE ( type blob ) from disk .
* *
* * FSDIR :
* *
* * Used as follows :
* *
* * SELECT * FROM fsdir ( $ path [ , $ dir ] ) ;
* *
* * Parameter $ path is an absolute or relative pathname . If the file that it
* * refers to does not exist , it is an error . If the path refers to a regular
* * file or symbolic link , it returns a single row . Or , if the path refers
* * to a directory , it returns one row for the directory , and one row for each
* * file within the hierarchy rooted at $ path .
* *
* * Each row has the following columns :
* *
* * name : Path to file or directory ( text value ) .
* * mode : Value of stat . st_mode for directory entry ( an integer ) .
* * mtime : Value of stat . st_mtime for directory entry ( an integer ) .
* * data : For a regular file , a blob containing the file data . For a
* * symlink , a text value containing the text of the link . For a
* * directory , NULL .
* * level : Directory hierarchy level . Topmost is 1 .
* *
* * If a non - NULL value is specified for the optional $ dir parameter and
* * $ path is a relative path , then $ path is interpreted relative to $ dir .
* * And the paths returned in the " name " column of the table are also
* * relative to directory $ dir .
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if !defined (_WIN32) && !defined (WIN32)
# include <unistd.h>
# include <dirent.h>
# include <utime.h>
# include <sys/time.h>
# define STRUCT_STAT struct stat
# include <limits.h>
# include <stdlib.h>
#else
/* # include "windirent.h" */
# include <direct.h>
# define STRUCT_STAT struct _stat
# define chmod(path,mode) fileio_chmod(path,mode)
# define mkdir(path,mode) fileio_mkdir(path)
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *);
extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
#endif
#include <time.h>
#include <errno.h>
/* When used as part of the CLI, the sqlite3_stdio.h module will have
* * been included before this one . In that case use the sqlite3_stdio . h
* * # defines . If not , create our own for fopen ( ) .
*/
#ifndef _SQLITE3_STDIO_H_
# define sqlite3_fopen fopen
#endif
/*
* * Structure of the fsdir ( ) table - valued function
*/
/* 0 1 2 3 4 5 6 */
#define FSDIR_SCHEMA "(name,mode,mtime,data,level,path HIDDEN,dir HIDDEN)"
#define FSDIR_COLUMN_NAME 0 /* Name of the file */
#define FSDIR_COLUMN_MODE 1 /* Access mode */
#define FSDIR_COLUMN_MTIME 2 /* Last modification time */
#define FSDIR_COLUMN_DATA 3 /* File content */
#define FSDIR_COLUMN_LEVEL 4 /* Level. Topmost is 1 */
#define FSDIR_COLUMN_PATH 5 /* Path to top of search */
#define FSDIR_COLUMN_DIR 6 /* Path is relative to this directory */
/*
* * UTF8 chmod ( ) function for Windows
*/
#if defined (_WIN32) || defined (WIN32)
static int fileio_chmod(const char *zPath, int pmode){
int rc;
wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath);
if ( b1==0 ) return -1 ;
rc = _wchmod(b1, pmode);
sqlite3_free(b1);
return rc;
}
#endif
/*
* * UTF8 mkdir ( ) function for Windows
*/
#if defined (_WIN32) || defined (WIN32)
static int fileio_mkdir(const char *zPath){
int rc;
wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath);
if ( b1==0 ) return -1 ;
rc = _wmkdir(b1);
sqlite3_free(b1);
return rc;
}
#endif
/*
* * Set the result stored by context ctx to a blob containing the
* * contents of file zName . Or , leave the result unchanged ( NULL )
* * if the file does not exist or is unreadable .
* *
* * If the file exceeds the SQLite blob size limit , through an
* * SQLITE_TOOBIG error .
* *
* * Throw an SQLITE_IOERR if there are difficulties pulling the file
* * off of disk .
*/
static void readFileContents(sqlite3_context *ctx, const char *zName){
FILE *in;
sqlite3_int64 nIn;
void *pBuf;
sqlite3 *db;
int mxBlob;
in = sqlite3_fopen(zName, "rb" );
if ( in==0 ){
/* File does not exist or is unreadable. Leave the result set to NULL. */
return ;
}
fseek(in, 0 , SEEK_END);
nIn = ftell(in);
rewind(in);
db = sqlite3_context_db_handle(ctx);
mxBlob = sqlite3_limit(db, SQLITE_LIMIT_LENGTH, -1 );
if ( nIn>mxBlob ){
sqlite3_result_error_code(ctx, SQLITE_TOOBIG);
fclose(in);
return ;
}
pBuf = sqlite3_malloc64( nIn ? nIn : 1 );
if ( pBuf==0 ){
sqlite3_result_error_nomem(ctx);
fclose(in);
return ;
}
if ( nIn==(sqlite3_int64)fread(pBuf, 1 , (size_t)nIn, in) ){
sqlite3_result_blob64(ctx, pBuf, nIn, sqlite3_free);
}else {
sqlite3_result_error_code(ctx, SQLITE_IOERR);
sqlite3_free(pBuf);
}
fclose(in);
}
/*
* * Implementation of the " readfile ( X ) " SQL function . The entire content
* * of the file named X is read and returned as a BLOB . NULL is returned
* * if the file does not exist or is unreadable .
*/
static void readfileFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zName;
(void )(argc); /* Unused parameter */
zName = (const char *)sqlite3_value_text(argv[0 ]);
if ( zName==0 ) return ;
readFileContents(context, zName);
}
/*
* * Set the error message contained in context ctx to the results of
* * vprintf ( zFmt , . . . ) .
*/
static void ctxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
char *zMsg = 0 ;
va_list ap;
va_start(ap, zFmt);
zMsg = sqlite3_vmprintf(zFmt, ap);
sqlite3_result_error(ctx, zMsg, -1 );
sqlite3_free(zMsg);
va_end(ap);
}
#if defined (_WIN32)
/*
* * This function is designed to convert a Win32 FILETIME structure into the
* * number of seconds since the Unix Epoch ( 1970 - 01 - 01 00 : 00 : 00 UTC ) .
*/
static sqlite3_uint64 fileTimeToUnixTime(
LPFILETIME pFileTime
){
SYSTEMTIME epochSystemTime;
ULARGE_INTEGER epochIntervals;
FILETIME epochFileTime;
ULARGE_INTEGER fileIntervals;
memset(&epochSystemTime, 0 , sizeof (SYSTEMTIME));
epochSystemTime.wYear = 1970 ;
epochSystemTime.wMonth = 1 ;
epochSystemTime.wDay = 1 ;
SystemTimeToFileTime(&epochSystemTime, &epochFileTime);
epochIntervals.LowPart = epochFileTime.dwLowDateTime;
epochIntervals.HighPart = epochFileTime.dwHighDateTime;
fileIntervals.LowPart = pFileTime->dwLowDateTime;
fileIntervals.HighPart = pFileTime->dwHighDateTime;
return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000 ;
}
#endif /* _WIN32 */
/*
* * This function is used in place of stat ( ) . On Windows , special handling
* * is required in order for the included time to be returned as UTC . On all
* * other systems , this function simply calls stat ( ) .
*/
static int fileStat(
const char *zPath,
STRUCT_STAT *pStatBuf
){
#if defined (_WIN32)
int rc;
wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath);
if ( b1==0 ) return 1 ;
rc = _wstat(b1, pStatBuf);
if ( rc==0 ){
HANDLE hFindFile;
WIN32_FIND_DATAW fd;
memset(&fd, 0 , sizeof (WIN32_FIND_DATAW));
hFindFile = FindFirstFileW(b1, &fd);
if ( hFindFile!=NULL ){
pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
FindClose(hFindFile);
}
}
sqlite3_free(b1);
return rc;
#else
return stat(zPath, pStatBuf);
#endif
}
/*
* * This function is used in place of lstat ( ) . On Windows , special handling
* * is required in order for the included time to be returned as UTC . On all
* * other systems , this function simply calls lstat ( ) .
*/
static int fileLinkStat(
const char *zPath,
STRUCT_STAT *pStatBuf
){
#if defined (_WIN32)
return fileStat(zPath, pStatBuf);
#else
return lstat(zPath, pStatBuf);
#endif
}
/*
* * Argument zFile is the name of a file that will be created and / or written
* * by SQL function writefile ( ) . This function ensures that the directory
* * zFile will be written to exists , creating it if required . The permissions
* * for any path components created by this function are set in accordance
* * with the current umask .
* *
* * If an OOM condition is encountered , SQLITE_NOMEM is returned . Otherwise ,
* * SQLITE_OK is returned if the directory is successfully created , or
* * SQLITE_ERROR otherwise .
*/
static int makeDirectory(
const char *zFile
){
char *zCopy = sqlite3_mprintf("%s" , zFile);
int rc = SQLITE_OK;
if ( zCopy==0 ){
rc = SQLITE_NOMEM;
}else {
int nCopy = (int )strlen(zCopy);
int i = 1 ;
while ( rc==SQLITE_OK ){
STRUCT_STAT sStat;
int rc2;
for (; zCopy[i]!='/' && i<nCopy; i++);
if ( i==nCopy ) break ;
zCopy[i] = '\0' ;
rc2 = fileStat(zCopy, &sStat);
if ( rc2!=0 ){
if ( mkdir(zCopy, 0777 ) ) rc = SQLITE_ERROR;
}else {
if ( !S_ISDIR(sStat.st_mode) ) rc = SQLITE_ERROR;
}
zCopy[i] = '/' ;
i++;
}
sqlite3_free(zCopy);
}
return rc;
}
/*
* * This function does the work for the writefile ( ) UDF . Refer to
* * header comments at the top of this file for details .
*/
static int writeFile(
sqlite3_context *pCtx, /* Context to return bytes written in */
const char *zFile, /* File to write */
sqlite3_value *pData, /* Data to write */
mode_t mode, /* MODE parameter passed to writefile() */
sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */
){
if ( zFile==0 ) return 1 ;
#if !defined (_WIN32) && !defined (WIN32)
if ( S_ISLNK(mode) ){
const char *zTo = (const char *)sqlite3_value_text(pData);
if ( zTo==0 ) return 1 ;
unlink(zFile);
if ( symlink(zTo, zFile)<0 ) return 1 ;
}else
#endif
{
if ( S_ISDIR(mode) ){
if ( mkdir(zFile, mode) ){
/* The mkdir() call to create the directory failed. This might not
* * be an error though - if there is already a directory at the same
* * path and either the permissions already match or can be changed
** to do so using chmod(), it is not an error. */
STRUCT_STAT sStat;
if ( errno!=EEXIST
|| 0 !=fileStat(zFile, &sStat)
|| !S_ISDIR(sStat.st_mode)
|| ((sStat.st_mode&0777 )!=(mode&>0777 ) && 0 !=chmod(zFile, mode&0777 ))
){
return 1 ;
}
}
}else {
sqlite3_int64 nWrite = 0 ;
const char *z;
int rc = 0 ;
FILE *out = sqlite3_fopen(zFile, "wb" );
if ( out==0 ) return 1 ;
z = (const char *)sqlite3_value_blob(pData);
if ( z ){
sqlite3_int64 n = fwrite(z, 1 , sqlite3_value_bytes(pData), out);
nWrite = sqlite3_value_bytes(pData);
if ( nWrite!=n ){
rc = 1 ;
}
}
fclose(out);
if ( rc==0 && mode && chmod(zFile, mode & 0777 ) ){
rc = 1 ;
}
if ( rc ) return 2 ;
sqlite3_result_int64(pCtx, nWrite);
}
}
if ( mtime>=0 ){
#if defined (_WIN32)
/* Windows */
FILETIME lastAccess;
FILETIME lastWrite;
SYSTEMTIME currentTime;
LONGLONG intervals;
HANDLE hFile;
LPWSTR zUnicodeName;
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *);
GetSystemTime(¤tTime);
SystemTimeToFileTime(¤tTime, &lastAccess);
intervals = (mtime*10000000 ) + 116444736000000000 ;
lastWrite.dwLowDateTime = (DWORD)intervals;
lastWrite.dwHighDateTime = intervals >> 32 ;
zUnicodeName = sqlite3_win32_utf8_to_unicode(zFile);
if ( zUnicodeName==0 ){
return 1 ;
}
hFile = CreateFileW(
zUnicodeName, FILE_WRITE_ATTRIBUTES, 0 , NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL
);
sqlite3_free(zUnicodeName);
if ( hFile!=INVALID_HANDLE_VALUE ){
BOOL bResult = SetFileTime(hFile, NULL, &lastAccess, &lastWrite);
CloseHandle(hFile);
return !bResult;
}else {
return 1 ;
}
#elif defined (AT_FDCWD) && 0 /* utimensat() is not universally available */
/* Recent unix */
struct timespec times[2 ];
times[0 ].tv_nsec = times[1 ].tv_nsec = 0 ;
times[0 ].tv_sec = time(0 );
times[1 ].tv_sec = mtime;
if ( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){
return 1 ;
}
#else
/* Legacy unix.
* *
* * Do not use utimes ( ) on a symbolic link - it sees through the link and
* * modifies the timestamps on the target . Or fails if the target does
** not exist. */
if ( 0 ==S_ISLNK(mode) ){
struct timeval times[2 ];
times[0 ].tv_usec = times[1 ].tv_usec = 0 ;
times[0 ].tv_sec = time(0 );
times[1 ].tv_sec = mtime;
if ( utimes(zFile, times) ){
return 1 ;
}
}
#endif
}
return 0 ;
}
/*
* * Implementation of the " writefile ( W , X [ , Y [ , Z ] ] ] ) " SQL function .
* * Refer to header comments at the top of this file for details .
*/
static void writefileFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zFile;
mode_t mode = 0 ;
int res;
sqlite3_int64 mtime = -1 ;
if ( argc<2 || argc>4 ){
sqlite3_result_error(context,
"wrong number of arguments to function writefile()" , -1
);
return ;
}
zFile = (const char *)sqlite3_value_text(argv[0 ]);
if ( zFile==0 ) return ;
if ( argc>=3 ){
mode = (mode_t)sqlite3_value_int(argv[2 ]);
}
if ( argc==4 ){
mtime = sqlite3_value_int64(argv[3 ]);
}
res = writeFile(context, zFile, argv[1 ], mode, mtime);
if ( res==1 && errno==ENOENT ){
if ( makeDirectory(zFile)==SQLITE_OK ){
res = writeFile(context, zFile, argv[1 ], mode, mtime);
}
}
if ( argc>2 && res!=0 ){
if ( S_ISLNK(mode) ){
ctxErrorMsg(context, "failed to create symlink: %s" , zFile);
}else if ( S_ISDIR(mode) ){
ctxErrorMsg(context, "failed to create directory: %s" , zFile);
}else {
ctxErrorMsg(context, "failed to write file: %s" , zFile);
}
}
}
/*
* * SQL function : lsmode ( MODE )
* *
* * Given a numberic st_mode from stat ( ) , convert it into a human - readable
* * text string in the style of " ls - l " .
*/
static void lsModeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int i;
int iMode = sqlite3_value_int(argv[0 ]);
char z[16 ];
(void )argc;
if ( S_ISLNK(iMode) ){
z[0 ] = 'l' ;
}else if ( S_ISREG(iMode) ){
z[0 ] = '-' ;
}else if ( S_ISDIR(iMode) ){
z[0 ] = 'd' ;
}else {
z[0 ] = '?' ;
}
for (i=0 ; i<3 ; i++){
int m = (iMode >> ((2 -i)*3 ));
char *a = &z[1 + i*3 ];
a[0 ] = (m & 0 x4) ? 'r' : '-' ;
a[1 ] = (m & 0 x2) ? 'w' : '-' ;
a[2 ] = (m & 0 x1) ? 'x' : '-' ;
}
z[10 ] = '\0' ;
sqlite3_result_text(context, z, -1 , SQLITE_TRANSIENT);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
* * Cursor type for recursively iterating through a directory structure .
*/
typedef struct fsdir_cursor fsdir_cursor;
typedef struct FsdirLevel FsdirLevel;
struct FsdirLevel {
DIR *pDir; /* From opendir() */
char *zDir; /* Name of directory (nul-terminated) */
};
struct fsdir_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
int nLvl; /* Number of entries in aLvl[] array */
int mxLvl; /* Maximum level */
int iLvl; /* Index of current entry */
FsdirLevel *aLvl; /* Hierarchy of directories being traversed */
const char *zBase;
int nBase;
STRUCT_STAT sStat; /* Current lstat() results */
char *zPath; /* Path to current entry */
sqlite3_int64 iRowid; /* Current rowid */
};
typedef struct fsdir_tab fsdir_tab;
struct fsdir_tab {
sqlite3_vtab base; /* Base class - must be first */
};
/*
* * Construct a new fsdir virtual table object .
*/
static int fsdirConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const *argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
fsdir_tab *pNew = 0 ;
int rc;
(void )pAux;
(void )argc;
(void )argv;
(void )pzErr;
rc = sqlite3_declare_vtab(db, "CREATE TABLE x" FSDIR_SCHEMA);
if ( rc==SQLITE_OK ){
pNew = (fsdir_tab*)sqlite3_malloc64( sizeof (*pNew) );
if ( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0 , sizeof (*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
}
*ppVtab = (sqlite3_vtab*)pNew;
return rc;
}
/*
* * This method is the destructor for fsdir vtab objects .
*/
static int fsdirDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
/*
* * Constructor for a new fsdir_cursor object .
*/
static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
fsdir_cursor *pCur;
(void )p;
pCur = sqlite3_malloc64( sizeof (*pCur) );
if ( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0 , sizeof (*pCur));
pCur->iLvl = -1 ;
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
* * Reset a cursor back to the state it was in when first returned
* * by fsdirOpen ( ) .
*/
static void fsdirResetCursor(fsdir_cursor *pCur){
int i;
for (i=0 ; i<=pCur->iLvl; i++){
FsdirLevel *pLvl = &pCur->aLvl[i];
if ( pLvl->pDir ) closedir(pLvl->pDir);
sqlite3_free(pLvl->zDir);
}
sqlite3_free(pCur->zPath);
sqlite3_free(pCur->aLvl);
pCur->aLvl = 0 ;
pCur->zPath = 0 ;
pCur->zBase = 0 ;
pCur->nBase = 0 ;
pCur->nLvl = 0 ;
pCur->iLvl = -1 ;
pCur->iRowid = 1 ;
}
/*
* * Destructor for an fsdir_cursor .
*/
static int fsdirClose(sqlite3_vtab_cursor *cur){
fsdir_cursor *pCur = (fsdir_cursor*)cur;
fsdirResetCursor(pCur);
sqlite3_free(pCur);
return SQLITE_OK;
}
/*
* * Set the error message for the virtual table associated with cursor
* * pCur to the results of vprintf ( zFmt , . . . ) .
*/
static void fsdirSetErrmsg(fsdir_cursor *pCur, const char *zFmt, ...){
va_list ap;
va_start(ap, zFmt);
pCur->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
}
/*
* * Advance an fsdir_cursor to its next row of output .
*/
static int fsdirNext(sqlite3_vtab_cursor *cur){
fsdir_cursor *pCur = (fsdir_cursor*)cur;
mode_t m = pCur->sStat.st_mode;
pCur->iRowid++;
if ( S_ISDIR(m) && pCur->iLvl+3 <pCur->mxLvl ){
/* Descend into this directory */
int iNew = pCur->iLvl + 1 ;
FsdirLevel *pLvl;
if ( iNew>=pCur->nLvl ){
int nNew = iNew+1 ;
sqlite3_int64 nByte = nNew*sizeof (FsdirLevel);
FsdirLevel *aNew = (FsdirLevel*)sqlite3_realloc64(pCur->aLvl, nByte);
if ( aNew==0 ) return SQLITE_NOMEM;
memset(&aNew[pCur->nLvl], 0 , sizeof (FsdirLevel)*(nNew-pCur->nLvl));
pCur->aLvl = aNew;
pCur->nLvl = nNew;
}
pCur->iLvl = iNew;
pLvl = &pCur->aLvl[iNew];
pLvl->zDir = pCur->zPath;
pCur->zPath = 0 ;
pLvl->pDir = opendir(pLvl->zDir);
if ( pLvl->pDir==0 ){
fsdirSetErrmsg(pCur, "cannot read directory: %s" , pLvl->zDir);
return SQLITE_ERROR;
}
}
while ( pCur->iLvl>=0 ){
FsdirLevel *pLvl = &pCur->aLvl[pCur->iLvl];
struct dirent *pEntry = readdir(pLvl->pDir);
if ( pEntry ){
if ( pEntry->d_name[0 ]=='.' ){
if ( pEntry->d_name[1 ]=='.' && pEntry->d_name[2 ]=='\0' ) continue ;
if ( pEntry->d_name[1 ]=='\0' ) continue ;
}
sqlite3_free(pCur->zPath);
pCur->zPath = sqlite3_mprintf("%s/%s" , pLvl->zDir, pEntry->d_name);
if ( pCur->zPath==0 ) return SQLITE_NOMEM;
if ( fileLinkStat(pCur->zPath, &pCur->sStat) ){
fsdirSetErrmsg(pCur, "cannot stat file: %s" , pCur->zPath);
return SQLITE_ERROR;
}
return SQLITE_OK;
}
closedir(pLvl->pDir);
sqlite3_free(pLvl->zDir);
pLvl->pDir = 0 ;
pLvl->zDir = 0 ;
pCur->iLvl--;
}
/* EOF */
sqlite3_free(pCur->zPath);
pCur->zPath = 0 ;
return SQLITE_OK;
}
/*
* * Return values of columns for the row at which the series_cursor
* * is currently pointing .
*/
static int fsdirColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
fsdir_cursor *pCur = (fsdir_cursor*)cur;
switch ( i ){
case FSDIR_COLUMN_NAME: {
sqlite3_result_text(ctx, &pCur->zPath[pCur->nBase], -1 , SQLITE_TRANSIENT);
break ;
}
case FSDIR_COLUMN_MODE:
sqlite3_result_int64(ctx, pCur->sStat.st_mode);
break ;
case FSDIR_COLUMN_MTIME:
sqlite3_result_int64(ctx, pCur->sStat.st_mtime);
break ;
case FSDIR_COLUMN_DATA: {
mode_t m = pCur->sStat.st_mode;
if ( S_ISDIR(m) ){
sqlite3_result_null(ctx);
#if !defined (_WIN32) && !defined (WIN32)
}else if ( S_ISLNK(m) ){
char aStatic[64 ];
char *aBuf = aStatic;
sqlite3_int64 nBuf = 64 ;
int n;
while ( 1 ){
n = readlink(pCur->zPath, aBuf, nBuf);
if ( n<nBuf ) break ;
if ( aBuf!=aStatic ) sqlite3_free(aBuf);
nBuf = nBuf*2 ;
aBuf = sqlite3_malloc64(nBuf);
if ( aBuf==0 ){
sqlite3_result_error_nomem(ctx);
return SQLITE_NOMEM;
}
}
sqlite3_result_text(ctx, aBuf, n, SQLITE_TRANSIENT);
if ( aBuf!=aStatic ) sqlite3_free(aBuf);
#endif
}else {
readFileContents(ctx, pCur->zPath);
}
break ;
}
case FSDIR_COLUMN_LEVEL:
sqlite3_result_int(ctx, pCur->iLvl+2 );
break ;
case FSDIR_COLUMN_PATH:
default : {
/* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters.
** always return their values as NULL */
break ;
}
}
return SQLITE_OK;
}
/*
* * Return the rowid for the current row . In this implementation , the
* * first row returned is assigned rowid value 1 , and each subsequent
* * row a value 1 more than that of the previous .
*/
static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
fsdir_cursor *pCur = (fsdir_cursor*)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
/*
* * Return TRUE if the cursor has been moved off of the last
* * row of output .
*/
static int fsdirEof(sqlite3_vtab_cursor *cur){
fsdir_cursor *pCur = (fsdir_cursor*)cur;
return (pCur->zPath==0 );
}
/*
* * xFilter callback .
* *
* * idxNum bit Meaning
* * 0 x01 PATH = N
* * 0 x02 DIR = N
* * 0 x04 LEVEL < N
* * 0 x08 LEVEL < = N
*/
static int fsdirFilter(
sqlite3_vtab_cursor *cur,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
const char *zDir = 0 ;
fsdir_cursor *pCur = (fsdir_cursor*)cur;
int i;
(void )idxStr;
fsdirResetCursor(pCur);
if ( idxNum==0 ){
fsdirSetErrmsg(pCur, "table function fsdir requires an argument" );
return SQLITE_ERROR;
}
assert( (idxNum & 0 x01)!=0 && argc>0 );
zDir = (const char *)sqlite3_value_text(argv[0 ]);
if ( zDir==0 ){
fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument" );
return SQLITE_ERROR;
}
i = 1 ;
if ( (idxNum & 0 x02)!=0 ){
assert( argc>i );
pCur->zBase = (const char *)sqlite3_value_text(argv[i++]);
}
if ( (idxNum & 0 x0c)!=0 ){
assert( argc>i );
pCur->mxLvl = sqlite3_value_int(argv[i++]);
if ( idxNum & 0 x08 ) pCur->mxLvl++;
if ( pCur->mxLvl<=0 ) pCur->mxLvl = 1000000000 ;
}else {
pCur->mxLvl = 1000000000 ;
}
if ( pCur->zBase ){
pCur->nBase = (int )strlen(pCur->zBase)+1 ;
pCur->zPath = sqlite3_mprintf("%s/%s" , pCur->zBase, zDir);
}else {
pCur->zPath = sqlite3_mprintf("%s" , zDir);
}
if ( pCur->zPath==0 ){
return SQLITE_NOMEM;
}
if ( fileLinkStat(pCur->zPath, &pCur->sStat) ){
fsdirSetErrmsg(pCur, "cannot stat file: %s" , pCur->zPath);
return SQLITE_ERROR;
}
return SQLITE_OK;
}
/*
* * SQLite will invoke this method one or more times while planning a query
* * that uses the generate_series virtual table . This routine needs to create
* * a query plan for each invocation and compute an estimated cost for that
* * plan .
* *
* * In this implementation idxNum is used to represent the
* * query plan . idxStr is unused .
* *
* * The query plan is represented by bits in idxNum :
* *
* * 0 x01 The path value is supplied by argv [ 0 ]
* * 0 x02 dir is in argv [ 1 ]
* * 0 x04 maxdepth is in argv [ 1 ] or [ 2 ]
*/
static int fsdirBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i; /* Loop over constraints */
int idxPath = -1 ; /* Index in pIdxInfo->aConstraint of PATH= */
int idxDir = -1 ; /* Index in pIdxInfo->aConstraint of DIR= */
int idxLevel = -1 ; /* Index in pIdxInfo->aConstraint of LEVEL< or <= */
int idxLevelEQ = 0 ; /* 0x08 for LEVEL<= or LEVEL=. 0x04 for LEVEL< */
int omitLevel = 0 ; /* omit the LEVEL constraint */
int seenPath = 0 ; /* True if an unusable PATH= constraint is seen */
int seenDir = 0 ; /* True if an unusable DIR= constraint is seen */
const struct sqlite3_index_constraint *pConstraint;
(void )tab;
pConstraint = pIdxInfo->aConstraint;
for (i=0 ; i<pIdxInfo->nConstraint; i++, pConstraint++){
if ( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
switch ( pConstraint->iColumn ){
case FSDIR_COLUMN_PATH: {
if ( pConstraint->usable ){
idxPath = i;
seenPath = 0 ;
}else if ( idxPath<0 ){
seenPath = 1 ;
}
break ;
}
case FSDIR_COLUMN_DIR: {
if ( pConstraint->usable ){
idxDir = i;
seenDir = 0 ;
}else if ( idxDir<0 ){
seenDir = 1 ;
}
break ;
}
case FSDIR_COLUMN_LEVEL: {
if ( pConstraint->usable && idxLevel<0 ){
idxLevel = i;
idxLevelEQ = 0 x08;
omitLevel = 0 ;
}
break ;
}
}
}else
if ( pConstraint->iColumn==FSDIR_COLUMN_LEVEL
&& pConstraint->usable
&& idxLevel<0
){
if ( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
idxLevel = i;
idxLevelEQ = 0 x08;
omitLevel = 1 ;
}else if ( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
idxLevel = i;
idxLevelEQ = 0 x04;
omitLevel = 1 ;
}
}
}
if ( seenPath || seenDir ){
/* If input parameters are unusable, disallow this plan */
return SQLITE_CONSTRAINT;
}
if ( idxPath<0 ){
pIdxInfo->idxNum = 0 ;
/* The pIdxInfo->estimatedCost should have been initialized to a huge
** number. Leave it unchanged. */
pIdxInfo->estimatedRows = 0 x7fffffff;
}else {
pIdxInfo->aConstraintUsage[idxPath].omit = 1 ;
pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1 ;
pIdxInfo->idxNum = 0 x01;
pIdxInfo->estimatedCost = 1 .0 e9;
i = 2 ;
if ( idxDir>=0 ){
pIdxInfo->aConstraintUsage[idxDir].omit = 1 ;
pIdxInfo->aConstraintUsage[idxDir].argvIndex = i++;
pIdxInfo->idxNum |= 0 x02;
pIdxInfo->estimatedCost /= 1 .0 e4;
}
if ( idxLevel>=0 ){
pIdxInfo->aConstraintUsage[idxLevel].omit = omitLevel;
pIdxInfo->aConstraintUsage[idxLevel].argvIndex = i++;
pIdxInfo->idxNum |= idxLevelEQ;
pIdxInfo->estimatedCost /= 1 .0 e4;
}
}
return SQLITE_OK;
}
/*
* * Register the " fsdir " virtual table .
*/
static int fsdirRegister(sqlite3 *db){
static sqlite3_module fsdirModule = {
0 , /* iVersion */
0 , /* xCreate */
fsdirConnect, /* xConnect */
fsdirBestIndex, /* xBestIndex */
fsdirDisconnect, /* xDisconnect */
0 , /* xDestroy */
fsdirOpen, /* xOpen - open a cursor */
fsdirClose, /* xClose - close a cursor */
fsdirFilter, /* xFilter - configure scan constraints */
fsdirNext, /* xNext - advance a cursor */
fsdirEof, /* xEof - check for end of scan */
fsdirColumn, /* xColumn - read data */
fsdirRowid, /* xRowid - read data */
0 , /* xUpdate */
0 , /* xBegin */
0 , /* xSync */
0 , /* xCommit */
0 , /* xRollback */
0 , /* xFindMethod */
0 , /* xRename */
0 , /* xSavepoint */
0 , /* xRelease */
0 , /* xRollbackTo */
0 , /* xShadowName */
0 /* xIntegrity */
};
int rc = sqlite3_create_module(db, "fsdir" , &fsdirModule, 0 );
return rc;
}
#else /* SQLITE_OMIT_VIRTUALTABLE */
# define fsdirRegister(x) SQLITE_OK
#endif
/*
* * This version of realpath ( ) works on any system . The string
* * returned is held in memory allocated using sqlite3_malloc64 ( ) .
* * The caller is responsible for calling sqlite3_free ( ) .
*/
static char *portable_realpath(const char *zPath){
#if !defined (_WIN32) /* BEGIN unix */
char *zOut = 0 ; /* Result */
char *z; /* Temporary buffer */
#if defined (PATH_MAX)
char zBuf[PATH_MAX+1 ]; /* Space for the temporary buffer */
#endif
if ( zPath==0 ) return 0 ;
#if defined (PATH_MAX)
z = realpath(zPath, zBuf);
if ( z ){
zOut = sqlite3_mprintf("%s" , zBuf);
}
#endif /* defined(PATH_MAX) */
if ( zOut==0 ){
/* Try POSIX.1-2008 malloc behavior */
z = realpath(zPath, NULL);
if ( z ){
zOut = sqlite3_mprintf("%s" , z);
free(z);
}
}
return zOut;
#else /* End UNIX, Begin WINDOWS */
wchar_t *zPath16; /* UTF16 translation of zPath */
char *zOut = 0 ; /* Result */
wchar_t *z = 0 ; /* Temporary buffer */
if ( zPath==0 ) return 0 ;
zPath16 = sqlite3_win32_utf8_to_unicode(zPath);
if ( zPath16==0 ) return 0 ;
z = _wfullpath(NULL, zPath16, 0 );
sqlite3_free(zPath16);
if ( z ){
zOut = sqlite3_win32_unicode_to_utf8(z);
free(z);
}
return zOut;
#endif /* End WINDOWS, Begin common code */
}
/*
* * SQL function : realpath ( X )
* *
* * Try to convert file or pathname X into its real , absolute pathname .
* * Return NULL if unable .
* *
* * The file or directory X is not required to exist . The answer is formed
* * by calling system realpath ( ) on the prefix of X that does exist and
* * appending the tail of X that does not ( yet ) exist .
*/
static void realpathFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zPath; /* Original input path */
char *zCopy; /* An editable copy of zPath */
char *zOut; /* The result */
char cSep = 0 ; /* Separator turned into \000 */
size_t len; /* Prefix length before cSep */
#ifdef _WIN32
const int isWin = 1 ;
#else
const int isWin = 0 ;
#endif
(void )argc;
zPath = (const char *)sqlite3_value_text(argv[0 ]);
if ( zPath==0 ) return ;
if ( zPath[0 ]==0 ) zPath = "." ;
zCopy = sqlite3_mprintf("%s" ,zPath);
len = strlen(zCopy);
while ( len>1 && (zCopy[len-1 ]=='/' || (isWin && zCopy[len-1 ]=='\\' )) ){
len--;
}
zCopy[len] = 0 ;
while ( 1 /*exit-by-break*/ ){
zOut = portable_realpath(zCopy);
zCopy[len] = cSep;
if ( zOut ){
if ( cSep ){
zOut = sqlite3_mprintf("%z%s" ,zOut,&zCopy[len]);
}
break ;
}else {
size_t i = len-1 ;
while ( i>0 ){
if ( zCopy[i]=='/' || (isWin && zCopy[i]=='\\' ) ) break ;
i--;
}
if ( i<=0 ){
if ( zCopy[0 ]=='/' ){
zOut = zCopy;
zCopy = 0 ;
}else if ( (zOut = portable_realpath("." ))!=0 ){
zOut = sqlite3_mprintf("%z/%s" , zOut, zCopy);
}
break ;
}
cSep = zCopy[i];
zCopy[i] = 0 ;
len = i;
}
}
sqlite3_free(zCopy);
if ( zOut ){
/* Simplify any "/./" or "/../" that might have snuck into the
* * pathname due to appending of zCopy . We only have to consider
* * unix " / " separators , because the _ wfilepath ( ) system call on
** Windows will have already done this simplification for us. */
size_t i, j, n;
n = strlen(zOut);
for (i=j=0 ; i<n; i++){
if ( zOut[i]=='/' ){
if ( zOut[i+1 ]=='/' ) continue ;
if ( zOut[i+1 ]=='.' && i+2 <n && zOut[i+2 ]=='/' ){
i += 1 ;
continue ;
}
if ( zOut[i+1 ]=='.' && i+3 <n && zOut[i+2 ]=='.' && zOut[i+3 ]=='/' ){
while ( j>0 && zOut[j-1 ]!='/' ){ j--; }
if ( j>0 ){ j--; }
i += 2 ;
continue ;
}
}
zOut[j++] = zOut[i];
}
zOut[j] = 0 ;
/* Return the result */
sqlite3_result_text(context, zOut, -1 , sqlite3_free);
}
}
#ifdef _WIN32
#endif
int sqlite3_fileio_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void )pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "readfile" , 1 ,
SQLITE_UTF8|SQLITE_DIRECTONLY, 0 ,
readfileFunc, 0 , 0 );
if ( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "writefile" , -1 ,
SQLITE_UTF8|SQLITE_DIRECTONLY, 0 ,
writefileFunc, 0 , 0 );
}
if ( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "lsmode" , 1 , SQLITE_UTF8, 0 ,
lsModeFunc, 0 , 0 );
}
if ( rc==SQLITE_OK ){
rc = fsdirRegister(db);
}
if ( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "realpath" , 1 ,
SQLITE_UTF8, 0 ,
realpathFunc, 0 , 0 );
}
return rc;
}
/************************* End ext/misc/fileio.c ********************/
/************************* Begin ext/misc/completion.c ******************/
/*
* * 2017 - 07 - 10
* *
* * 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 an eponymous virtual table that returns suggested
* * completions for a partial SQL input .
* *
* * Suggested usage :
* *
* * SELECT DISTINCT candidate COLLATE nocase
* * FROM completion ( $ prefix , $ wholeline )
* * ORDER BY 1 ;
* *
* * The two query parameters are optional . $ prefix is the text of the
* * current word being typed and that is to be completed . $ wholeline is
* * the complete input line , used for context .
* *
* * The raw completion ( ) table might return the same candidate multiple
* * times , for example if the same column name is used to two or more
* * tables . And the candidates are returned in an arbitrary order . Hence ,
* * the DISTINCT and ORDER BY are recommended .
* *
* * This virtual table operates at the speed of human typing , and so there
* * is no attempt to make it fast . Even a slow implementation will be much
* * faster than any human can type .
* *
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <ctype.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
#ifndef IsAlnum
#define IsAlnum(X) isalnum((unsigned char )X)
#endif
/* completion_vtab is a subclass of sqlite3_vtab which will
* * serve as the underlying representation of a completion virtual table
*/
typedef struct completion_vtab completion_vtab;
struct completion_vtab {
sqlite3_vtab base; /* Base class - must be first */
sqlite3 *db; /* Database connection for this completion vtab */
};
/* completion_cursor is a subclass of sqlite3_vtab_cursor which will
* * serve as the underlying representation of a cursor that scans
* * over rows of the result
*/
typedef struct completion_cursor completion_cursor;
struct completion_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3 *db; /* Database connection for this cursor */
int nPrefix, nLine; /* Number of bytes in zPrefix and zLine */
char *zPrefix; /* The prefix for the word we want to complete */
char *zLine; /* The whole that we want to complete */
const char *zCurrentRow; /* Current output row */
int szRow; /* Length of the zCurrentRow string */
sqlite3_stmt *pStmt; /* Current statement */
sqlite3_int64 iRowid; /* The rowid */
int ePhase; /* Current phase */
int j; /* inter-phase counter */
};
/* Values for ePhase:
*/
#define COMPLETION_FIRST_PHASE 1
#define COMPLETION_KEYWORDS 1
#define COMPLETION_PRAGMAS 2
#define COMPLETION_FUNCTIONS 3
#define COMPLETION_COLLATIONS 4
#define COMPLETION_INDEXES 5
#define COMPLETION_TRIGGERS 6
#define COMPLETION_DATABASES 7
#define COMPLETION_TABLES 8 /* Also VIEWs and TRIGGERs */
#define COMPLETION_COLUMNS 9
#define COMPLETION_MODULES 10
#define COMPLETION_EOF 11
/*
* * The completionConnect ( ) method is invoked to create a new
* * completion_vtab that describes the completion virtual table .
* *
* * Think of this routine as the constructor for completion_vtab objects .
* *
* * All this routine needs to do is :
* *
* * ( 1 ) Allocate the completion_vtab object and initialize all fields .
* *
* * ( 2 ) Tell SQLite ( via the sqlite3_declare_vtab ( ) interface ) what the
* * result set of queries against completion will look like .
*/
static int completionConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const *argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
completion_vtab *pNew;
int rc;
(void )(pAux); /* Unused parameter */
(void )(argc); /* Unused parameter */
(void )(argv); /* Unused parameter */
(void )(pzErr); /* Unused parameter */
/* Column numbers */
#define COMPLETION_COLUMN_CANDIDATE 0 /* Suggested completion of the input */
#define COMPLETION_COLUMN_PREFIX 1 /* Prefix of the word to be completed */
#define COMPLETION_COLUMN_WHOLELINE 2 /* Entire line seen so far */
#define COMPLETION_COLUMN_PHASE 3 /* ePhase - used for debugging only */
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x("
" candidate TEXT,"
" prefix TEXT HIDDEN,"
" wholeline TEXT HIDDEN,"
" phase INT HIDDEN" /* Used for debugging only */
")" );
if ( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof (*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if ( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0 , sizeof (*pNew));
pNew->db = db;
}
return rc;
}
/*
* * This method is the destructor for completion_cursor objects .
*/
static int completionDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
/*
* * Constructor for a new completion_cursor object .
*/
static int completionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
completion_cursor *pCur;
pCur = sqlite3_malloc64( sizeof (*pCur) );
if ( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0 , sizeof (*pCur));
pCur->db = ((completion_vtab*)p)->db;
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
* * Reset the completion_cursor .
*/
static void completionCursorReset(completion_cursor *pCur){
sqlite3_free(pCur->zPrefix); pCur->zPrefix = 0 ; pCur->nPrefix = 0 ;
sqlite3_free(pCur->zLine); pCur->zLine = 0 ; pCur->nLine = 0 ;
sqlite3_finalize(pCur->pStmt); pCur->pStmt = 0 ;
pCur->j = 0 ;
}
/*
* * Destructor for a completion_cursor .
*/
static int completionClose(sqlite3_vtab_cursor *cur){
completionCursorReset((completion_cursor*)cur);
sqlite3_free(cur);
return SQLITE_OK;
}
/*
* * Advance a completion_cursor to its next row of output .
* *
* * The - > ePhase , - > j , and - > pStmt fields of the completion_cursor object
* * record the current state of the scan . This routine sets - > zCurrentRow
* * to the current row of output and then returns . If no more rows remain ,
* * then - > ePhase is set to COMPLETION_EOF which will signal the virtual
* * table that has reached the end of its scan .
* *
* * The current implementation just lists potential identifiers and
* * keywords and filters them by zPrefix . Future enhancements should
* * take zLine into account to try to restrict the set of identifiers and
* * keywords based on what would be legal at the current point of input .
*/
static int completionNext(sqlite3_vtab_cursor *cur){
completion_cursor *pCur = (completion_cursor*)cur;
int eNextPhase = 0 ; /* Next phase to try if current phase reaches end */
int iCol = -1 ; /* If >=0, step pCur->pStmt and use the i-th column */
int rc;
pCur->iRowid++;
while ( pCur->ePhase!=COMPLETION_EOF ){
switch ( pCur->ePhase ){
case COMPLETION_KEYWORDS: {
if ( pCur->j >= sqlite3_keyword_count() ){
pCur->zCurrentRow = 0 ;
pCur->ePhase = COMPLETION_DATABASES;
}else {
sqlite3_keyword_name(pCur->j++, &pCur->zCurrentRow, &pCur->szRow);
}
iCol = -1 ;
break ;
}
case COMPLETION_DATABASES: {
if ( pCur->pStmt==0 ){
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list" , -1 ,
&pCur->pStmt, 0 );
}
iCol = 1 ;
eNextPhase = COMPLETION_TABLES;
break ;
}
case COMPLETION_TABLES: {
if ( pCur->pStmt==0 ){
sqlite3_stmt *pS2;
sqlite3_str* pStr = sqlite3_str_new(pCur->db);
char *zSql = 0 ;
const char *zSep = "" ;
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list" , -1 , &pS2, 0 );
while ( sqlite3_step(pS2)==SQLITE_ROW ){
const char *zDb = (const char *)sqlite3_column_text(pS2, 1 );
sqlite3_str_appendf(pStr,
"%s"
"SELECT name FROM \" %w\".sqlite_schema" ,
zSep, zDb
);
zSep = " UNION " ;
}
rc = sqlite3_finalize(pS2);
zSql = sqlite3_str_finish(pStr);
if ( zSql==0 ) return SQLITE_NOMEM;
if ( rc==SQLITE_OK ){
sqlite3_prepare_v2(pCur->db, zSql, -1 , &pCur->pStmt, 0 );
}
sqlite3_free(zSql);
if ( rc ) return rc;
}
iCol = 0 ;
eNextPhase = COMPLETION_COLUMNS;
break ;
}
case COMPLETION_COLUMNS: {
if ( pCur->pStmt==0 ){
sqlite3_stmt *pS2;
sqlite3_str *pStr = sqlite3_str_new(pCur->db);
char *zSql = 0 ;
const char *zSep = "" ;
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list" , -1 , &pS2, 0 );
while ( sqlite3_step(pS2)==SQLITE_ROW ){
const char *zDb = (const char *)sqlite3_column_text(pS2, 1 );
sqlite3_str_appendf(pStr,
"%s"
"SELECT pti.name FROM \" %w\".sqlite_schema AS sm"
" JOIN pragma_table_xinfo(sm.name,%Q) AS pti"
" WHERE sm.type='table'" ,
zSep, zDb, zDb
);
zSep = " UNION " ;
}
rc = sqlite3_finalize(pS2);
zSql = sqlite3_str_finish(pStr);
if ( zSql==0 ) return SQLITE_NOMEM;
if ( rc==SQLITE_OK ){
sqlite3_prepare_v2(pCur->db, zSql, -1 , &pCur->pStmt, 0 );
}
sqlite3_free(zSql);
if ( rc ) return rc;
}
iCol = 0 ;
eNextPhase = COMPLETION_EOF;
break ;
}
}
if ( iCol<0 ){
/* This case is when the phase presets zCurrentRow */
if ( pCur->zCurrentRow==0 ) continue ;
}else {
if ( sqlite3_step(pCur->pStmt)==SQLITE_ROW ){
/* Extract the next row of content */
pCur->zCurrentRow = (const char *)sqlite3_column_text(pCur->pStmt, iCol);
pCur->szRow = sqlite3_column_bytes(pCur->pStmt, iCol);
}else {
/* When all rows are finished, advance to the next phase */
rc = sqlite3_finalize(pCur->pStmt);
pCur->pStmt = 0 ;
pCur->ePhase = eNextPhase;
if ( rc ) return rc;
continue ;
}
}
if ( pCur->nPrefix==0 ) break ;
if ( pCur->nPrefix<=pCur->szRow
&& sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0
){
break ;
}
}
return SQLITE_OK;
}
/*
* * Return values of columns for the row at which the completion_cursor
* * is currently pointing .
*/
static int completionColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
completion_cursor *pCur = (completion_cursor*)cur;
switch ( i ){
case COMPLETION_COLUMN_CANDIDATE: {
sqlite3_result_text(ctx, pCur->zCurrentRow, pCur->szRow,SQLITE_TRANSIENT);
break ;
}
case COMPLETION_COLUMN_PREFIX: {
sqlite3_result_text(ctx, pCur->zPrefix, -1 , SQLITE_TRANSIENT);
break ;
}
case COMPLETION_COLUMN_WHOLELINE: {
sqlite3_result_text(ctx, pCur->zLine, -1 , SQLITE_TRANSIENT);
break ;
}
case COMPLETION_COLUMN_PHASE: {
sqlite3_result_int(ctx, pCur->ePhase);
break ;
}
}
return SQLITE_OK;
}
/*
* * Return the rowid for the current row . In this implementation , the
* * rowid is the same as the output value .
*/
static int completionRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
completion_cursor *pCur = (completion_cursor*)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
/*
* * Return TRUE if the cursor has been moved off of the last
* * row of output .
*/
static int completionEof(sqlite3_vtab_cursor *cur){
completion_cursor *pCur = (completion_cursor*)cur;
return pCur->ePhase >= COMPLETION_EOF;
}
/*
* * This method is called to " rewind " the completion_cursor object back
* * to the first row of output . This method is always called at least
* * once prior to any call to completionColumn ( ) or completionRowid ( ) or
* * completionEof ( ) .
*/
static int completionFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
completion_cursor *pCur = (completion_cursor *)pVtabCursor;
int iArg = 0 ;
(void )(idxStr); /* Unused parameter */
(void )(argc); /* Unused parameter */
completionCursorReset(pCur);
if ( idxNum & 1 ){
pCur->nPrefix = sqlite3_value_bytes(argv[iArg]);
if ( pCur->nPrefix>0 ){
pCur->zPrefix = sqlite3_mprintf("%s" , sqlite3_value_text(argv[iArg]));
if ( pCur->zPrefix==0 ) return SQLITE_NOMEM;
pCur->nPrefix = (int )strlen(pCur->zPrefix);
}
iArg = 1 ;
}
if ( idxNum & 2 ){
pCur->nLine = sqlite3_value_bytes(argv[iArg]);
if ( pCur->nLine>0 ){
pCur->zLine = sqlite3_mprintf("%s" , sqlite3_value_text(argv[iArg]));
if ( pCur->zLine==0 ) return SQLITE_NOMEM;
pCur->nLine = (int )strlen(pCur->zLine);
}
}
if ( pCur->zLine!=0 && pCur->zPrefix==0 ){
int i = pCur->nLine;
while ( i>0 && (IsAlnum(pCur->zLine[i-1 ]) || pCur->zLine[i-1 ]=='_' ) ){
i--;
}
pCur->nPrefix = pCur->nLine - i;
if ( pCur->nPrefix>0 ){
pCur->zPrefix = sqlite3_mprintf("%.*s" , pCur->nPrefix, pCur->zLine + i);
if ( pCur->zPrefix==0 ) return SQLITE_NOMEM;
pCur->nPrefix = (int )strlen(pCur->zPrefix);
}
}
pCur->iRowid = 0 ;
pCur->ePhase = COMPLETION_FIRST_PHASE;
return completionNext(pVtabCursor);
}
/*
* * SQLite will invoke this method one or more times while planning a query
* * that uses the completion virtual table . This routine needs to create
* * a query plan for each invocation and compute an estimated cost for that
* * plan .
* *
* * There are two hidden parameters that act as arguments to the table - valued
* * function : " prefix " and " wholeline " . Bit 0 of idxNum is set if " prefix "
* * is available and bit 1 is set if " wholeline " is available .
*/
static int completionBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i; /* Loop over constraints */
int idxNum = 0 ; /* The query plan bitmask */
int prefixIdx = -1 ; /* Index of the start= constraint, or -1 if none */
int wholelineIdx = -1 ; /* Index of the stop= constraint, or -1 if none */
int nArg = 0 ; /* Number of arguments that completeFilter() expects */
const struct sqlite3_index_constraint *pConstraint;
(void )(tab); /* Unused parameter */
pConstraint = pIdxInfo->aConstraint;
for (i=0 ; i<pIdxInfo->nConstraint; i++, pConstraint++){
if ( pConstraint->usable==0 ) continue ;
if ( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue ;
switch ( pConstraint->iColumn ){
case COMPLETION_COLUMN_PREFIX:
prefixIdx = i;
idxNum |= 1 ;
break ;
case COMPLETION_COLUMN_WHOLELINE:
wholelineIdx = i;
idxNum |= 2 ;
break ;
}
}
if ( prefixIdx>=0 ){
pIdxInfo->aConstraintUsage[prefixIdx].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[prefixIdx].omit = 1 ;
}
if ( wholelineIdx>=0 ){
pIdxInfo->aConstraintUsage[wholelineIdx].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[wholelineIdx].omit = 1 ;
}
pIdxInfo->idxNum = idxNum;
pIdxInfo->estimatedCost = (double )5000 - 1000 *nArg;
pIdxInfo->estimatedRows = 500 - 100 *nArg;
return SQLITE_OK;
}
/*
* * This following structure defines all the methods for the
* * completion virtual table .
*/
static sqlite3_module completionModule = {
0 , /* iVersion */
0 , /* xCreate */
completionConnect, /* xConnect */
completionBestIndex, /* xBestIndex */
completionDisconnect, /* xDisconnect */
0 , /* xDestroy */
completionOpen, /* xOpen - open a cursor */
completionClose, /* xClose - close a cursor */
completionFilter, /* xFilter - configure scan constraints */
completionNext, /* xNext - advance a cursor */
completionEof, /* xEof - check for end of scan */
completionColumn, /* xColumn - read data */
completionRowid, /* xRowid - read data */
0 , /* xUpdate */
0 , /* xBegin */
0 , /* xSync */
0 , /* xCommit */
0 , /* xRollback */
0 , /* xFindMethod */
0 , /* xRename */
0 , /* xSavepoint */
0 , /* xRelease */
0 , /* xRollbackTo */
0 , /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
int sqlite3CompletionVtabInit(sqlite3 *db){
int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_VIRTUALTABLE
rc = sqlite3_create_module(db, "completion" , &completionModule, 0 );
#endif
return rc;
}
#ifdef _WIN32
#endif
int sqlite3_completion_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void )(pzErrMsg); /* Unused parameter */
#ifndef SQLITE_OMIT_VIRTUALTABLE
rc = sqlite3CompletionVtabInit(db);
#endif
return rc;
}
/************************* End ext/misc/completion.c ********************/
/************************* Begin ext/misc/appendvfs.c ******************/
/*
* * 2017 - 10 - 20
* *
* * 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 a VFS shim that allows an SQLite database to be
* * appended onto the end of some other file , such as an executable .
* *
* * A special record must appear at the end of the file that identifies the
* * file as an appended database and provides the offset to the first page
* * of the exposed content . ( Or , it is the length of the content prefix . )
* * For best performance page 1 should be located at a disk page boundary ,
* * though that is not required .
* *
* * When opening a database using this VFS , the connection might treat
* * the file as an ordinary SQLite database , or it might treat it as a
* * database appended onto some other file . The decision is made by
* * applying the following rules in order :
* *
* * ( 1 ) An empty file is an ordinary database .
* *
* * ( 2 ) If the file ends with the appendvfs trailer string
* * " Start - Of - SQLite3 - NNNNNNNN " that file is an appended database .
* *
* * ( 3 ) If the file begins with the standard SQLite prefix string
* * " SQLite format 3 " , that file is an ordinary database .
* *
* * ( 4 ) If none of the above apply and the SQLITE_OPEN_CREATE flag is
* * set , then a new database is appended to the already existing file .
* *
* * ( 5 ) Otherwise , SQLITE_CANTOPEN is returned .
* *
* * To avoid unnecessary complications with the PENDING_BYTE , the size of
* * the file containing the database is limited to 1 GiB . ( 1073741824 bytes )
* * This VFS will not read or write past the 1 GiB mark . This restriction
* * might be lifted in future versions . For now , if you need a larger
* * database , then keep it in a separate file .
* *
* * If the file being opened is a plain database ( not an appended one ) , then
* * this shim is a pass - through into the default underlying VFS . ( rule 3 )
**/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <string.h>
#include <assert.h>
/* The append mark at the end of the database is:
* *
* * Start - Of - SQLite3 - NNNNNNNN
* * 123456789 123456789 12345
* *
* * The NNNNNNNN represents a 64 - bit big - endian unsigned integer which is
* * the offset to page 1 , and also the length of the prefix content .
*/
#define APND_MARK_PREFIX "Start-Of-SQLite3-"
#define APND_MARK_PREFIX_SZ 17
#define APND_MARK_FOS_SZ 8
#define APND_MARK_SIZE (APND_MARK_PREFIX_SZ+APND_MARK_FOS_SZ)
/*
* * Maximum size of the combined prefix + database + append - mark . This
* * must be less than 0 x40000000 to avoid locking issues on Windows .
*/
#define APND_MAX_SIZE (0 x40000000)
/*
* * Try to align the database to an even multiple of APND_ROUNDUP bytes .
*/
#ifndef APND_ROUNDUP
#define APND_ROUNDUP 4096
#endif
#define APND_ALIGN_MASK ((sqlite3_int64)(APND_ROUNDUP-1 ))
#define APND_START_ROUNDUP(fsz) (((fsz)+APND_ALIGN_MASK) & ~APND_ALIGN_MASK)
/*
* * Forward declaration of objects used by this utility
*/
typedef struct sqlite3_vfs ApndVfs;
typedef struct ApndFile ApndFile;
/* Access to a lower-level VFS that (might) implement dynamic loading,
* * access to randomness , etc .
*/
#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
#define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1 ))
/* An open appendvfs file
* *
* * An instance of this structure describes the appended database file .
* * A separate sqlite3_file object is always appended . The appended
* * sqlite3_file object ( which can be accessed using ORIGFILE ( ) ) describes
* * the entire file , including the prefix , the database , and the
* * append - mark .
* *
* * The structure of an AppendVFS database is like this :
* *
* * + - - - - - - - - - - - - - + - - - - - - - - - + - - - - - - - - - - + - - - - - - - - - - - - - +
* * | prefix - file | padding | database | append - mark |
* * + - - - - - - - - - - - - - + - - - - - - - - - + - - - - - - - - - - + - - - - - - - - - - - - - +
* * ^ ^
* * | |
* * iPgOne iMark
* *
* *
* * " prefix file " - file onto which the database has been appended .
* * " padding " - zero or more bytes inserted so that " database "
* * starts on an APND_ROUNDUP boundary
* * " database " - The SQLite database file
* * " append - mark " - The 25 - byte " Start - Of - SQLite3 - NNNNNNNN " that indicates
* * the offset from the start of prefix - file to the start
* * of " database " .
* *
* * The size of the database is iMark - iPgOne .
* *
* * The NNNNNNNN in the " Start - Of - SQLite3 - NNNNNNNN " suffix is the value
* * of iPgOne stored as a big - ending 64 - bit integer .
* *
* * iMark will be the size of the underlying file minus 25 ( APND_MARKSIZE ) .
* * Or , iMark is - 1 to indicate that it has not yet been written .
*/
struct ApndFile {
sqlite3_file base; /* Subclass. MUST BE FIRST! */
sqlite3_int64 iPgOne; /* Offset to the start of the database */
sqlite3_int64 iMark; /* Offset of the append mark. -1 if unwritten */
/* Always followed by another sqlite3_file that describes the whole file */
};
/*
* * Methods for ApndFile
*/
static int apndClose(sqlite3_file*);
static int apndRead(sqlite3_file*, void *, int iAmt, sqlite3_int64 iOfst);
static int apndWrite(sqlite3_file*,const void *,int iAmt, sqlite3_int64 iOfst);
static int apndTruncate(sqlite3_file*, sqlite3_int64 size);
static int apndSync(sqlite3_file*, int flags);
static int apndFileSize(sqlite3_file*, sqlite3_int64 *pSize);
static int apndLock(sqlite3_file*, int );
static int apndUnlock(sqlite3_file*, int );
static int apndCheckReservedLock(sqlite3_file*, int *pResOut);
static int apndFileControl(sqlite3_file*, int op, void *pArg);
static int apndSectorSize(sqlite3_file*);
static int apndDeviceCharacteristics(sqlite3_file*);
static int apndShmMap(sqlite3_file*, int iPg, int pgsz, int , void volatile **);
static int apndShmLock(sqlite3_file*, int offset, int n, int flags);
static void apndShmBarrier(sqlite3_file*);
static int apndShmUnmap(sqlite3_file*, int deleteFlag);
static int apndFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
static int apndUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
/*
* * Methods for ApndVfs
*/
static int apndOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
static int apndDelete(sqlite3_vfs*, const char *zName, int syncDir);
static int apndAccess(sqlite3_vfs*, const char *zName, int flags, int *);
static int apndFullPathname(sqlite3_vfs*, const char *zName, int , char *zOut);
static void *apndDlOpen(sqlite3_vfs*, const char *zFilename);
static void apndDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void );
static void apndDlClose(sqlite3_vfs*, void *);
static int apndRandomness(sqlite3_vfs*, int nByte, char *zOut);
static int apndSleep(sqlite3_vfs*, int microseconds);
static int apndCurrentTime(sqlite3_vfs*, double *);
static int apndGetLastError(sqlite3_vfs*, int , char *);
static int apndCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
static int apndSetSystemCall(sqlite3_vfs*, const char *,sqlite3_syscall_ptr);
static sqlite3_syscall_ptr apndGetSystemCall(sqlite3_vfs*, const char *z);
static const char *apndNextSystemCall(sqlite3_vfs*, const char *zName);
static sqlite3_vfs apnd_vfs = {
3 , /* iVersion (set when registered) */
0 , /* szOsFile (set when registered) */
1024 , /* mxPathname */
0 , /* pNext */
"apndvfs" , /* zName */
0 , /* pAppData (set when registered) */
apndOpen, /* xOpen */
apndDelete, /* xDelete */
apndAccess, /* xAccess */
apndFullPathname, /* xFullPathname */
apndDlOpen, /* xDlOpen */
apndDlError, /* xDlError */
apndDlSym, /* xDlSym */
apndDlClose, /* xDlClose */
apndRandomness, /* xRandomness */
apndSleep, /* xSleep */
apndCurrentTime, /* xCurrentTime */
apndGetLastError, /* xGetLastError */
apndCurrentTimeInt64, /* xCurrentTimeInt64 */
apndSetSystemCall, /* xSetSystemCall */
apndGetSystemCall, /* xGetSystemCall */
apndNextSystemCall /* xNextSystemCall */
};
static const sqlite3_io_methods apnd_io_methods = {
3 , /* iVersion */
apndClose, /* xClose */
apndRead, /* xRead */
apndWrite, /* xWrite */
apndTruncate, /* xTruncate */
apndSync, /* xSync */
apndFileSize, /* xFileSize */
apndLock, /* xLock */
apndUnlock, /* xUnlock */
apndCheckReservedLock, /* xCheckReservedLock */
apndFileControl, /* xFileControl */
apndSectorSize, /* xSectorSize */
apndDeviceCharacteristics, /* xDeviceCharacteristics */
apndShmMap, /* xShmMap */
apndShmLock, /* xShmLock */
apndShmBarrier, /* xShmBarrier */
apndShmUnmap, /* xShmUnmap */
apndFetch, /* xFetch */
apndUnfetch /* xUnfetch */
};
/*
* * Close an apnd - file .
*/
static int apndClose(sqlite3_file *pFile){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xClose(pFile);
}
/*
* * Read data from an apnd - file .
*/
static int apndRead(
sqlite3_file *pFile,
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
ApndFile *paf = (ApndFile *)pFile;
pFile = ORIGFILE(pFile);
return pFile->pMethods->xRead(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
}
/*
* * Add the append - mark onto what should become the end of the file .
* If and only if this succeeds , internal ApndFile . iMark is updated .
* Parameter iWriteEnd is the appendvfs - relative offset of the new mark .
*/
static int apndWriteMark(
ApndFile *paf,
sqlite3_file *pFile,
sqlite_int64 iWriteEnd
){
sqlite_int64 iPgOne = paf->iPgOne;
unsigned char a[APND_MARK_SIZE];
int i = APND_MARK_FOS_SZ;
int rc;
assert(pFile == ORIGFILE(paf));
memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
while ( --i >= 0 ){
a[APND_MARK_PREFIX_SZ+i] = (unsigned char )(iPgOne & 0 xff);
iPgOne >>= 8 ;
}
iWriteEnd += paf->iPgOne;
if ( SQLITE_OK==(rc = pFile->pMethods->xWrite
(pFile, a, APND_MARK_SIZE, iWriteEnd)) ){
paf->iMark = iWriteEnd;
}
return rc;
}
/*
* * Write data to an apnd - file .
*/
static int apndWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
ApndFile *paf = (ApndFile *)pFile;
sqlite_int64 iWriteEnd = iOfst + iAmt;
if ( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL;
pFile = ORIGFILE(pFile);
/* If append-mark is absent or will be overwritten, write it. */
if ( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){
int rc = apndWriteMark(paf, pFile, iWriteEnd);
if ( SQLITE_OK!=rc ) return rc;
}
return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
}
/*
* * Truncate an apnd - file .
*/
static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
ApndFile *paf = (ApndFile *)pFile;
pFile = ORIGFILE(pFile);
/* The append mark goes out first so truncate failure does not lose it. */
if ( SQLITE_OK!=apndWriteMark(paf, pFile, size) ) return SQLITE_IOERR;
/* Truncate underlying file just past append mark */
return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE);
}
/*
* * Sync an apnd - file .
*/
static int apndSync(sqlite3_file *pFile, int flags){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xSync(pFile, flags);
}
/*
* * Return the current file - size of an apnd - file .
* * If the append mark is not yet there , the file - size is 0 .
*/
static int apndFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
ApndFile *paf = (ApndFile *)pFile;
*pSize = ( paf->iMark >= 0 )? (paf->iMark - paf->iPgOne) : 0 ;
return SQLITE_OK;
}
/*
* * Lock an apnd - file .
*/
static int apndLock(sqlite3_file *pFile, int eLock){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xLock(pFile, eLock);
}
/*
* * Unlock an apnd - file .
*/
static int apndUnlock(sqlite3_file *pFile, int eLock){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xUnlock(pFile, eLock);
}
/*
* * Check if another file - handle holds a RESERVED lock on an apnd - file .
*/
static int apndCheckReservedLock(sqlite3_file *pFile, int *pResOut){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
}
/*
* * File control method . For custom operations on an apnd - file .
*/
static int apndFileControl(sqlite3_file *pFile, int op, void *pArg){
ApndFile *paf = (ApndFile *)pFile;
int rc;
pFile = ORIGFILE(pFile);
if ( op==SQLITE_FCNTL_SIZE_HINT ) *(sqlite3_int64*)pArg += paf->iPgOne;
rc = pFile->pMethods->xFileControl(pFile, op, pArg);
if ( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
*(char **)pArg = sqlite3_mprintf("apnd(%lld)/%z" , paf->iPgOne,*(char **)pArg);
}
return rc;
}
/*
* * Return the sector - size in bytes for an apnd - file .
*/
static int apndSectorSize(sqlite3_file *pFile){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xSectorSize(pFile);
}
/*
* * Return the device characteristic flags supported by an apnd - file .
*/
static int apndDeviceCharacteristics(sqlite3_file *pFile){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xDeviceCharacteristics(pFile);
}
/* Create a shared memory file mapping */
static int apndShmMap(
sqlite3_file *pFile,
int iPg,
int pgsz,
int bExtend,
void volatile **pp
){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp);
}
/* Perform locking on a shared-memory segment */
static int apndShmLock(sqlite3_file *pFile, int offset, int n, int flags){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xShmLock(pFile,offset,n,flags);
}
/* Memory barrier operation on shared memory */
static void apndShmBarrier(sqlite3_file *pFile){
pFile = ORIGFILE(pFile);
pFile->pMethods->xShmBarrier(pFile);
}
/* Unmap a shared memory segment */
static int apndShmUnmap(sqlite3_file *pFile, int deleteFlag){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xShmUnmap(pFile,deleteFlag);
}
/* Fetch a page of a memory-mapped file */
static int apndFetch(
sqlite3_file *pFile,
sqlite3_int64 iOfst,
int iAmt,
void **pp
){
ApndFile *p = (ApndFile *)pFile;
if ( p->iMark < 0 || iOfst+iAmt > p->iMark ){
return SQLITE_IOERR; /* Cannot read what is not yet there. */
}
pFile = ORIGFILE(pFile);
return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp);
}
/* Release a memory-mapped page */
static int apndUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
ApndFile *p = (ApndFile *)pFile;
pFile = ORIGFILE(pFile);
return pFile->pMethods->xUnfetch(pFile, iOfst+p->iPgOne, pPage);
}
/*
* * Try to read the append - mark off the end of a file . Return the
* * start of the appended database if the append - mark is present .
* * If there is no valid append - mark , return - 1 ;
* *
* * An append - mark is only valid if the NNNNNNNN start - of - database offset
* * indicates that the appended database contains at least one page . The
* * start - of - database value must be a multiple of 512 .
*/
static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){
int rc, i;
sqlite3_int64 iMark;
int msbs = 8 * (APND_MARK_FOS_SZ-1 );
unsigned char a[APND_MARK_SIZE];
if ( APND_MARK_SIZE!=(sz & 0 x1ff) ) return -1 ;
rc = pFile->pMethods->xRead(pFile, a, APND_MARK_SIZE, sz-APND_MARK_SIZE);
if ( rc ) return -1 ;
if ( memcmp(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ)!=0 ) return -1 ;
iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ] & 0 x7f)) << msbs;
for (i=1 ; i<8 ; i++){
msbs -= 8 ;
iMark |= (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<msbs;
}
if ( iMark > (sz - APND_MARK_SIZE - 512 ) ) return -1 ;
if ( iMark & 0 x1ff ) return -1 ;
return iMark;
}
static const char apvfsSqliteHdr[] = "SQLite format 3" ;
/*
* * Check to see if the file is an appendvfs SQLite database file .
* * Return true iff it is such . Parameter sz is the file ' s size .
*/
static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){
int rc;
char zHdr[16 ];
sqlite3_int64 iMark = apndReadMark(sz, pFile);
if ( iMark>=0 ){
/* If file has the correct end-marker, the expected odd size, and the
* * SQLite DB type marker where the end - marker puts it , then it
* * is an appendvfs database .
*/
rc = pFile->pMethods->xRead(pFile, zHdr, sizeof (zHdr), iMark);
if ( SQLITE_OK==rc
&& memcmp(zHdr, apvfsSqliteHdr, sizeof (zHdr))==0
&& (sz & 0 x1ff) == APND_MARK_SIZE
&& sz>=512 +APND_MARK_SIZE
){
return 1 ; /* It's an appendvfs database */
}
}
return 0 ;
}
/*
* * Check to see if the file is an ordinary SQLite database file .
* * Return true iff so . Parameter sz is the file ' s size .
*/
static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
char zHdr[16 ];
if ( apndIsAppendvfsDatabase(sz, pFile) /* rule 2 */
|| (sz & 0 x1ff) != 0
|| SQLITE_OK!=pFile->pMethods->xRead(pFile, zHdr, sizeof (zHdr), 0 )
|| memcmp(zHdr, apvfsSqliteHdr, sizeof (zHdr))!=0
){
return 0 ;
}else {
return 1 ;
}
}
/*
* * Open an apnd file handle .
*/
static int apndOpen(
sqlite3_vfs *pApndVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
ApndFile *pApndFile = (ApndFile*)pFile;
sqlite3_file *pBaseFile = ORIGFILE(pFile);
sqlite3_vfs *pBaseVfs = ORIGVFS(pApndVfs);
int rc;
sqlite3_int64 sz = 0 ;
if ( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
/* The appendvfs is not to be used for transient or temporary databases.
* * Just use the base VFS open to initialize the given file object and
* * open the underlying file . ( Appendvfs is then unused for this file . )
*/
return pBaseVfs->xOpen(pBaseVfs, zName, pFile, flags, pOutFlags);
}
memset(pApndFile, 0 , sizeof (ApndFile));
pFile->pMethods = &apnd_io_methods;
pApndFile->iMark = -1 ; /* Append mark not yet written */
rc = pBaseVfs->xOpen(pBaseVfs, zName, pBaseFile, flags, pOutFlags);
if ( rc==SQLITE_OK ){
rc = pBaseFile->pMethods->xFileSize(pBaseFile, &sz);
if ( rc ){
pBaseFile->pMethods->xClose(pBaseFile);
}
}
if ( rc ){
pFile->pMethods = 0 ;
return rc;
}
if ( apndIsOrdinaryDatabaseFile(sz, pBaseFile) ){
/* The file being opened appears to be just an ordinary DB. Copy
* * the base dispatch - table so this instance mimics the base VFS .
*/
memmove(pApndFile, pBaseFile, pBaseVfs->szOsFile);
return SQLITE_OK;
}
pApndFile->iPgOne = apndReadMark(sz, pFile);
if ( pApndFile->iPgOne>=0 ){
pApndFile->iMark = sz - APND_MARK_SIZE; /* Append mark found */
return SQLITE_OK;
}
if ( (flags & SQLITE_OPEN_CREATE)==0 ){
pBaseFile->pMethods->xClose(pBaseFile);
rc = SQLITE_CANTOPEN;
pFile->pMethods = 0 ;
}else {
/* Round newly added appendvfs location to #define'd page boundary.
* * Note that nothing has yet been written to the underlying file .
* * The append mark will be written along with first content write .
* * Until then , paf - > iMark value indicates it is not yet written .
*/
pApndFile->iPgOne = APND_START_ROUNDUP(sz);
}
return rc;
}
/*
* * Delete an apnd file .
* * For an appendvfs , this could mean delete the appendvfs portion ,
* * leaving the appendee as it was before it gained an appendvfs .
* * For now , this code deletes the underlying file too .
*/
static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
}
/*
* * All other VFS methods are pass - thrus .
*/
static int apndAccess(
sqlite3_vfs *pVfs,
const char *zPath,
int flags,
int *pResOut
){
return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
}
static int apndFullPathname(
sqlite3_vfs *pVfs,
const char *zPath,
int nOut,
char *zOut
){
return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut);
}
static void *apndDlOpen(sqlite3_vfs *pVfs, const char *zPath){
return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
}
static void apndDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
}
static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void ){
return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
}
static void apndDlClose(sqlite3_vfs *pVfs, void *pHandle){
ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
}
static int apndRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
}
static int apndSleep(sqlite3_vfs *pVfs, int nMicro){
return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
}
static int apndCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
}
static int apndGetLastError(sqlite3_vfs *pVfs, int a, char *b){
return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
}
static int apndCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
}
static int apndSetSystemCall(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_syscall_ptr pCall
){
return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall);
}
static sqlite3_syscall_ptr apndGetSystemCall(
sqlite3_vfs *pVfs,
const char *zName
){
return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName);
}
static const char *apndNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
}
#ifdef _WIN32
#endif
/*
* * This routine is called when the extension is loaded .
* * Register the new VFS .
*/
int sqlite3_appendvfs_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
sqlite3_vfs *pOrig;
SQLITE_EXTENSION_INIT2(pApi);
(void )pzErrMsg;
(void )db;
pOrig = sqlite3_vfs_find(0 );
if ( pOrig==0 ) return SQLITE_ERROR;
apnd_vfs.iVersion = pOrig->iVersion;
apnd_vfs.pAppData = pOrig;
apnd_vfs.szOsFile = pOrig->szOsFile + sizeof (ApndFile);
rc = sqlite3_vfs_register(&apnd_vfs, 0 );
#ifdef APPENDVFS_TEST
if ( rc==SQLITE_OK ){
rc = sqlite3_auto_extension((void (*)(void ))apndvfsRegister);
}
#endif
if ( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
return rc;
}
/************************* End ext/misc/appendvfs.c ********************/
#endif
#ifdef SQLITE_HAVE_ZLIB
/************************* Begin ext/misc/zipfile.c ******************/
/*
* * 2017 - 12 - 26
* *
* * 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 a virtual table for reading and writing ZIP archive
* * files .
* *
* * Usage example :
* *
* * SELECT name , sz , datetime ( mtime , ' unixepoch ' ) FROM zipfile ( $ filename ) ;
* *
* * Current limitations :
* *
* * * No support for encryption
* * * No support for ZIP archives spanning multiple files
* * * No support for zip64 extensions
* * * Only the " inflate / deflate " ( zlib ) compression method is supported
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <stdio.h>
#include <string.h>
#include <assert.h>
#ifndef SQLITE_NO_STDINT
# include <stdint.h>
#endif
#include <zlib.h>
/* When used as part of the CLI, the sqlite3_stdio.h module will have
* * been included before this one . In that case use the sqlite3_stdio . h
* * # defines . If not , create our own for fopen ( ) .
*/
#ifndef _SQLITE3_STDIO_H_
# define sqlite3_fopen fopen
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
#ifndef SQLITE_AMALGAMATION
#ifndef UINT32_TYPE
# ifdef HAVE_UINT32_T
# define UINT32_TYPE uint32_t
# else
# define UINT32_TYPE unsigned int
# endif
#endif
#ifndef UINT16_TYPE
# ifdef HAVE_UINT16_T
# define UINT16_TYPE uint16_t
# else
# define UINT16_TYPE unsigned short int
# endif
#endif
/* typedef sqlite3_int64 i64; */
/* typedef unsigned char u8; */
/* typedef UINT32_TYPE u32; // 4-byte unsigned integer // */
/* typedef UINT16_TYPE u16; // 2-byte unsigned integer // */
#define MIN(a,b) ((a)<(b) ? (a) : (b))
#if defined (SQLITE_COVERAGE_TEST) || defined (SQLITE_MUTATION_TEST)
# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
#endif
#if defined (SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
# define ALWAYS(X) (1 )
# define NEVER(X) (0 )
#elif !defined (NDEBUG)
# define ALWAYS(X) ((X)?1 :(assert(0 ),0 ))
# define NEVER(X) ((X)?(assert(0 ),1 ):0 )
#else
# define ALWAYS(X) (X)
# define NEVER(X) (X)
#endif
#endif /* SQLITE_AMALGAMATION */
/*
* * Definitions for mode bitmasks S_IFDIR , S_IFREG and S_IFLNK .
* *
* * In some ways it would be better to obtain these values from system
* * header files . But , the dependency is undesirable and ( a ) these
* * have been stable for decades , ( b ) the values are part of POSIX and
* * are also made explicit in [ man stat ] , and ( c ) are part of the
* * file format for zip archives .
*/
#ifndef S_IFDIR
# define S_IFDIR 0040000
#endif
#ifndef S_IFREG
# define S_IFREG 0100000
#endif
#ifndef S_IFLNK
# define S_IFLNK 0120000
#endif
static const char ZIPFILE_SCHEMA[] =
"CREATE TABLE y("
"name PRIMARY KEY," /* 0: Name of file in zip archive */
"mode," /* 1: POSIX mode for file */
"mtime," /* 2: Last modification time (secs since 1970)*/
"sz," /* 3: Size of object */
"rawdata," /* 4: Raw data */
"data," /* 5: Uncompressed data */
"method," /* 6: Compression method (integer) */
"z HIDDEN" /* 7: Name of zip file */
") WITHOUT ROWID;" ;
#define ZIPFILE_F_COLUMN_IDX 7 /* Index of column "file" in the above */
#define ZIPFILE_MX_NAME (250 ) /* Windows limitation on filename size */
/*
* * The buffer should be large enough to contain 3 65536 byte strings - the
* * filename , the extra field and the file comment .
*/
#define ZIPFILE_BUFFER_SIZE (200 *1024 )
/*
* * Magic numbers used to read and write zip files .
* *
* * ZIPFILE_NEWENTRY_MADEBY :
* * Use this value for the " version - made - by " field in new zip file
* * entries . The upper byte indicates " unix " , and the lower byte
* * indicates that the zip file matches pkzip specification 3 . 0 .
* * This is what info - zip seems to do .
* *
* * ZIPFILE_NEWENTRY_REQUIRED :
* * Value for " version - required - to - extract " field of new entries .
* * Version 2 . 0 is required to support folders and deflate compression .
* *
* * ZIPFILE_NEWENTRY_FLAGS :
* * Value for " general - purpose - bit - flags " field of new entries . Bit
* * 11 means " utf - 8 filename and comment " .
* *
* * ZIPFILE_SIGNATURE_CDS :
* * First 4 bytes of a valid CDS record .
* *
* * ZIPFILE_SIGNATURE_LFH :
* * First 4 bytes of a valid LFH record .
* *
* * ZIPFILE_SIGNATURE_EOCD
* * First 4 bytes of a valid EOCD record .
*/
#define ZIPFILE_EXTRA_TIMESTAMP 0 x5455
#define ZIPFILE_NEWENTRY_MADEBY ((3 <<8 ) + 30 )
#define ZIPFILE_NEWENTRY_REQUIRED 20
#define ZIPFILE_NEWENTRY_FLAGS 0 x800
#define ZIPFILE_SIGNATURE_CDS 0 x02014b50
#define ZIPFILE_SIGNATURE_LFH 0 x04034b50
#define ZIPFILE_SIGNATURE_EOCD 0 x06054b50
/*
* * The sizes of the fixed - size part of each of the three main data
* * structures in a zip archive .
*/
#define ZIPFILE_LFH_FIXED_SZ 30
#define ZIPFILE_EOCD_FIXED_SZ 22
#define ZIPFILE_CDS_FIXED_SZ 46
/*
* * * 4 . 3 . 16 End of central directory record :
* * *
* * * end of central dir signature 4 bytes ( 0 x06054b50 )
* * * number of this disk 2 bytes
* * * number of the disk with the
* * * start of the central directory 2 bytes
* * * total number of entries in the
* * * central directory on this disk 2 bytes
* * * total number of entries in
* * * the central directory 2 bytes
* * * size of the central directory 4 bytes
* * * offset of start of central
* * * directory with respect to
* * * the starting disk number 4 bytes
* * * . ZIP file comment length 2 bytes
* * * . ZIP file comment ( variable size )
*/
typedef struct ZipfileEOCD ZipfileEOCD;
struct ZipfileEOCD {
u16 iDisk;
u16 iFirstDisk;
u16 nEntry;
u16 nEntryTotal;
u32 nSize;
u32 iOffset;
};
/*
* * * 4 . 3 . 12 Central directory structure :
* * *
* * * . . .
* * *
* * * central file header signature 4 bytes ( 0 x02014b50 )
* * * version made by 2 bytes
* * * version needed to extract 2 bytes
* * * general purpose bit flag 2 bytes
* * * compression method 2 bytes
* * * last mod file time 2 bytes
* * * last mod file date 2 bytes
* * * crc - 32 4 bytes
* * * compressed size 4 bytes
* * * uncompressed size 4 bytes
* * * file name length 2 bytes
* * * extra field length 2 bytes
* * * file comment length 2 bytes
* * * disk number start 2 bytes
* * * internal file attributes 2 bytes
* * * external file attributes 4 bytes
* * * relative offset of local header 4 bytes
*/
typedef struct ZipfileCDS ZipfileCDS;
struct ZipfileCDS {
u16 iVersionMadeBy;
u16 iVersionExtract;
u16 flags;
u16 iCompression;
u16 mTime;
u16 mDate;
u32 crc32;
u32 szCompressed;
u32 szUncompressed;
u16 nFile;
u16 nExtra;
u16 nComment;
u16 iDiskStart;
u16 iInternalAttr;
u32 iExternalAttr;
u32 iOffset;
char *zFile; /* Filename (sqlite3_malloc()) */
};
/*
* * * 4 . 3 . 7 Local file header :
* * *
* * * local file header signature 4 bytes ( 0 x04034b50 )
* * * version needed to extract 2 bytes
* * * general purpose bit flag 2 bytes
* * * compression method 2 bytes
* * * last mod file time 2 bytes
* * * last mod file date 2 bytes
* * * crc - 32 4 bytes
* * * compressed size 4 bytes
* * * uncompressed size 4 bytes
* * * file name length 2 bytes
* * * extra field length 2 bytes
* * *
*/
typedef struct ZipfileLFH ZipfileLFH;
struct ZipfileLFH {
u16 iVersionExtract;
u16 flags;
u16 iCompression;
u16 mTime;
u16 mDate;
u32 crc32;
u32 szCompressed;
u32 szUncompressed;
u16 nFile;
u16 nExtra;
};
typedef struct ZipfileEntry ZipfileEntry;
struct ZipfileEntry {
ZipfileCDS cds; /* Parsed CDS record */
u32 mUnixTime; /* Modification time, in UNIX format */
u8 *aExtra; /* cds.nExtra+cds.nComment bytes of extra data */
i64 iDataOff; /* Offset to data in file (if aData==0) */
u8 *aData; /* cds.szCompressed bytes of compressed data */
ZipfileEntry *pNext; /* Next element in in-memory CDS */
};
/*
* * Cursor type for zipfile tables .
*/
typedef struct ZipfileCsr ZipfileCsr;
struct ZipfileCsr {
sqlite3_vtab_cursor base; /* Base class - must be first */
i64 iId; /* Cursor ID */
u8 bEof; /* True when at EOF */
u8 bNoop; /* If next xNext() call is no-op */
/* Used outside of write transactions */
FILE *pFile; /* Zip file */
i64 iNextOff; /* Offset of next record in central directory */
ZipfileEOCD eocd; /* Parse of central directory record */
ZipfileEntry *pFreeEntry; /* Free this list when cursor is closed or reset */
ZipfileEntry *pCurrent; /* Current entry */
ZipfileCsr *pCsrNext; /* Next cursor on same virtual table */
};
typedef struct ZipfileTab ZipfileTab;
struct ZipfileTab {
sqlite3_vtab base; /* Base class - must be first */
char *zFile; /* Zip file this table accesses (may be NULL) */
sqlite3 *db; /* Host database connection */
u8 *aBuffer; /* Temporary buffer used for various tasks */
ZipfileCsr *pCsrList; /* List of cursors */
i64 iNextCsrid;
/* The following are used by write transactions only */
ZipfileEntry *pFirstEntry; /* Linked list of all files (if pWriteFd!=0) */
ZipfileEntry *pLastEntry; /* Last element in pFirstEntry list */
FILE *pWriteFd; /* File handle open on zip archive */
i64 szCurrent; /* Current size of zip archive */
i64 szOrig; /* Size of archive at start of transaction */
};
/*
* * Set the error message contained in context ctx to the results of
* * vprintf ( zFmt , . . . ) .
*/
static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
char *zMsg = 0 ;
va_list ap;
va_start(ap, zFmt);
zMsg = sqlite3_vmprintf(zFmt, ap);
sqlite3_result_error(ctx, zMsg, -1 );
sqlite3_free(zMsg);
va_end(ap);
}
/*
* * If string zIn is quoted , dequote it in place . Otherwise , if the string
* * is not quoted , do nothing .
*/
static void zipfileDequote(char *zIn){
char q = zIn[0 ];
if ( q=='"' || q=='\' ' || q==' `' || q==' [' ){
int iIn = 1 ;
int iOut = 0 ;
if ( q=='[' ) q = ']' ;
while ( ALWAYS(zIn[iIn]) ){
char c = zIn[iIn++];
if ( c==q && zIn[iIn++]!=q ) break ;
zIn[iOut++] = c;
}
zIn[iOut] = '\0' ;
}
}
/*
* * Construct a new ZipfileTab virtual table object .
* *
* * argv [ 0 ] - > module name ( " zipfile " )
* * argv [ 1 ] - > database name
* * argv [ 2 ] - > table name
* * argv [ . . . ] - > " column name " and other module argument fields .
*/
static int zipfileConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const *argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
int nByte = sizeof (ZipfileTab) + ZIPFILE_BUFFER_SIZE;
int nFile = 0 ;
const char *zFile = 0 ;
ZipfileTab *pNew = 0 ;
int rc;
(void )pAux;
/* If the table name is not "zipfile", require that the argument be
* * specified . This stops zipfile tables from being created as :
* *
* * CREATE VIRTUAL TABLE zzz USING zipfile ( ) ;
* *
* * It does not prevent :
* *
* * CREATE VIRTUAL TABLE zipfile USING zipfile ( ) ;
*/
assert( 0 ==sqlite3_stricmp(argv[0 ], "zipfile" ) );
if ( (0 !=sqlite3_stricmp(argv[2 ], "zipfile" ) && argc<4 ) || argc>4 ){
*pzErr = sqlite3_mprintf("zipfile constructor requires one argument" );
return SQLITE_ERROR;
}
if ( argc>3 ){
zFile = argv[3 ];
nFile = (int )strlen(zFile)+1 ;
}
rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
if ( rc==SQLITE_OK ){
pNew = (ZipfileTab*)sqlite3_malloc64((i64)nByte+nFile);
if ( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0 , nByte+nFile);
pNew->db = db;
pNew->aBuffer = (u8*)&pNew[1 ];
if ( zFile ){
pNew->zFile = (char *)&pNew->aBuffer[ZIPFILE_BUFFER_SIZE];
memcpy(pNew->zFile, zFile, nFile);
zipfileDequote(pNew->zFile);
}
}
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
*ppVtab = (sqlite3_vtab*)pNew;
return rc;
}
/*
* * Free the ZipfileEntry structure indicated by the only argument .
*/
static void zipfileEntryFree(ZipfileEntry *p){
if ( p ){
sqlite3_free(p->cds.zFile);
sqlite3_free(p);
}
}
/*
* * Release resources that should be freed at the end of a write
* * transaction .
*/
static void zipfileCleanupTransaction(ZipfileTab *pTab){
ZipfileEntry *pEntry;
ZipfileEntry *pNext;
if ( pTab->pWriteFd ){
fclose(pTab->pWriteFd);
pTab->pWriteFd = 0 ;
}
for (pEntry=pTab->pFirstEntry; pEntry; pEntry=pNext){
pNext = pEntry->pNext;
zipfileEntryFree(pEntry);
}
pTab->pFirstEntry = 0 ;
pTab->pLastEntry = 0 ;
pTab->szCurrent = 0 ;
pTab->szOrig = 0 ;
}
/*
* * This method is the destructor for zipfile vtab objects .
*/
static int zipfileDisconnect(sqlite3_vtab *pVtab){
zipfileCleanupTransaction((ZipfileTab*)pVtab);
sqlite3_free(pVtab);
return SQLITE_OK;
}
/*
* * Constructor for a new ZipfileCsr object .
*/
static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){
ZipfileTab *pTab = (ZipfileTab*)p;
ZipfileCsr *pCsr;
pCsr = sqlite3_malloc64(sizeof (*pCsr));
*ppCsr = (sqlite3_vtab_cursor*)pCsr;
if ( pCsr==0 ){
return SQLITE_NOMEM;
}
memset(pCsr, 0 , sizeof (*pCsr));
pCsr->iId = ++pTab->iNextCsrid;
pCsr->pCsrNext = pTab->pCsrList;
pTab->pCsrList = pCsr;
return SQLITE_OK;
}
/*
* * Reset a cursor back to the state it was in when first returned
* * by zipfileOpen ( ) .
*/
static void zipfileResetCursor(ZipfileCsr *pCsr){
ZipfileEntry *p;
ZipfileEntry *pNext;
pCsr->bEof = 0 ;
if ( pCsr->pFile ){
fclose(pCsr->pFile);
pCsr->pFile = 0 ;
zipfileEntryFree(pCsr->pCurrent);
pCsr->pCurrent = 0 ;
}
for (p=pCsr->pFreeEntry; p; p=pNext){
pNext = p->pNext;
zipfileEntryFree(p);
}
}
/*
* * Destructor for an ZipfileCsr .
*/
static int zipfileClose(sqlite3_vtab_cursor *cur){
ZipfileCsr *pCsr = (ZipfileCsr*)cur;
ZipfileTab *pTab = (ZipfileTab*)(pCsr->base.pVtab);
ZipfileCsr **pp;
zipfileResetCursor(pCsr);
/* Remove this cursor from the ZipfileTab.pCsrList list. */
for (pp=&pTab->pCsrList; *pp!=pCsr; pp=&((*pp)->pCsrNext));
*pp = pCsr->pCsrNext;
sqlite3_free(pCsr);
return SQLITE_OK;
}
/*
* * Set the error message for the virtual table associated with cursor
* * pCsr to the results of vprintf ( zFmt , . . . ) .
*/
static void zipfileTableErr(ZipfileTab *pTab, const char *zFmt, ...){
va_list ap;
va_start(ap, zFmt);
sqlite3_free(pTab->base.zErrMsg);
pTab->base.zErrMsg = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
}
static void zipfileCursorErr(ZipfileCsr *pCsr, const char *zFmt, ...){
va_list ap;
va_start(ap, zFmt);
sqlite3_free(pCsr->base.pVtab->zErrMsg);
pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
}
/*
* * Read nRead bytes of data from offset iOff of file pFile into buffer
* * aRead [ ] . Return SQLITE_OK if successful , or an SQLite error code
* * otherwise .
* *
* * If an error does occur , output variable ( * pzErrmsg ) may be set to point
* * to an English language error message . It is the responsibility of the
* * caller to eventually free this buffer using
* * sqlite3_free ( ) .
*/
static int zipfileReadData(
FILE *pFile, /* Read from this file */
u8 *aRead, /* Read into this buffer */
i64 nRead, /* Number of bytes to read */
i64 iOff, /* Offset to read from */
char **pzErrmsg /* OUT: Error message (from sqlite3_malloc) */
){
size_t n;
fseek(pFile, (long )iOff, SEEK_SET);
n = fread(aRead, 1 , (long )nRead, pFile);
if ( n!=(size_t)nRead ){
sqlite3_free(*pzErrmsg);
*pzErrmsg = sqlite3_mprintf("error in fread()" );
return SQLITE_ERROR;
}
return SQLITE_OK;
}
static int zipfileAppendData(
ZipfileTab *pTab,
const u8 *aWrite,
int nWrite
){
if ( nWrite>0 ){
size_t n = nWrite;
fseek(pTab->pWriteFd, (long )pTab->szCurrent, SEEK_SET);
n = fwrite(aWrite, 1 , nWrite, pTab->pWriteFd);
if ( (int )n!=nWrite ){
zipfileTableErr(pTab,"error in fwrite()" );
return SQLITE_ERROR;
}
pTab->szCurrent += nWrite;
}
return SQLITE_OK;
}
/*
* * Read and return a 16 - bit little - endian unsigned integer from buffer aBuf .
*/
static u16 zipfileGetU16(const u8 *aBuf){
return (aBuf[1 ] << 8 ) + aBuf[0 ];
}
/*
* * Read and return a 32 - bit little - endian unsigned integer from buffer aBuf .
*/
static u32 zipfileGetU32(const u8 *aBuf){
if ( aBuf==0 ) return 0 ;
return ((u32)(aBuf[3 ]) << 24 )
+ ((u32)(aBuf[2 ]) << 16 )
+ ((u32)(aBuf[1 ]) << 8 )
+ ((u32)(aBuf[0 ]) << 0 );
}
/*
* * Write a 16 - bit little endiate integer into buffer aBuf .
*/
static void zipfilePutU16(u8 *aBuf, u16 val){
aBuf[0 ] = val & 0 xFF;
aBuf[1 ] = (val>>8 ) & 0 xFF;
}
/*
* * Write a 32 - bit little endiate integer into buffer aBuf .
*/
static void zipfilePutU32(u8 *aBuf, u32 val){
aBuf[0 ] = val & 0 xFF;
aBuf[1 ] = (val>>8 ) & 0 xFF;
aBuf[2 ] = (val>>16 ) & 0 xFF;
aBuf[3 ] = (val>>24 ) & 0 xFF;
}
#define zipfileRead32(aBuf) ( aBuf+=4 , zipfileGetU32(aBuf-4 ) )
#define zipfileRead16(aBuf) ( aBuf+=2 , zipfileGetU16(aBuf-2 ) )
#define zipfileWrite32(aBuf,val) { zipfilePutU32(aBuf,val); aBuf+=4 ; }
#define zipfileWrite16(aBuf,val) { zipfilePutU16(aBuf,val); aBuf+=2 ; }
/*
* * Magic numbers used to read CDS records .
*/
#define ZIPFILE_CDS_NFILE_OFF 28
#define ZIPFILE_CDS_SZCOMPRESSED_OFF 20
/*
* * Decode the CDS record in buffer aBuf into ( * pCDS ) . Return SQLITE_ERROR
* * if the record is not well - formed , or SQLITE_OK otherwise .
*/
static int zipfileReadCDS(u8 *aBuf, ZipfileCDS *pCDS){
u8 *aRead = aBuf;
u32 sig = zipfileRead32(aRead);
int rc = SQLITE_OK;
if ( sig!=ZIPFILE_SIGNATURE_CDS ){
rc = SQLITE_ERROR;
}else {
pCDS->iVersionMadeBy = zipfileRead16(aRead);
pCDS->iVersionExtract = zipfileRead16(aRead);
pCDS->flags = zipfileRead16(aRead);
pCDS->iCompression = zipfileRead16(aRead);
pCDS->mTime = zipfileRead16(aRead);
pCDS->mDate = zipfileRead16(aRead);
pCDS->crc32 = zipfileRead32(aRead);
pCDS->szCompressed = zipfileRead32(aRead);
pCDS->szUncompressed = zipfileRead32(aRead);
assert( aRead==&aBuf[ZIPFILE_CDS_NFILE_OFF] );
pCDS->nFile = zipfileRead16(aRead);
pCDS->nExtra = zipfileRead16(aRead);
pCDS->nComment = zipfileRead16(aRead);
pCDS->iDiskStart = zipfileRead16(aRead);
pCDS->iInternalAttr = zipfileRead16(aRead);
pCDS->iExternalAttr = zipfileRead32(aRead);
pCDS->iOffset = zipfileRead32(aRead);
assert( aRead==&aBuf[ZIPFILE_CDS_FIXED_SZ] );
}
return rc;
}
/*
* * Decode the LFH record in buffer aBuf into ( * pLFH ) . Return SQLITE_ERROR
* * if the record is not well - formed , or SQLITE_OK otherwise .
*/
static int zipfileReadLFH(
u8 *aBuffer,
ZipfileLFH *pLFH
){
u8 *aRead = aBuffer;
int rc = SQLITE_OK;
u32 sig = zipfileRead32(aRead);
if ( sig!=ZIPFILE_SIGNATURE_LFH ){
rc = SQLITE_ERROR;
}else {
pLFH->iVersionExtract = zipfileRead16(aRead);
pLFH->flags = zipfileRead16(aRead);
pLFH->iCompression = zipfileRead16(aRead);
pLFH->mTime = zipfileRead16(aRead);
pLFH->mDate = zipfileRead16(aRead);
pLFH->crc32 = zipfileRead32(aRead);
pLFH->szCompressed = zipfileRead32(aRead);
pLFH->szUncompressed = zipfileRead32(aRead);
pLFH->nFile = zipfileRead16(aRead);
pLFH->nExtra = zipfileRead16(aRead);
if ( pLFH->nFile>ZIPFILE_MX_NAME ) rc = SQLITE_ERROR;
}
return rc;
}
/*
* * Buffer aExtra ( size nExtra bytes ) contains zip archive " extra " fields .
* * Scan through this buffer to find an " extra - timestamp " field . If one
* * exists , extract the 32 - bit modification - timestamp from it and store
* * the value in output parameter * pmTime .
* *
* * Zero is returned if no extra - timestamp record could be found ( and so
* * * pmTime is left unchanged ) , or non - zero otherwise .
* *
* * The general format of an extra field is :
* *
* * Header ID 2 bytes
* * Data Size 2 bytes
* * Data N bytes
*/
static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){
int ret = 0 ;
u8 *p = aExtra;
u8 *pEnd = &aExtra[nExtra];
/* Stop when there are less than 9 bytes left to scan in the buffer. This
* * is because the timestamp field requires exactly 9 bytes - 4 bytes of
* * header fields and 5 bytes of data . If there are less than 9 bytes
* * remaining , either it is some other field or else the extra data
** is corrupt. Either way, do not process it. */
while ( p+(2 *sizeof (u16) + 1 + sizeof (u32))<=pEnd ){
u16 id = zipfileRead16(p);
u16 nByte = zipfileRead16(p);
switch ( id ){
case ZIPFILE_EXTRA_TIMESTAMP: {
u8 b = p[0 ];
if ( b & 0 x01 ){ /* 0x01 -> modtime is present */
*pmTime = zipfileGetU32(&p[1 ]);
ret = 1 ;
}
break ;
}
}
p += nByte;
}
return ret;
}
/*
* * Convert the standard MS - DOS timestamp stored in the mTime and mDate
* * fields of the CDS structure passed as the only argument to a 32 - bit
* * UNIX seconds - since - the - epoch timestamp . Return the result .
* *
* * " Standard " MS - DOS time format :
* *
* * File modification time :
* * Bits 00 - 04 : seconds divided by 2
* * Bits 05 - 10 : minute
* * Bits 11 - 15 : hour
* * File modification date :
* * Bits 00 - 04 : day
* * Bits 05 - 08 : month ( 1 - 12 )
* * Bits 09 - 15 : years from 1980
* *
* * https : //msdn.microsoft.com/en-us/library/9kkf9tah.aspx
*/
static u32 zipfileMtime(ZipfileCDS *pCDS){
int Y,M,D,X1,X2,A,B,sec,min,hr;
i64 JDsec;
Y = (1980 + ((pCDS->mDate >> 9 ) & 0 x7F));
M = ((pCDS->mDate >> 5 ) & 0 x0F);
D = (pCDS->mDate & 0 x1F);
sec = (pCDS->mTime & 0 x1F)*2 ;
min = (pCDS->mTime >> 5 ) & 0 x3F;
hr = (pCDS->mTime >> 11 ) & 0 x1F;
if ( M<=2 ){
Y--;
M += 12 ;
}
X1 = 36525 *(Y+4716 )/100 ;
X2 = 306001 *(M+1 )/10000 ;
A = Y/100 ;
B = 2 - A + (A/4 );
JDsec = (i64)((X1 + X2 + D + B - 1524 .5 )*86400 ) + hr*3600 + min*60 + sec;
return (u32)(JDsec - (i64)24405875 *(i64)8640 );
}
/*
* * The opposite of zipfileMtime ( ) . This function populates the mTime and
* * mDate fields of the CDS structure passed as the first argument according
* * to the UNIX timestamp value passed as the second .
*/
static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){
/* Convert unix timestamp to JD (2440588 is noon on 1/1/1970) */
i64 JD = (i64)2440588 + mUnixTime / (24 *60 *60 );
int A, B, C, D, E;
int yr, mon, day;
int hr, min, sec;
A = (int )((JD - 1867216 .25 )/36524 .25 );
A = (int )(JD + 1 + A - (A/4 ));
B = A + 1524 ;
C = (int )((B - 122 .1 )/365 .25 );
D = (36525 *(C&32767 ))/100 ;
E = (int )((B-D)/30 .6001 );
day = B - D - (int )(30 .6001 *E);
mon = (E<14 ? E-1 : E-13 );
yr = mon>2 ? C-4716 : C-4715 ;
hr = (mUnixTime % (24 *60 *60 )) / (60 *60 );
min = (mUnixTime % (60 *60 )) / 60 ;
sec = (mUnixTime % 60 );
if ( yr>=1980 ){
pCds->mDate = (u16)(day + (mon << 5 ) + ((yr-1980 ) << 9 ));
pCds->mTime = (u16)(sec/2 + (min<<5 ) + (hr<<11 ));
}else {
pCds->mDate = pCds->mTime = 0 ;
}
assert( mUnixTime<315507600
|| mUnixTime==zipfileMtime(pCds)
|| ((mUnixTime % 2 ) && mUnixTime-1 ==zipfileMtime(pCds))
/* || (mUnixTime % 2) */
);
}
/*
* * Set ( * pzErr ) to point to a buffer from sqlite3_malloc ( ) containing a
* * generic corruption message and return SQLITE_CORRUPT ;
*/
static int zipfileCorrupt(char **pzErr){
sqlite3_free(*pzErr);
*pzErr = sqlite3_mprintf("zip archive is corrupt" );
return SQLITE_CORRUPT;
}
/*
* * If aBlob is not NULL , then it is a pointer to a buffer ( nBlob bytes in
* * size ) containing an entire zip archive image . Or , if aBlob is NULL ,
* * then pFile is a file - handle open on a zip file . In either case , this
* * function creates a ZipfileEntry object based on the zip archive entry
* * for which the CDS record is at offset iOff .
* *
* * If successful , SQLITE_OK is returned and ( * ppEntry ) set to point to
* * the new object . Otherwise , an SQLite error code is returned and the
* * final value of ( * ppEntry ) undefined .
*/
static int zipfileGetEntry(
ZipfileTab *pTab, /* Store any error message here */
const u8 *aBlob, /* Pointer to in-memory file image */
i64 nBlob, /* Size of aBlob[] in bytes */
FILE *pFile, /* If aBlob==0, read from this file */
i64 iOff, /* Offset of CDS record */
ZipfileEntry **ppEntry /* OUT: Pointer to new object */
){
u8 *aRead;
char **pzErr = &pTab->base.zErrMsg;
int rc = SQLITE_OK;
if ( aBlob==0 ){
aRead = pTab->aBuffer;
rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
}else {
if ( (iOff+ZIPFILE_CDS_FIXED_SZ)>nBlob ){
/* Not enough data for the CDS structure. Corruption. */
return zipfileCorrupt(pzErr);
}
aRead = (u8*)&aBlob[iOff];
}
if ( rc==SQLITE_OK ){
sqlite3_int64 nAlloc;
ZipfileEntry *pNew;
int nFile = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF]);
int nExtra = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+2 ]);
nExtra += zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+4 ]);
nAlloc = sizeof (ZipfileEntry) + nExtra;
if ( aBlob ){
nAlloc += zipfileGetU32(&aRead[ZIPFILE_CDS_SZCOMPRESSED_OFF]);
}
pNew = (ZipfileEntry*)sqlite3_malloc64(nAlloc);
if ( pNew==0 ){
rc = SQLITE_NOMEM;
}else {
memset(pNew, 0 , sizeof (ZipfileEntry));
rc = zipfileReadCDS(aRead, &pNew->cds);
if ( rc!=SQLITE_OK ){
zipfileTableErr(pTab, "failed to read CDS at offset %lld" , iOff);
}else if ( aBlob==0 ){
rc = zipfileReadData(
pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
);
}else {
aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
if ( (iOff + ZIPFILE_CDS_FIXED_SZ + nFile + nExtra)>nBlob ){
rc = zipfileCorrupt(pzErr);
}
}
}
if ( rc==SQLITE_OK ){
u32 *pt = &pNew->mUnixTime;
pNew->cds.zFile = sqlite3_mprintf("%.*s" , nFile, aRead);
pNew->aExtra = (u8*)&pNew[1 ];
memcpy(pNew->aExtra, &aRead[nFile], nExtra);
if ( pNew->cds.zFile==0 ){
rc = SQLITE_NOMEM;
}else if ( 0 ==zipfileScanExtra(&aRead[nFile], pNew->cds.nExtra, pt) ){
pNew->mUnixTime = zipfileMtime(&pNew->cds);
}
}
if ( rc==SQLITE_OK ){
static const int szFix = ZIPFILE_LFH_FIXED_SZ;
ZipfileLFH lfh;
if ( pFile ){
rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
}else {
aRead = (u8*)&aBlob[pNew->cds.iOffset];
if ( ((i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){
rc = zipfileCorrupt(pzErr);
}
}
memset(&lfh, 0 , sizeof (lfh));
if ( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
if ( rc==SQLITE_OK ){
pNew->iDataOff = (i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
pNew->iDataOff += lfh.nFile + lfh.nExtra;
if ( aBlob && pNew->cds.szCompressed ){
if ( pNew->iDataOff + pNew->cds.szCompressed > nBlob ){
rc = zipfileCorrupt(pzErr);
}else {
pNew->aData = &pNew->aExtra[nExtra];
memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed);
}
}
}else {
zipfileTableErr(pTab, "failed to read LFH at offset %d" ,
(int )pNew->cds.iOffset
);
}
}
if ( rc!=SQLITE_OK ){
zipfileEntryFree(pNew);
}else {
*ppEntry = pNew;
}
}
return rc;
}
/*
* * Advance an ZipfileCsr to its next row of output .
*/
static int zipfileNext(sqlite3_vtab_cursor *cur){
ZipfileCsr *pCsr = (ZipfileCsr*)cur;
int rc = SQLITE_OK;
if ( pCsr->pFile ){
i64 iEof = (i64)pCsr->eocd.iOffset + (i64)pCsr->eocd.nSize;
zipfileEntryFree(pCsr->pCurrent);
pCsr->pCurrent = 0 ;
if ( pCsr->iNextOff>=iEof ){
pCsr->bEof = 1 ;
}else {
ZipfileEntry *p = 0 ;
ZipfileTab *pTab = (ZipfileTab*)(cur->pVtab);
rc = zipfileGetEntry(pTab, 0 , 0 , pCsr->pFile, pCsr->iNextOff, &p);
if ( rc==SQLITE_OK ){
pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ;
pCsr->iNextOff += (int )p->cds.nExtra + p->cds.nFile + p->cds.nComment;
}
pCsr->pCurrent = p;
}
}else {
if ( !pCsr->bNoop ){
pCsr->pCurrent = pCsr->pCurrent->pNext;
}
if ( pCsr->pCurrent==0 ){
pCsr->bEof = 1 ;
}
}
pCsr->bNoop = 0 ;
return rc;
}
static void zipfileFree(void *p) {
sqlite3_free(p);
}
/*
* * Buffer aIn ( size nIn bytes ) contains compressed data . Uncompressed , the
* * size is nOut bytes . This function uncompresses the data and sets the
* * return value in context pCtx to the result ( a blob ) .
* *
* * If an error occurs , an error code is left in pCtx instead .
*/
static void zipfileInflate(
sqlite3_context *pCtx, /* Store result here */
const u8 *aIn, /* Compressed data */
int nIn, /* Size of buffer aIn[] in bytes */
int nOut /* Expected output size */
){
u8 *aRes = sqlite3_malloc64(nOut);
if ( aRes==0 ){
sqlite3_result_error_nomem(pCtx);
}else {
int err;
z_stream str;
memset(&str, 0 , sizeof (str));
str.next_in = (Byte*)aIn;
str.avail_in = nIn;
str.next_out = (Byte*)aRes;
str.avail_out = nOut;
err = inflateInit2(&str, -15 );
if ( err!=Z_OK ){
zipfileCtxErrorMsg(pCtx, "inflateInit2() failed (%d)" , err);
}else {
err = inflate(&str, Z_NO_FLUSH);
if ( err!=Z_STREAM_END ){
zipfileCtxErrorMsg(pCtx, "inflate() failed (%d)" , err);
}else {
sqlite3_result_blob(pCtx, aRes, (int )str.total_out, zipfileFree);
aRes = 0 ;
}
}
sqlite3_free(aRes);
inflateEnd(&str);
}
}
/*
* * Buffer aIn ( size nIn bytes ) contains uncompressed data . This function
* * compresses it and sets ( * ppOut ) to point to a buffer containing the
* * compressed data . The caller is responsible for eventually calling
* * sqlite3_free ( ) to release buffer ( * ppOut ) . Before returning , ( * pnOut )
* * is set to the size of buffer ( * ppOut ) in bytes .
* *
* * If no error occurs , SQLITE_OK is returned . Otherwise , an SQLite error
* * code is returned and an error message left in virtual - table handle
* * pTab . The values of ( * ppOut ) and ( * pnOut ) are left unchanged in this
* * case .
*/
static int zipfileDeflate(
const u8 *aIn, int nIn, /* Input */
u8 **ppOut, int *pnOut, /* Output */
char **pzErr /* OUT: Error message */
){
int rc = SQLITE_OK;
sqlite3_int64 nAlloc;
z_stream str;
u8 *aOut;
memset(&str, 0 , sizeof (str));
str.next_in = (Bytef*)aIn;
str.avail_in = nIn;
deflateInit2(&str, 9 , Z_DEFLATED, -15 , 8 , Z_DEFAULT_STRATEGY);
nAlloc = deflateBound(&str, nIn);
aOut = (u8*)sqlite3_malloc64(nAlloc);
if ( aOut==0 ){
rc = SQLITE_NOMEM;
}else {
int res;
str.next_out = aOut;
str.avail_out = nAlloc;
res = deflate(&str, Z_FINISH);
if ( res==Z_STREAM_END ){
*ppOut = aOut;
*pnOut = (int )str.total_out;
}else {
sqlite3_free(aOut);
*pzErr = sqlite3_mprintf("zipfile: deflate() error" );
rc = SQLITE_ERROR;
}
deflateEnd(&str);
}
return rc;
}
/*
* * Return values of columns for the row at which the series_cursor
* * is currently pointing .
*/
static int zipfileColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
ZipfileCsr *pCsr = (ZipfileCsr*)cur;
ZipfileCDS *pCDS = &pCsr->pCurrent->cds;
int rc = SQLITE_OK;
switch ( i ){
case 0 : /* name */
sqlite3_result_text(ctx, pCDS->zFile, -1 , SQLITE_TRANSIENT);
break ;
case 1 : /* mode */
/* TODO: Whether or not the following is correct surely depends on
** the platform on which the archive was created. */
sqlite3_result_int(ctx, pCDS->iExternalAttr >> 16 );
break ;
case 2 : { /* mtime */
sqlite3_result_int64(ctx, pCsr->pCurrent->mUnixTime);
break ;
}
case 3 : { /* sz */
if ( sqlite3_vtab_nochange(ctx)==0 ){
sqlite3_result_int64(ctx, pCDS->szUncompressed);
}
break ;
}
case 4 : /* rawdata */
if ( sqlite3_vtab_nochange(ctx) ) break ;
case 5 : { /* data */
if ( i==4 || pCDS->iCompression==0 || pCDS->iCompression==8 ){
int sz = pCDS->szCompressed;
int szFinal = pCDS->szUncompressed;
if ( szFinal>0 ){
u8 *aBuf;
u8 *aFree = 0 ;
if ( pCsr->pCurrent->aData ){
aBuf = pCsr->pCurrent->aData;
}else {
aBuf = aFree = sqlite3_malloc64(sz);
if ( aBuf==0 ){
rc = SQLITE_NOMEM;
}else {
FILE *pFile = pCsr->pFile;
if ( pFile==0 ){
pFile = ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd;
}
rc = zipfileReadData(pFile, aBuf, sz, pCsr->pCurrent->iDataOff,
&pCsr->base.pVtab->zErrMsg
);
}
}
if ( rc==SQLITE_OK ){
if ( i==5 && pCDS->iCompression ){
zipfileInflate(ctx, aBuf, sz, szFinal);
}else {
sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT);
}
}
sqlite3_free(aFree);
}else {
/* Figure out if this is a directory or a zero-sized file. Consider
* * it to be a directory either if the mode suggests so , or if
** the final character in the name is '/'. */
u32 mode = pCDS->iExternalAttr >> 16 ;
if ( !(mode & S_IFDIR)
&& pCDS->nFile>=1
&& pCDS->zFile[pCDS->nFile-1 ]!='/'
){
sqlite3_result_blob(ctx, "" , 0 , SQLITE_STATIC);
}
}
}
break ;
}
case 6 : /* method */
sqlite3_result_int(ctx, pCDS->iCompression);
break ;
default : /* z */
assert( i==7 );
sqlite3_result_int64(ctx, pCsr->iId);
break ;
}
return rc;
}
/*
* * Return TRUE if the cursor is at EOF .
*/
static int zipfileEof(sqlite3_vtab_cursor *cur){
ZipfileCsr *pCsr = (ZipfileCsr*)cur;
return pCsr->bEof;
}
/*
* * If aBlob is not NULL , then it points to a buffer nBlob bytes in size
* * containing an entire zip archive image . Or , if aBlob is NULL , then pFile
* * is guaranteed to be a file - handle open on a zip file .
* *
* * This function attempts to locate the EOCD record within the zip archive
* * and populate * pEOCD with the results of decoding it . SQLITE_OK is
* * returned if successful . Otherwise , an SQLite error code is returned and
* * an English language error message may be left in virtual - table pTab .
*/
static int zipfileReadEOCD(
ZipfileTab *pTab, /* Return errors here */
const u8 *aBlob, /* Pointer to in-memory file image */
i64 nBlob, /* Size of aBlob[] in bytes */
FILE *pFile, /* Read from this file if aBlob==0 */
ZipfileEOCD *pEOCD /* Object to populate */
){
u8 *aRead = pTab->aBuffer; /* Temporary buffer */
i64 nRead; /* Bytes to read from file */
int rc = SQLITE_OK;
memset(pEOCD, 0 , sizeof (ZipfileEOCD));
if ( aBlob==0 ){
i64 iOff; /* Offset to read from */
i64 szFile; /* Total size of file in bytes */
fseek(pFile, 0 , SEEK_END);
szFile = (i64)ftell(pFile);
if ( szFile==0 ){
return SQLITE_OK;
}
nRead = (int )(MIN(szFile, ZIPFILE_BUFFER_SIZE));
iOff = szFile - nRead;
rc = zipfileReadData(pFile, aRead, nRead, iOff, &pTab->base.zErrMsg);
}else {
nRead = (int )(MIN(nBlob, ZIPFILE_BUFFER_SIZE));
aRead = (u8*)&aBlob[nBlob-nRead];
}
if ( rc==SQLITE_OK ){
i64 i;
/* Scan backwards looking for the signature bytes */
for (i=nRead-20 ; i>=0 ; i--){
if ( aRead[i]==0 x50 && aRead[i+1 ]==0 x4b
&& aRead[i+2 ]==0 x05 && aRead[i+3 ]==0 x06
){
break ;
}
}
if ( i<0 ){
zipfileTableErr(pTab, "cannot find end of central directory record" );
return SQLITE_ERROR;
}
aRead += i+4 ;
pEOCD->iDisk = zipfileRead16(aRead);
pEOCD->iFirstDisk = zipfileRead16(aRead);
pEOCD->nEntry = zipfileRead16(aRead);
pEOCD->nEntryTotal = zipfileRead16(aRead);
pEOCD->nSize = zipfileRead32(aRead);
pEOCD->iOffset = zipfileRead32(aRead);
}
return rc;
}
/*
* * Add object pNew to the linked list that begins at ZipfileTab . pFirstEntry
* * and ends with pLastEntry . If argument pBefore is NULL , then pNew is added
* * to the end of the list . Otherwise , it is added to the list immediately
* * before pBefore ( which is guaranteed to be a part of said list ) .
*/
static void zipfileAddEntry(
ZipfileTab *pTab,
ZipfileEntry *pBefore,
ZipfileEntry *pNew
){
assert( (pTab->pFirstEntry==0 )==(pTab->pLastEntry==0 ) );
assert( pNew->pNext==0 );
if ( pBefore==0 ){
if ( pTab->pFirstEntry==0 ){
pTab->pFirstEntry = pTab->pLastEntry = pNew;
}else {
assert( pTab->pLastEntry->pNext==0 );
pTab->pLastEntry->pNext = pNew;
pTab->pLastEntry = pNew;
}
}else {
ZipfileEntry **pp;
for (pp=&pTab->pFirstEntry; *pp!=pBefore; pp=&((*pp)->pNext));
pNew->pNext = pBefore;
*pp = pNew;
}
}
static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, i64 nBlob){
ZipfileEOCD eocd;
int rc;
int i;
i64 iOff;
rc = zipfileReadEOCD(pTab, aBlob, nBlob, pTab->pWriteFd, &eocd);
iOff = eocd.iOffset;
for (i=0 ; rc==SQLITE_OK && i<eocd.nEntry; i++){
ZipfileEntry *pNew = 0 ;
rc = zipfileGetEntry(pTab, aBlob, nBlob, pTab->pWriteFd, iOff, &pNew);
if ( rc==SQLITE_OK ){
zipfileAddEntry(pTab, 0 , pNew);
iOff += ZIPFILE_CDS_FIXED_SZ;
iOff += (int )pNew->cds.nExtra + pNew->cds.nFile + pNew->cds.nComment;
}
}
return rc;
}
/*
* * xFilter callback .
*/
static int zipfileFilter(
sqlite3_vtab_cursor *cur,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
ZipfileTab *pTab = (ZipfileTab*)cur->pVtab;
ZipfileCsr *pCsr = (ZipfileCsr*)cur;
const char *zFile = 0 ; /* Zip file to scan */
int rc = SQLITE_OK; /* Return Code */
int bInMemory = 0 ; /* True for an in-memory zipfile */
(void )idxStr;
(void )argc;
zipfileResetCursor(pCsr);
if ( pTab->zFile ){
zFile = pTab->zFile;
}else if ( idxNum==0 ){
zipfileCursorErr(pCsr, "zipfile() function requires an argument" );
return SQLITE_ERROR;
}else if ( sqlite3_value_type(argv[0 ])==SQLITE_BLOB ){
static const u8 aEmptyBlob = 0 ;
const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0 ]);
i64 nBlob = sqlite3_value_bytes(argv[0 ]);
assert( pTab->pFirstEntry==0 );
if ( aBlob==0 ){
aBlob = &aEmptyBlob;
nBlob = 0 ;
}
rc = zipfileLoadDirectory(pTab, aBlob, nBlob);
pCsr->pFreeEntry = pTab->pFirstEntry;
pTab->pFirstEntry = pTab->pLastEntry = 0 ;
if ( rc!=SQLITE_OK ) return rc;
bInMemory = 1 ;
}else {
zFile = (const char *)sqlite3_value_text(argv[0 ]);
}
if ( 0 ==pTab->pWriteFd && 0 ==bInMemory ){
pCsr->pFile = zFile ? sqlite3_fopen(zFile, "rb" ) : 0 ;
if ( pCsr->pFile==0 ){
zipfileCursorErr(pCsr, "cannot open file: %s" , zFile);
rc = SQLITE_ERROR;
}else {
rc = zipfileReadEOCD(pTab, 0 , 0 , pCsr->pFile, &pCsr->eocd);
if ( rc==SQLITE_OK ){
if ( pCsr->eocd.nEntry==0 ){
pCsr->bEof = 1 ;
}else {
pCsr->iNextOff = pCsr->eocd.iOffset;
rc = zipfileNext(cur);
}
}
}
}else {
pCsr->bNoop = 1 ;
pCsr->pCurrent = pCsr->pFreeEntry ? pCsr->pFreeEntry : pTab->pFirstEntry;
rc = zipfileNext(cur);
}
return rc;
}
/*
* * xBestIndex callback .
*/
static int zipfileBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i;
int idx = -1 ;
int unusable = 0 ;
(void )tab;
for (i=0 ; i<pIdxInfo->nConstraint; i++){
const struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i];
if ( pCons->iColumn!=ZIPFILE_F_COLUMN_IDX ) continue ;
if ( pCons->usable==0 ){
unusable = 1 ;
}else if ( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
idx = i;
}
}
pIdxInfo->estimatedCost = 1000 .0 ;
if ( idx>=0 ){
pIdxInfo->aConstraintUsage[idx].argvIndex = 1 ;
pIdxInfo->aConstraintUsage[idx].omit = 1 ;
pIdxInfo->idxNum = 1 ;
}else if ( unusable ){
return SQLITE_CONSTRAINT;
}
return SQLITE_OK;
}
static ZipfileEntry *zipfileNewEntry(const char *zPath){
ZipfileEntry *pNew;
pNew = sqlite3_malloc64(sizeof (ZipfileEntry));
if ( pNew ){
memset(pNew, 0 , sizeof (ZipfileEntry));
pNew->cds.zFile = sqlite3_mprintf("%s" , zPath);
if ( pNew->cds.zFile==0 ){
sqlite3_free(pNew);
pNew = 0 ;
}
}
return pNew;
}
static int zipfileSerializeLFH(ZipfileEntry *pEntry, u8 *aBuf){
ZipfileCDS *pCds = &pEntry->cds;
u8 *a = aBuf;
pCds->nExtra = 9 ;
/* Write the LFH itself */
zipfileWrite32(a, ZIPFILE_SIGNATURE_LFH);
zipfileWrite16(a, pCds->iVersionExtract);
zipfileWrite16(a, pCds->flags);
zipfileWrite16(a, pCds->iCompression);
zipfileWrite16(a, pCds->mTime);
zipfileWrite16(a, pCds->mDate);
zipfileWrite32(a, pCds->crc32);
zipfileWrite32(a, pCds->szCompressed);
zipfileWrite32(a, pCds->szUncompressed);
zipfileWrite16(a, (u16)pCds->nFile);
zipfileWrite16(a, pCds->nExtra);
assert( a==&aBuf[ZIPFILE_LFH_FIXED_SZ] );
/* Add the file name */
memcpy(a, pCds->zFile, (int )pCds->nFile);
a += (int )pCds->nFile;
/* The "extra" data */
zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP);
zipfileWrite16(a, 5 );
*a++ = 0 x01;
zipfileWrite32(a, pEntry->mUnixTime);
return a-aBuf;
}
static int zipfileAppendEntry(
ZipfileTab *pTab,
ZipfileEntry *pEntry,
const u8 *pData,
int nData
){
u8 *aBuf = pTab->aBuffer;
int nBuf;
int rc;
nBuf = zipfileSerializeLFH(pEntry, aBuf);
rc = zipfileAppendData(pTab, aBuf, nBuf);
if ( rc==SQLITE_OK ){
pEntry->iDataOff = pTab->szCurrent;
rc = zipfileAppendData(pTab, pData, nData);
}
return rc;
}
static int zipfileGetMode(
sqlite3_value *pVal,
int bIsDir, /* If true, default to directory */
u32 *pMode, /* OUT: Mode value */
char **pzErr /* OUT: Error message */
){
const char *z = (const char *)sqlite3_value_text(pVal);
u32 mode = 0 ;
if ( z==0 ){
mode = (bIsDir ? (S_IFDIR + 0755 ) : (S_IFREG + 0644 ));
}else if ( z[0 ]>='0' && z[0 ]<='9' ){
mode = (unsigned int )sqlite3_value_int(pVal);
}else {
const char zTemplate[11 ] = "-rwxrwxrwx" ;
int i;
if ( strlen(z)!=10 ) goto parse_error;
switch ( z[0 ] ){
case '-' : mode |= S_IFREG; break ;
case 'd' : mode |= S_IFDIR; break ;
case 'l' : mode |= S_IFLNK; break ;
default : goto parse_error;
}
for (i=1 ; i<10 ; i++){
if ( z[i]==zTemplate[i] ) mode |= 1 << (9 -i);
else if ( z[i]!='-' ) goto parse_error;
}
}
if ( ((mode & S_IFDIR)==0 )==bIsDir ){
/* The "mode" attribute is a directory, but data has been specified.
** Or vice-versa - no data but "mode" is a file or symlink. */
*pzErr = sqlite3_mprintf("zipfile: mode does not match data" );
return SQLITE_CONSTRAINT;
}
*pMode = mode;
return SQLITE_OK;
parse_error:
*pzErr = sqlite3_mprintf("zipfile: parse error in mode: %s" , z);
return SQLITE_ERROR;
}
/*
* * Both ( const char * ) arguments point to nul - terminated strings . Argument
* * nB is the value of strlen ( zB ) . This function returns 0 if the strings are
** identical, ignoring any trailing '/' character in either path. */
static int zipfileComparePath(const char *zA, const char *zB, int nB){
int nA = (int )strlen(zA);
if ( nA>0 && zA[nA-1 ]=='/' ) nA--;
if ( nB>0 && zB[nB-1 ]=='/' ) nB--;
if ( nA==nB && memcmp(zA, zB, nA)==0 ) return 0 ;
return 1 ;
}
static int zipfileBegin(sqlite3_vtab *pVtab){
ZipfileTab *pTab = (ZipfileTab*)pVtab;
int rc = SQLITE_OK;
assert( pTab->pWriteFd==0 );
if ( pTab->zFile==0 || pTab->zFile[0 ]==0 ){
zipfileTableErr(pTab, "zipfile: missing filename" );
return SQLITE_ERROR;
}
/* Open a write fd on the file. Also load the entire central directory
* * structure into memory . During the transaction any new file data is
* * appended to the archive file , but the central directory is accumulated
** in main-memory until the transaction is committed. */
pTab->pWriteFd = sqlite3_fopen(pTab->zFile, "ab+" );
if ( pTab->pWriteFd==0 ){
zipfileTableErr(pTab,
"zipfile: failed to open file %s for writing" , pTab->zFile
);
rc = SQLITE_ERROR;
}else {
fseek(pTab->pWriteFd, 0 , SEEK_END);
pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd);
rc = zipfileLoadDirectory(pTab, 0 , 0 );
}
if ( rc!=SQLITE_OK ){
zipfileCleanupTransaction(pTab);
}
return rc;
}
/*
* * Return the current time as a 32 - bit timestamp in UNIX epoch format ( like
* * time ( 2 ) ) .
*/
static u32 zipfileTime(void ){
sqlite3_vfs *pVfs = sqlite3_vfs_find(0 );
u32 ret;
if ( pVfs==0 ) return 0 ;
if ( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){
i64 ms;
pVfs->xCurrentTimeInt64(pVfs, &ms);
ret = (u32)((ms/1000 ) - ((i64)24405875 * 8640 ));
}else {
double day;
pVfs->xCurrentTime(pVfs, &day);
ret = (u32)((day - 2440587 .5 ) * 86400 );
}
return ret;
}
/*
* * Return a 32 - bit timestamp in UNIX epoch format .
* *
* * If the value passed as the only argument is either NULL or an SQL NULL ,
* * return the current time . Otherwise , return the value stored in ( * pVal )
* * cast to a 32 - bit unsigned integer .
*/
static u32 zipfileGetTime(sqlite3_value *pVal){
if ( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){
return zipfileTime();
}
return (u32)sqlite3_value_int64(pVal);
}
/*
* * Unless it is NULL , entry pOld is currently part of the pTab - > pFirstEntry
* * linked list . Remove it from the list and free the object .
*/
static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){
if ( pOld ){
if ( pTab->pFirstEntry==pOld ){
pTab->pFirstEntry = pOld->pNext;
if ( pTab->pLastEntry==pOld ) pTab->pLastEntry = 0 ;
}else {
ZipfileEntry *p;
for (p=pTab->pFirstEntry; p; p=p->pNext){
if ( p->pNext==pOld ){
p->pNext = pOld->pNext;
if ( pTab->pLastEntry==pOld ) pTab->pLastEntry = p;
break ;
}
}
}
zipfileEntryFree(pOld);
}
}
/*
* * xUpdate method .
*/
static int zipfileUpdate(
sqlite3_vtab *pVtab,
int nVal,
sqlite3_value **apVal,
sqlite_int64 *pRowid
){
ZipfileTab *pTab = (ZipfileTab*)pVtab;
int rc = SQLITE_OK; /* Return Code */
ZipfileEntry *pNew = 0 ; /* New in-memory CDS entry */
u32 mode = 0 ; /* Mode for new entry */
u32 mTime = 0 ; /* Modification time for new entry */
i64 sz = 0 ; /* Uncompressed size */
const char *zPath = 0 ; /* Path for new entry */
int nPath = 0 ; /* strlen(zPath) */
const u8 *pData = 0 ; /* Pointer to buffer containing content */
int nData = 0 ; /* Size of pData buffer in bytes */
int iMethod = 0 ; /* Compression method for new entry */
u8 *pFree = 0 ; /* Free this */
char *zFree = 0 ; /* Also free this */
ZipfileEntry *pOld = 0 ;
ZipfileEntry *pOld2 = 0 ;
int bUpdate = 0 ; /* True for an update that modifies "name" */
int bIsDir = 0 ;
u32 iCrc32 = 0 ;
(void )pRowid;
if ( pTab->pWriteFd==0 ){
rc = zipfileBegin(pVtab);
if ( rc!=SQLITE_OK ) return rc;
}
/* If this is a DELETE or UPDATE, find the archive entry to delete. */
if ( sqlite3_value_type(apVal[0 ])!=SQLITE_NULL ){
const char *zDelete = (const char *)sqlite3_value_text(apVal[0 ]);
int nDelete = (int )strlen(zDelete);
if ( nVal>1 ){
const char *zUpdate = (const char *)sqlite3_value_text(apVal[1 ]);
if ( zUpdate && zipfileComparePath(zUpdate, zDelete, nDelete)!=0 ){
bUpdate = 1 ;
}
}
for (pOld=pTab->pFirstEntry; 1 ; pOld=pOld->pNext){
if ( zipfileComparePath(pOld->cds.zFile, zDelete, nDelete)==0 ){
break ;
}
assert( pOld->pNext );
}
}
if ( nVal>1 ){
/* Check that "sz" and "rawdata" are both NULL: */
if ( sqlite3_value_type(apVal[5 ])!=SQLITE_NULL ){
zipfileTableErr(pTab, "sz must be NULL" );
rc = SQLITE_CONSTRAINT;
}
if ( sqlite3_value_type(apVal[6 ])!=SQLITE_NULL ){
zipfileTableErr(pTab, "rawdata must be NULL" );
rc = SQLITE_CONSTRAINT;
}
if ( rc==SQLITE_OK ){
if ( sqlite3_value_type(apVal[7 ])==SQLITE_NULL ){
/* data=NULL. A directory */
bIsDir = 1 ;
}else {
/* Value specified for "data", and possibly "method". This must be
** a regular file or a symlink. */
const u8 *aIn = sqlite3_value_blob(apVal[7 ]);
int nIn = sqlite3_value_bytes(apVal[7 ]);
int bAuto = sqlite3_value_type(apVal[8 ])==SQLITE_NULL;
iMethod = sqlite3_value_int(apVal[8 ]);
sz = nIn;
pData = aIn;
nData = nIn;
if ( iMethod!=0 && iMethod!=8 ){
zipfileTableErr(pTab, "unknown compression method: %d" , iMethod);
rc = SQLITE_CONSTRAINT;
}else {
if ( bAuto || iMethod ){
int nCmp;
rc = zipfileDeflate(aIn, nIn, &pFree, &nCmp, &pTab->base.zErrMsg);
if ( rc==SQLITE_OK ){
if ( iMethod || nCmp<nIn ){
iMethod = 8 ;
pData = pFree;
nData = nCmp;
}
}
}
iCrc32 = crc32(0 , aIn, nIn);
}
}
}
if ( rc==SQLITE_OK ){
rc = zipfileGetMode(apVal[3 ], bIsDir, &mode, &pTab->base.zErrMsg);
}
if ( rc==SQLITE_OK ){
zPath = (const char *)sqlite3_value_text(apVal[2 ]);
if ( zPath==0 ) zPath = "" ;
nPath = (int )strlen(zPath);
if ( nPath>ZIPFILE_MX_NAME ){
zipfileTableErr(pTab, "filename too long; max: %d bytes" ,
ZIPFILE_MX_NAME);
rc = SQLITE_CONSTRAINT;
}
mTime = zipfileGetTime(apVal[4 ]);
}
if ( rc==SQLITE_OK && bIsDir ){
/* For a directory, check that the last character in the path is a
* * ' / ' . This appears to be required for compatibility with info - zip
* * ( the unzip command on unix ) . It does not create directories
** otherwise. */
if ( nPath<=0 || zPath[nPath-1 ]!='/' ){
zFree = sqlite3_mprintf("%s/" , zPath);
zPath = (const char *)zFree;
if ( zFree==0 ){
rc = SQLITE_NOMEM;
nPath = 0 ;
}else {
nPath = (int )strlen(zPath);
}
}
}
/* Check that we're not inserting a duplicate entry -OR- updating an
** entry with a path, thereby making it into a duplicate. */
if ( (pOld==0 || bUpdate) && rc==SQLITE_OK ){
ZipfileEntry *p;
for (p=pTab->pFirstEntry; p; p=p->pNext){
if ( zipfileComparePath(p->cds.zFile, zPath, nPath)==0 ){
switch ( sqlite3_vtab_on_conflict(pTab->db) ){
case SQLITE_IGNORE: {
goto zipfile_update_done;
}
case SQLITE_REPLACE: {
pOld2 = p;
break ;
}
default : {
zipfileTableErr(pTab, "duplicate name: \" %s\"" , zPath);
rc = SQLITE_CONSTRAINT;
break ;
}
}
break ;
}
}
}
if ( rc==SQLITE_OK ){
/* Create the new CDS record. */
pNew = zipfileNewEntry(zPath);
if ( pNew==0 ){
rc = SQLITE_NOMEM;
}else {
pNew->cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY;
pNew->cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED;
pNew->cds.flags = ZIPFILE_NEWENTRY_FLAGS;
pNew->cds.iCompression = (u16)iMethod;
zipfileMtimeToDos(&pNew->cds, mTime);
pNew->cds.crc32 = iCrc32;
pNew->cds.szCompressed = nData;
pNew->cds.szUncompressed = (u32)sz;
pNew->cds.iExternalAttr = (mode<<16 );
pNew->cds.iOffset = (u32)pTab->szCurrent;
pNew->cds.nFile = (u16)nPath;
pNew->mUnixTime = (u32)mTime;
rc = zipfileAppendEntry(pTab, pNew, pData, nData);
zipfileAddEntry(pTab, pOld, pNew);
}
}
}
if ( rc==SQLITE_OK && (pOld || pOld2) ){
ZipfileCsr *pCsr;
for (pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){
if ( pCsr->pCurrent && (pCsr->pCurrent==pOld || pCsr->pCurrent==pOld2) ){
pCsr->pCurrent = pCsr->pCurrent->pNext;
pCsr->bNoop = 1 ;
}
}
zipfileRemoveEntryFromList(pTab, pOld);
zipfileRemoveEntryFromList(pTab, pOld2);
}
zipfile_update_done:
sqlite3_free(pFree);
sqlite3_free(zFree);
return rc;
}
static int zipfileSerializeEOCD(ZipfileEOCD *p, u8 *aBuf){
u8 *a = aBuf;
zipfileWrite32(a, ZIPFILE_SIGNATURE_EOCD);
zipfileWrite16(a, p->iDisk);
zipfileWrite16(a, p->iFirstDisk);
zipfileWrite16(a, p->nEntry);
zipfileWrite16(a, p->nEntryTotal);
zipfileWrite32(a, p->nSize);
zipfileWrite32(a, p->iOffset);
zipfileWrite16(a, 0 ); /* Size of trailing comment in bytes*/
return a-aBuf;
}
static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){
int nBuf = zipfileSerializeEOCD(p, pTab->aBuffer);
assert( nBuf==ZIPFILE_EOCD_FIXED_SZ );
return zipfileAppendData(pTab, pTab->aBuffer, nBuf);
}
/*
* * Serialize the CDS structure into buffer aBuf [ ] . Return the number
* * of bytes written .
*/
static int zipfileSerializeCDS(ZipfileEntry *pEntry, u8 *aBuf){
u8 *a = aBuf;
ZipfileCDS *pCDS = &pEntry->cds;
if ( pEntry->aExtra==0 ){
pCDS->nExtra = 9 ;
}
zipfileWrite32(a, ZIPFILE_SIGNATURE_CDS);
zipfileWrite16(a, pCDS->iVersionMadeBy);
zipfileWrite16(a, pCDS->iVersionExtract);
zipfileWrite16(a, pCDS->flags);
zipfileWrite16(a, pCDS->iCompression);
zipfileWrite16(a, pCDS->mTime);
zipfileWrite16(a, pCDS->mDate);
zipfileWrite32(a, pCDS->crc32);
zipfileWrite32(a, pCDS->szCompressed);
zipfileWrite32(a, pCDS->szUncompressed);
assert( a==&aBuf[ZIPFILE_CDS_NFILE_OFF] );
zipfileWrite16(a, pCDS->nFile);
zipfileWrite16(a, pCDS->nExtra);
zipfileWrite16(a, pCDS->nComment);
zipfileWrite16(a, pCDS->iDiskStart);
zipfileWrite16(a, pCDS->iInternalAttr);
zipfileWrite32(a, pCDS->iExternalAttr);
zipfileWrite32(a, pCDS->iOffset);
memcpy(a, pCDS->zFile, pCDS->nFile);
a += pCDS->nFile;
if ( pEntry->aExtra ){
int n = (int )pCDS->nExtra + (int )pCDS->nComment;
memcpy(a, pEntry->aExtra, n);
a += n;
}else {
assert( pCDS->nExtra==9 );
zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP);
zipfileWrite16(a, 5 );
*a++ = 0 x01;
zipfileWrite32(a, pEntry->mUnixTime);
}
return a-aBuf;
}
static int zipfileCommit(sqlite3_vtab *pVtab){
ZipfileTab *pTab = (ZipfileTab*)pVtab;
int rc = SQLITE_OK;
if ( pTab->pWriteFd ){
i64 iOffset = pTab->szCurrent;
ZipfileEntry *p;
ZipfileEOCD eocd;
int nEntry = 0 ;
/* Write out all entries */
for (p=pTab->pFirstEntry; rc==SQLITE_OK && p; p=p->pNext){
int n = zipfileSerializeCDS(p, pTab->aBuffer);
rc = zipfileAppendData(pTab, pTab->aBuffer, n);
nEntry++;
}
/* Write out the EOCD record */
eocd.iDisk = 0 ;
eocd.iFirstDisk = 0 ;
eocd.nEntry = (u16)nEntry;
eocd.nEntryTotal = (u16)nEntry;
eocd.nSize = (u32)(pTab->szCurrent - iOffset);
eocd.iOffset = (u32)iOffset;
rc = zipfileAppendEOCD(pTab, &eocd);
zipfileCleanupTransaction(pTab);
}
return rc;
}
static int zipfileRollback(sqlite3_vtab *pVtab){
return zipfileCommit(pVtab);
}
static ZipfileCsr *zipfileFindCursor(ZipfileTab *pTab, i64 iId){
ZipfileCsr *pCsr;
for (pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){
if ( iId==pCsr->iId ) break ;
}
return pCsr;
}
static void zipfileFunctionCds(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
ZipfileCsr *pCsr;
ZipfileTab *pTab = (ZipfileTab*)sqlite3_user_data(context);
assert( argc>0 );
pCsr = zipfileFindCursor(pTab, sqlite3_value_int64(argv[0 ]));
if ( pCsr ){
ZipfileCDS *p = &pCsr->pCurrent->cds;
char *zRes = sqlite3_mprintf("{"
"\" version-made-by\" : %u, "
"\" version-to-extract\" : %u, "
"\" flags\" : %u, "
"\" compression\" : %u, "
"\" time\" : %u, "
"\" date\" : %u, "
"\" crc32\" : %u, "
"\" compressed-size\" : %u, "
"\" uncompressed-size\" : %u, "
"\" file-name-length\" : %u, "
"\" extra-field-length\" : %u, "
"\" file-comment-length\" : %u, "
"\" disk-number-start\" : %u, "
"\" internal-attr\" : %u, "
"\" external-attr\" : %u, "
"\" offset\" : %u }" ,
(u32)p->iVersionMadeBy, (u32)p->iVersionExtract,
(u32)p->flags, (u32)p->iCompression,
(u32)p->mTime, (u32)p->mDate,
(u32)p->crc32, (u32)p->szCompressed,
(u32)p->szUncompressed, (u32)p->nFile,
(u32)p->nExtra, (u32)p->nComment,
(u32)p->iDiskStart, (u32)p->iInternalAttr,
(u32)p->iExternalAttr, (u32)p->iOffset
);
if ( zRes==0 ){
sqlite3_result_error_nomem(context);
}else {
sqlite3_result_text(context, zRes, -1 , SQLITE_TRANSIENT);
sqlite3_free(zRes);
}
}
}
/*
* * xFindFunction method .
*/
static int zipfileFindFunction(
sqlite3_vtab *pVtab, /* Virtual table handle */
int nArg, /* Number of SQL function arguments */
const char *zName, /* Name of SQL function */
void (**pxFunc)(sqlite3_context*,int ,sqlite3_value**), /* OUT: Result */
void **ppArg /* OUT: User data for *pxFunc */
){
(void )nArg;
if ( sqlite3_stricmp("zipfile_cds" , zName)==0 ){
*pxFunc = zipfileFunctionCds;
*ppArg = (void *)pVtab;
return 1 ;
}
return 0 ;
}
typedef struct ZipfileBuffer ZipfileBuffer;
struct ZipfileBuffer {
u8 *a; /* Pointer to buffer */
int n; /* Size of buffer in bytes */
int nAlloc; /* Byte allocated at a[] */
};
typedef struct ZipfileCtx ZipfileCtx;
struct ZipfileCtx {
int nEntry;
ZipfileBuffer body;
ZipfileBuffer cds;
};
static int zipfileBufferGrow(ZipfileBuffer *pBuf, i64 nByte){
if ( pBuf->n+nByte>pBuf->nAlloc ){
u8 *aNew;
sqlite3_int64 nNew = pBuf->n ? pBuf->n*2 : 512 ;
int nReq = pBuf->n + nByte;
while ( nNew<nReq ) nNew = nNew*2 ;
aNew = sqlite3_realloc64(pBuf->a, nNew);
if ( aNew==0 ) return SQLITE_NOMEM;
pBuf->a = aNew;
pBuf->nAlloc = (int )nNew;
}
return SQLITE_OK;
}
/*
* * xStep ( ) callback for the zipfile ( ) aggregate . This can be called in
* * any of the following ways :
* *
* * SELECT zipfile ( name , data ) . . .
* * SELECT zipfile ( name , mode , mtime , data ) . . .
* * SELECT zipfile ( name , mode , mtime , data , method ) . . .
*/
static void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
ZipfileCtx *p; /* Aggregate function context */
ZipfileEntry e; /* New entry to add to zip archive */
sqlite3_value *pName = 0 ;
sqlite3_value *pMode = 0 ;
sqlite3_value *pMtime = 0 ;
sqlite3_value *pData = 0 ;
sqlite3_value *pMethod = 0 ;
int bIsDir = 0 ;
u32 mode;
int rc = SQLITE_OK;
char *zErr = 0 ;
int iMethod = -1 ; /* Compression method to use (0 or 8) */
const u8 *aData = 0 ; /* Possibly compressed data for new entry */
int nData = 0 ; /* Size of aData[] in bytes */
int szUncompressed = 0 ; /* Size of data before compression */
u8 *aFree = 0 ; /* Free this before returning */
u32 iCrc32 = 0 ; /* crc32 of uncompressed data */
char *zName = 0 ; /* Path (name) of new entry */
int nName = 0 ; /* Size of zName in bytes */
char *zFree = 0 ; /* Free this before returning */
i64 nByte;
memset(&e, 0 , sizeof (e));
p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof (ZipfileCtx));
if ( p==0 ) return ;
/* Martial the arguments into stack variables */
if ( nVal!=2 && nVal!=4 && nVal!=5 ){
zErr = sqlite3_mprintf("wrong number of arguments to function zipfile()" );
rc = SQLITE_ERROR;
goto zipfile_step_out;
}
pName = apVal[0 ];
if ( nVal==2 ){
pData = apVal[1 ];
}else {
pMode = apVal[1 ];
pMtime = apVal[2 ];
pData = apVal[3 ];
if ( nVal==5 ){
pMethod = apVal[4 ];
}
}
/* Check that the 'name' parameter looks ok. */
zName = (char *)sqlite3_value_text(pName);
nName = sqlite3_value_bytes(pName);
if ( zName==0 ){
zErr = sqlite3_mprintf("first argument to zipfile() must be non-NULL" );
rc = SQLITE_ERROR;
goto zipfile_step_out;
}
if ( nName>ZIPFILE_MX_NAME ){
zErr = sqlite3_mprintf(
"filename argument to zipfile() too big; max: %d bytes" ,
ZIPFILE_MX_NAME);
rc = SQLITE_ERROR;
goto zipfile_step_out;
}
/* Inspect the 'method' parameter. This must be either 0 (store), 8 (use
** deflate compression) or NULL (choose automatically). */
if ( pMethod && SQLITE_NULL!=sqlite3_value_type(pMethod) ){
iMethod = (int )sqlite3_value_int64(pMethod);
if ( iMethod!=0 && iMethod!=8 ){
zErr = sqlite3_mprintf("illegal method value: %d" , iMethod);
rc = SQLITE_ERROR;
goto zipfile_step_out;
}
}
/* Now inspect the data. If this is NULL, then the new entry must be a
* * directory . Otherwise , figure out whether or not the data should
** be deflated or simply stored in the zip archive. */
if ( sqlite3_value_type(pData)==SQLITE_NULL ){
bIsDir = 1 ;
iMethod = 0 ;
}else {
aData = sqlite3_value_blob(pData);
szUncompressed = nData = sqlite3_value_bytes(pData);
iCrc32 = crc32(0 , aData, nData);
if ( iMethod<0 || iMethod==8 ){
int nOut = 0 ;
rc = zipfileDeflate(aData, nData, &aFree, &nOut, &zErr);
if ( rc!=SQLITE_OK ){
goto zipfile_step_out;
}
if ( iMethod==8 || nOut<nData ){
aData = aFree;
nData = nOut;
iMethod = 8 ;
}else {
iMethod = 0 ;
}
}
}
/* Decode the "mode" argument. */
rc = zipfileGetMode(pMode, bIsDir, &mode, &zErr);
if ( rc ) goto zipfile_step_out;
/* Decode the "mtime" argument. */
e.mUnixTime = zipfileGetTime(pMtime);
/* If this is a directory entry, ensure that there is exactly one '/'
* * at the end of the path . Or , if this is not a directory and the path
** ends in '/' it is an error. */
if ( bIsDir==0 ){
if ( nName>0 && zName[nName-1 ]=='/' ){
zErr = sqlite3_mprintf("non-directory name must not end with /" );
rc = SQLITE_ERROR;
goto zipfile_step_out;
}
}else {
if ( nName==0 || zName[nName-1 ]!='/' ){
zName = zFree = sqlite3_mprintf("%s/" , zName);
if ( zName==0 ){
rc = SQLITE_NOMEM;
goto zipfile_step_out;
}
nName = (int )strlen(zName);
}else {
while ( nName>1 && zName[nName-2 ]=='/' ) nName--;
}
}
/* Assemble the ZipfileEntry object for the new zip archive entry */
e.cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY;
e.cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED;
e.cds.flags = ZIPFILE_NEWENTRY_FLAGS;
e.cds.iCompression = (u16)iMethod;
zipfileMtimeToDos(&e.cds, (u32)e.mUnixTime);
e.cds.crc32 = iCrc32;
e.cds.szCompressed = nData;
e.cds.szUncompressed = szUncompressed;
e.cds.iExternalAttr = (mode<<16 );
e.cds.iOffset = p->body.n;
e.cds.nFile = (u16)nName;
e.cds.zFile = zName;
/* Append the LFH to the body of the new archive */
nByte = ZIPFILE_LFH_FIXED_SZ + e.cds.nFile + 9 ;
if ( (rc = zipfileBufferGrow(&p->body, nByte)) ) goto zipfile_step_out;
p->body.n += zipfileSerializeLFH(&e, &p->body.a[p->body.n]);
/* Append the data to the body of the new archive */
if ( nData>0 ){
if ( (rc = zipfileBufferGrow(&p->body, nData)) ) goto zipfile_step_out;
memcpy(&p->body.a[p->body.n], aData, nData);
p->body.n += nData;
}
/* Append the CDS record to the directory of the new archive */
nByte = ZIPFILE_CDS_FIXED_SZ + e.cds.nFile + 9 ;
if ( (rc = zipfileBufferGrow(&p->cds, nByte)) ) goto zipfile_step_out;
p->cds.n += zipfileSerializeCDS(&e, &p->cds.a[p->cds.n]);
/* Increment the count of entries in the archive */
p->nEntry++;
zipfile_step_out:
sqlite3_free(aFree);
sqlite3_free(zFree);
if ( rc ){
if ( zErr ){
sqlite3_result_error(pCtx, zErr, -1 );
}else {
sqlite3_result_error_code(pCtx, rc);
}
}
sqlite3_free(zErr);
}
/*
* * xFinalize ( ) callback for zipfile aggregate function .
*/
static void zipfileFinal(sqlite3_context *pCtx){
ZipfileCtx *p;
ZipfileEOCD eocd;
sqlite3_int64 nZip;
u8 *aZip;
p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof (ZipfileCtx));
if ( p==0 ) return ;
if ( p->nEntry>0 ){
memset(&eocd, 0 , sizeof (eocd));
eocd.nEntry = (u16)p->nEntry;
eocd.nEntryTotal = (u16)p->nEntry;
eocd.nSize = p->cds.n;
eocd.iOffset = p->body.n;
nZip = p->body.n + p->cds.n + ZIPFILE_EOCD_FIXED_SZ;
aZip = (u8*)sqlite3_malloc64(nZip);
if ( aZip==0 ){
sqlite3_result_error_nomem(pCtx);
}else {
memcpy(aZip, p->body.a, p->body.n);
memcpy(&aZip[p->body.n], p->cds.a, p->cds.n);
zipfileSerializeEOCD(&eocd, &aZip[p->body.n + p->cds.n]);
sqlite3_result_blob(pCtx, aZip, (int )nZip, zipfileFree);
}
}
sqlite3_free(p->body.a);
sqlite3_free(p->cds.a);
}
/*
* * Register the " zipfile " virtual table .
*/
static int zipfileRegister(sqlite3 *db){
static sqlite3_module zipfileModule = {
1 , /* iVersion */
zipfileConnect, /* xCreate */
zipfileConnect, /* xConnect */
zipfileBestIndex, /* xBestIndex */
zipfileDisconnect, /* xDisconnect */
zipfileDisconnect, /* xDestroy */
zipfileOpen, /* xOpen - open a cursor */
zipfileClose, /* xClose - close a cursor */
zipfileFilter, /* xFilter - configure scan constraints */
zipfileNext, /* xNext - advance a cursor */
zipfileEof, /* xEof - check for end of scan */
zipfileColumn, /* xColumn - read data */
0 , /* xRowid - read data */
zipfileUpdate, /* xUpdate */
zipfileBegin, /* xBegin */
0 , /* xSync */
zipfileCommit, /* xCommit */
zipfileRollback, /* xRollback */
zipfileFindFunction, /* xFindMethod */
0 , /* xRename */
0 , /* xSavepoint */
0 , /* xRelease */
0 , /* xRollback */
0 , /* xShadowName */
0 /* xIntegrity */
};
int rc = sqlite3_create_module(db, "zipfile" , &zipfileModule, 0 );
if ( rc==SQLITE_OK ) rc = sqlite3_overload_function(db, "zipfile_cds" , -1 );
if ( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "zipfile" , -1 , SQLITE_UTF8, 0 , 0 ,
zipfileStep, zipfileFinal
);
}
assert( sizeof (i64)==8 );
assert( sizeof (u32)==4 );
assert( sizeof (u16)==2 );
assert( sizeof (u8)==1 );
return rc;
}
#else /* SQLITE_OMIT_VIRTUALTABLE */
# define zipfileRegister(x) SQLITE_OK
#endif
#ifdef _WIN32
#endif
int sqlite3_zipfile_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
SQLITE_EXTENSION_INIT2(pApi);
(void )pzErrMsg; /* Unused parameter */
return zipfileRegister(db);
}
/************************* End ext/misc/zipfile.c ********************/
/************************* Begin ext/misc/sqlar.c ******************/
/*
* * 2017 - 12 - 17
* *
* * 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 .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* * Utility functions sqlar_compress ( ) and sqlar_uncompress ( ) . Useful
* * for working with sqlar archives and used by the shell tool ' s built - in
* * sqlar support .
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <zlib.h>
#include <assert.h>
/*
* * Implementation of the " sqlar_compress ( X ) " SQL function .
* *
* * If the type of X is SQLITE_BLOB , and compressing that blob using
* * zlib utility function compress ( ) yields a smaller blob , return the
* * compressed blob . Otherwise , return a copy of X .
* *
* * SQLar uses the " zlib format " for compressed content . The zlib format
* * contains a two - byte identification header and a four - byte checksum at
* * the end . This is different from ZIP which uses the raw deflate format .
* *
* * Future enhancements to SQLar might add support for new compression formats .
* * If so , those new formats will be identified by alternative headers in the
* * compressed data .
*/
static void sqlarCompressFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
assert( argc==1 );
if ( sqlite3_value_type(argv[0 ])==SQLITE_BLOB ){
const Bytef *pData = sqlite3_value_blob(argv[0 ]);
uLong nData = sqlite3_value_bytes(argv[0 ]);
uLongf nOut = compressBound(nData);
Bytef *pOut;
pOut = (Bytef*)sqlite3_malloc64(nOut);
if ( pOut==0 ){
sqlite3_result_error_nomem(context);
return ;
}else {
if ( Z_OK!=compress(pOut, &nOut, pData, nData) ){
sqlite3_result_error(context, "error in compress()" , -1 );
}else if ( nOut<nData ){
sqlite3_result_blob(context, pOut, nOut, SQLITE_TRANSIENT);
}else {
sqlite3_result_value(context, argv[0 ]);
}
sqlite3_free(pOut);
}
}else {
sqlite3_result_value(context, argv[0 ]);
}
}
/*
* * Implementation of the " sqlar_uncompress ( X , SZ ) " SQL function
* *
* * Parameter SZ is interpreted as an integer . If it is less than or
* * equal to zero , then this function returns a copy of X . Or , if
* * SZ is equal to the size of X when interpreted as a blob , also
* * return a copy of X . Otherwise , decompress blob X using zlib
* * utility function uncompress ( ) and return the results ( another
* * blob ) .
*/
static void sqlarUncompressFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
uLong nData;
sqlite3_int64 sz;
assert( argc==2 );
sz = sqlite3_value_int64(argv[1 ]);
if ( sz<=0 || sz==(nData = sqlite3_value_bytes(argv[0 ])) ){
sqlite3_result_value(context, argv[0 ]);
}else {
uLongf szf = sz;
const Bytef *pData= sqlite3_value_blob(argv[0 ]);
Bytef *pOut = sqlite3_malloc64(sz);
if ( pOut==0 ){
sqlite3_result_error_nomem(context);
}else if ( Z_OK!=uncompress(pOut, &szf, pData, nData) ){
sqlite3_result_error(context, "error in uncompress()" , -1 );
}else {
sqlite3_result_blob(context, pOut, szf, SQLITE_TRANSIENT);
}
sqlite3_free(pOut);
}
}
#ifdef _WIN32
#endif
int sqlite3_sqlar_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void )pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "sqlar_compress" , 1 ,
SQLITE_UTF8|SQLITE_INNOCUOUS, 0 ,
sqlarCompressFunc, 0 , 0 );
if ( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sqlar_uncompress" , 2 ,
SQLITE_UTF8|SQLITE_INNOCUOUS, 0 ,
sqlarUncompressFunc, 0 , 0 );
}
return rc;
}
/************************* End ext/misc/sqlar.c ********************/
#endif
#if !defined (SQLITE_OMIT_VIRTUALTABLE) && !defined (SQLITE_OMIT_AUTHORIZATION)
/************************* Begin ext/expert/sqlite3expert.h ******************/
/*
* * 2017 April 07
* *
* * 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 .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
#if !defined (SQLITEEXPERT_H)
#define SQLITEEXPERT_H 1
/* #include "sqlite3.h" */
typedef struct sqlite3expert sqlite3expert;
/*
* * Create a new sqlite3expert object .
* *
* * If successful , a pointer to the new object is returned and ( * pzErr ) set
* * to NULL . Or , if an error occurs , NULL is returned and ( * pzErr ) set to
* * an English - language error message . In this case it is the responsibility
* * of the caller to eventually free the error message buffer using
* * sqlite3_free ( ) .
*/
sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErr);
/*
* * Configure an sqlite3expert object .
* *
* * EXPERT_CONFIG_SAMPLE :
* * By default , sqlite3_expert_analyze ( ) generates sqlite_stat1 data for
* * each candidate index . This involves scanning and sorting the entire
* * contents of each user database table once for each candidate index
* * associated with the table . For large databases , this can be
* * prohibitively slow . This option allows the sqlite3expert object to
* * be configured so that sqlite_stat1 data is instead generated based on a
* * subset of each table , or so that no sqlite_stat1 data is used at all .
* *
* * A single integer argument is passed to this option . If the value is less
* * than or equal to zero , then no sqlite_stat1 data is generated or used by
* * the analysis - indexes are recommended based on the database schema only .
* * Or , if the value is 100 or greater , complete sqlite_stat1 data is
* * generated for each candidate index ( this is the default ) . Finally , if the
* * value falls between 0 and 100 , then it represents the percentage of user
* * table rows that should be considered when generating sqlite_stat1 data .
* *
* * Examples :
* *
* * // Do not generate any sqlite_stat1 data
* * sqlite3_expert_config ( pExpert , EXPERT_CONFIG_SAMPLE , 0 ) ;
* *
* * // Generate sqlite_stat1 data based on 10% of the rows in each table.
* * sqlite3_expert_config ( pExpert , EXPERT_CONFIG_SAMPLE , 10 ) ;
*/
int sqlite3_expert_config(sqlite3expert *p, int op, ...);
#define EXPERT_CONFIG_SAMPLE 1 /* int */
/*
* * Specify zero or more SQL statements to be included in the analysis .
* *
* * Buffer zSql must contain zero or more complete SQL statements . This
* * function parses all statements contained in the buffer and adds them
* * to the internal list of statements to analyze . If successful , SQLITE_OK
* * is returned and ( * pzErr ) set to NULL . Or , if an error occurs - for example
* * due to a error in the SQL - an SQLite error code is returned and ( * pzErr )
* * may be set to point to an English language error message . In this case
* * the caller is responsible for eventually freeing the error message buffer
* * using sqlite3_free ( ) .
* *
* * If an error does occur while processing one of the statements in the
* * buffer passed as the second argument , none of the statements in the
* * buffer are added to the analysis .
* *
* * This function must be called before sqlite3_expert_analyze ( ) . If a call
* * to this function is made on an sqlite3expert object that has already
* * been passed to sqlite3_expert_analyze ( ) SQLITE_MISUSE is returned
* * immediately and no statements are added to the analysis .
*/
int sqlite3_expert_sql(
sqlite3expert *p, /* From a successful sqlite3_expert_new() */
const char *zSql, /* SQL statement(s) to add */
char **pzErr /* OUT: Error message (if any) */
);
/*
* * This function is called after the sqlite3expert object has been configured
* * with all SQL statements using sqlite3_expert_sql ( ) to actually perform
* * the analysis . Once this function has been called , it is not possible to
* * add further SQL statements to the analysis .
* *
* * If successful , SQLITE_OK is returned and ( * pzErr ) is set to NULL . Or , if
* * an error occurs , an SQLite error code is returned and ( * pzErr ) set to
* * point to a buffer containing an English language error message . In this
* * case it is the responsibility of the caller to eventually free the buffer
* * using sqlite3_free ( ) .
* *
* * If an error does occur within this function , the sqlite3expert object
* * is no longer useful for any purpose . At that point it is no longer
* * possible to add further SQL statements to the object or to re - attempt
* * the analysis . The sqlite3expert object must still be freed using a call
* * sqlite3_expert_destroy ( ) .
*/
int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr);
/*
* * Return the total number of statements loaded using sqlite3_expert_sql ( ) .
* * The total number of SQL statements may be different from the total number
* * to calls to sqlite3_expert_sql ( ) .
*/
int sqlite3_expert_count(sqlite3expert*);
/*
* * Return a component of the report .
* *
* * This function is called after sqlite3_expert_analyze ( ) to extract the
* * results of the analysis . Each call to this function returns either a
* * NULL pointer or a pointer to a buffer containing a nul - terminated string .
* * The value passed as the third argument must be one of the EXPERT_REPORT_ *
* * # define constants defined below .
* *
* * For some EXPERT_REPORT_ * parameters , the buffer returned contains
* * information relating to a specific SQL statement . In these cases that
* * SQL statement is identified by the value passed as the second argument .
* * SQL statements are numbered from 0 in the order in which they are parsed .
* * If an out - of - range value ( less than zero or equal to or greater than the
* * value returned by sqlite3_expert_count ( ) ) is passed as the second argument
* * along with such an EXPERT_REPORT_ * parameter , NULL is always returned .
* *
* * EXPERT_REPORT_SQL :
* * Return the text of SQL statement iStmt .
* *
* * EXPERT_REPORT_INDEXES :
* * Return a buffer containing the CREATE INDEX statements for all recommended
* * indexes for statement iStmt . If there are no new recommeded indexes , NULL
* * is returned .
* *
* * EXPERT_REPORT_PLAN :
* * Return a buffer containing the EXPLAIN QUERY PLAN output for SQL query
* * iStmt after the proposed indexes have been added to the database schema .
* *
* * EXPERT_REPORT_CANDIDATES :
* * Return a pointer to a buffer containing the CREATE INDEX statements
* * for all indexes that were tested ( for all SQL statements ) . The iStmt
* * parameter is ignored for EXPERT_REPORT_CANDIDATES calls .
*/
const char *sqlite3_expert_report(sqlite3expert*, int iStmt, int eReport);
/*
* * Values for the third argument passed to sqlite3_expert_report ( ) .
*/
#define EXPERT_REPORT_SQL 1
#define EXPERT_REPORT_INDEXES 2
#define EXPERT_REPORT_PLAN 3
#define EXPERT_REPORT_CANDIDATES 4
/*
* * Free an ( sqlite3expert * ) handle and all associated resources . There
* * should be one call to this function for each successful call to
* * sqlite3 - expert_new ( ) .
*/
void sqlite3_expert_destroy(sqlite3expert*);
#endif /* !defined(SQLITEEXPERT_H) */
/************************* End ext/expert/sqlite3expert.h ********************/
/************************* Begin ext/expert/sqlite3expert.c ******************/
/*
* * 2017 April 09
* *
* * The author disclaims copyright to this source code . In place of
* * a legal notice , here is a blessing :
* *
* * May you do good and not evil .
* * May you find forgiveness for yourself and forgive others .
* * May you share freely , never taking more than you give .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/* #include "sqlite3expert.h" */
#include <assert.h>
#include <string.h>
#include <stdio.h>
#if !defined (SQLITE_AMALGAMATION)
#if defined (SQLITE_COVERAGE_TEST) || defined (SQLITE_MUTATION_TEST)
# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
#endif
#if defined (SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
# define ALWAYS(X) (1 )
# define NEVER(X) (0 )
#elif !defined (NDEBUG)
# define ALWAYS(X) ((X)?1 :(assert(0 ),0 ))
# define NEVER(X) ((X)?(assert(0 ),1 ):0 )
#else
# define ALWAYS(X) (X)
# define NEVER(X) (X)
#endif
#endif /* !defined(SQLITE_AMALGAMATION) */
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* typedef sqlite3_int64 i64; */
/* typedef sqlite3_uint64 u64; */
typedef struct IdxColumn IdxColumn;
typedef struct IdxConstraint IdxConstraint;
typedef struct IdxScan IdxScan;
typedef struct IdxStatement IdxStatement;
typedef struct IdxTable IdxTable;
typedef struct IdxWrite IdxWrite;
#define STRLEN (int )strlen
/*
* * A temp table name that we assume no user database will actually use .
* * If this assumption proves incorrect triggers on the table with the
* * conflicting name will be ignored .
*/
#define UNIQUE_TABLE_NAME "t592690916721053953805701627921227776"
/*
* * A single constraint . Equivalent to either " col = ? " or " col < ? " ( or
* * any other type of single - ended range constraint on a column ) .
* *
* * pLink :
* * Used to temporarily link IdxConstraint objects into lists while
* * creating candidate indexes .
*/
struct IdxConstraint {
char *zColl; /* Collation sequence */
int bRange; /* True for range, false for eq */
int iCol; /* Constrained table column */
int bFlag; /* Used by idxFindCompatible() */
int bDesc; /* True if ORDER BY <expr> DESC */
IdxConstraint *pNext; /* Next constraint in pEq or pRange list */
IdxConstraint *pLink; /* See above */
};
/*
* * A single scan of a single table .
*/
struct IdxScan {
IdxTable *pTab; /* Associated table object */
int iDb; /* Database containing table zTable */
i64 covering; /* Mask of columns required for cov. index */
IdxConstraint *pOrder; /* ORDER BY columns */
IdxConstraint *pEq; /* List of == constraints */
IdxConstraint *pRange; /* List of < constraints */
IdxScan *pNextScan; /* Next IdxScan object for same analysis */
};
/*
* * Information regarding a single database table . Extracted from
* * " PRAGMA table_info " by function idxGetTableInfo ( ) .
*/
struct IdxColumn {
char *zName;
char *zColl;
int iPk;
};
struct IdxTable {
int nCol;
char *zName; /* Table name */
IdxColumn *aCol;
IdxTable *pNext; /* Next table in linked list of all tables */
};
/*
* * An object of the following type is created for each unique table / write - op
* * seen . The objects are stored in a singly - linked list beginning at
* * sqlite3expert . pWrite .
*/
struct IdxWrite {
IdxTable *pTab;
int eOp; /* SQLITE_UPDATE, DELETE or INSERT */
IdxWrite *pNext;
};
/*
* * Each statement being analyzed is represented by an instance of this
* * structure .
*/
struct IdxStatement {
int iId; /* Statement number */
char *zSql; /* SQL statement */
char *zIdx; /* Indexes */
char *zEQP; /* Plan */
IdxStatement *pNext;
};
/*
* * A hash table for storing strings . With space for a payload string
* * with each entry . Methods are :
* *
* * idxHashInit ( )
* * idxHashClear ( )
* * idxHashAdd ( )
* * idxHashSearch ( )
*/
#define IDX_HASH_SIZE 1023
typedef struct IdxHashEntry IdxHashEntry;
typedef struct IdxHash IdxHash;
struct IdxHashEntry {
char *zKey; /* nul-terminated key */
char *zVal; /* nul-terminated value string */
char *zVal2; /* nul-terminated value string 2 */
IdxHashEntry *pHashNext; /* Next entry in same hash bucket */
IdxHashEntry *pNext; /* Next entry in hash */
};
struct IdxHash {
IdxHashEntry *pFirst;
IdxHashEntry *aHash[IDX_HASH_SIZE];
};
/*
* * sqlite3expert object .
*/
struct sqlite3expert {
int iSample; /* Percentage of tables to sample for stat1 */
sqlite3 *db; /* User database */
sqlite3 *dbm; /* In-memory db for this analysis */
sqlite3 *dbv; /* Vtab schema for this analysis */
IdxTable *pTable; /* List of all IdxTable objects */
IdxScan *pScan; /* List of scan objects */
IdxWrite *pWrite; /* List of write objects */
IdxStatement *pStatement; /* List of IdxStatement objects */
int bRun; /* True once analysis has run */
char **pzErrmsg;
int rc; /* Error code from whereinfo hook */
IdxHash hIdx; /* Hash containing all candidate indexes */
char *zCandidates; /* For EXPERT_REPORT_CANDIDATES */
};
/*
* * Allocate and return nByte bytes of zeroed memory using sqlite3_malloc ( ) .
* * If the allocation fails , set * pRc to SQLITE_NOMEM and return NULL .
*/
static void *idxMalloc(int *pRc, i64 nByte){
void *pRet;
assert( *pRc==SQLITE_OK );
assert( nByte>0 );
pRet = sqlite3_malloc64(nByte);
if ( pRet ){
memset(pRet, 0 , nByte);
}else {
*pRc = SQLITE_NOMEM;
}
return pRet;
}
/*
* * Initialize an IdxHash hash table .
*/
static void idxHashInit(IdxHash *pHash){
memset(pHash, 0 , sizeof (IdxHash));
}
/*
* * Reset an IdxHash hash table .
*/
static void idxHashClear(IdxHash *pHash){
int i;
for (i=0 ; i<IDX_HASH_SIZE; i++){
IdxHashEntry *pEntry;
IdxHashEntry *pNext;
for (pEntry=pHash->aHash[i]; pEntry; pEntry=pNext){
pNext = pEntry->pHashNext;
sqlite3_free(pEntry->zVal2);
sqlite3_free(pEntry);
}
}
memset(pHash, 0 , sizeof (IdxHash));
}
/*
* * Return the index of the hash bucket that the string specified by the
* * arguments to this function belongs .
*/
static int idxHashString(const char *z, int n){
unsigned int ret = 0 ;
int i;
for (i=0 ; i<n; i++){
ret += (ret<<3 ) + (unsigned char )(z[i]);
}
return (int )(ret % IDX_HASH_SIZE);
}
/*
* * If zKey is already present in the hash table , return non - zero and do
* * nothing . Otherwise , add an entry with key zKey and payload string zVal to
* * the hash table passed as the second argument .
*/
static int idxHashAdd(
int *pRc,
IdxHash *pHash,
const char *zKey,
const char *zVal
){
int nKey = STRLEN(zKey);
int iHash = idxHashString(zKey, nKey);
int nVal = (zVal ? STRLEN(zVal) : 0 );
IdxHashEntry *pEntry;
assert( iHash>=0 );
for (pEntry=pHash->aHash[iHash]; pEntry; pEntry=pEntry->pHashNext){
if ( STRLEN(pEntry->zKey)==nKey && 0 ==memcmp(pEntry->zKey, zKey, nKey) ){
return 1 ;
}
}
pEntry = idxMalloc(pRc, sizeof (IdxHashEntry) + (i64)nKey+1 + (i64)nVal+1 );
if ( pEntry ){
pEntry->zKey = (char *)&pEntry[1 ];
memcpy(pEntry->zKey, zKey, nKey);
if ( zVal ){
pEntry->zVal = &pEntry->zKey[nKey+1 ];
memcpy(pEntry->zVal, zVal, nVal);
}
pEntry->pHashNext = pHash->aHash[iHash];
pHash->aHash[iHash] = pEntry;
pEntry->pNext = pHash->pFirst;
pHash->pFirst = pEntry;
}
return 0 ;
}
/*
* * If zKey / nKey is present in the hash table , return a pointer to the
* * hash - entry object .
*/
static IdxHashEntry *idxHashFind(IdxHash *pHash, const char *zKey, int nKey){
int iHash;
IdxHashEntry *pEntry;
if ( nKey<0 ) nKey = STRLEN(zKey);
iHash = idxHashString(zKey, nKey);
assert( iHash>=0 );
for (pEntry=pHash->aHash[iHash]; pEntry; pEntry=pEntry->pHashNext){
if ( STRLEN(pEntry->zKey)==nKey && 0 ==memcmp(pEntry->zKey, zKey, nKey) ){
return pEntry;
}
}
return 0 ;
}
/*
* * If the hash table contains an entry with a key equal to the string
* * passed as the final two arguments to this function , return a pointer
* * to the payload string . Otherwise , if zKey / nKey is not present in the
* * hash table , return NULL .
*/
static const char *idxHashSearch(IdxHash *pHash, const char *zKey, int nKey){
IdxHashEntry *pEntry = idxHashFind(pHash, zKey, nKey);
if ( pEntry ) return pEntry->zVal;
return 0 ;
}
/*
* * Allocate and return a new IdxConstraint object . Set the IdxConstraint . zColl
* * variable to point to a copy of nul - terminated string zColl .
*/
static IdxConstraint *idxNewConstraint(int *pRc, const char *zColl){
IdxConstraint *pNew;
int nColl = STRLEN(zColl);
assert( *pRc==SQLITE_OK );
pNew = (IdxConstraint*)idxMalloc(pRc, sizeof (IdxConstraint) * nColl + 1 );
if ( pNew ){
pNew->zColl = (char *)&pNew[1 ];
memcpy(pNew->zColl, zColl, nColl+1 );
}
return pNew;
}
/*
* * An error associated with database handle db has just occurred . Pass
* * the error message to callback function xOut .
*/
static void idxDatabaseError(
sqlite3 *db, /* Database handle */
char **pzErrmsg /* Write error here */
){
*pzErrmsg = sqlite3_mprintf("%s" , sqlite3_errmsg(db));
}
/*
* * Prepare an SQL statement .
*/
static int idxPrepareStmt(
sqlite3 *db, /* Database handle to compile against */
sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */
char **pzErrmsg, /* OUT: sqlite3_malloc()ed error message */
const char *zSql /* SQL statement to compile */
){
int rc = sqlite3_prepare_v2(db, zSql, -1 , ppStmt, 0 );
if ( rc!=SQLITE_OK ){
*ppStmt = 0 ;
idxDatabaseError(db, pzErrmsg);
}
return rc;
}
/*
* * Prepare an SQL statement using the results of a printf ( ) formatting .
*/
static int idxPrintfPrepareStmt(
sqlite3 *db, /* Database handle to compile against */
sqlite3_stmt **ppStmt, /* OUT: Compiled SQL statement */
char **pzErrmsg, /* OUT: sqlite3_malloc()ed error message */
const char *zFmt, /* printf() format of SQL statement */
... /* Trailing printf() arguments */
){
va_list ap;
int rc;
char *zSql;
va_start(ap, zFmt);
zSql = sqlite3_vmprintf(zFmt, ap);
if ( zSql==0 ){
rc = SQLITE_NOMEM;
}else {
rc = idxPrepareStmt(db, ppStmt, pzErrmsg, zSql);
sqlite3_free(zSql);
}
va_end(ap);
return rc;
}
/*************************************************************************
* * Beginning of virtual table implementation .
*/
typedef struct ExpertVtab ExpertVtab;
struct ExpertVtab {
sqlite3_vtab base;
IdxTable *pTab;
sqlite3expert *pExpert;
};
typedef struct ExpertCsr ExpertCsr;
struct ExpertCsr {
sqlite3_vtab_cursor base;
sqlite3_stmt *pData;
};
static char *expertDequote(const char *zIn){
i64 n = STRLEN(zIn);
char *zRet = sqlite3_malloc64(n);
assert( zIn[0 ]=='\' ' );
assert( zIn[n-1 ]=='\' ' );
if ( zRet ){
i64 iOut = 0 ;
i64 iIn = 0 ;
for (iIn=1 ; iIn<(n-1 ); iIn++){
if ( zIn[iIn]=='\' ' ){
assert( zIn[iIn+1 ]=='\' ' );
iIn++;
}
zRet[iOut++] = zIn[iIn];
}
zRet[iOut] = '\0' ;
}
return zRet;
}
/*
* * This function is the implementation of both the xConnect and xCreate
* * methods of the r - tree virtual table .
* *
* * argv [ 0 ] - > module name
* * argv [ 1 ] - > database name
* * argv [ 2 ] - > table name
* * argv [ . . . ] - > column names . . .
*/
static int expertConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const *argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
sqlite3expert *pExpert = (sqlite3expert*)pAux;
ExpertVtab *p = 0 ;
int rc;
if ( argc!=4 ){
*pzErr = sqlite3_mprintf("internal error!" );
rc = SQLITE_ERROR;
}else {
char *zCreateTable = expertDequote(argv[3 ]);
if ( zCreateTable ){
rc = sqlite3_declare_vtab(db, zCreateTable);
if ( rc==SQLITE_OK ){
p = idxMalloc(&rc, sizeof (ExpertVtab));
}
if ( rc==SQLITE_OK ){
p->pExpert = pExpert;
p->pTab = pExpert->pTable;
assert( sqlite3_stricmp(p->pTab->zName, argv[2 ])==0 );
}
sqlite3_free(zCreateTable);
}else {
rc = SQLITE_NOMEM;
}
}
*ppVtab = (sqlite3_vtab*)p;
return rc;
}
static int expertDisconnect(sqlite3_vtab *pVtab){
ExpertVtab *p = (ExpertVtab*)pVtab;
sqlite3_free(p);
return SQLITE_OK;
}
static int expertBestIndex(sqlite3_vtab *pVtab, sqlite3_index_info *pIdxInfo){
ExpertVtab *p = (ExpertVtab*)pVtab;
int rc = SQLITE_OK;
int n = 0 ;
IdxScan *pScan;
const int opmask =
SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_GT |
SQLITE_INDEX_CONSTRAINT_LT | SQLITE_INDEX_CONSTRAINT_GE |
SQLITE_INDEX_CONSTRAINT_LE;
pScan = idxMalloc(&rc, sizeof (IdxScan));
if ( pScan ){
int i;
/* Link the new scan object into the list */
pScan->pTab = p->pTab;
pScan->pNextScan = p->pExpert->pScan;
p->pExpert->pScan = pScan;
/* Add the constraints to the IdxScan object */
for (i=0 ; i<pIdxInfo->nConstraint; i++){
struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i];
if ( pCons->usable
&& pCons->iColumn>=0
&& p->pTab->aCol[pCons->iColumn].iPk==0
&& (pCons->op & opmask)
){
IdxConstraint *pNew;
const char *zColl = sqlite3_vtab_collation(pIdxInfo, i);
pNew = idxNewConstraint(&rc, zColl);
if ( pNew ){
pNew->iCol = pCons->iColumn;
if ( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
pNew->pNext = pScan->pEq;
pScan->pEq = pNew;
}else {
pNew->bRange = 1 ;
pNew->pNext = pScan->pRange;
pScan->pRange = pNew;
}
}
n++;
pIdxInfo->aConstraintUsage[i].argvIndex = n;
}
}
/* Add the ORDER BY to the IdxScan object */
for (i=pIdxInfo->nOrderBy-1 ; i>=0 ; i--){
int iCol = pIdxInfo->aOrderBy[i].iColumn;
if ( iCol>=0 ){
IdxConstraint *pNew = idxNewConstraint(&rc, p->pTab->aCol[iCol].zColl);
if ( pNew ){
pNew->iCol = iCol;
pNew->bDesc = pIdxInfo->aOrderBy[i].desc;
pNew->pNext = pScan->pOrder;
pNew->pLink = pScan->pOrder;
pScan->pOrder = pNew;
n++;
}
}
}
}
pIdxInfo->estimatedCost = 1000000 .0 / (n+1 );
return rc;
}
static int expertUpdate(
sqlite3_vtab *pVtab,
int nData,
sqlite3_value **azData,
sqlite_int64 *pRowid
){
(void )pVtab;
(void )nData;
(void )azData;
(void )pRowid;
return SQLITE_OK;
}
/*
* * Virtual table module xOpen method .
*/
static int expertOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
int rc = SQLITE_OK;
ExpertCsr *pCsr;
(void )pVTab;
pCsr = idxMalloc(&rc, sizeof (ExpertCsr));
*ppCursor = (sqlite3_vtab_cursor*)pCsr;
return rc;
}
/*
* * Virtual table module xClose method .
*/
static int expertClose(sqlite3_vtab_cursor *cur){
ExpertCsr *pCsr = (ExpertCsr*)cur;
sqlite3_finalize(pCsr->pData);
sqlite3_free(pCsr);
return SQLITE_OK;
}
/*
* * Virtual table module xEof method .
* *
* * Return non - zero if the cursor does not currently point to a valid
* * record ( i . e if the scan has finished ) , or zero otherwise .
*/
static int expertEof(sqlite3_vtab_cursor *cur){
ExpertCsr *pCsr = (ExpertCsr*)cur;
return pCsr->pData==0 ;
}
/*
* * Virtual table module xNext method .
*/
static int expertNext(sqlite3_vtab_cursor *cur){
ExpertCsr *pCsr = (ExpertCsr*)cur;
int rc = SQLITE_OK;
assert( pCsr->pData );
rc = sqlite3_step(pCsr->pData);
if ( rc!=SQLITE_ROW ){
rc = sqlite3_finalize(pCsr->pData);
pCsr->pData = 0 ;
}else {
rc = SQLITE_OK;
}
return rc;
}
/*
* * Virtual table module xRowid method .
*/
static int expertRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
(void )cur;
*pRowid = 0 ;
return SQLITE_OK;
}
/*
* * Virtual table module xColumn method .
*/
static int expertColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
ExpertCsr *pCsr = (ExpertCsr*)cur;
sqlite3_value *pVal;
pVal = sqlite3_column_value(pCsr->pData, i);
if ( pVal ){
sqlite3_result_value(ctx, pVal);
}
return SQLITE_OK;
}
/*
* * Virtual table module xFilter method .
*/
static int expertFilter(
sqlite3_vtab_cursor *cur,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
ExpertCsr *pCsr = (ExpertCsr*)cur;
ExpertVtab *pVtab = (ExpertVtab*)(cur->pVtab);
sqlite3expert *pExpert = pVtab->pExpert;
int rc;
(void )idxNum;
(void )idxStr;
(void )argc;
(void )argv;
rc = sqlite3_finalize(pCsr->pData);
pCsr->pData = 0 ;
if ( rc==SQLITE_OK ){
rc = idxPrintfPrepareStmt(pExpert->db, &pCsr->pData, &pVtab->base.zErrMsg,
"SELECT * FROM main.%Q WHERE sqlite_expert_sample()" , pVtab->pTab->zName
);
}
if ( rc==SQLITE_OK ){
rc = expertNext(cur);
}
return rc;
}
static int idxRegisterVtab(sqlite3expert *p){
static sqlite3_module expertModule = {
2 , /* iVersion */
expertConnect, /* xCreate - create a table */
expertConnect, /* xConnect - connect to an existing table */
expertBestIndex, /* xBestIndex - Determine search strategy */
expertDisconnect, /* xDisconnect - Disconnect from a table */
expertDisconnect, /* xDestroy - Drop a table */
expertOpen, /* xOpen - open a cursor */
expertClose, /* xClose - close a cursor */
expertFilter, /* xFilter - configure scan constraints */
expertNext, /* xNext - advance a cursor */
expertEof, /* xEof */
expertColumn, /* xColumn - read data */
expertRowid, /* xRowid - read data */
expertUpdate, /* xUpdate - write data */
0 , /* xBegin - begin transaction */
0 , /* xSync - sync transaction */
0 , /* xCommit - commit transaction */
0 , /* xRollback - rollback transaction */
0 , /* xFindFunction - function overloading */
0 , /* xRename - rename the table */
0 , /* xSavepoint */
0 , /* xRelease */
0 , /* xRollbackTo */
0 , /* xShadowName */
0 , /* xIntegrity */
};
return sqlite3_create_module(p->dbv, "expert" , &expertModule, (void *)p);
}
/*
* * End of virtual table implementation .
*************************************************************************/
/*
* * Finalize SQL statement pStmt . If ( * pRc ) is SQLITE_OK when this function
* * is called , set it to the return value of sqlite3_finalize ( ) before
* * returning . Otherwise , discard the sqlite3_finalize ( ) return value .
*/
static void idxFinalize(int *pRc, sqlite3_stmt *pStmt){
int rc = sqlite3_finalize(pStmt);
if ( *pRc==SQLITE_OK ) *pRc = rc;
}
/*
* * Attempt to allocate an IdxTable structure corresponding to table zTab
* * in the main database of connection db . If successful , set ( * ppOut ) to
* * point to the new object and return SQLITE_OK . Otherwise , return an
* * SQLite error code and set ( * ppOut ) to NULL . In this case * pzErrmsg may be
* * set to point to an error string .
* *
* * It is the responsibility of the caller to eventually free either the
* * IdxTable object or error message using sqlite3_free ( ) .
*/
static int idxGetTableInfo(
sqlite3 *db, /* Database connection to read details from */
const char *zTab, /* Table name */
IdxTable **ppOut, /* OUT: New object (if successful) */
char **pzErrmsg /* OUT: Error message (if not) */
){
sqlite3_stmt *p1 = 0 ;
int nCol = 0 ;
int nTab;
i64 nByte;
IdxTable *pNew = 0 ;
int rc, rc2;
char *pCsr = 0 ;
int nPk = 0 ;
*ppOut = 0 ;
if ( zTab==0 ) return SQLITE_ERROR;
nTab = STRLEN(zTab);
nByte = sizeof (IdxTable) + nTab + 1 ;
rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_xinfo=%Q" , zTab);
while ( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
const char *zCol = (const char *)sqlite3_column_text(p1, 1 );
const char *zColSeq = 0 ;
if ( zCol==0 ){
rc = SQLITE_ERROR;
break ;
}
nByte += 1 + STRLEN(zCol);
rc = sqlite3_table_column_metadata(
db, "main" , zTab, zCol, 0 , &zColSeq, 0 , 0 , 0
);
if ( zColSeq==0 ) zColSeq = "binary" ;
nByte += 1 + STRLEN(zColSeq);
nCol++;
nPk += (sqlite3_column_int(p1, 5 )>0 );
}
rc2 = sqlite3_reset(p1);
if ( rc==SQLITE_OK ) rc = rc2;
nByte += sizeof (IdxColumn) * nCol;
if ( rc==SQLITE_OK ){
pNew = idxMalloc(&rc, nByte);
}
if ( rc==SQLITE_OK ){
pNew->aCol = (IdxColumn*)&pNew[1 ];
pNew->nCol = nCol;
pCsr = (char *)&pNew->aCol[nCol];
}
nCol = 0 ;
while ( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
const char *zCol = (const char *)sqlite3_column_text(p1, 1 );
const char *zColSeq = 0 ;
int nCopy;
if ( zCol==0 ) continue ;
nCopy = STRLEN(zCol) + 1 ;
pNew->aCol[nCol].zName = pCsr;
pNew->aCol[nCol].iPk = (sqlite3_column_int(p1, 5 )==1 && nPk==1 );
memcpy(pCsr, zCol, nCopy);
pCsr += nCopy;
rc = sqlite3_table_column_metadata(
db, "main" , zTab, zCol, 0 , &zColSeq, 0 , 0 , 0
);
if ( rc==SQLITE_OK ){
if ( zColSeq==0 ) zColSeq = "binary" ;
nCopy = STRLEN(zColSeq) + 1 ;
pNew->aCol[nCol].zColl = pCsr;
memcpy(pCsr, zColSeq, nCopy);
pCsr += nCopy;
}
nCol++;
}
idxFinalize(&rc, p1);
if ( rc!=SQLITE_OK ){
sqlite3_free(pNew);
pNew = 0 ;
}else if ( ALWAYS(pNew!=0 ) ){
pNew->zName = pCsr;
if ( ALWAYS(pNew->zName!=0 ) ) memcpy(pNew->zName, zTab, nTab+1 );
}
*ppOut = pNew;
return rc;
}
/*
* * This function is a no - op if * pRc is set to anything other than
* * SQLITE_OK when it is called .
* *
* * If * pRc is initially set to SQLITE_OK , then the text specified by
* * the printf ( ) style arguments is appended to zIn and the result returned
* * in a buffer allocated by sqlite3_malloc ( ) . sqlite3_free ( ) is called on
* * zIn before returning .
*/
static char *idxAppendText(int *pRc, char *zIn, const char *zFmt, ...){
va_list ap;
char *zAppend = 0 ;
char *zRet = 0 ;
i64 nIn = zIn ? STRLEN(zIn) : 0 ;
i64 nAppend = 0 ;
va_start(ap, zFmt);
if ( *pRc==SQLITE_OK ){
zAppend = sqlite3_vmprintf(zFmt, ap);
if ( zAppend ){
nAppend = STRLEN(zAppend);
zRet = (char *)sqlite3_malloc64(nIn + nAppend + 1 );
}
if ( zAppend && zRet ){
if ( nIn ) memcpy(zRet, zIn, nIn);
memcpy(&zRet[nIn], zAppend, nAppend+1 );
}else {
sqlite3_free(zRet);
zRet = 0 ;
*pRc = SQLITE_NOMEM;
}
sqlite3_free(zAppend);
sqlite3_free(zIn);
}
va_end(ap);
return zRet;
}
/*
* * Return true if zId must be quoted in order to use it as an SQL
* * identifier , or false otherwise .
*/
static int idxIdentifierRequiresQuotes(const char *zId){
int i;
int nId = STRLEN(zId);
if ( sqlite3_keyword_check(zId, nId) ) return 1 ;
for (i=0 ; zId[i]; i++){
if ( !(zId[i]=='_' )
&& !(zId[i]>='0' && zId[i]<='9' )
&& !(zId[i]>='a' && zId[i]<='z' )
&& !(zId[i]>='A' && zId[i]<='Z' )
){
return 1 ;
}
}
return 0 ;
}
/*
* * This function appends an index column definition suitable for constraint
* * pCons to the string passed as zIn and returns the result .
*/
static char *idxAppendColDefn(
int *pRc, /* IN/OUT: Error code */
char *zIn, /* Column defn accumulated so far */
IdxTable *pTab, /* Table index will be created on */
IdxConstraint *pCons
){
char *zRet = zIn;
IdxColumn *p = &pTab->aCol[pCons->iCol];
if ( zRet ) zRet = idxAppendText(pRc, zRet, ", " );
if ( idxIdentifierRequiresQuotes(p->zName) ){
zRet = idxAppendText(pRc, zRet, "%Q" , p->zName);
}else {
zRet = idxAppendText(pRc, zRet, "%s" , p->zName);
}
if ( sqlite3_stricmp(p->zColl, pCons->zColl) ){
if ( idxIdentifierRequiresQuotes(pCons->zColl) ){
zRet = idxAppendText(pRc, zRet, " COLLATE %Q" , pCons->zColl);
}else {
zRet = idxAppendText(pRc, zRet, " COLLATE %s" , pCons->zColl);
}
}
if ( pCons->bDesc ){
zRet = idxAppendText(pRc, zRet, " DESC" );
}
return zRet;
}
/*
* * Search database dbm for an index compatible with the one idxCreateFromCons ( )
* * would create from arguments pScan , pEq and pTail . If no error occurs and
* * such an index is found , return non - zero . Or , if no such index is found ,
* * return zero .
* *
* * If an error occurs , set * pRc to an SQLite error code and return zero .
*/
static int idxFindCompatible(
int *pRc, /* OUT: Error code */
sqlite3* dbm, /* Database to search */
IdxScan *pScan, /* Scan for table to search for index on */
IdxConstraint *pEq, /* List of == constraints */
IdxConstraint *pTail /* List of range constraints */
){
const char *zTbl = pScan->pTab->zName;
sqlite3_stmt *pIdxList = 0 ;
IdxConstraint *pIter;
int nEq = 0 ; /* Number of elements in pEq */
int rc;
/* Count the elements in list pEq */
for (pIter=pEq; pIter; pIter=pIter->pLink) nEq++;
rc = idxPrintfPrepareStmt(dbm, &pIdxList, 0 , "PRAGMA index_list=%Q" , zTbl);
while ( rc==SQLITE_OK && sqlite3_step(pIdxList)==SQLITE_ROW ){
int bMatch = 1 ;
IdxConstraint *pT = pTail;
sqlite3_stmt *pInfo = 0 ;
const char *zIdx = (const char *)sqlite3_column_text(pIdxList, 1 );
if ( zIdx==0 ) continue ;
/* Zero the IdxConstraint.bFlag values in the pEq list */
for (pIter=pEq; pIter; pIter=pIter->pLink) pIter->bFlag = 0 ;
rc = idxPrintfPrepareStmt(dbm, &pInfo, 0 , "PRAGMA index_xInfo=%Q" , zIdx);
while ( rc==SQLITE_OK && sqlite3_step(pInfo)==SQLITE_ROW ){
int iIdx = sqlite3_column_int(pInfo, 0 );
int iCol = sqlite3_column_int(pInfo, 1 );
const char *zColl = (const char *)sqlite3_column_text(pInfo, 4 );
if ( iIdx<nEq ){
for (pIter=pEq; pIter; pIter=pIter->pLink){
if ( pIter->bFlag ) continue ;
if ( pIter->iCol!=iCol ) continue ;
if ( sqlite3_stricmp(pIter->zColl, zColl) ) continue ;
pIter->bFlag = 1 ;
break ;
}
if ( pIter==0 ){
bMatch = 0 ;
break ;
}
}else {
if ( pT ){
if ( pT->iCol!=iCol || sqlite3_stricmp(pT->zColl, zColl) ){
bMatch = 0 ;
break ;
}
pT = pT->pLink;
}
}
}
idxFinalize(&rc, pInfo);
if ( rc==SQLITE_OK && bMatch ){
sqlite3_finalize(pIdxList);
return 1 ;
}
}
idxFinalize(&rc, pIdxList);
*pRc = rc;
return 0 ;
}
/* Callback for sqlite3_exec() with query with leading count(*) column.
* The first argument is expected to be an int * , referent to be incremented
* if that leading column is not exactly ' 0 ' .
*/
static int countNonzeros(void * pCount, int nc,
char * azResults[], char * azColumns[]){
(void )azColumns; /* Suppress unused parameter warning */
if ( nc>0 && (azResults[0 ][0 ]!='0' || azResults[0 ][1 ]!=0 ) ){
*((int *)pCount) += 1 ;
}
return 0 ;
}
static int idxCreateFromCons(
sqlite3expert *p,
IdxScan *pScan,
IdxConstraint *pEq,
IdxConstraint *pTail
){
sqlite3 *dbm = p->dbm;
int rc = SQLITE_OK;
if ( (pEq || pTail) && 0 ==idxFindCompatible(&rc, dbm, pScan, pEq, pTail) ){
IdxTable *pTab = pScan->pTab;
char *zCols = 0 ;
char *zIdx = 0 ;
IdxConstraint *pCons;
unsigned int h = 0 ;
const char *zFmt;
for (pCons=pEq; pCons; pCons=pCons->pLink){
zCols = idxAppendColDefn(&rc, zCols, pTab, pCons);
}
for (pCons=pTail; pCons; pCons=pCons->pLink){
zCols = idxAppendColDefn(&rc, zCols, pTab, pCons);
}
if ( rc==SQLITE_OK ){
/* Hash the list of columns to come up with a name for the index */
const char *zTable = pScan->pTab->zName;
int quoteTable = idxIdentifierRequiresQuotes(zTable);
char *zName = 0 ; /* Index name */
int collisions = 0 ;
do {
int i;
char *zFind;
for (i=0 ; zCols[i]; i++){
h += ((h<<3 ) + zCols[i]);
}
sqlite3_free(zName);
zName = sqlite3_mprintf("%s_idx_%08x" , zTable, h);
if ( zName==0 ) break ;
/* Is is unique among table, view and index names? */
zFmt = "SELECT count(*) FROM sqlite_schema WHERE name=%Q"
" AND type in ('index','table','view')" ;
zFind = sqlite3_mprintf(zFmt, zName);
i = 0 ;
rc = sqlite3_exec(dbm, zFind, countNonzeros, &i, 0 );
assert(rc==SQLITE_OK);
sqlite3_free(zFind);
if ( i==0 ){
collisions = 0 ;
break ;
}
++collisions;
}while ( collisions<50 && zName!=0 );
if ( collisions ){
/* This return means "Gave up trying to find a unique index name." */
rc = SQLITE_BUSY_TIMEOUT;
}else if ( zName==0 ){
rc = SQLITE_NOMEM;
}else {
if ( quoteTable ){
zFmt = "CREATE INDEX \" %w\" ON \" %w\"(%s)" ;
}else {
zFmt = "CREATE INDEX %s ON %s(%s)" ;
}
zIdx = sqlite3_mprintf(zFmt, zName, zTable, zCols);
if ( !zIdx ){
rc = SQLITE_NOMEM;
}else {
rc = sqlite3_exec(dbm, zIdx, 0 , 0 , p->pzErrmsg);
if ( rc!=SQLITE_OK ){
rc = SQLITE_BUSY_TIMEOUT;
}else {
idxHashAdd(&rc, &p->hIdx, zName, zIdx);
}
}
sqlite3_free(zName);
sqlite3_free(zIdx);
}
}
sqlite3_free(zCols);
}
return rc;
}
/*
* * Return true if list pList ( linked by IdxConstraint . pLink ) contains
* * a constraint compatible with * p . Otherwise return false .
*/
static int idxFindConstraint(IdxConstraint *pList, IdxConstraint *p){
IdxConstraint *pCmp;
for (pCmp=pList; pCmp; pCmp=pCmp->pLink){
if ( p->iCol==pCmp->iCol ) return 1 ;
}
return 0 ;
}
static int idxCreateFromWhere(
sqlite3expert *p,
IdxScan *pScan, /* Create indexes for this scan */
IdxConstraint *pTail /* range/ORDER BY constraints for inclusion */
){
IdxConstraint *p1 = 0 ;
IdxConstraint *pCon;
int rc;
/* Gather up all the == constraints. */
for (pCon=pScan->pEq; pCon; pCon=pCon->pNext){
if ( !idxFindConstraint(p1, pCon) && !idxFindConstraint(pTail, pCon) ){
pCon->pLink = p1;
p1 = pCon;
}
}
/* Create an index using the == constraints collected above. And the
** range constraint/ORDER BY terms passed in by the caller, if any. */
rc = idxCreateFromCons(p, pScan, p1, pTail);
/* If no range/ORDER BY passed by the caller, create a version of the
** index for each range constraint. */
if ( pTail==0 ){
for (pCon=pScan->pRange; rc==SQLITE_OK && pCon; pCon=pCon->pNext){
assert( pCon->pLink==0 );
if ( !idxFindConstraint(p1, pCon) && !idxFindConstraint(pTail, pCon) ){
rc = idxCreateFromCons(p, pScan, p1, pCon);
}
}
}
return rc;
}
/*
* * Create candidate indexes in database [ dbm ] based on the data in
* * linked - list pScan .
*/
static int idxCreateCandidates(sqlite3expert *p){
int rc = SQLITE_OK;
IdxScan *pIter;
for (pIter=p->pScan; pIter && rc==SQLITE_OK; pIter=pIter->pNextScan){
rc = idxCreateFromWhere(p, pIter, 0 );
if ( rc==SQLITE_OK && pIter->pOrder ){
rc = idxCreateFromWhere(p, pIter, pIter->pOrder);
}
}
return rc;
}
/*
* * Free all elements of the linked list starting at pConstraint .
*/
static void idxConstraintFree(IdxConstraint *pConstraint){
IdxConstraint *pNext;
IdxConstraint *p;
for (p=pConstraint; p; p=pNext){
pNext = p->pNext;
sqlite3_free(p);
}
}
/*
* * Free all elements of the linked list starting from pScan up until pLast
* * ( pLast is not freed ) .
*/
static void idxScanFree(IdxScan *pScan, IdxScan *pLast){
IdxScan *p;
IdxScan *pNext;
for (p=pScan; p!=pLast; p=pNext){
pNext = p->pNextScan;
idxConstraintFree(p->pOrder);
idxConstraintFree(p->pEq);
idxConstraintFree(p->pRange);
sqlite3_free(p);
}
}
/*
* * Free all elements of the linked list starting from pStatement up
* * until pLast ( pLast is not freed ) .
*/
static void idxStatementFree(IdxStatement *pStatement, IdxStatement *pLast){
IdxStatement *p;
IdxStatement *pNext;
for (p=pStatement; p!=pLast; p=pNext){
pNext = p->pNext;
sqlite3_free(p->zEQP);
sqlite3_free(p->zIdx);
sqlite3_free(p);
}
}
/*
* * Free the linked list of IdxTable objects starting at pTab .
*/
static void idxTableFree(IdxTable *pTab){
IdxTable *pIter;
IdxTable *pNext;
for (pIter=pTab; pIter; pIter=pNext){
pNext = pIter->pNext;
sqlite3_free(pIter);
}
}
/*
* * Free the linked list of IdxWrite objects starting at pTab .
*/
static void idxWriteFree(IdxWrite *pTab){
IdxWrite *pIter;
IdxWrite *pNext;
for (pIter=pTab; pIter; pIter=pNext){
pNext = pIter->pNext;
sqlite3_free(pIter);
}
}
/*
* * This function is called after candidate indexes have been created . It
* * runs all the queries to see which indexes they prefer , and populates
* * IdxStatement . zIdx and IdxStatement . zEQP with the results .
*/
static int idxFindIndexes(
sqlite3expert *p,
char **pzErr /* OUT: Error message (sqlite3_malloc) */
){
IdxStatement *pStmt;
sqlite3 *dbm = p->dbm;
int rc = SQLITE_OK;
IdxHash hIdx;
idxHashInit(&hIdx);
for (pStmt=p->pStatement; rc==SQLITE_OK && pStmt; pStmt=pStmt->pNext){
IdxHashEntry *pEntry;
sqlite3_stmt *pExplain = 0 ;
idxHashClear(&hIdx);
rc = idxPrintfPrepareStmt(dbm, &pExplain, pzErr,
"EXPLAIN QUERY PLAN %s" , pStmt->zSql
);
while ( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){
/* int iId = sqlite3_column_int(pExplain, 0); */
/* int iParent = sqlite3_column_int(pExplain, 1); */
/* int iNotUsed = sqlite3_column_int(pExplain, 2); */
const char *zDetail = (const char *)sqlite3_column_text(pExplain, 3 );
int nDetail;
int i;
if ( !zDetail ) continue ;
nDetail = STRLEN(zDetail);
for (i=0 ; i<nDetail; i++){
const char *zIdx = 0 ;
if ( i+13 <nDetail && memcmp(&zDetail[i], " USING INDEX " , 13 )==0 ){
zIdx = &zDetail[i+13 ];
}else if ( i+22 <nDetail
&& memcmp(&zDetail[i], " USING COVERING INDEX " , 22 )==0
){
zIdx = &zDetail[i+22 ];
}
if ( zIdx ){
const char *zSql;
int nIdx = 0 ;
while ( zIdx[nIdx]!='\0' && (zIdx[nIdx]!=' ' || zIdx[nIdx+1 ]!='(' ) ){
nIdx++;
}
zSql = idxHashSearch(&p->hIdx, zIdx, nIdx);
if ( zSql ){
idxHashAdd(&rc, &hIdx, zSql, 0 );
if ( rc ) goto find_indexes_out;
}
break ;
}
}
if ( zDetail[0 ]!='-' ){
pStmt->zEQP = idxAppendText(&rc, pStmt->zEQP, "%s\n" , zDetail);
}
}
for (pEntry=hIdx.pFirst; pEntry; pEntry=pEntry->pNext){
pStmt->zIdx = idxAppendText(&rc, pStmt->zIdx, "%s;\n" , pEntry->zKey);
}
idxFinalize(&rc, pExplain);
}
find_indexes_out:
idxHashClear(&hIdx);
return rc;
}
static int idxAuthCallback(
void *pCtx,
int eOp,
const char *z3,
const char *z4,
const char *zDb,
const char *zTrigger
){
int rc = SQLITE_OK;
(void )z4;
(void )zTrigger;
if ( eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE || eOp==SQLITE_DELETE ){
if ( sqlite3_stricmp(zDb, "main" )==0 ){
sqlite3expert *p = (sqlite3expert*)pCtx;
IdxTable *pTab;
for (pTab=p->pTable; pTab; pTab=pTab->pNext){
if ( 0 ==sqlite3_stricmp(z3, pTab->zName) ) break ;
}
if ( pTab ){
IdxWrite *pWrite;
for (pWrite=p->pWrite; pWrite; pWrite=pWrite->pNext){
if ( pWrite->pTab==pTab && pWrite->eOp==eOp ) break ;
}
if ( pWrite==0 ){
pWrite = idxMalloc(&rc, sizeof (IdxWrite));
if ( rc==SQLITE_OK ){
pWrite->pTab = pTab;
pWrite->eOp = eOp;
pWrite->pNext = p->pWrite;
p->pWrite = pWrite;
}
}
}
}
}
return rc;
}
static int idxProcessOneTrigger(
sqlite3expert *p,
IdxWrite *pWrite,
char **pzErr
){
static const char *zInt = UNIQUE_TABLE_NAME;
static const char *zDrop = "DROP TABLE " UNIQUE_TABLE_NAME;
IdxTable *pTab = pWrite->pTab;
const char *zTab = pTab->zName;
const char *zSql =
"SELECT 'CREATE TEMP' || substr(sql, 7) FROM sqlite_schema "
"WHERE tbl_name = %Q AND type IN ('table', 'trigger') "
"ORDER BY type;" ;
sqlite3_stmt *pSelect = 0 ;
int rc = SQLITE_OK;
char *zWrite = 0 ;
/* Create the table and its triggers in the temp schema */
rc = idxPrintfPrepareStmt(p->db, &pSelect, pzErr, zSql, zTab, zTab);
while ( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSelect) ){
const char *zCreate = (const char *)sqlite3_column_text(pSelect, 0 );
if ( zCreate==0 ) continue ;
rc = sqlite3_exec(p->dbv, zCreate, 0 , 0 , pzErr);
}
idxFinalize(&rc, pSelect);
/* Rename the table in the temp schema to zInt */
if ( rc==SQLITE_OK ){
char *z = sqlite3_mprintf("ALTER TABLE temp.%Q RENAME TO %Q" , zTab, zInt);
if ( z==0 ){
rc = SQLITE_NOMEM;
}else {
rc = sqlite3_exec(p->dbv, z, 0 , 0 , pzErr);
sqlite3_free(z);
}
}
switch ( pWrite->eOp ){
case SQLITE_INSERT: {
int i;
zWrite = idxAppendText(&rc, zWrite, "INSERT INTO %Q VALUES(" , zInt);
for (i=0 ; i<pTab->nCol; i++){
zWrite = idxAppendText(&rc, zWrite, "%s?" , i==0 ? "" : ", " );
}
zWrite = idxAppendText(&rc, zWrite, ")" );
break ;
}
case SQLITE_UPDATE: {
int i;
zWrite = idxAppendText(&rc, zWrite, "UPDATE %Q SET " , zInt);
for (i=0 ; i<pTab->nCol; i++){
zWrite = idxAppendText(&rc, zWrite, "%s%Q=?" , i==0 ? "" : ", " ,
pTab->aCol[i].zName
);
}
break ;
}
default : {
assert( pWrite->eOp==SQLITE_DELETE );
if ( rc==SQLITE_OK ){
zWrite = sqlite3_mprintf("DELETE FROM %Q" , zInt);
if ( zWrite==0 ) rc = SQLITE_NOMEM;
}
}
}
if ( rc==SQLITE_OK ){
sqlite3_stmt *pX = 0 ;
rc = sqlite3_prepare_v2(p->dbv, zWrite, -1 , &pX, 0 );
idxFinalize(&rc, pX);
if ( rc!=SQLITE_OK ){
idxDatabaseError(p->dbv, pzErr);
}
}
sqlite3_free(zWrite);
if ( rc==SQLITE_OK ){
rc = sqlite3_exec(p->dbv, zDrop, 0 , 0 , pzErr);
}
return rc;
}
static int idxProcessTriggers(sqlite3expert *p, char **pzErr){
int rc = SQLITE_OK;
IdxWrite *pEnd = 0 ;
IdxWrite *pFirst = p->pWrite;
while ( rc==SQLITE_OK && pFirst!=pEnd ){
IdxWrite *pIter;
for (pIter=pFirst; rc==SQLITE_OK && pIter!=pEnd; pIter=pIter->pNext){
rc = idxProcessOneTrigger(p, pIter, pzErr);
}
pEnd = pFirst;
pFirst = p->pWrite;
}
return rc;
}
/*
* * This function tests if the schema of the main database of database handle
* * db contains an object named zTab . Assuming no error occurs , output parameter
* * ( * pbContains ) is set to true if zTab exists , or false if it does not .
* *
* * Or , if an error occurs , an SQLite error code is returned . The final value
* * of ( * pbContains ) is undefined in this case .
*/
static int expertDbContainsObject(
sqlite3 *db,
const char *zTab,
int *pbContains /* OUT: True if object exists */
){
const char *zSql = "SELECT 1 FROM sqlite_schema WHERE name = ?" ;
sqlite3_stmt *pSql = 0 ;
int rc = SQLITE_OK;
int ret = 0 ;
rc = sqlite3_prepare_v2(db, zSql, -1 , &pSql, 0 );
if ( rc==SQLITE_OK ){
sqlite3_bind_text(pSql, 1 , zTab, -1 , SQLITE_STATIC);
if ( SQLITE_ROW==sqlite3_step(pSql) ){
ret = 1 ;
}
rc = sqlite3_finalize(pSql);
}
*pbContains = ret;
return rc;
}
/*
* * Execute SQL command zSql using database handle db . If no error occurs ,
* * set ( * pzErr ) to NULL and return SQLITE_OK .
* *
* * If an error does occur , return an SQLite error code and set ( * pzErr ) to
* * point to a buffer containing an English language error message . Except ,
* * if the error message begins with " no such module : " , then ignore the
* * error and return as if the SQL statement had succeeded .
* *
* * This is used to copy as much of the database schema as possible while
* * ignoring any errors related to missing virtual table modules .
*/
static int expertSchemaSql(sqlite3 *db, const char *zSql, char **pzErr){
int rc = SQLITE_OK;
char *zErr = 0 ;
rc = sqlite3_exec(db, zSql, 0 , 0 , &zErr);
if ( rc!=SQLITE_OK && zErr ){
int nErr = STRLEN(zErr);
if ( nErr>=15 && memcmp(zErr, "no such module:" , 15 )==0 ){
sqlite3_free(zErr);
rc = SQLITE_OK;
zErr = 0 ;
}
}
*pzErr = zErr;
return rc;
}
static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){
int rc = idxRegisterVtab(p);
sqlite3_stmt *pSchema = 0 ;
/* For each table in the main db schema:
* *
* * 1 ) Add an entry to the p - > pTable list , and
* * 2 ) Create the equivalent virtual table in dbv .
*/
rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg,
"SELECT type, name, sql, 1, "
" substr(sql,1,14)=='create virtual' COLLATE nocase "
"FROM sqlite_schema "
"WHERE type IN ('table','view') AND "
" substr(name,1,7)!='sqlite_' COLLATE nocase "
" UNION ALL "
"SELECT type, name, sql, 2, 0 FROM sqlite_schema "
"WHERE type = 'trigger'"
" AND tbl_name IN(SELECT name FROM sqlite_schema WHERE type = 'view') "
"ORDER BY 4, 5 DESC, 1"
);
while ( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){
const char *zType = (const char *)sqlite3_column_text(pSchema, 0 );
const char *zName = (const char *)sqlite3_column_text(pSchema, 1 );
const char *zSql = (const char *)sqlite3_column_text(pSchema, 2 );
int bVirtual = sqlite3_column_int(pSchema, 4 );
int bExists = 0 ;
if ( zType==0 || zName==0 ) continue ;
rc = expertDbContainsObject(p->dbv, zName, &bExists);
if ( rc || bExists ) continue ;
if ( zType[0 ]=='v' || zType[1 ]=='r' || bVirtual ){
/* A view. Or a trigger on a view. */
if ( zSql ) rc = expertSchemaSql(p->dbv, zSql, pzErrmsg);
}else {
IdxTable *pTab;
rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg);
if ( rc==SQLITE_OK && ALWAYS(pTab!=0 ) ){
int i;
char *zInner = 0 ;
char *zOuter = 0 ;
pTab->pNext = p->pTable;
p->pTable = pTab;
/* The statement the vtab will pass to sqlite3_declare_vtab() */
zInner = idxAppendText(&rc, 0 , "CREATE TABLE x(" );
for (i=0 ; i<pTab->nCol; i++){
zInner = idxAppendText(&rc, zInner, "%s%Q COLLATE %s" ,
(i==0 ? "" : ", " ), pTab->aCol[i].zName, pTab->aCol[i].zColl
);
}
zInner = idxAppendText(&rc, zInner, ")" );
/* The CVT statement to create the vtab */
zOuter = idxAppendText(&rc, 0 ,
"CREATE VIRTUAL TABLE %Q USING expert(%Q)" , zName, zInner
);
if ( rc==SQLITE_OK ){
rc = sqlite3_exec(p->dbv, zOuter, 0 , 0 , pzErrmsg);
}
sqlite3_free(zInner);
sqlite3_free(zOuter);
}
}
}
idxFinalize(&rc, pSchema);
return rc;
}
struct IdxSampleCtx {
int iTarget;
double target; /* Target nRet/nRow value */
double nRow; /* Number of rows seen */
double nRet; /* Number of rows returned */
};
static void idxSampleFunc(
sqlite3_context *pCtx,
int argc,
sqlite3_value **argv
){
struct IdxSampleCtx *p = (struct IdxSampleCtx*)sqlite3_user_data(pCtx);
int bRet;
(void )argv;
assert( argc==0 );
if ( p->nRow==0 .0 ){
bRet = 1 ;
}else {
bRet = (p->nRet / p->nRow) <= p->target;
if ( bRet==0 ){
unsigned short rnd;
sqlite3_randomness(2 , (void *)&rnd);
bRet = ((int )rnd % 100 ) <= p->iTarget;
}
}
sqlite3_result_int(pCtx, bRet);
p->nRow += 1 .0 ;
p->nRet += (double )bRet;
}
struct IdxRemCtx {
int nSlot;
struct IdxRemSlot {
int eType; /* SQLITE_NULL, INTEGER, REAL, TEXT, BLOB */
i64 iVal; /* SQLITE_INTEGER value */
double rVal; /* SQLITE_FLOAT value */
i64 nByte; /* Bytes of space allocated at z */
i64 n; /* Size of buffer z */
char *z; /* SQLITE_TEXT/BLOB value */
} aSlot[1 ];
};
/*
* * Implementation of scalar function sqlite_expert_rem ( ) .
*/
static void idxRemFunc(
sqlite3_context *pCtx,
int argc,
sqlite3_value **argv
){
struct IdxRemCtx *p = (struct IdxRemCtx*)sqlite3_user_data(pCtx);
struct IdxRemSlot *pSlot;
int iSlot;
assert( argc==2 );
iSlot = sqlite3_value_int(argv[0 ]);
assert( iSlot<p->nSlot );
pSlot = &p->aSlot[iSlot];
switch ( pSlot->eType ){
case SQLITE_NULL:
/* no-op */
break ;
case SQLITE_INTEGER:
sqlite3_result_int64(pCtx, pSlot->iVal);
break ;
case SQLITE_FLOAT:
sqlite3_result_double(pCtx, pSlot->rVal);
break ;
case SQLITE_BLOB:
assert( pSlot->n <= 0 x7fffffff );
sqlite3_result_blob(pCtx, pSlot->z, (int )pSlot->n, SQLITE_TRANSIENT);
break ;
case SQLITE_TEXT:
assert( pSlot->n <= 0 x7fffffff );
sqlite3_result_text(pCtx, pSlot->z, (int )pSlot->n, SQLITE_TRANSIENT);
break ;
}
pSlot->eType = sqlite3_value_type(argv[1 ]);
switch ( pSlot->eType ){
case SQLITE_NULL:
/* no-op */
break ;
case SQLITE_INTEGER:
pSlot->iVal = sqlite3_value_int64(argv[1 ]);
break ;
case SQLITE_FLOAT:
pSlot->rVal = sqlite3_value_double(argv[1 ]);
break ;
case SQLITE_BLOB:
case SQLITE_TEXT: {
i64 nByte = sqlite3_value_bytes(argv[1 ]);
const void *pData = 0 ;
if ( nByte>pSlot->nByte ){
char *zNew = (char *)sqlite3_realloc64(pSlot->z, nByte*2 );
if ( zNew==0 ){
sqlite3_result_error_nomem(pCtx);
return ;
}
pSlot->nByte = nByte*2 ;
pSlot->z = zNew;
}
pSlot->n = nByte;
if ( pSlot->eType==SQLITE_BLOB ){
pData = sqlite3_value_blob(argv[1 ]);
if ( pData ) memcpy(pSlot->z, pData, nByte);
}else {
pData = sqlite3_value_text(argv[1 ]);
memcpy(pSlot->z, pData, nByte);
}
break ;
}
}
}
static int idxLargestIndex(sqlite3 *db, int *pnMax, char **pzErr){
int rc = SQLITE_OK;
const char *zMax =
"SELECT max(i.seqno) FROM "
" sqlite_schema AS s, "
" pragma_index_list(s.name) AS l, "
" pragma_index_info(l.name) AS i "
"WHERE s.type = 'table'" ;
sqlite3_stmt *pMax = 0 ;
*pnMax = 0 ;
rc = idxPrepareStmt(db, &pMax, pzErr, zMax);
if ( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pMax) ){
*pnMax = sqlite3_column_int(pMax, 0 ) + 1 ;
}
idxFinalize(&rc, pMax);
return rc;
}
static int idxPopulateOneStat1(
sqlite3expert *p,
sqlite3_stmt *pIndexXInfo,
sqlite3_stmt *pWriteStat,
const char *zTab,
const char *zIdx,
char **pzErr
){
char *zCols = 0 ;
char *zOrder = 0 ;
char *zQuery = 0 ;
int nCol = 0 ;
int i;
sqlite3_stmt *pQuery = 0 ;
i64 *aStat = 0 ;
int rc = SQLITE_OK;
assert( p->iSample>0 );
/* Formulate the query text */
sqlite3_bind_text(pIndexXInfo, 1 , zIdx, -1 , SQLITE_STATIC);
while ( SQLITE_OK==rc && SQLITE_ROW==sqlite3_step(pIndexXInfo) ){
const char *zComma = zCols==0 ? "" : ", " ;
const char *zName = (const char *)sqlite3_column_text(pIndexXInfo, 0 );
const char *zColl = (const char *)sqlite3_column_text(pIndexXInfo, 1 );
if ( zName==0 ){
/* This index contains an expression. Ignore it. */
sqlite3_free(zCols);
sqlite3_free(zOrder);
return sqlite3_reset(pIndexXInfo);
}
zCols = idxAppendText(&rc, zCols,
"%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %s" ,
zComma, zName, nCol, zName, zColl
);
zOrder = idxAppendText(&rc, zOrder, "%s%d" , zComma, ++nCol);
}
sqlite3_reset(pIndexXInfo);
if ( rc==SQLITE_OK ){
if ( p->iSample==100 ){
zQuery = sqlite3_mprintf(
"SELECT %s FROM %Q x ORDER BY %s" , zCols, zTab, zOrder
);
}else {
zQuery = sqlite3_mprintf(
"SELECT %s FROM temp." UNIQUE_TABLE_NAME" x ORDER BY %s" , zCols, zOrder
);
}
}
sqlite3_free(zCols);
sqlite3_free(zOrder);
/* Formulate the query text */
if ( rc==SQLITE_OK ){
sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
rc = idxPrepareStmt(dbrem, &pQuery, pzErr, zQuery);
}
sqlite3_free(zQuery);
if ( rc==SQLITE_OK ){
aStat = (i64*)idxMalloc(&rc, sizeof (i64)*(nCol+1 ));
}
if ( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pQuery) ){
IdxHashEntry *pEntry;
char *zStat = 0 ;
for (i=0 ; i<=nCol; i++) aStat[i] = 1 ;
while ( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pQuery) ){
aStat[0 ]++;
for (i=0 ; i<nCol; i++){
if ( sqlite3_column_int(pQuery, i)==0 ) break ;
}
for (/*no-op*/; i<nCol; i++){
aStat[i+1 ]++;
}
}
if ( rc==SQLITE_OK ){
i64 s0 = aStat[0 ];
zStat = sqlite3_mprintf("%lld" , s0);
if ( zStat==0 ) rc = SQLITE_NOMEM;
for (i=1 ; rc==SQLITE_OK && i<=nCol; i++){
zStat = idxAppendText(&rc, zStat, " %lld" , (s0+aStat[i]/2 ) / aStat[i]);
}
}
if ( rc==SQLITE_OK ){
sqlite3_bind_text(pWriteStat, 1 , zTab, -1 , SQLITE_STATIC);
sqlite3_bind_text(pWriteStat, 2 , zIdx, -1 , SQLITE_STATIC);
sqlite3_bind_text(pWriteStat, 3 , zStat, -1 , SQLITE_STATIC);
sqlite3_step(pWriteStat);
rc = sqlite3_reset(pWriteStat);
}
pEntry = idxHashFind(&p->hIdx, zIdx, STRLEN(zIdx));
if ( pEntry ){
assert( pEntry->zVal2==0 );
pEntry->zVal2 = zStat;
}else {
sqlite3_free(zStat);
}
}
sqlite3_free(aStat);
idxFinalize(&rc, pQuery);
return rc;
}
static int idxBuildSampleTable(sqlite3expert *p, const char *zTab){
int rc;
char *zSql;
rc = sqlite3_exec(p->dbv,"DROP TABLE IF EXISTS temp." UNIQUE_TABLE_NAME,0 ,0 ,0 );
if ( rc!=SQLITE_OK ) return rc;
zSql = sqlite3_mprintf(
"CREATE TABLE temp." UNIQUE_TABLE_NAME " AS SELECT * FROM %Q" , zTab
);
if ( zSql==0 ) return SQLITE_NOMEM;
rc = sqlite3_exec(p->dbv, zSql, 0 , 0 , 0 );
sqlite3_free(zSql);
return rc;
}
/*
* * This function is called as part of sqlite3_expert_analyze ( ) . Candidate
* * indexes have already been created in database sqlite3expert . dbm , this
* * function populates sqlite_stat1 table in the same database .
* *
* * The stat1 data is generated by querying the
*/
static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
int rc = SQLITE_OK;
int nMax =0 ;
struct IdxRemCtx *pCtx = 0 ;
struct IdxSampleCtx samplectx;
int i;
i64 iPrev = -100000 ;
sqlite3_stmt *pAllIndex = 0 ;
sqlite3_stmt *pIndexXInfo = 0 ;
sqlite3_stmt *pWrite = 0 ;
const char *zAllIndex =
"SELECT s.rowid, s.name, l.name FROM "
" sqlite_schema AS s, "
" pragma_index_list(s.name) AS l "
"WHERE s.type = 'table'" ;
const char *zIndexXInfo =
"SELECT name, coll FROM pragma_index_xinfo(?) WHERE key" ;
const char *zWrite = "INSERT INTO sqlite_stat1 VALUES(?, ?, ?)" ;
/* If iSample==0, no sqlite_stat1 data is required. */
if ( p->iSample==0 ) return SQLITE_OK;
rc = idxLargestIndex(p->dbm, &nMax, pzErr);
if ( nMax<=0 || rc!=SQLITE_OK ) return rc;
rc = sqlite3_exec(p->dbm, "ANALYZE; PRAGMA writable_schema=1" , 0 , 0 , 0 );
if ( rc==SQLITE_OK ){
i64 nByte = sizeof (struct IdxRemCtx) + (sizeof (struct IdxRemSlot) * nMax);
pCtx = (struct IdxRemCtx*)idxMalloc(&rc, nByte);
}
if ( rc==SQLITE_OK ){
sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
rc = sqlite3_create_function(dbrem, "sqlite_expert_rem" ,
2 , SQLITE_UTF8, (void *)pCtx, idxRemFunc, 0 , 0
);
}
if ( rc==SQLITE_OK ){
rc = sqlite3_create_function(p->db, "sqlite_expert_sample" ,
0 , SQLITE_UTF8, (void *)&samplectx, idxSampleFunc, 0 , 0
);
}
if ( rc==SQLITE_OK ){
pCtx->nSlot = (i64)nMax+1 ;
rc = idxPrepareStmt(p->dbm, &pAllIndex, pzErr, zAllIndex);
}
if ( rc==SQLITE_OK ){
rc = idxPrepareStmt(p->dbm, &pIndexXInfo, pzErr, zIndexXInfo);
}
if ( rc==SQLITE_OK ){
rc = idxPrepareStmt(p->dbm, &pWrite, pzErr, zWrite);
}
while ( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pAllIndex) ){
i64 iRowid = sqlite3_column_int64(pAllIndex, 0 );
const char *zTab = (const char *)sqlite3_column_text(pAllIndex, 1 );
const char *zIdx = (const char *)sqlite3_column_text(pAllIndex, 2 );
if ( zTab==0 || zIdx==0 ) continue ;
if ( p->iSample<100 && iPrev!=iRowid ){
samplectx.target = (double )p->iSample / 100 .0 ;
samplectx.iTarget = p->iSample;
samplectx.nRow = 0 .0 ;
samplectx.nRet = 0 .0 ;
rc = idxBuildSampleTable(p, zTab);
if ( rc!=SQLITE_OK ) break ;
}
rc = idxPopulateOneStat1(p, pIndexXInfo, pWrite, zTab, zIdx, pzErr);
iPrev = iRowid;
}
if ( rc==SQLITE_OK && p->iSample<100 ){
rc = sqlite3_exec(p->dbv,
"DROP TABLE IF EXISTS temp." UNIQUE_TABLE_NAME, 0 ,0 ,0
);
}
idxFinalize(&rc, pAllIndex);
idxFinalize(&rc, pIndexXInfo);
idxFinalize(&rc, pWrite);
if ( pCtx ){
for (i=0 ; i<pCtx->nSlot; i++){
sqlite3_free(pCtx->aSlot[i].z);
}
sqlite3_free(pCtx);
}
if ( rc==SQLITE_OK ){
rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema" , 0 , 0 , 0 );
}
sqlite3_create_function(p->db, "sqlite_expert_rem" , 2 , SQLITE_UTF8, 0 ,0 ,0 ,0 );
sqlite3_create_function(p->db, "sqlite_expert_sample" , 0 ,SQLITE_UTF8,0 ,0 ,0 ,0 );
sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp." UNIQUE_TABLE_NAME,0 ,0 ,0 );
return rc;
}
/*
* * Define and possibly pretend to use a useless collation sequence .
* * This pretense allows expert to accept SQL using custom collations .
*/
int dummyCompare(void *up1, int up2, const void *up3, int up4, const void *up5){
(void )up1;
(void )up2;
(void )up3;
(void )up4;
(void )up5;
assert(0 ); /* VDBE should never be run. */
return 0 ;
}
/* And a callback to register above upon actual need */
void useDummyCS(void *up1, sqlite3 *db, int etr, const char *zName){
(void )up1;
sqlite3_create_collation_v2(db, zName, etr, 0 , dummyCompare, 0 );
}
#if !defined (SQLITE_OMIT_SCHEMA_PRAGMAS) \
&& !defined (SQLITE_OMIT_INTROSPECTION_PRAGMAS)
/*
* * dummy functions for no - op implementation of UDFs during expert ' s work
*/
void dummyUDF(sqlite3_context *up1, int up2, sqlite3_value **up3){
(void )up1;
(void )up2;
(void )up3;
assert(0 ); /* VDBE should never be run. */
}
void dummyUDFvalue(sqlite3_context *up1){
(void )up1;
assert(0 ); /* VDBE should never be run. */
}
/*
* * Register UDFs from user database with another .
*/
int registerUDFs(sqlite3 *dbSrc, sqlite3 *dbDst){
sqlite3_stmt *pStmt;
int rc = sqlite3_prepare_v2(dbSrc,
"SELECT name,type,enc,narg,flags "
"FROM pragma_function_list() "
"WHERE builtin==0" , -1 , &pStmt, 0 );
if ( rc==SQLITE_OK ){
while ( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
int nargs = sqlite3_column_int(pStmt,3 );
int flags = sqlite3_column_int(pStmt,4 );
const char *name = (char *)sqlite3_column_text(pStmt,0 );
const char *type = (char *)sqlite3_column_text(pStmt,1 );
const char *enc = (char *)sqlite3_column_text(pStmt,2 );
if ( name==0 || type==0 || enc==0 ){
/* no-op. Only happens on OOM */
}else {
int ienc = SQLITE_UTF8;
int rcf = SQLITE_ERROR;
if ( strcmp(enc,"utf16le" )==0 ) ienc = SQLITE_UTF16LE;
else if ( strcmp(enc,"utf16be" )==0 ) ienc = SQLITE_UTF16BE;
ienc |= (flags & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY));
if ( strcmp(type,"w" )==0 ){
rcf = sqlite3_create_window_function(dbDst,name,nargs,ienc,0 ,
dummyUDF,dummyUDFvalue,0 ,0 ,0 );
}else if ( strcmp(type,"a" )==0 ){
rcf = sqlite3_create_function(dbDst,name,nargs,ienc,0 ,
0 ,dummyUDF,dummyUDFvalue);
}else if ( strcmp(type,"s" )==0 ){
rcf = sqlite3_create_function(dbDst,name,nargs,ienc,0 ,
dummyUDF,0 ,0 );
}
if ( rcf!=SQLITE_OK ){
rc = rcf;
break ;
}
}
}
sqlite3_finalize(pStmt);
if ( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
return rc;
}
#endif
/*
* * Allocate a new sqlite3expert object .
*/
sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){
int rc = SQLITE_OK;
sqlite3expert *pNew;
pNew = (sqlite3expert*)idxMalloc(&rc, sizeof (sqlite3expert));
/* Open two in-memory databases to work with. The "vtab database" (dbv)
* * will contain a virtual table corresponding to each real table in
* * the user database schema , and a copy of each view . It is used to
* * collect information regarding the WHERE , ORDER BY and other clauses
* * of the user ' s query .
*/
if ( rc==SQLITE_OK ){
pNew->db = db;
pNew->iSample = 100 ;
rc = sqlite3_open(":memory:" , &pNew->dbv);
}
if ( rc==SQLITE_OK ){
rc = sqlite3_open(":memory:" , &pNew->dbm);
if ( rc==SQLITE_OK ){
sqlite3_db_config(pNew->dbm, SQLITE_DBCONFIG_TRIGGER_EQP, 1 , (int *)0 );
}
}
/* Allow custom collations to be dealt with through prepare. */
if ( rc==SQLITE_OK ) rc = sqlite3_collation_needed(pNew->dbm,0 ,useDummyCS);
if ( rc==SQLITE_OK ) rc = sqlite3_collation_needed(pNew->dbv,0 ,useDummyCS);
#if !defined (SQLITE_OMIT_SCHEMA_PRAGMAS) \
&& !defined (SQLITE_OMIT_INTROSPECTION_PRAGMAS)
/* Register UDFs from database [db] with [dbm] and [dbv]. */
if ( rc==SQLITE_OK ){
rc = registerUDFs(pNew->db, pNew->dbm);
}
if ( rc==SQLITE_OK ){
rc = registerUDFs(pNew->db, pNew->dbv);
}
#endif
/* Copy the entire schema of database [db] into [dbm]. */
if ( rc==SQLITE_OK ){
sqlite3_stmt *pSql = 0 ;
rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg,
"SELECT sql, name, substr(sql,1,14)=='create virtual' COLLATE nocase"
" FROM sqlite_schema WHERE substr(name,1,7)!='sqlite_' COLLATE nocase"
" ORDER BY 3 DESC, rowid"
);
while ( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
const char *zSql = (const char *)sqlite3_column_text(pSql, 0 );
const char *zName = (const char *)sqlite3_column_text(pSql, 1 );
int bExists = 0 ;
rc = expertDbContainsObject(pNew->dbm, zName, &bExists);
if ( rc==SQLITE_OK && zSql && bExists==0 ){
rc = expertSchemaSql(pNew->dbm, zSql, pzErrmsg);
}
}
idxFinalize(&rc, pSql);
}
/* Create the vtab schema */
if ( rc==SQLITE_OK ){
rc = idxCreateVtabSchema(pNew, pzErrmsg);
}
/* Register the auth callback with dbv */
if ( rc==SQLITE_OK ){
sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void *)pNew);
}
/* If an error has occurred, free the new object and return NULL. Otherwise,
** return the new sqlite3expert handle. */
if ( rc!=SQLITE_OK ){
sqlite3_expert_destroy(pNew);
pNew = 0 ;
}
return pNew;
}
/*
* * Configure an sqlite3expert object .
*/
int sqlite3_expert_config(sqlite3expert *p, int op, ...){
int rc = SQLITE_OK;
va_list ap;
va_start(ap, op);
switch ( op ){
case EXPERT_CONFIG_SAMPLE: {
int iVal = va_arg(ap, int );
if ( iVal<0 ) iVal = 0 ;
if ( iVal>100 ) iVal = 100 ;
p->iSample = iVal;
break ;
}
default :
rc = SQLITE_NOTFOUND;
break ;
}
va_end(ap);
return rc;
}
/*
* * Add an SQL statement to the analysis .
*/
int sqlite3_expert_sql(
sqlite3expert *p, /* From sqlite3_expert_new() */
const char *zSql, /* SQL statement to add */
char **pzErr /* OUT: Error message (if any) */
){
IdxScan *pScanOrig = p->pScan;
IdxStatement *pStmtOrig = p->pStatement;
int rc = SQLITE_OK;
const char *zStmt = zSql;
if ( p->bRun ) return SQLITE_MISUSE;
while ( rc==SQLITE_OK && zStmt && zStmt[0 ] ){
sqlite3_stmt *pStmt = 0 ;
/* Ensure that the provided statement compiles against user's DB. */
rc = idxPrepareStmt(p->db, &pStmt, pzErr, zStmt);
if ( rc!=SQLITE_OK ) break ;
sqlite3_finalize(pStmt);
rc = sqlite3_prepare_v2(p->dbv, zStmt, -1 , &pStmt, &zStmt);
if ( rc==SQLITE_OK ){
if ( pStmt ){
IdxStatement *pNew;
const char *z = sqlite3_sql(pStmt);
i64 n = STRLEN(z);
pNew = (IdxStatement*)idxMalloc(&rc, sizeof (IdxStatement) + n+1 );
if ( rc==SQLITE_OK ){
pNew->zSql = (char *)&pNew[1 ];
memcpy(pNew->zSql, z, n+1 );
pNew->pNext = p->pStatement;
if ( p->pStatement ) pNew->iId = p->pStatement->iId+1 ;
p->pStatement = pNew;
}
sqlite3_finalize(pStmt);
}
}else {
idxDatabaseError(p->dbv, pzErr);
}
}
if ( rc!=SQLITE_OK ){
idxScanFree(p->pScan, pScanOrig);
idxStatementFree(p->pStatement, pStmtOrig);
p->pScan = pScanOrig;
p->pStatement = pStmtOrig;
}
return rc;
}
int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr){
int rc;
IdxHashEntry *pEntry;
/* Do trigger processing to collect any extra IdxScan structures */
rc = idxProcessTriggers(p, pzErr);
/* Create candidate indexes within the in-memory database file */
if ( rc==SQLITE_OK ){
rc = idxCreateCandidates(p);
}else if ( rc==SQLITE_BUSY_TIMEOUT ){
if ( pzErr )
*pzErr = sqlite3_mprintf("Cannot find a unique index name to propose." );
return rc;
}
/* Generate the stat1 data */
if ( rc==SQLITE_OK ){
rc = idxPopulateStat1(p, pzErr);
}
/* Formulate the EXPERT_REPORT_CANDIDATES text */
for (pEntry=p->hIdx.pFirst; pEntry; pEntry=pEntry->pNext){
p->zCandidates = idxAppendText(&rc, p->zCandidates,
"%s;%s%s\n" , pEntry->zVal,
pEntry->zVal2 ? " -- stat1: " : "" , pEntry->zVal2
);
}
/* Figure out which of the candidate indexes are preferred by the query
** planner and report the results to the user. */
if ( rc==SQLITE_OK ){
rc = idxFindIndexes(p, pzErr);
}
if ( rc==SQLITE_OK ){
p->bRun = 1 ;
}
return rc;
}
/*
* * Return the total number of statements that have been added to this
* * sqlite3expert using sqlite3_expert_sql ( ) .
*/
int sqlite3_expert_count(sqlite3expert *p){
int nRet = 0 ;
if ( p->pStatement ) nRet = p->pStatement->iId+1 ;
return nRet;
}
/*
* * Return a component of the report .
*/
const char *sqlite3_expert_report(sqlite3expert *p, int iStmt, int eReport){
const char *zRet = 0 ;
IdxStatement *pStmt;
if ( p->bRun==0 ) return 0 ;
for (pStmt=p->pStatement; pStmt && pStmt->iId!=iStmt; pStmt=pStmt->pNext);
switch ( eReport ){
case EXPERT_REPORT_SQL:
if ( pStmt ) zRet = pStmt->zSql;
break ;
case EXPERT_REPORT_INDEXES:
if ( pStmt ) zRet = pStmt->zIdx;
break ;
case EXPERT_REPORT_PLAN:
if ( pStmt ) zRet = pStmt->zEQP;
break ;
case EXPERT_REPORT_CANDIDATES:
zRet = p->zCandidates;
break ;
}
return zRet;
}
/*
* * Free an sqlite3expert object .
*/
void sqlite3_expert_destroy(sqlite3expert *p){
if ( p ){
sqlite3_close(p->dbm);
sqlite3_close(p->dbv);
idxScanFree(p->pScan, 0 );
idxStatementFree(p->pStatement, 0 );
idxTableFree(p->pTable);
idxWriteFree(p->pWrite);
idxHashClear(&p->hIdx);
sqlite3_free(p->zCandidates);
sqlite3_free(p);
}
}
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
/************************* End ext/expert/sqlite3expert.c ********************/
#endif
/************************* Begin ext/intck/sqlite3intck.h ******************/
/*
* * 2024 - 02 - 08
* *
* * 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 .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/*
* * Incremental Integrity - Check Extension
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
* * This module contains code to check whether or not an SQLite database
* * is well - formed or corrupt . This is the same task as performed by SQLite ' s
* * built - in " PRAGMA integrity_check " command . This module differs from
* * " PRAGMA integrity_check " in that :
* *
* * + It is less thorough - this module does not detect certain types
* * of corruption that are detected by the PRAGMA command . However ,
* * it does detect all kinds of corruption that are likely to cause
* * errors in SQLite applications .
* *
* * + It is slower . Sometimes up to three times slower .
* *
* * + It allows integrity - check operations to be split into multiple
* * transactions , so that the database does not need to be read - locked
* * for the duration of the integrity - check .
* *
* * One way to use the API to run integrity - check on the " main " database
* * of handle db is :
* *
* * int rc = SQLITE_OK ;
* * sqlite3_intck * p = 0 ;
* *
* * sqlite3_intck_open ( db , " main " , & p ) ;
* * while ( SQLITE_OK = = sqlite3_intck_step ( p ) ) {
* * const char * zMsg = sqlite3_intck_message ( p ) ;
* * if ( zMsg ) printf ( " corruption : % s \ n " , zMsg ) ;
* * }
* * rc = sqlite3_intck_error ( p , & zErr ) ;
* * if ( rc ! = SQLITE_OK ) {
* * printf ( " error occured ( rc = % d ) , ( errmsg = % s ) \ n " , rc , zErr ) ;
* * }
* * sqlite3_intck_close ( p ) ;
* *
* * Usually , the sqlite3_intck object opens a read transaction within the
* * first call to sqlite3_intck_step ( ) and holds it open until the
* * integrity - check is complete . However , if sqlite3_intck_unlock ( ) is
* * called , the read transaction is ended and a new read transaction opened
* * by the subsequent call to sqlite3_intck_step ( ) .
*/
#ifndef _SQLITE_INTCK_H
#define _SQLITE_INTCK_H
/* #include "sqlite3.h" */
#ifdef __cplusplus
extern "C" {
#endif
/*
* * An ongoing incremental integrity - check operation is represented by an
* * opaque pointer of the following type .
*/
typedef struct sqlite3_intck sqlite3_intck;
/*
* * Open a new incremental integrity - check object . If successful , populate
* * output variable ( * ppOut ) with the new object handle and return SQLITE_OK .
* * Or , if an error occurs , set ( * ppOut ) to NULL and return an SQLite error
* * code ( e . g . SQLITE_NOMEM ) .
* *
* * The integrity - check will be conducted on database zDb ( which must be " main " ,
* * " temp " , or the name of an attached database ) of database handle db . Once
* * this function has been called successfully , the caller should not use
* * database handle db until the integrity - check object has been destroyed
* * using sqlite3_intck_close ( ) .
*/
int sqlite3_intck_open(
sqlite3 *db, /* Database handle */
const char *zDb, /* Database name ("main", "temp" etc.) */
sqlite3_intck **ppOut /* OUT: New sqlite3_intck handle */
);
/*
* * Close and release all resources associated with a handle opened by an
* * earlier call to sqlite3_intck_open ( ) . The results of using an
* * integrity - check handle after it has been passed to this function are
* * undefined .
*/
void sqlite3_intck_close(sqlite3_intck *pCk);
/*
* * Do the next step of the integrity - check operation specified by the handle
* * passed as the only argument . This function returns SQLITE_DONE if the
* * integrity - check operation is finished , or an SQLite error code if
* * an error occurs , or SQLITE_OK if no error occurs but the integrity - check
* * is not finished . It is not considered an error if database corruption
* * is encountered .
* *
* * Following a successful call to sqlite3_intck_step ( ) ( one that returns
* * SQLITE_OK ) , sqlite3_intck_message ( ) returns a non - NULL value if
* * corruption was detected in the db .
* *
* * If an error occurs and a value other than SQLITE_OK or SQLITE_DONE is
* * returned , then the integrity - check handle is placed in an error state .
* * In this state all subsequent calls to sqlite3_intck_step ( ) or
* * sqlite3_intck_unlock ( ) will immediately return the same error . The
* * sqlite3_intck_error ( ) method may be used to obtain an English language
* * error message in this case .
*/
int sqlite3_intck_step(sqlite3_intck *pCk);
/*
* * If the previous call to sqlite3_intck_step ( ) encountered corruption
* * within the database , then this function returns a pointer to a buffer
* * containing a nul - terminated string describing the corruption in
* * English . If the previous call to sqlite3_intck_step ( ) did not encounter
* * corruption , or if there was no previous call , this function returns
* * NULL .
*/
const char *sqlite3_intck_message(sqlite3_intck *pCk);
/*
* * Close any read - transaction opened by an earlier call to
* * sqlite3_intck_step ( ) . Any subsequent call to sqlite3_intck_step ( ) will
* * open a new transaction . Return SQLITE_OK if successful , or an SQLite error
* * code otherwise .
* *
* * If an error occurs , then the integrity - check handle is placed in an error
* * state . In this state all subsequent calls to sqlite3_intck_step ( ) or
* * sqlite3_intck_unlock ( ) will immediately return the same error . The
* * sqlite3_intck_error ( ) method may be used to obtain an English language
* * error message in this case .
*/
int sqlite3_intck_unlock(sqlite3_intck *pCk);
/*
* * If an error has occurred in an earlier call to sqlite3_intck_step ( )
* * or sqlite3_intck_unlock ( ) , then this method returns the associated
* * SQLite error code . Additionally , if pzErr is not NULL , then ( * pzErr )
* * may be set to point to a nul - terminated string containing an English
* * language error message . Or , if no error message is available , to
* * NULL .
* *
* * If no error has occurred within sqlite3_intck_step ( ) or
* * sqlite_intck_unlock ( ) calls on the handle passed as the first argument ,
* * then SQLITE_OK is returned and ( * pzErr ) set to NULL .
*/
int sqlite3_intck_error(sqlite3_intck *pCk, const char **pzErr);
/*
* * This API is used for testing only . It returns the full - text of an SQL
* * statement used to test object zObj , which may be a table or index .
* * The returned buffer is valid until the next call to either this function
* * or sqlite3_intck_close ( ) on the same sqlite3_intck handle .
*/
const char *sqlite3_intck_test_sql(sqlite3_intck *pCk, const char *zObj);
#ifdef __cplusplus
} /* end of the 'extern "C"' block */
#endif
#endif /* ifndef _SQLITE_INTCK_H */
/************************* End ext/intck/sqlite3intck.h ********************/
/************************* Begin ext/intck/sqlite3intck.c ******************/
/*
* * 2024 - 02 - 08
* *
* * 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 .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/* #include "sqlite3intck.h" */
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
/*
* * nKeyVal :
* * The number of values that make up the ' key ' for the current pCheck
* * statement .
* *
* * rc :
* * Error code returned by most recent sqlite3_intck_step ( ) or
* * sqlite3_intck_unlock ( ) call . This is set to SQLITE_DONE when
* * the integrity - check operation is finished .
* *
* * zErr :
* * If the object has entered the error state , this is the error message .
* * Is freed using sqlite3_free ( ) when the object is deleted .
* *
* * zTestSql :
* * The value returned by the most recent call to sqlite3_intck_testsql ( ) .
* * Each call to testsql ( ) frees the previous zTestSql value ( using
* * sqlite3_free ( ) ) and replaces it with the new value it will return .
*/
struct sqlite3_intck {
sqlite3 *db;
const char *zDb; /* Copy of zDb parameter to _open() */
char *zObj; /* Current object. Or NULL. */
sqlite3_stmt *pCheck; /* Current check statement */
char *zKey;
int nKeyVal;
char *zMessage;
int bCorruptSchema;
int rc; /* Error code */
char *zErr; /* Error message */
char *zTestSql; /* Returned by sqlite3_intck_test_sql() */
};
/*
* * Some error has occurred while using database p - > db . Save the error message
* * and error code currently held by the database handle in p - > rc and p - > zErr .
*/
static void intckSaveErrmsg(sqlite3_intck *p){
p->rc = sqlite3_errcode(p->db);
sqlite3_free(p->zErr);
p->zErr = sqlite3_mprintf("%s" , sqlite3_errmsg(p->db));
}
/*
* * If the handle passed as the first argument is already in the error state ,
* * then this function is a no - op ( returns NULL immediately ) . Otherwise , if an
* * error occurs within this function , it leaves an error in said handle .
* *
* * Otherwise , this function attempts to prepare SQL statement zSql and
* * return the resulting statement handle to the user .
*/
static sqlite3_stmt *intckPrepare(sqlite3_intck *p, const char *zSql){
sqlite3_stmt *pRet = 0 ;
if ( p->rc==SQLITE_OK ){
p->rc = sqlite3_prepare_v2(p->db, zSql, -1 , &pRet, 0 );
if ( p->rc!=SQLITE_OK ){
intckSaveErrmsg(p);
assert( pRet==0 );
}
}
return pRet;
}
/*
* * If the handle passed as the first argument is already in the error state ,
* * then this function is a no - op ( returns NULL immediately ) . Otherwise , if an
* * error occurs within this function , it leaves an error in said handle .
* *
* * Otherwise , this function treats argument zFmt as a printf ( ) style format
* * string . It formats it according to the trailing arguments and then
* * attempts to prepare the results and return the resulting prepared
* * statement .
*/
static sqlite3_stmt *intckPrepareFmt(sqlite3_intck *p, const char *zFmt, ...){
sqlite3_stmt *pRet = 0 ;
va_list ap;
char *zSql = 0 ;
va_start(ap, zFmt);
zSql = sqlite3_vmprintf(zFmt, ap);
if ( p->rc==SQLITE_OK && zSql==0 ){
p->rc = SQLITE_NOMEM;
}
pRet = intckPrepare(p, zSql);
sqlite3_free(zSql);
va_end(ap);
return pRet;
}
/*
* * Finalize SQL statement pStmt . If an error occurs and the handle passed
* * as the first argument does not already contain an error , store the
* * error in the handle .
*/
static void intckFinalize(sqlite3_intck *p, sqlite3_stmt *pStmt){
int rc = sqlite3_finalize(pStmt);
if ( p->rc==SQLITE_OK && rc!=SQLITE_OK ){
intckSaveErrmsg(p);
}
}
/*
* * If there is already an error in handle p , return it . Otherwise , call
* * sqlite3_step ( ) on the statement handle and return that value .
*/
static int intckStep(sqlite3_intck *p, sqlite3_stmt *pStmt){
if ( p->rc ) return p->rc;
return sqlite3_step(pStmt);
}
/*
* * Execute SQL statement zSql . There is no way to obtain any results
* * returned by the statement . This function uses the sqlite3_intck error
* * code convention .
*/
static void intckExec(sqlite3_intck *p, const char *zSql){
sqlite3_stmt *pStmt = 0 ;
pStmt = intckPrepare(p, zSql);
intckStep(p, pStmt);
intckFinalize(p, pStmt);
}
/*
* * A wrapper around sqlite3_mprintf ( ) that uses the sqlite3_intck error
* * code convention .
*/
static char *intckMprintf(sqlite3_intck *p, const char *zFmt, ...){
va_list ap;
char *zRet = 0 ;
va_start(ap, zFmt);
zRet = sqlite3_vmprintf(zFmt, ap);
if ( p->rc==SQLITE_OK ){
if ( zRet==0 ){
p->rc = SQLITE_NOMEM;
}
}else {
sqlite3_free(zRet);
zRet = 0 ;
}
va_end(ap);
return zRet;
}
/*
* * This is used by sqlite3_intck_unlock ( ) to save the vector key value
* * required to restart the current pCheck query as a nul - terminated string
* * in p - > zKey .
*/
static void intckSaveKey(sqlite3_intck *p){
int ii;
char *zSql = 0 ;
sqlite3_stmt *pStmt = 0 ;
sqlite3_stmt *pXinfo = 0 ;
const char *zDir = 0 ;
assert( p->pCheck );
assert( p->zKey==0 );
pXinfo = intckPrepareFmt(p,
"SELECT group_concat(desc, '') FROM %Q.sqlite_schema s, "
"pragma_index_xinfo(%Q, %Q) "
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 in Prozent C=91 H=91 G=90
¤ Dauer der Verarbeitung: 3.53 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland