mirror of
https://github.com/rqlite/rqlite.git
synced 2026-01-25 04:16:26 +00:00
Move SQLite extensions src to own repo
This commit is contained in:
@@ -34,9 +34,6 @@ RUN go build -ldflags=" \
|
||||
WORKDIR /extensions
|
||||
WORKDIR /app
|
||||
|
||||
RUN gcc -fPIC -shared extensions/src/icu/icu.c -I extensions/src/ `pkg-config --libs --cflags icu-uc icu-io` -o icu.so && \
|
||||
zip /extensions/icu.zip icu.so
|
||||
|
||||
RUN curl -L `curl -s https://api.github.com/repos/nalgeon/sqlean/releases/latest | grep "tarball_url" | cut -d '"' -f 4` -o sqlean.tar.gz && \
|
||||
tar xvfz sqlean.tar.gz && \
|
||||
cd nalgeon* && make prepare-dist download-sqlite download-external compile-linux && zip -j /extensions/sqlean.zip dist/sqlean.so
|
||||
@@ -45,7 +42,9 @@ RUN curl -L `curl -s https://api.github.com/repos/asg017/sqlite-vec/releases/lat
|
||||
tar xvfz sqlite-vec.tar.gz && \
|
||||
cd asg017* && sh scripts/vendor.sh && echo "#include <sys/types.h>" | cat - sqlite-vec.c > temp && mv temp sqlite-vec.c && make loadable && zip -j /extensions/sqlite-vec.zip dist/vec0.so
|
||||
|
||||
RUN cd extensions/src/misc/ && make && zip /extensions/misc.zip *.so
|
||||
RUN git clone https://github.com/rqlite/rqlite-sqlite-ext.git
|
||||
RUN cd rqlite-sqlite-ext/misc && make && zip /extensions/misc.zip *.so
|
||||
RUN cd rqlite-sqlite-ext/icu && gcc -fPIC -shared icu.c -I .. `pkg-config --libs --cflags icu-uc icu-io` -o icu.so && zip /extensions/icu.zip icu.so
|
||||
|
||||
#######################################################################
|
||||
# Phase 2: Create the final image.
|
||||
|
||||
@@ -1,588 +0,0 @@
|
||||
/*
|
||||
** 2007 May 6
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: icu.c,v 1.7 2007/12/13 21:54:11 drh Exp $
|
||||
**
|
||||
** This file implements an integration between the ICU library
|
||||
** ("International Components for Unicode", an open-source library
|
||||
** for handling unicode data) and SQLite. The integration uses
|
||||
** ICU to provide the following to SQLite:
|
||||
**
|
||||
** * An implementation of the SQL regexp() function (and hence REGEXP
|
||||
** operator) using the ICU uregex_XX() APIs.
|
||||
**
|
||||
** * Implementations of the SQL scalar upper() and lower() functions
|
||||
** for case mapping.
|
||||
**
|
||||
** * Integration of ICU and SQLite collation sequences.
|
||||
**
|
||||
** * An implementation of the LIKE operator that uses ICU to
|
||||
** provide case-independent matching.
|
||||
*/
|
||||
|
||||
#if !defined(SQLITE_CORE) \
|
||||
|| defined(SQLITE_ENABLE_ICU) \
|
||||
|| defined(SQLITE_ENABLE_ICU_COLLATIONS)
|
||||
|
||||
/* Include ICU headers */
|
||||
#include <unicode/utypes.h>
|
||||
#include <unicode/uregex.h>
|
||||
#include <unicode/ustring.h>
|
||||
#include <unicode/ucol.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef SQLITE_CORE
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#else
|
||||
#include "sqlite3.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This function is called when an ICU function called from within
|
||||
** the implementation of an SQL scalar function returns an error.
|
||||
**
|
||||
** The scalar function context passed as the first argument is
|
||||
** loaded with an error message based on the following two args.
|
||||
*/
|
||||
static void icuFunctionError(
|
||||
sqlite3_context *pCtx, /* SQLite scalar function context */
|
||||
const char *zName, /* Name of ICU function that failed */
|
||||
UErrorCode e /* Error code returned by ICU function */
|
||||
){
|
||||
char zBuf[128];
|
||||
sqlite3_snprintf(128, zBuf, "ICU error: %s(): %s", zName, u_errorName(e));
|
||||
zBuf[127] = '\0';
|
||||
sqlite3_result_error(pCtx, zBuf, -1);
|
||||
}
|
||||
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
|
||||
|
||||
/*
|
||||
** Maximum length (in bytes) of the pattern in a LIKE or GLOB
|
||||
** operator.
|
||||
*/
|
||||
#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
|
||||
# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Version of sqlite3_free() that is always a function, never a macro.
|
||||
*/
|
||||
static void xFree(void *p){
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** This lookup table is used to help decode the first byte of
|
||||
** a multi-byte UTF8 character. It is copied here from SQLite source
|
||||
** code file utf8.c.
|
||||
*/
|
||||
static const unsigned char icuUtf8Trans1[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
|
||||
};
|
||||
|
||||
#define SQLITE_ICU_READ_UTF8(zIn, c) \
|
||||
c = *(zIn++); \
|
||||
if( c>=0xc0 ){ \
|
||||
c = icuUtf8Trans1[c-0xc0]; \
|
||||
while( (*zIn & 0xc0)==0x80 ){ \
|
||||
c = (c<<6) + (0x3f & *(zIn++)); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SQLITE_ICU_SKIP_UTF8(zIn) \
|
||||
assert( *zIn ); \
|
||||
if( *(zIn++)>=0xc0 ){ \
|
||||
while( (*zIn & 0xc0)==0x80 ){zIn++;} \
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Compare two UTF-8 strings for equality where the first string is
|
||||
** a "LIKE" expression. Return true (1) if they are the same and
|
||||
** false (0) if they are different.
|
||||
*/
|
||||
static int icuLikeCompare(
|
||||
const uint8_t *zPattern, /* LIKE pattern */
|
||||
const uint8_t *zString, /* The UTF-8 string to compare against */
|
||||
const UChar32 uEsc /* The escape character */
|
||||
){
|
||||
static const uint32_t MATCH_ONE = (uint32_t)'_';
|
||||
static const uint32_t MATCH_ALL = (uint32_t)'%';
|
||||
|
||||
int prevEscape = 0; /* True if the previous character was uEsc */
|
||||
|
||||
while( 1 ){
|
||||
|
||||
/* Read (and consume) the next character from the input pattern. */
|
||||
uint32_t uPattern;
|
||||
SQLITE_ICU_READ_UTF8(zPattern, uPattern);
|
||||
if( uPattern==0 ) break;
|
||||
|
||||
/* There are now 4 possibilities:
|
||||
**
|
||||
** 1. uPattern is an unescaped match-all character "%",
|
||||
** 2. uPattern is an unescaped match-one character "_",
|
||||
** 3. uPattern is an unescaped escape character, or
|
||||
** 4. uPattern is to be handled as an ordinary character
|
||||
*/
|
||||
if( uPattern==MATCH_ALL && !prevEscape && uPattern!=(uint32_t)uEsc ){
|
||||
/* Case 1. */
|
||||
uint8_t c;
|
||||
|
||||
/* Skip any MATCH_ALL or MATCH_ONE characters that follow a
|
||||
** MATCH_ALL. For each MATCH_ONE, skip one character in the
|
||||
** test string.
|
||||
*/
|
||||
while( (c=*zPattern) == MATCH_ALL || c == MATCH_ONE ){
|
||||
if( c==MATCH_ONE ){
|
||||
if( *zString==0 ) return 0;
|
||||
SQLITE_ICU_SKIP_UTF8(zString);
|
||||
}
|
||||
zPattern++;
|
||||
}
|
||||
|
||||
if( *zPattern==0 ) return 1;
|
||||
|
||||
while( *zString ){
|
||||
if( icuLikeCompare(zPattern, zString, uEsc) ){
|
||||
return 1;
|
||||
}
|
||||
SQLITE_ICU_SKIP_UTF8(zString);
|
||||
}
|
||||
return 0;
|
||||
|
||||
}else if( uPattern==MATCH_ONE && !prevEscape && uPattern!=(uint32_t)uEsc ){
|
||||
/* Case 2. */
|
||||
if( *zString==0 ) return 0;
|
||||
SQLITE_ICU_SKIP_UTF8(zString);
|
||||
|
||||
}else if( uPattern==(uint32_t)uEsc && !prevEscape ){
|
||||
/* Case 3. */
|
||||
prevEscape = 1;
|
||||
|
||||
}else{
|
||||
/* Case 4. */
|
||||
uint32_t uString;
|
||||
SQLITE_ICU_READ_UTF8(zString, uString);
|
||||
uString = (uint32_t)u_foldCase((UChar32)uString, U_FOLD_CASE_DEFAULT);
|
||||
uPattern = (uint32_t)u_foldCase((UChar32)uPattern, U_FOLD_CASE_DEFAULT);
|
||||
if( uString!=uPattern ){
|
||||
return 0;
|
||||
}
|
||||
prevEscape = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return *zString==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the like() SQL function. This function implements
|
||||
** the build-in LIKE operator. The first argument to the function is the
|
||||
** pattern and the second argument is the string. So, the SQL statements:
|
||||
**
|
||||
** A LIKE B
|
||||
**
|
||||
** is implemented as like(B, A). If there is an escape character E,
|
||||
**
|
||||
** A LIKE B ESCAPE E
|
||||
**
|
||||
** is mapped to like(B, A, E).
|
||||
*/
|
||||
static void icuLikeFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const unsigned char *zA = sqlite3_value_text(argv[0]);
|
||||
const unsigned char *zB = sqlite3_value_text(argv[1]);
|
||||
UChar32 uEsc = 0;
|
||||
|
||||
/* Limit the length of the LIKE or GLOB pattern to avoid problems
|
||||
** of deep recursion and N*N behavior in patternCompare().
|
||||
*/
|
||||
if( sqlite3_value_bytes(argv[0])>SQLITE_MAX_LIKE_PATTERN_LENGTH ){
|
||||
sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if( argc==3 ){
|
||||
/* The escape character string must consist of a single UTF-8 character.
|
||||
** Otherwise, return an error.
|
||||
*/
|
||||
int nE= sqlite3_value_bytes(argv[2]);
|
||||
const unsigned char *zE = sqlite3_value_text(argv[2]);
|
||||
int i = 0;
|
||||
if( zE==0 ) return;
|
||||
U8_NEXT(zE, i, nE, uEsc);
|
||||
if( i!=nE){
|
||||
sqlite3_result_error(context,
|
||||
"ESCAPE expression must be a single character", -1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( zA && zB ){
|
||||
sqlite3_result_int(context, icuLikeCompare(zA, zB, uEsc));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Function to delete compiled regexp objects. Registered as
|
||||
** a destructor function with sqlite3_set_auxdata().
|
||||
*/
|
||||
static void icuRegexpDelete(void *p){
|
||||
URegularExpression *pExpr = (URegularExpression *)p;
|
||||
uregex_close(pExpr);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of SQLite REGEXP operator. This scalar function takes
|
||||
** two arguments. The first is a regular expression pattern to compile
|
||||
** the second is a string to match against that pattern. If either
|
||||
** argument is an SQL NULL, then NULL Is returned. Otherwise, the result
|
||||
** is 1 if the string matches the pattern, or 0 otherwise.
|
||||
**
|
||||
** SQLite maps the regexp() function to the regexp() operator such
|
||||
** that the following two are equivalent:
|
||||
**
|
||||
** zString REGEXP zPattern
|
||||
** regexp(zPattern, zString)
|
||||
**
|
||||
** Uses the following ICU regexp APIs:
|
||||
**
|
||||
** uregex_open()
|
||||
** uregex_matches()
|
||||
** uregex_close()
|
||||
*/
|
||||
static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
URegularExpression *pExpr;
|
||||
UBool res;
|
||||
const UChar *zString = sqlite3_value_text16(apArg[1]);
|
||||
|
||||
(void)nArg; /* Unused parameter */
|
||||
|
||||
/* If the left hand side of the regexp operator is NULL,
|
||||
** then the result is also NULL.
|
||||
*/
|
||||
if( !zString ){
|
||||
return;
|
||||
}
|
||||
|
||||
pExpr = sqlite3_get_auxdata(p, 0);
|
||||
if( !pExpr ){
|
||||
const UChar *zPattern = sqlite3_value_text16(apArg[0]);
|
||||
if( !zPattern ){
|
||||
return;
|
||||
}
|
||||
pExpr = uregex_open(zPattern, -1, 0, 0, &status);
|
||||
|
||||
if( U_SUCCESS(status) ){
|
||||
sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete);
|
||||
pExpr = sqlite3_get_auxdata(p, 0);
|
||||
}
|
||||
if( !pExpr ){
|
||||
icuFunctionError(p, "uregex_open", status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Configure the text that the regular expression operates on. */
|
||||
uregex_setText(pExpr, zString, -1, &status);
|
||||
if( !U_SUCCESS(status) ){
|
||||
icuFunctionError(p, "uregex_setText", status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Attempt the match */
|
||||
res = uregex_matches(pExpr, 0, &status);
|
||||
if( !U_SUCCESS(status) ){
|
||||
icuFunctionError(p, "uregex_matches", status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the text that the regular expression operates on to a NULL
|
||||
** pointer. This is not really necessary, but it is tidier than
|
||||
** leaving the regular expression object configured with an invalid
|
||||
** pointer after this function returns.
|
||||
*/
|
||||
uregex_setText(pExpr, 0, 0, &status);
|
||||
|
||||
/* Return 1 or 0. */
|
||||
sqlite3_result_int(p, res ? 1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementations of scalar functions for case mapping - upper() and
|
||||
** lower(). Function upper() converts its input to upper-case (ABC).
|
||||
** Function lower() converts to lower-case (abc).
|
||||
**
|
||||
** ICU provides two types of case mapping, "general" case mapping and
|
||||
** "language specific". Refer to ICU documentation for the differences
|
||||
** between the two.
|
||||
**
|
||||
** To utilise "general" case mapping, the upper() or lower() scalar
|
||||
** functions are invoked with one argument:
|
||||
**
|
||||
** upper('ABC') -> 'abc'
|
||||
** lower('abc') -> 'ABC'
|
||||
**
|
||||
** To access ICU "language specific" case mapping, upper() or lower()
|
||||
** should be invoked with two arguments. The second argument is the name
|
||||
** of the locale to use. Passing an empty string ("") or SQL NULL value
|
||||
** as the second argument is the same as invoking the 1 argument version
|
||||
** of upper() or lower().
|
||||
**
|
||||
** lower('I', 'en_us') -> 'i'
|
||||
** lower('I', 'tr_tr') -> '\u131' (small dotless i)
|
||||
**
|
||||
** http://www.icu-project.org/userguide/posix.html#case_mappings
|
||||
*/
|
||||
static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
||||
const UChar *zInput; /* Pointer to input string */
|
||||
UChar *zOutput = 0; /* Pointer to output buffer */
|
||||
int nInput; /* Size of utf-16 input string in bytes */
|
||||
int nOut; /* Size of output buffer in bytes */
|
||||
int cnt;
|
||||
int bToUpper; /* True for toupper(), false for tolower() */
|
||||
UErrorCode status;
|
||||
const char *zLocale = 0;
|
||||
|
||||
assert(nArg==1 || nArg==2);
|
||||
bToUpper = (sqlite3_user_data(p)!=0);
|
||||
if( nArg==2 ){
|
||||
zLocale = (const char *)sqlite3_value_text(apArg[1]);
|
||||
}
|
||||
|
||||
zInput = sqlite3_value_text16(apArg[0]);
|
||||
if( !zInput ){
|
||||
return;
|
||||
}
|
||||
nOut = nInput = sqlite3_value_bytes16(apArg[0]);
|
||||
if( nOut==0 ){
|
||||
sqlite3_result_text16(p, "", 0, SQLITE_STATIC);
|
||||
return;
|
||||
}
|
||||
|
||||
for(cnt=0; cnt<2; cnt++){
|
||||
UChar *zNew = sqlite3_realloc(zOutput, nOut);
|
||||
if( zNew==0 ){
|
||||
sqlite3_free(zOutput);
|
||||
sqlite3_result_error_nomem(p);
|
||||
return;
|
||||
}
|
||||
zOutput = zNew;
|
||||
status = U_ZERO_ERROR;
|
||||
if( bToUpper ){
|
||||
nOut = 2*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
|
||||
}else{
|
||||
nOut = 2*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
|
||||
}
|
||||
|
||||
if( U_SUCCESS(status) ){
|
||||
sqlite3_result_text16(p, zOutput, nOut, xFree);
|
||||
}else if( status==U_BUFFER_OVERFLOW_ERROR ){
|
||||
assert( cnt==0 );
|
||||
continue;
|
||||
}else{
|
||||
icuFunctionError(p, bToUpper ? "u_strToUpper" : "u_strToLower", status);
|
||||
}
|
||||
return;
|
||||
}
|
||||
assert( 0 ); /* Unreachable */
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
|
||||
|
||||
/*
|
||||
** Collation sequence destructor function. The pCtx argument points to
|
||||
** a UCollator structure previously allocated using ucol_open().
|
||||
*/
|
||||
static void icuCollationDel(void *pCtx){
|
||||
UCollator *p = (UCollator *)pCtx;
|
||||
ucol_close(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Collation sequence comparison function. The pCtx argument points to
|
||||
** a UCollator structure previously allocated using ucol_open().
|
||||
*/
|
||||
static int icuCollationColl(
|
||||
void *pCtx,
|
||||
int nLeft,
|
||||
const void *zLeft,
|
||||
int nRight,
|
||||
const void *zRight
|
||||
){
|
||||
UCollationResult res;
|
||||
UCollator *p = (UCollator *)pCtx;
|
||||
res = ucol_strcoll(p, (UChar *)zLeft, nLeft/2, (UChar *)zRight, nRight/2);
|
||||
switch( res ){
|
||||
case UCOL_LESS: return -1;
|
||||
case UCOL_GREATER: return +1;
|
||||
case UCOL_EQUAL: return 0;
|
||||
}
|
||||
assert(!"Unexpected return value from ucol_strcoll()");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the scalar function icu_load_collation().
|
||||
**
|
||||
** This scalar function is used to add ICU collation based collation
|
||||
** types to an SQLite database connection. It is intended to be called
|
||||
** as follows:
|
||||
**
|
||||
** SELECT icu_load_collation(<locale>, <collation-name>);
|
||||
**
|
||||
** Where <locale> is a string containing an ICU locale identifier (i.e.
|
||||
** "en_AU", "tr_TR" etc.) and <collation-name> is the name of the
|
||||
** collation sequence to create.
|
||||
*/
|
||||
static void icuLoadCollation(
|
||||
sqlite3_context *p,
|
||||
int nArg,
|
||||
sqlite3_value **apArg
|
||||
){
|
||||
sqlite3 *db = (sqlite3 *)sqlite3_user_data(p);
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
const char *zLocale; /* Locale identifier - (eg. "jp_JP") */
|
||||
const char *zName; /* SQL Collation sequence name (eg. "japanese") */
|
||||
UCollator *pUCollator; /* ICU library collation object */
|
||||
int rc; /* Return code from sqlite3_create_collation_x() */
|
||||
|
||||
assert(nArg==2 || nArg==3);
|
||||
(void)nArg; /* Unused parameter */
|
||||
zLocale = (const char *)sqlite3_value_text(apArg[0]);
|
||||
zName = (const char *)sqlite3_value_text(apArg[1]);
|
||||
|
||||
if( !zLocale || !zName ){
|
||||
return;
|
||||
}
|
||||
|
||||
pUCollator = ucol_open(zLocale, &status);
|
||||
if( !U_SUCCESS(status) ){
|
||||
icuFunctionError(p, "ucol_open", status);
|
||||
return;
|
||||
}
|
||||
assert(p);
|
||||
if(nArg==3){
|
||||
const char *zOption = (const char*)sqlite3_value_text(apArg[2]);
|
||||
static const struct {
|
||||
const char *zName;
|
||||
UColAttributeValue val;
|
||||
} aStrength[] = {
|
||||
{ "PRIMARY", UCOL_PRIMARY },
|
||||
{ "SECONDARY", UCOL_SECONDARY },
|
||||
{ "TERTIARY", UCOL_TERTIARY },
|
||||
{ "DEFAULT", UCOL_DEFAULT_STRENGTH },
|
||||
{ "QUARTERNARY", UCOL_QUATERNARY },
|
||||
{ "IDENTICAL", UCOL_IDENTICAL },
|
||||
};
|
||||
unsigned int i;
|
||||
for(i=0; i<sizeof(aStrength)/sizeof(aStrength[0]); i++){
|
||||
if( sqlite3_stricmp(zOption,aStrength[i].zName)==0 ){
|
||||
ucol_setStrength(pUCollator, aStrength[i].val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( i>=sizeof(aStrength)/sizeof(aStrength[0]) ){
|
||||
sqlite3_str *pStr = sqlite3_str_new(sqlite3_context_db_handle(p));
|
||||
sqlite3_str_appendf(pStr,
|
||||
"unknown collation strength \"%s\" - should be one of:",
|
||||
zOption);
|
||||
for(i=0; i<sizeof(aStrength)/sizeof(aStrength[0]); i++){
|
||||
sqlite3_str_appendf(pStr, " %s", aStrength[i].zName);
|
||||
}
|
||||
sqlite3_result_error(p, sqlite3_str_value(pStr), -1);
|
||||
sqlite3_free(sqlite3_str_finish(pStr));
|
||||
return;
|
||||
}
|
||||
}
|
||||
rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator,
|
||||
icuCollationColl, icuCollationDel
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
ucol_close(pUCollator);
|
||||
sqlite3_result_error(p, "Error registering collation function", -1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the ICU extension functions with database db.
|
||||
*/
|
||||
int sqlite3IcuInit(sqlite3 *db){
|
||||
# define SQLITEICU_EXTRAFLAGS (SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS)
|
||||
static const struct IcuScalar {
|
||||
const char *zName; /* Function name */
|
||||
unsigned char nArg; /* Number of arguments */
|
||||
unsigned int enc; /* Optimal text encoding */
|
||||
unsigned char iContext; /* sqlite3_user_data() context */
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} scalars[] = {
|
||||
{"icu_load_collation",2,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
|
||||
{"icu_load_collation",3,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
|
||||
{"regexp", 2, SQLITE_ANY|SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc},
|
||||
{"lower", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
|
||||
{"lower", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
|
||||
{"like", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
|
||||
{"like", 3, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
|
||||
};
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
|
||||
const struct IcuScalar *p = &scalars[i];
|
||||
rc = sqlite3_create_function(
|
||||
db, p->zName, p->nArg, p->enc,
|
||||
p->iContext ? (void*)db : (void*)0,
|
||||
p->xFunc, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if !SQLITE_CORE
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_icu_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
SQLITE_EXTENSION_INIT2(pApi)
|
||||
return sqlite3IcuInit(db);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
** 2008 May 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 header file is used by programs that want to link against the
|
||||
** ICU extension. All it does is declare the sqlite3IcuInit() interface.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int sqlite3IcuInit(sqlite3 *db);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# Define the source files by searching the current directory
|
||||
SRCS := $(wildcard *.c)
|
||||
|
||||
# Define the output files by replacing .c with .so
|
||||
OBJS := $(SRCS:.c=.so)
|
||||
|
||||
# Subset of source files that need to be linked against zlib
|
||||
ZLIB_SRCS := compress.c
|
||||
ZLIB_OBJS := $(ZLIB_SRCS:.c=.so)
|
||||
|
||||
all: $(OBJS)
|
||||
|
||||
# Rule to compile and link .c files that require zlib
|
||||
$(ZLIB_OBJS): %.so: %.c
|
||||
gcc -g -fPIC -I../. -shared $< -o $@ -lz
|
||||
|
||||
# Rule to compile and link all other .c files
|
||||
$(filter-out $(ZLIB_OBJS),$(OBJS)): %.so: %.c
|
||||
gcc -g -fPIC -I../. -shared $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS)
|
||||
|
||||
.PHONY: all clean
|
||||
@@ -1,60 +0,0 @@
|
||||
## Miscellaneous Extensions
|
||||
|
||||
This folder contains a collection of smaller loadable extensions.
|
||||
See <https://www.sqlite.org/loadext.html> for instructions on how
|
||||
to compile and use loadable extensions.
|
||||
Each extension in this folder is implemented in a single file of C code.
|
||||
|
||||
Each source file contains a description in its header comment. See the
|
||||
header comments for details about each extension. Additional notes are
|
||||
as follows:
|
||||
|
||||
* **carray.c** — This module implements the
|
||||
[carray](https://www.sqlite.org/carray.html) table-valued function.
|
||||
It is a good example of how to go about implementing a custom
|
||||
[table-valued function](https://www.sqlite.org/vtab.html#tabfunc2).
|
||||
|
||||
* **csv.c** — A [virtual table](https://sqlite.org/vtab.html)
|
||||
for reading
|
||||
[Comma-Separated-Value (CSV) files](https://en.wikipedia.org/wiki/Comma-separated_values).
|
||||
|
||||
* **dbdump.c** — This is not actually a loadable extension, but
|
||||
rather a library that implements an approximate equivalent to the
|
||||
".dump" command of the
|
||||
[command-line shell](https://www.sqlite.org/cli.html).
|
||||
|
||||
* **json1.c** — Various SQL functions and table-valued functions
|
||||
for processing JSON. This extension is already built into the
|
||||
[SQLite amalgamation](https://sqlite.org/amalgamation.html). See
|
||||
<https://sqlite.org/json1.html> for additional information.
|
||||
|
||||
* **memvfs.c** — This file implements a custom
|
||||
[VFS](https://www.sqlite.org/vfs.html) that stores an entire database
|
||||
file in a single block of RAM. It serves as a good example of how
|
||||
to implement a simple custom VFS.
|
||||
|
||||
* **rot13.c** — This file implements the very simple rot13()
|
||||
substitution function. This file makes a good template for implementing
|
||||
new custom SQL functions for SQLite.
|
||||
|
||||
* **series.c** — This is an implementation of the
|
||||
"generate_series" [virtual table](https://www.sqlite.org/vtab.html).
|
||||
It can make a good template for new custom virtual table implementations.
|
||||
|
||||
* **shathree.c** — An implementation of the sha3() and
|
||||
sha3_query() SQL functions. The file is named "shathree.c" instead
|
||||
of "sha3.c" because the default entry point names in SQLite are based
|
||||
on the source filename with digits removed, so if we used the name
|
||||
"sha3.c" then the entry point would conflict with the prior "sha1.c"
|
||||
extension.
|
||||
|
||||
* **unionvtab.c** — Implementation of the unionvtab and
|
||||
[swarmvtab](https://sqlite.org/swarmvtab.html) virtual tables.
|
||||
These virtual tables allow a single
|
||||
large table to be spread out across multiple database files. In the
|
||||
case of swarmvtab, the individual database files can be attached on
|
||||
demand.
|
||||
|
||||
* **zipfile.c** — A [virtual table](https://sqlite.org/vtab.html)
|
||||
that can read and write a
|
||||
[ZIP archive](https://en.wikipedia.org/wiki/Zip_%28file_format%29).
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
** 2017-04-16
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file implements a run-time loadable extension to SQLite that
|
||||
** registers a sqlite3_collation_needed() callback to register a fake
|
||||
** collating function for any unknown collating sequence. The fake
|
||||
** collating function works like BINARY.
|
||||
**
|
||||
** This extension can be used to load schemas that contain one or more
|
||||
** unknown collating sequences.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
|
||||
static int anyCollFunc(
|
||||
void *NotUsed,
|
||||
int nKey1, const void *pKey1,
|
||||
int nKey2, const void *pKey2
|
||||
){
|
||||
int rc, n;
|
||||
n = nKey1<nKey2 ? nKey1 : nKey2;
|
||||
rc = memcmp(pKey1, pKey2, n);
|
||||
if( rc==0 ) rc = nKey1 - nKey2;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void anyCollNeeded(
|
||||
void *NotUsed,
|
||||
sqlite3 *db,
|
||||
int eTextRep,
|
||||
const char *zCollName
|
||||
){
|
||||
sqlite3_create_collation(db, zCollName, eTextRep, 0, anyCollFunc);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_anycollseq_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
rc = sqlite3_collation_needed(db, 0, anyCollNeeded);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
/*
|
||||
** 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 0x80 /* pad character */
|
||||
#define WS 0x81 /* whitespace */
|
||||
#define ND 0x82 /* 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))<0x80)? (u8)(b64DigitValues[(u8)(c)]) : 0x80)
|
||||
#define IS_BX_DIGIT(bdp) (((u8)(bdp))<0x80)
|
||||
#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))&0x3f);
|
||||
pOut[2] = BX_NUMERAL(((pIn[1]&0xf)<<2)|(pIn[2]>>6));
|
||||
pOut[3] = BX_NUMERAL(pIn[2]&0x3f);
|
||||
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 & 0x3f)) : 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 = 0L;
|
||||
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;
|
||||
case WS:
|
||||
/* Treat whitespace as pad and terminate this group.*/
|
||||
nti = nac;
|
||||
deliberate_fall_through;
|
||||
case PC:
|
||||
bdp = 0;
|
||||
--nbo;
|
||||
deliberate_fall_through;
|
||||
default: /* bdp is the digit value. */
|
||||
qv = qv<<6 | bdp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch( nbo ){
|
||||
case 3:
|
||||
pOut[2] = (qv) & 0xff;
|
||||
case 2:
|
||||
pOut[1] = (qv>>8) & 0xff;
|
||||
case 1:
|
||||
pOut[0] = (qv>>16) & 0xff;
|
||||
}
|
||||
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[]){
|
||||
int 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;
|
||||
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_malloc(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_malloc(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
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_base_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. */
|
||||
@@ -1,450 +0,0 @@
|
||||
/*
|
||||
** 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 0xff;
|
||||
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/85UL);
|
||||
unsigned char dv = qbv - 85UL*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 = 0L;
|
||||
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)&0xff;
|
||||
case 3:
|
||||
*pOut++ = (qv >> 16)&0xff;
|
||||
case 2:
|
||||
*pOut++ = (qv >> 8)&0xff;
|
||||
case 1:
|
||||
*pOut++ = qv&0xff;
|
||||
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[]){
|
||||
int 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_malloc(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_malloc(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
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_base_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
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
** 2022-11-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 source allows multiple SQLite extensions to be either: combined
|
||||
** into a single runtime-loadable library; or built into the SQLite shell
|
||||
** using a preprocessing convention set by src/shell.c.in (and shell.c).
|
||||
**
|
||||
** Presently, it combines the base64.c and base85.c extensions. However,
|
||||
** it can be used as a template for other combinations.
|
||||
**
|
||||
** Example usages:
|
||||
**
|
||||
** - Build a runtime-loadable extension from SQLite checkout directory:
|
||||
** *Nix, OSX: gcc -O2 -shared -I. -fPIC -o basexx.so ext/misc/basexx.c
|
||||
** Win32: cl /Os -I. ext/misc/basexx.c -link -dll -out:basexx.dll
|
||||
**
|
||||
** - Incorporate as built-in in sqlite3 shell:
|
||||
** *Nix, OSX with gcc on a like platform:
|
||||
** export mop1=-DSQLITE_SHELL_EXTSRC=ext/misc/basexx.c
|
||||
** export mop2=-DSQLITE_SHELL_EXTFUNCS=BASEXX
|
||||
** make sqlite3 "OPTS=$mop1 $mop2"
|
||||
** Win32 with Microsoft toolset on Windows:
|
||||
** set mop1=-DSQLITE_SHELL_EXTSRC=ext/misc/basexx.c
|
||||
** set mop2=-DSQLITE_SHELL_EXTFUNCS=BASEXX
|
||||
** set mops="OPTS=%mop1% %mop2%"
|
||||
** nmake -f Makefile.msc sqlite3.exe %mops%
|
||||
*/
|
||||
|
||||
#ifndef SQLITE_SHELL_EXTFUNCS /* Guard for #include as built-in extension. */
|
||||
# include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1;
|
||||
#endif
|
||||
|
||||
static void init_api_ptr(const sqlite3_api_routines *pApi){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
}
|
||||
|
||||
#undef SQLITE_EXTENSION_INIT1
|
||||
#define SQLITE_EXTENSION_INIT1 /* */
|
||||
#undef SQLITE_EXTENSION_INIT2
|
||||
#define SQLITE_EXTENSION_INIT2(v) (void)v
|
||||
|
||||
typedef unsigned char u8;
|
||||
#define U8_TYPEDEF
|
||||
|
||||
/* These next 2 undef's are only needed because the entry point names
|
||||
* collide when formulated per the rules stated for loadable extension
|
||||
* entry point names that will be deduced from the file basenames.
|
||||
*/
|
||||
#undef sqlite3_base_init
|
||||
#define sqlite3_base_init sqlite3_base64_init
|
||||
#include "base64.c"
|
||||
|
||||
#undef sqlite3_base_init
|
||||
#define sqlite3_base_init sqlite3_base85_init
|
||||
#include "base85.c"
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_basexx_init(sqlite3 *db, char **pzErr,
|
||||
const sqlite3_api_routines *pApi){
|
||||
int rc1;
|
||||
int rc2;
|
||||
|
||||
init_api_ptr(pApi);
|
||||
rc1 = BASE64_INIT(db);
|
||||
rc2 = BASE85_INIT(db);
|
||||
|
||||
if( rc1==SQLITE_OK && rc2==SQLITE_OK ){
|
||||
BASE64_EXPOSE(db, pzErr);
|
||||
BASE64_EXPOSE(db, pzErr);
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
# define BASEXX_INIT(db) sqlite3_basexx_init(db, 0, 0)
|
||||
# define BASEXX_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */
|
||||
@@ -1,430 +0,0 @@
|
||||
/*
|
||||
** 2017-10-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 file contains an implementation of the "sqlite_btreeinfo" virtual table.
|
||||
**
|
||||
** The sqlite_btreeinfo virtual table is a read-only eponymous-only virtual
|
||||
** table that shows information about all btrees in an SQLite database file.
|
||||
** The schema is like this:
|
||||
**
|
||||
** CREATE TABLE sqlite_btreeinfo(
|
||||
** type TEXT, -- "table" or "index"
|
||||
** name TEXT, -- Name of table or index for this btree.
|
||||
** tbl_name TEXT, -- Associated table
|
||||
** rootpage INT, -- The root page of the btree
|
||||
** sql TEXT, -- SQL for this btree - from sqlite_schema
|
||||
** hasRowid BOOLEAN, -- True if the btree has a rowid
|
||||
** nEntry INT, -- Estimated number of entries
|
||||
** nPage INT, -- Estimated number of pages
|
||||
** depth INT, -- Depth of the btree
|
||||
** szPage INT, -- Size of each page in bytes
|
||||
** zSchema TEXT HIDDEN -- The schema to which this btree belongs
|
||||
** );
|
||||
**
|
||||
** The first 5 fields are taken directly from the sqlite_schema table.
|
||||
** Considering only the first 5 fields, the only difference between
|
||||
** this virtual table and the sqlite_schema table is that this virtual
|
||||
** table omits all entries that have a 0 or NULL rowid - in other words
|
||||
** it omits triggers and views.
|
||||
**
|
||||
** The value added by this table comes in the next 5 fields.
|
||||
**
|
||||
** Note that nEntry and nPage are *estimated*. They are computed doing
|
||||
** a single search from the root to a leaf, counting the number of cells
|
||||
** at each level, and assuming that unvisited pages have a similar number
|
||||
** of cells.
|
||||
**
|
||||
** The sqlite_dbpage virtual table must be available for this virtual table
|
||||
** to operate.
|
||||
**
|
||||
** USAGE EXAMPLES:
|
||||
**
|
||||
** Show the table btrees in a schema order with the tables with the most
|
||||
** rows occuring first:
|
||||
**
|
||||
** SELECT name, nEntry
|
||||
** FROM sqlite_btreeinfo
|
||||
** WHERE type='table'
|
||||
** ORDER BY nEntry DESC, name;
|
||||
**
|
||||
** Show the names of all WITHOUT ROWID tables:
|
||||
**
|
||||
** SELECT name FROM sqlite_btreeinfo
|
||||
** WHERE type='table' AND NOT hasRowid;
|
||||
*/
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include "sqlite3ext.h"
|
||||
#endif
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* Columns available in this virtual table */
|
||||
#define BINFO_COLUMN_TYPE 0
|
||||
#define BINFO_COLUMN_NAME 1
|
||||
#define BINFO_COLUMN_TBL_NAME 2
|
||||
#define BINFO_COLUMN_ROOTPAGE 3
|
||||
#define BINFO_COLUMN_SQL 4
|
||||
#define BINFO_COLUMN_HASROWID 5
|
||||
#define BINFO_COLUMN_NENTRY 6
|
||||
#define BINFO_COLUMN_NPAGE 7
|
||||
#define BINFO_COLUMN_DEPTH 8
|
||||
#define BINFO_COLUMN_SZPAGE 9
|
||||
#define BINFO_COLUMN_SCHEMA 10
|
||||
|
||||
/* Forward declarations */
|
||||
typedef struct BinfoTable BinfoTable;
|
||||
typedef struct BinfoCursor BinfoCursor;
|
||||
|
||||
/* A cursor for the sqlite_btreeinfo table */
|
||||
struct BinfoCursor {
|
||||
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
||||
sqlite3_stmt *pStmt; /* Query against sqlite_schema */
|
||||
int rc; /* Result of previous sqlite_step() call */
|
||||
int hasRowid; /* hasRowid value. Negative if unknown. */
|
||||
sqlite3_int64 nEntry; /* nEntry value */
|
||||
int nPage; /* nPage value */
|
||||
int depth; /* depth value */
|
||||
int szPage; /* size of a btree page. 0 if unknown */
|
||||
char *zSchema; /* Schema being interrogated */
|
||||
};
|
||||
|
||||
/* The sqlite_btreeinfo table */
|
||||
struct BinfoTable {
|
||||
sqlite3_vtab base; /* Base class. Must be first */
|
||||
sqlite3 *db; /* The databse connection */
|
||||
};
|
||||
|
||||
/*
|
||||
** Connect to the sqlite_btreeinfo virtual table.
|
||||
*/
|
||||
static int binfoConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
BinfoTable *pTab = 0;
|
||||
int rc = SQLITE_OK;
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(\n"
|
||||
" type TEXT,\n"
|
||||
" name TEXT,\n"
|
||||
" tbl_name TEXT,\n"
|
||||
" rootpage INT,\n"
|
||||
" sql TEXT,\n"
|
||||
" hasRowid BOOLEAN,\n"
|
||||
" nEntry INT,\n"
|
||||
" nPage INT,\n"
|
||||
" depth INT,\n"
|
||||
" szPage INT,\n"
|
||||
" zSchema TEXT HIDDEN\n"
|
||||
")");
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab = (BinfoTable *)sqlite3_malloc64(sizeof(BinfoTable));
|
||||
if( pTab==0 ) rc = SQLITE_NOMEM;
|
||||
}
|
||||
assert( rc==SQLITE_OK || pTab==0 );
|
||||
if( pTab ){
|
||||
pTab->db = db;
|
||||
}
|
||||
*ppVtab = (sqlite3_vtab*)pTab;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Disconnect from or destroy a btreeinfo virtual table.
|
||||
*/
|
||||
static int binfoDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** idxNum:
|
||||
**
|
||||
** 0 Use "main" for the schema
|
||||
** 1 Schema identified by parameter ?1
|
||||
*/
|
||||
static int binfoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
int i;
|
||||
pIdxInfo->estimatedCost = 10000.0; /* Cost estimate */
|
||||
pIdxInfo->estimatedRows = 100;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++){
|
||||
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
|
||||
if( p->usable
|
||||
&& p->iColumn==BINFO_COLUMN_SCHEMA
|
||||
&& p->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
){
|
||||
pIdxInfo->estimatedCost = 1000.0;
|
||||
pIdxInfo->idxNum = 1;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new btreeinfo cursor.
|
||||
*/
|
||||
static int binfoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
BinfoCursor *pCsr;
|
||||
|
||||
pCsr = (BinfoCursor *)sqlite3_malloc64(sizeof(BinfoCursor));
|
||||
if( pCsr==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pCsr, 0, sizeof(BinfoCursor));
|
||||
pCsr->base.pVtab = pVTab;
|
||||
}
|
||||
|
||||
*ppCursor = (sqlite3_vtab_cursor *)pCsr;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a btreeinfo cursor.
|
||||
*/
|
||||
static int binfoClose(sqlite3_vtab_cursor *pCursor){
|
||||
BinfoCursor *pCsr = (BinfoCursor *)pCursor;
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
sqlite3_free(pCsr->zSchema);
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Move a btreeinfo cursor to the next entry in the file.
|
||||
*/
|
||||
static int binfoNext(sqlite3_vtab_cursor *pCursor){
|
||||
BinfoCursor *pCsr = (BinfoCursor *)pCursor;
|
||||
pCsr->rc = sqlite3_step(pCsr->pStmt);
|
||||
pCsr->hasRowid = -1;
|
||||
return pCsr->rc==SQLITE_ERROR ? SQLITE_ERROR : SQLITE_OK;
|
||||
}
|
||||
|
||||
/* We have reached EOF if previous sqlite3_step() returned
|
||||
** anything other than SQLITE_ROW;
|
||||
*/
|
||||
static int binfoEof(sqlite3_vtab_cursor *pCursor){
|
||||
BinfoCursor *pCsr = (BinfoCursor *)pCursor;
|
||||
return pCsr->rc!=SQLITE_ROW;
|
||||
}
|
||||
|
||||
/* Position a cursor back to the beginning.
|
||||
*/
|
||||
static int binfoFilter(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
BinfoCursor *pCsr = (BinfoCursor *)pCursor;
|
||||
BinfoTable *pTab = (BinfoTable *)pCursor->pVtab;
|
||||
char *zSql;
|
||||
int rc;
|
||||
|
||||
sqlite3_free(pCsr->zSchema);
|
||||
if( idxNum==1 && sqlite3_value_type(argv[0])!=SQLITE_NULL ){
|
||||
pCsr->zSchema = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
|
||||
}else{
|
||||
pCsr->zSchema = sqlite3_mprintf("main");
|
||||
}
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT 0, 'table','sqlite_schema','sqlite_schema',1,NULL "
|
||||
"UNION ALL "
|
||||
"SELECT rowid, type, name, tbl_name, rootpage, sql"
|
||||
" FROM \"%w\".sqlite_schema WHERE rootpage>=1",
|
||||
pCsr->zSchema);
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
pCsr->pStmt = 0;
|
||||
pCsr->hasRowid = -1;
|
||||
rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = binfoNext(pCursor);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Decode big-endian integers */
|
||||
static unsigned int get_uint16(unsigned char *a){
|
||||
return (a[0]<<8)|a[1];
|
||||
}
|
||||
static unsigned int get_uint32(unsigned char *a){
|
||||
return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|a[3];
|
||||
}
|
||||
|
||||
/* Examine the b-tree rooted at pgno and estimate its size.
|
||||
** Return non-zero if anything goes wrong.
|
||||
*/
|
||||
static int binfoCompute(sqlite3 *db, int pgno, BinfoCursor *pCsr){
|
||||
sqlite3_int64 nEntry = 1;
|
||||
int nPage = 1;
|
||||
unsigned char *aData;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int rc = SQLITE_OK;
|
||||
int pgsz = 0;
|
||||
int nCell;
|
||||
int iCell;
|
||||
|
||||
rc = sqlite3_prepare_v2(db,
|
||||
"SELECT data FROM sqlite_dbpage('main') WHERE pgno=?1", -1,
|
||||
&pStmt, 0);
|
||||
if( rc ) return rc;
|
||||
pCsr->depth = 1;
|
||||
while(1){
|
||||
sqlite3_bind_int(pStmt, 1, pgno);
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc!=SQLITE_ROW ){
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
pCsr->szPage = pgsz = sqlite3_column_bytes(pStmt, 0);
|
||||
aData = (unsigned char*)sqlite3_column_blob(pStmt, 0);
|
||||
if( aData==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
break;
|
||||
}
|
||||
if( pgno==1 ){
|
||||
aData += 100;
|
||||
pgsz -= 100;
|
||||
}
|
||||
pCsr->hasRowid = aData[0]!=2 && aData[0]!=10;
|
||||
nCell = get_uint16(aData+3);
|
||||
nEntry *= (nCell+1);
|
||||
if( aData[0]==10 || aData[0]==13 ) break;
|
||||
nPage *= (nCell+1);
|
||||
if( nCell<=1 ){
|
||||
pgno = get_uint32(aData+8);
|
||||
}else{
|
||||
iCell = get_uint16(aData+12+2*(nCell/2));
|
||||
if( pgno==1 ) iCell -= 100;
|
||||
if( iCell<=12 || iCell>=pgsz-4 ){
|
||||
rc = SQLITE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
pgno = get_uint32(aData+iCell);
|
||||
}
|
||||
pCsr->depth++;
|
||||
sqlite3_reset(pStmt);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
pCsr->nPage = nPage;
|
||||
pCsr->nEntry = nEntry;
|
||||
if( rc==SQLITE_ROW ) rc = SQLITE_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Return a column for the sqlite_btreeinfo table */
|
||||
static int binfoColumn(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
sqlite3_context *ctx,
|
||||
int i
|
||||
){
|
||||
BinfoCursor *pCsr = (BinfoCursor *)pCursor;
|
||||
if( i>=BINFO_COLUMN_HASROWID && i<=BINFO_COLUMN_SZPAGE && pCsr->hasRowid<0 ){
|
||||
int pgno = sqlite3_column_int(pCsr->pStmt, BINFO_COLUMN_ROOTPAGE+1);
|
||||
sqlite3 *db = sqlite3_context_db_handle(ctx);
|
||||
int rc = binfoCompute(db, pgno, pCsr);
|
||||
if( rc ){
|
||||
pCursor->pVtab->zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
switch( i ){
|
||||
case BINFO_COLUMN_NAME:
|
||||
case BINFO_COLUMN_TYPE:
|
||||
case BINFO_COLUMN_TBL_NAME:
|
||||
case BINFO_COLUMN_ROOTPAGE:
|
||||
case BINFO_COLUMN_SQL: {
|
||||
sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, i+1));
|
||||
break;
|
||||
}
|
||||
case BINFO_COLUMN_HASROWID: {
|
||||
sqlite3_result_int(ctx, pCsr->hasRowid);
|
||||
break;
|
||||
}
|
||||
case BINFO_COLUMN_NENTRY: {
|
||||
sqlite3_result_int64(ctx, pCsr->nEntry);
|
||||
break;
|
||||
}
|
||||
case BINFO_COLUMN_NPAGE: {
|
||||
sqlite3_result_int(ctx, pCsr->nPage);
|
||||
break;
|
||||
}
|
||||
case BINFO_COLUMN_DEPTH: {
|
||||
sqlite3_result_int(ctx, pCsr->depth);
|
||||
break;
|
||||
}
|
||||
case BINFO_COLUMN_SCHEMA: {
|
||||
sqlite3_result_text(ctx, pCsr->zSchema, -1, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Return the ROWID for the sqlite_btreeinfo table */
|
||||
static int binfoRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
|
||||
BinfoCursor *pCsr = (BinfoCursor *)pCursor;
|
||||
*pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoke this routine to register the "sqlite_btreeinfo" virtual table module
|
||||
*/
|
||||
int sqlite3BinfoRegister(sqlite3 *db){
|
||||
static sqlite3_module binfo_module = {
|
||||
0, /* iVersion */
|
||||
0, /* xCreate */
|
||||
binfoConnect, /* xConnect */
|
||||
binfoBestIndex, /* xBestIndex */
|
||||
binfoDisconnect, /* xDisconnect */
|
||||
0, /* xDestroy */
|
||||
binfoOpen, /* xOpen - open a cursor */
|
||||
binfoClose, /* xClose - close a cursor */
|
||||
binfoFilter, /* xFilter - configure scan constraints */
|
||||
binfoNext, /* xNext - advance a cursor */
|
||||
binfoEof, /* xEof - check for end of scan */
|
||||
binfoColumn, /* xColumn - read data */
|
||||
binfoRowid, /* 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 */
|
||||
};
|
||||
return sqlite3_create_module(db, "sqlite_btreeinfo", &binfo_module, 0);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_btreeinfo_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
return sqlite3BinfoRegister(db);
|
||||
}
|
||||
@@ -1,561 +0,0 @@
|
||||
/*
|
||||
** 2016-06-29
|
||||
**
|
||||
** 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 that
|
||||
** returns the values in a C-language array.
|
||||
** Examples:
|
||||
**
|
||||
** SELECT * FROM carray($ptr,5)
|
||||
**
|
||||
** The query above returns 5 integers contained in a C-language array
|
||||
** at the address $ptr. $ptr is a pointer to the array of integers.
|
||||
** The pointer value must be assigned to $ptr using the
|
||||
** sqlite3_bind_pointer() interface with a pointer type of "carray".
|
||||
** For example:
|
||||
**
|
||||
** static int aX[] = { 53, 9, 17, 2231, 4, 99 };
|
||||
** int i = sqlite3_bind_parameter_index(pStmt, "$ptr");
|
||||
** sqlite3_bind_pointer(pStmt, i, aX, "carray", 0);
|
||||
**
|
||||
** There is an optional third parameter to determine the datatype of
|
||||
** the C-language array. Allowed values of the third parameter are
|
||||
** 'int32', 'int64', 'double', 'char*', 'struct iovec'. Example:
|
||||
**
|
||||
** SELECT * FROM carray($ptr,10,'char*');
|
||||
**
|
||||
** The default value of the third parameter is 'int32'.
|
||||
**
|
||||
** HOW IT WORKS
|
||||
**
|
||||
** The carray "function" is really a virtual table with the
|
||||
** following schema:
|
||||
**
|
||||
** CREATE TABLE carray(
|
||||
** value,
|
||||
** pointer HIDDEN,
|
||||
** count HIDDEN,
|
||||
** ctype TEXT HIDDEN
|
||||
** );
|
||||
**
|
||||
** If the hidden columns "pointer" and "count" are unconstrained, then
|
||||
** the virtual table has no rows. Otherwise, the virtual table interprets
|
||||
** the integer value of "pointer" as a pointer to the array and "count"
|
||||
** as the number of elements in the array. The virtual table steps through
|
||||
** the array, element by element.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
struct iovec {
|
||||
void *iov_base;
|
||||
size_t iov_len;
|
||||
};
|
||||
#else
|
||||
# include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
/* Allowed values for the mFlags parameter to sqlite3_carray_bind().
|
||||
** Must exactly match the definitions in carray.h.
|
||||
*/
|
||||
#ifndef CARRAY_INT32
|
||||
# define CARRAY_INT32 0 /* Data is 32-bit signed integers */
|
||||
# define CARRAY_INT64 1 /* Data is 64-bit signed integers */
|
||||
# define CARRAY_DOUBLE 2 /* Data is doubles */
|
||||
# define CARRAY_TEXT 3 /* Data is char* */
|
||||
# define CARRAY_BLOB 4 /* Data is struct iovec* */
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_API
|
||||
# ifdef _WIN32
|
||||
# define SQLITE_API __declspec(dllexport)
|
||||
# else
|
||||
# define SQLITE_API
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/*
|
||||
** Names of allowed datatypes
|
||||
*/
|
||||
static const char *azType[] = { "int32", "int64", "double", "char*",
|
||||
"struct iovec" };
|
||||
|
||||
/*
|
||||
** Structure used to hold the sqlite3_carray_bind() information
|
||||
*/
|
||||
typedef struct carray_bind carray_bind;
|
||||
struct carray_bind {
|
||||
void *aData; /* The data */
|
||||
int nData; /* Number of elements */
|
||||
int mFlags; /* Control flags */
|
||||
void (*xDel)(void*); /* Destructor for aData */
|
||||
};
|
||||
|
||||
|
||||
/* carray_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 carray_cursor carray_cursor;
|
||||
struct carray_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
sqlite3_int64 iRowid; /* The rowid */
|
||||
void *pPtr; /* Pointer to the array of values */
|
||||
sqlite3_int64 iCnt; /* Number of integers in the array */
|
||||
unsigned char eType; /* One of the CARRAY_type values */
|
||||
};
|
||||
|
||||
/*
|
||||
** The carrayConnect() method is invoked to create a new
|
||||
** carray_vtab that describes the carray virtual table.
|
||||
**
|
||||
** Think of this routine as the constructor for carray_vtab objects.
|
||||
**
|
||||
** All this routine needs to do is:
|
||||
**
|
||||
** (1) Allocate the carray_vtab object and initialize all fields.
|
||||
**
|
||||
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
||||
** result set of queries against carray will look like.
|
||||
*/
|
||||
static int carrayConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
sqlite3_vtab *pNew;
|
||||
int rc;
|
||||
|
||||
/* Column numbers */
|
||||
#define CARRAY_COLUMN_VALUE 0
|
||||
#define CARRAY_COLUMN_POINTER 1
|
||||
#define CARRAY_COLUMN_COUNT 2
|
||||
#define CARRAY_COLUMN_CTYPE 3
|
||||
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(value,pointer hidden,count hidden,ctype hidden)");
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is the destructor for carray_cursor objects.
|
||||
*/
|
||||
static int carrayDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Constructor for a new carray_cursor object.
|
||||
*/
|
||||
static int carrayOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
carray_cursor *pCur;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor for a carray_cursor.
|
||||
*/
|
||||
static int carrayClose(sqlite3_vtab_cursor *cur){
|
||||
sqlite3_free(cur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance a carray_cursor to its next row of output.
|
||||
*/
|
||||
static int carrayNext(sqlite3_vtab_cursor *cur){
|
||||
carray_cursor *pCur = (carray_cursor*)cur;
|
||||
pCur->iRowid++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return values of columns for the row at which the carray_cursor
|
||||
** is currently pointing.
|
||||
*/
|
||||
static int carrayColumn(
|
||||
sqlite3_vtab_cursor *cur, /* The cursor */
|
||||
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
||||
int i /* Which column to return */
|
||||
){
|
||||
carray_cursor *pCur = (carray_cursor*)cur;
|
||||
sqlite3_int64 x = 0;
|
||||
switch( i ){
|
||||
case CARRAY_COLUMN_POINTER: return SQLITE_OK;
|
||||
case CARRAY_COLUMN_COUNT: x = pCur->iCnt; break;
|
||||
case CARRAY_COLUMN_CTYPE: {
|
||||
sqlite3_result_text(ctx, azType[pCur->eType], -1, SQLITE_STATIC);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
default: {
|
||||
switch( pCur->eType ){
|
||||
case CARRAY_INT32: {
|
||||
int *p = (int*)pCur->pPtr;
|
||||
sqlite3_result_int(ctx, p[pCur->iRowid-1]);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case CARRAY_INT64: {
|
||||
sqlite3_int64 *p = (sqlite3_int64*)pCur->pPtr;
|
||||
sqlite3_result_int64(ctx, p[pCur->iRowid-1]);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case CARRAY_DOUBLE: {
|
||||
double *p = (double*)pCur->pPtr;
|
||||
sqlite3_result_double(ctx, p[pCur->iRowid-1]);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case CARRAY_TEXT: {
|
||||
const char **p = (const char**)pCur->pPtr;
|
||||
sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case CARRAY_BLOB: {
|
||||
const struct iovec *p = (struct iovec*)pCur->pPtr;
|
||||
sqlite3_result_blob(ctx, p[pCur->iRowid-1].iov_base,
|
||||
(int)p[pCur->iRowid-1].iov_len, SQLITE_TRANSIENT);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_result_int64(ctx, x);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the rowid for the current row. In this implementation, the
|
||||
** rowid is the same as the output value.
|
||||
*/
|
||||
static int carrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
carray_cursor *pCur = (carray_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 carrayEof(sqlite3_vtab_cursor *cur){
|
||||
carray_cursor *pCur = (carray_cursor*)cur;
|
||||
return pCur->iRowid>pCur->iCnt;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is called to "rewind" the carray_cursor object back
|
||||
** to the first row of output.
|
||||
*/
|
||||
static int carrayFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
carray_cursor *pCur = (carray_cursor *)pVtabCursor;
|
||||
pCur->pPtr = 0;
|
||||
pCur->iCnt = 0;
|
||||
switch( idxNum ){
|
||||
case 1: {
|
||||
carray_bind *pBind = sqlite3_value_pointer(argv[0], "carray-bind");
|
||||
if( pBind==0 ) break;
|
||||
pCur->pPtr = pBind->aData;
|
||||
pCur->iCnt = pBind->nData;
|
||||
pCur->eType = pBind->mFlags & 0x07;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
case 3: {
|
||||
pCur->pPtr = sqlite3_value_pointer(argv[0], "carray");
|
||||
pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
|
||||
if( idxNum<3 ){
|
||||
pCur->eType = CARRAY_INT32;
|
||||
}else{
|
||||
unsigned char i;
|
||||
const char *zType = (const char*)sqlite3_value_text(argv[2]);
|
||||
for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
|
||||
if( sqlite3_stricmp(zType, azType[i])==0 ) break;
|
||||
}
|
||||
if( i>=sizeof(azType)/sizeof(azType[0]) ){
|
||||
pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
|
||||
"unknown datatype: %Q", zType);
|
||||
return SQLITE_ERROR;
|
||||
}else{
|
||||
pCur->eType = i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
pCur->iRowid = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the carray 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.
|
||||
**
|
||||
** idxNum is:
|
||||
**
|
||||
** 1 If only the pointer= constraint exists. In this case, the
|
||||
** parameter must be bound using sqlite3_carray_bind().
|
||||
**
|
||||
** 2 if the pointer= and count= constraints exist.
|
||||
**
|
||||
** 3 if the ctype= constraint also exists.
|
||||
**
|
||||
** idxNum is 0 otherwise and carray becomes an empty table.
|
||||
*/
|
||||
static int carrayBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
int i; /* Loop over constraints */
|
||||
int ptrIdx = -1; /* Index of the pointer= constraint, or -1 if none */
|
||||
int cntIdx = -1; /* Index of the count= constraint, or -1 if none */
|
||||
int ctypeIdx = -1; /* Index of the ctype= constraint, or -1 if none */
|
||||
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
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 CARRAY_COLUMN_POINTER:
|
||||
ptrIdx = i;
|
||||
break;
|
||||
case CARRAY_COLUMN_COUNT:
|
||||
cntIdx = i;
|
||||
break;
|
||||
case CARRAY_COLUMN_CTYPE:
|
||||
ctypeIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( ptrIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[ptrIdx].omit = 1;
|
||||
pIdxInfo->estimatedCost = (double)1;
|
||||
pIdxInfo->estimatedRows = 100;
|
||||
pIdxInfo->idxNum = 1;
|
||||
if( cntIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
|
||||
pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
|
||||
pIdxInfo->idxNum = 2;
|
||||
if( ctypeIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
|
||||
pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
|
||||
pIdxInfo->idxNum = 3;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
pIdxInfo->estimatedCost = (double)2147483647;
|
||||
pIdxInfo->estimatedRows = 2147483647;
|
||||
pIdxInfo->idxNum = 0;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This following structure defines all the methods for the
|
||||
** carray virtual table.
|
||||
*/
|
||||
static sqlite3_module carrayModule = {
|
||||
0, /* iVersion */
|
||||
0, /* xCreate */
|
||||
carrayConnect, /* xConnect */
|
||||
carrayBestIndex, /* xBestIndex */
|
||||
carrayDisconnect, /* xDisconnect */
|
||||
0, /* xDestroy */
|
||||
carrayOpen, /* xOpen - open a cursor */
|
||||
carrayClose, /* xClose - close a cursor */
|
||||
carrayFilter, /* xFilter - configure scan constraints */
|
||||
carrayNext, /* xNext - advance a cursor */
|
||||
carrayEof, /* xEof - check for end of scan */
|
||||
carrayColumn, /* xColumn - read data */
|
||||
carrayRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0, /* xShadow */
|
||||
0 /* xIntegrity */
|
||||
};
|
||||
|
||||
/*
|
||||
** Destructor for the carray_bind object
|
||||
*/
|
||||
static void carrayBindDel(void *pPtr){
|
||||
carray_bind *p = (carray_bind*)pPtr;
|
||||
if( p->xDel!=SQLITE_STATIC ){
|
||||
p->xDel(p->aData);
|
||||
}
|
||||
sqlite3_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoke this interface in order to bind to the single-argument
|
||||
** version of CARRAY().
|
||||
*/
|
||||
SQLITE_API int sqlite3_carray_bind(
|
||||
sqlite3_stmt *pStmt,
|
||||
int idx,
|
||||
void *aData,
|
||||
int nData,
|
||||
int mFlags,
|
||||
void (*xDestroy)(void*)
|
||||
){
|
||||
carray_bind *pNew;
|
||||
int i;
|
||||
pNew = sqlite3_malloc64(sizeof(*pNew));
|
||||
if( pNew==0 ){
|
||||
if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){
|
||||
xDestroy(aData);
|
||||
}
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pNew->nData = nData;
|
||||
pNew->mFlags = mFlags;
|
||||
if( xDestroy==SQLITE_TRANSIENT ){
|
||||
sqlite3_int64 sz = nData;
|
||||
switch( mFlags & 0x07 ){
|
||||
case CARRAY_INT32: sz *= 4; break;
|
||||
case CARRAY_INT64: sz *= 8; break;
|
||||
case CARRAY_DOUBLE: sz *= 8; break;
|
||||
case CARRAY_TEXT: sz *= sizeof(char*); break;
|
||||
case CARRAY_BLOB: sz *= sizeof(struct iovec); break;
|
||||
}
|
||||
if( (mFlags & 0x07)==CARRAY_TEXT ){
|
||||
for(i=0; i<nData; i++){
|
||||
const char *z = ((char**)aData)[i];
|
||||
if( z ) sz += strlen(z) + 1;
|
||||
}
|
||||
}else if( (mFlags & 0x07)==CARRAY_BLOB ){
|
||||
for(i=0; i<nData; i++){
|
||||
sz += ((struct iovec*)aData)[i].iov_len;
|
||||
}
|
||||
}
|
||||
pNew->aData = sqlite3_malloc64( sz );
|
||||
if( pNew->aData==0 ){
|
||||
sqlite3_free(pNew);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( (mFlags & 0x07)==CARRAY_TEXT ){
|
||||
char **az = (char**)pNew->aData;
|
||||
char *z = (char*)&az[nData];
|
||||
for(i=0; i<nData; i++){
|
||||
const char *zData = ((char**)aData)[i];
|
||||
sqlite3_int64 n;
|
||||
if( zData==0 ){
|
||||
az[i] = 0;
|
||||
continue;
|
||||
}
|
||||
az[i] = z;
|
||||
n = strlen(zData);
|
||||
memcpy(z, zData, n+1);
|
||||
z += n+1;
|
||||
}
|
||||
}else if( (mFlags & 0x07)==CARRAY_BLOB ){
|
||||
struct iovec *p = (struct iovec*)pNew->aData;
|
||||
unsigned char *z = (unsigned char*)&p[nData];
|
||||
for(i=0; i<nData; i++){
|
||||
size_t n = ((struct iovec*)aData)[i].iov_len;
|
||||
p[i].iov_len = n;
|
||||
p[i].iov_base = z;
|
||||
z += n;
|
||||
memcpy(p[i].iov_base, ((struct iovec*)aData)[i].iov_base, n);
|
||||
}
|
||||
}else{
|
||||
memcpy(pNew->aData, aData, sz);
|
||||
}
|
||||
pNew->xDel = sqlite3_free;
|
||||
}else{
|
||||
pNew->aData = aData;
|
||||
pNew->xDel = xDestroy;
|
||||
}
|
||||
return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** For testing purpose in the TCL test harness, we need a method for
|
||||
** setting the pointer value. The inttoptr(X) SQL function accomplishes
|
||||
** this. Tcl script will bind an integer to X and the inttoptr() SQL
|
||||
** function will use sqlite3_result_pointer() to convert that integer into
|
||||
** a pointer.
|
||||
**
|
||||
** This is for testing on TCL only.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
static void inttoptrFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
void *p;
|
||||
sqlite3_int64 i64;
|
||||
i64 = sqlite3_value_int64(argv[0]);
|
||||
if( sizeof(i64)==sizeof(p) ){
|
||||
memcpy(&p, &i64, sizeof(p));
|
||||
}else{
|
||||
int i32 = i64 & 0xffffffff;
|
||||
memcpy(&p, &i32, sizeof(p));
|
||||
}
|
||||
sqlite3_result_pointer(context, p, "carray", 0);
|
||||
}
|
||||
#endif /* SQLITE_TEST */
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
SQLITE_API int sqlite3_carray_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3_create_module(db, "carray", &carrayModule, 0);
|
||||
#ifdef SQLITE_TEST
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "inttoptr", 1, SQLITE_UTF8, 0,
|
||||
inttoptrFunc, 0, 0);
|
||||
}
|
||||
#endif /* SQLITE_TEST */
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
return rc;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
** 2020-11-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.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** Interface definitions for the CARRAY table-valued function
|
||||
** extension.
|
||||
*/
|
||||
|
||||
#ifndef _CARRAY_H
|
||||
#define _CARRAY_H
|
||||
|
||||
#include "sqlite3.h" /* Required for error code definitions */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Use this interface to bind an array to the single-argument version
|
||||
** of CARRAY().
|
||||
*/
|
||||
SQLITE_API int sqlite3_carray_bind(
|
||||
sqlite3_stmt *pStmt, /* Statement to be bound */
|
||||
int i, /* Parameter index */
|
||||
void *aData, /* Pointer to array data */
|
||||
int nData, /* Number of data elements */
|
||||
int mFlags, /* CARRAY flags */
|
||||
void (*xDel)(void*) /* Destructgor for aData*/
|
||||
);
|
||||
|
||||
/* Allowed values for the mFlags parameter to sqlite3_carray_bind().
|
||||
*/
|
||||
#define CARRAY_INT32 0 /* Data is 32-bit signed integers */
|
||||
#define CARRAY_INT64 1 /* Data is 64-bit signed integers */
|
||||
#define CARRAY_DOUBLE 2 /* Data is doubles */
|
||||
#define CARRAY_TEXT 3 /* Data is char* */
|
||||
#define CARRAY_BLOB 4 /* Data is struct iovec */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end of the 'extern "C"' block */
|
||||
#endif
|
||||
|
||||
#endif /* ifndef _CARRAY_H */
|
||||
@@ -1,880 +0,0 @@
|
||||
/*
|
||||
** 2020-04-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 writes a checksum on each page
|
||||
** of an SQLite database file. When reading pages, the checksum is verified
|
||||
** and an error is raised if the checksum is incorrect.
|
||||
**
|
||||
** COMPILING
|
||||
**
|
||||
** This extension requires SQLite 3.32.0 or later. It uses the
|
||||
** sqlite3_database_file_object() interface which was added in
|
||||
** version 3.32.0, so it will not link with an earlier version of
|
||||
** SQLite.
|
||||
**
|
||||
** To build this extension as a separately loaded shared library or
|
||||
** DLL, use compiler command-lines similar to the following:
|
||||
**
|
||||
** (linux) gcc -fPIC -shared cksumvfs.c -o cksumvfs.so
|
||||
** (mac) clang -fPIC -dynamiclib cksumvfs.c -o cksumvfs.dylib
|
||||
** (windows) cl cksumvfs.c -link -dll -out:cksumvfs.dll
|
||||
**
|
||||
** You may want to add additional compiler options, of course,
|
||||
** according to the needs of your project.
|
||||
**
|
||||
** If you want to statically link this extension with your product,
|
||||
** then compile it like any other C-language module but add the
|
||||
** "-DSQLITE_CKSUMVFS_STATIC" option so that this module knows that
|
||||
** it is being statically linked rather than dynamically linked
|
||||
**
|
||||
** LOADING
|
||||
**
|
||||
** To load this extension as a shared library, you first have to
|
||||
** bring up a dummy SQLite database connection to use as the argument
|
||||
** to the sqlite3_load_extension() API call. Then you invoke the
|
||||
** sqlite3_load_extension() API and shutdown the dummy database
|
||||
** connection. All subsequent database connections that are opened
|
||||
** will include this extension. For example:
|
||||
**
|
||||
** sqlite3 *db;
|
||||
** sqlite3_open(":memory:", &db);
|
||||
** sqlite3_load_extension(db, "./cksumvfs");
|
||||
** sqlite3_close(db);
|
||||
**
|
||||
** If this extension is compiled with -DSQLITE_CKSUMVFS_STATIC and
|
||||
** statically linked against the application, initialize it using
|
||||
** a single API call as follows:
|
||||
**
|
||||
** sqlite3_register_cksumvfs();
|
||||
**
|
||||
** Cksumvfs is a VFS Shim. When loaded, "cksmvfs" becomes the new
|
||||
** default VFS and it uses the prior default VFS as the next VFS
|
||||
** down in the stack. This is normally what you want. However, in
|
||||
** complex situations where multiple VFS shims are being loaded,
|
||||
** it might be important to ensure that cksumvfs is loaded in the
|
||||
** correct order so that it sequences itself into the default VFS
|
||||
** Shim stack in the right order.
|
||||
**
|
||||
** USING
|
||||
**
|
||||
** Open database connections using the sqlite3_open() or
|
||||
** sqlite3_open_v2() interfaces, as normal. Ordinary database files
|
||||
** (without a checksum) will operate normally. Databases with
|
||||
** checksums will return an SQLITE_IOERR_DATA error if a page is
|
||||
** encountered that contains an invalid checksum.
|
||||
**
|
||||
** Checksumming only works on databases that have a reserve-bytes
|
||||
** value of exactly 8. The default value for reserve-bytes is 0.
|
||||
** Hence, newly created database files will omit the checksum by
|
||||
** default. To create a database that includes a checksum, change
|
||||
** the reserve-bytes value to 8 by runing:
|
||||
**
|
||||
** int n = 8;
|
||||
** sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVE_BYTES, &n);
|
||||
**
|
||||
** If you do this immediately after creating a new database file,
|
||||
** before anything else has been written into the file, then that
|
||||
** might be all that you need to do. Otherwise, the API call
|
||||
** above should be followed by:
|
||||
**
|
||||
** sqlite3_exec(db, "VACUUM", 0, 0, 0);
|
||||
**
|
||||
** It never hurts to run the VACUUM, even if you don't need it.
|
||||
** If the database is in WAL mode, you should shutdown and
|
||||
** reopen all database connections before continuing.
|
||||
**
|
||||
** From the CLI, use the ".filectrl reserve_bytes 8" command,
|
||||
** followed by "VACUUM;".
|
||||
**
|
||||
** Note that SQLite allows the number of reserve-bytes to be
|
||||
** increased but not decreased. So if a database file already
|
||||
** has a reserve-bytes value greater than 8, there is no way to
|
||||
** activate checksumming on that database, other than to dump
|
||||
** and restore the database file. Note also that other extensions
|
||||
** might also make use of the reserve-bytes. Checksumming will
|
||||
** be incompatible with those other extensions.
|
||||
**
|
||||
** VERIFICATION OF CHECKSUMS
|
||||
**
|
||||
** If any checksum is incorrect, the "PRAGMA quick_check" command
|
||||
** will find it. To verify that checksums are actually enabled
|
||||
** and running, use the following query:
|
||||
**
|
||||
** SELECT count(*), verify_checksum(data)
|
||||
** FROM sqlite_dbpage
|
||||
** GROUP BY 2;
|
||||
**
|
||||
** There are three possible outputs form the verify_checksum()
|
||||
** function: 1, 0, and NULL. 1 is returned if the checksum is
|
||||
** correct. 0 is returned if the checksum is incorrect. NULL
|
||||
** is returned if the page is unreadable. If checksumming is
|
||||
** enabled, the read will fail if the checksum is wrong, so the
|
||||
** usual result from verify_checksum() on a bad checksum is NULL.
|
||||
**
|
||||
** If everything is OK, the query above should return a single
|
||||
** row where the second column is 1. Any other result indicates
|
||||
** either that there is a checksum error, or checksum validation
|
||||
** is disabled.
|
||||
**
|
||||
** CONTROLLING CHECKSUM VERIFICATION
|
||||
**
|
||||
** The cksumvfs extension implements a new PRAGMA statement that can
|
||||
** be used to disable, re-enable, or query the status of checksum
|
||||
** verification:
|
||||
**
|
||||
** PRAGMA checksum_verification; -- query status
|
||||
** PRAGMA checksum_verification=OFF; -- disable verification
|
||||
** PRAGMA checksum_verification=ON; -- re-enable verification
|
||||
**
|
||||
** The "checksum_verification" pragma will return "1" (true) or "0"
|
||||
** (false) if checksum verification is enabled or disabled, respectively.
|
||||
** "Verification" in this context means the feature that causes
|
||||
** SQLITE_IOERR_DATA errors if a checksum mismatch is detected while
|
||||
** reading. Checksums are always kept up-to-date as long as the
|
||||
** reserve-bytes value of the database is 8, regardless of the setting
|
||||
** of this pragma. Checksum verification can be disabled (for example)
|
||||
** to do forensic analysis of a database that has previously reported
|
||||
** a checksum error.
|
||||
**
|
||||
** The "checksum_verification" pragma will always respond with "0" if
|
||||
** the database file does not have a reserve-bytes value of 8. The
|
||||
** pragma will return no rows at all if the cksumvfs extension is
|
||||
** not loaded.
|
||||
**
|
||||
** IMPLEMENTATION NOTES
|
||||
**
|
||||
** The checksum is stored in the last 8 bytes of each page. This
|
||||
** module only operates if the "bytes of reserved space on each page"
|
||||
** value at offset 20 the SQLite database header is exactly 8. If
|
||||
** the reserved-space value is not 8, this module is a no-op.
|
||||
*/
|
||||
#if defined(SQLITE_AMALGAMATION) && !defined(SQLITE_CKSUMVFS_STATIC)
|
||||
# define SQLITE_CKSUMVFS_STATIC
|
||||
#endif
|
||||
#ifdef SQLITE_CKSUMVFS_STATIC
|
||||
# include "sqlite3.h"
|
||||
#else
|
||||
# include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
/*
|
||||
** Forward declaration of objects used by this utility
|
||||
*/
|
||||
typedef struct sqlite3_vfs CksmVfs;
|
||||
typedef struct CksmFile CksmFile;
|
||||
|
||||
/*
|
||||
** Useful datatype abbreviations
|
||||
*/
|
||||
#if !defined(SQLITE_AMALGAMATION)
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned int u32;
|
||||
#endif
|
||||
|
||||
/* 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*)(((CksmFile*)(p))+1))
|
||||
|
||||
/* An open file */
|
||||
struct CksmFile {
|
||||
sqlite3_file base; /* IO methods */
|
||||
const char *zFName; /* Original name of the file */
|
||||
char computeCksm; /* True to compute checksums.
|
||||
** Always true if reserve size is 8. */
|
||||
char verifyCksm; /* True to verify checksums */
|
||||
char isWal; /* True if processing a WAL file */
|
||||
char inCkpt; /* Currently doing a checkpoint */
|
||||
CksmFile *pPartner; /* Ptr from WAL to main-db, or from main-db to WAL */
|
||||
};
|
||||
|
||||
/*
|
||||
** Methods for CksmFile
|
||||
*/
|
||||
static int cksmClose(sqlite3_file*);
|
||||
static int cksmRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
||||
static int cksmWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
||||
static int cksmTruncate(sqlite3_file*, sqlite3_int64 size);
|
||||
static int cksmSync(sqlite3_file*, int flags);
|
||||
static int cksmFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
||||
static int cksmLock(sqlite3_file*, int);
|
||||
static int cksmUnlock(sqlite3_file*, int);
|
||||
static int cksmCheckReservedLock(sqlite3_file*, int *pResOut);
|
||||
static int cksmFileControl(sqlite3_file*, int op, void *pArg);
|
||||
static int cksmSectorSize(sqlite3_file*);
|
||||
static int cksmDeviceCharacteristics(sqlite3_file*);
|
||||
static int cksmShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
|
||||
static int cksmShmLock(sqlite3_file*, int offset, int n, int flags);
|
||||
static void cksmShmBarrier(sqlite3_file*);
|
||||
static int cksmShmUnmap(sqlite3_file*, int deleteFlag);
|
||||
static int cksmFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
|
||||
static int cksmUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
|
||||
|
||||
/*
|
||||
** Methods for CksmVfs
|
||||
*/
|
||||
static int cksmOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
||||
static int cksmDelete(sqlite3_vfs*, const char *zName, int syncDir);
|
||||
static int cksmAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
||||
static int cksmFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
||||
static void *cksmDlOpen(sqlite3_vfs*, const char *zFilename);
|
||||
static void cksmDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
||||
static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
||||
static void cksmDlClose(sqlite3_vfs*, void*);
|
||||
static int cksmRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
||||
static int cksmSleep(sqlite3_vfs*, int microseconds);
|
||||
static int cksmCurrentTime(sqlite3_vfs*, double*);
|
||||
static int cksmGetLastError(sqlite3_vfs*, int, char *);
|
||||
static int cksmCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
||||
static int cksmSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr);
|
||||
static sqlite3_syscall_ptr cksmGetSystemCall(sqlite3_vfs*, const char *z);
|
||||
static const char *cksmNextSystemCall(sqlite3_vfs*, const char *zName);
|
||||
|
||||
static sqlite3_vfs cksm_vfs = {
|
||||
3, /* iVersion (set when registered) */
|
||||
0, /* szOsFile (set when registered) */
|
||||
1024, /* mxPathname */
|
||||
0, /* pNext */
|
||||
"cksmvfs", /* zName */
|
||||
0, /* pAppData (set when registered) */
|
||||
cksmOpen, /* xOpen */
|
||||
cksmDelete, /* xDelete */
|
||||
cksmAccess, /* xAccess */
|
||||
cksmFullPathname, /* xFullPathname */
|
||||
cksmDlOpen, /* xDlOpen */
|
||||
cksmDlError, /* xDlError */
|
||||
cksmDlSym, /* xDlSym */
|
||||
cksmDlClose, /* xDlClose */
|
||||
cksmRandomness, /* xRandomness */
|
||||
cksmSleep, /* xSleep */
|
||||
cksmCurrentTime, /* xCurrentTime */
|
||||
cksmGetLastError, /* xGetLastError */
|
||||
cksmCurrentTimeInt64, /* xCurrentTimeInt64 */
|
||||
cksmSetSystemCall, /* xSetSystemCall */
|
||||
cksmGetSystemCall, /* xGetSystemCall */
|
||||
cksmNextSystemCall /* xNextSystemCall */
|
||||
};
|
||||
|
||||
static const sqlite3_io_methods cksm_io_methods = {
|
||||
3, /* iVersion */
|
||||
cksmClose, /* xClose */
|
||||
cksmRead, /* xRead */
|
||||
cksmWrite, /* xWrite */
|
||||
cksmTruncate, /* xTruncate */
|
||||
cksmSync, /* xSync */
|
||||
cksmFileSize, /* xFileSize */
|
||||
cksmLock, /* xLock */
|
||||
cksmUnlock, /* xUnlock */
|
||||
cksmCheckReservedLock, /* xCheckReservedLock */
|
||||
cksmFileControl, /* xFileControl */
|
||||
cksmSectorSize, /* xSectorSize */
|
||||
cksmDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
cksmShmMap, /* xShmMap */
|
||||
cksmShmLock, /* xShmLock */
|
||||
cksmShmBarrier, /* xShmBarrier */
|
||||
cksmShmUnmap, /* xShmUnmap */
|
||||
cksmFetch, /* xFetch */
|
||||
cksmUnfetch /* xUnfetch */
|
||||
};
|
||||
|
||||
/* Do byte swapping on a unsigned 32-bit integer */
|
||||
#define BYTESWAP32(x) ( \
|
||||
(((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) \
|
||||
+ (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \
|
||||
)
|
||||
|
||||
/* Compute a checksum on a buffer */
|
||||
static void cksmCompute(
|
||||
u8 *a, /* Content to be checksummed */
|
||||
int nByte, /* Bytes of content in a[]. Must be a multiple of 8. */
|
||||
u8 *aOut /* OUT: Final 8-byte checksum value output */
|
||||
){
|
||||
u32 s1 = 0, s2 = 0;
|
||||
u32 *aData = (u32*)a;
|
||||
u32 *aEnd = (u32*)&a[nByte];
|
||||
u32 x = 1;
|
||||
|
||||
assert( nByte>=8 );
|
||||
assert( (nByte&0x00000007)==0 );
|
||||
assert( nByte<=65536 );
|
||||
|
||||
if( 1 == *(u8*)&x ){
|
||||
/* Little-endian */
|
||||
do {
|
||||
s1 += *aData++ + s2;
|
||||
s2 += *aData++ + s1;
|
||||
}while( aData<aEnd );
|
||||
}else{
|
||||
/* Big-endian */
|
||||
do {
|
||||
s1 += BYTESWAP32(aData[0]) + s2;
|
||||
s2 += BYTESWAP32(aData[1]) + s1;
|
||||
aData += 2;
|
||||
}while( aData<aEnd );
|
||||
s1 = BYTESWAP32(s1);
|
||||
s2 = BYTESWAP32(s2);
|
||||
}
|
||||
memcpy(aOut, &s1, 4);
|
||||
memcpy(aOut+4, &s2, 4);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: verify_checksum(BLOB)
|
||||
**
|
||||
** Return 0 or 1 if the checksum is invalid or valid. Or return
|
||||
** NULL if the input is not a BLOB that is the right size for a
|
||||
** database page.
|
||||
*/
|
||||
static void cksmVerifyFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int nByte;
|
||||
u8 *data;
|
||||
u8 cksum[8];
|
||||
data = (u8*)sqlite3_value_blob(argv[0]);
|
||||
if( data==0 ) return;
|
||||
if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ) return;
|
||||
nByte = sqlite3_value_bytes(argv[0]);
|
||||
if( nByte<512 || nByte>65536 || (nByte & (nByte-1))!=0 ) return;
|
||||
cksmCompute(data, nByte-8, cksum);
|
||||
sqlite3_result_int(context, memcmp(data+nByte-8,cksum,8)==0);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME
|
||||
/*
|
||||
** SQL function: initialize_cksumvfs(SCHEMANAME)
|
||||
**
|
||||
** This SQL functions (whose name is actually determined at compile-time
|
||||
** by the value of the SQLITE_CKSUMVFS_INIT_FUNCNAME macro) invokes:
|
||||
**
|
||||
** sqlite3_file_control(db, SCHEMANAME, SQLITE_FCNTL_RESERVE_BYTE, &n);
|
||||
**
|
||||
** In order to set the reserve bytes value to 8, so that cksumvfs will
|
||||
** operation. This feature is provided (if and only if the
|
||||
** SQLITE_CKSUMVFS_INIT_FUNCNAME compile-time option is set to a string
|
||||
** which is the name of the SQL function) so as to provide the ability
|
||||
** to invoke the file-control in programming languages that lack
|
||||
** direct access to the sqlite3_file_control() interface (ex: Java).
|
||||
**
|
||||
** This interface is undocumented, apart from this comment. Usage
|
||||
** example:
|
||||
**
|
||||
** 1. Compile with -DSQLITE_CKSUMVFS_INIT_FUNCNAME="ckvfs_init"
|
||||
** 2. Run: "SELECT cksum_init('main'); VACUUM;"
|
||||
*/
|
||||
static void cksmInitFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int nByte = 8;
|
||||
const char *zSchemaName = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
sqlite3_file_control(db, zSchemaName, SQLITE_FCNTL_RESERVE_BYTES, &nByte);
|
||||
/* Return NULL */
|
||||
}
|
||||
#endif /* SQLITE_CKSUMBFS_INIT_FUNCNAME */
|
||||
|
||||
/*
|
||||
** Close a cksm-file.
|
||||
*/
|
||||
static int cksmClose(sqlite3_file *pFile){
|
||||
CksmFile *p = (CksmFile *)pFile;
|
||||
if( p->pPartner ){
|
||||
assert( p->pPartner->pPartner==p );
|
||||
p->pPartner->pPartner = 0;
|
||||
p->pPartner = 0;
|
||||
}
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xClose(pFile);
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the computeCkSm and verifyCksm flags, if they need to be
|
||||
** changed.
|
||||
*/
|
||||
static void cksmSetFlags(CksmFile *p, int hasCorrectReserveSize){
|
||||
if( hasCorrectReserveSize!=p->computeCksm ){
|
||||
p->computeCksm = p->verifyCksm = hasCorrectReserveSize;
|
||||
if( p->pPartner ){
|
||||
p->pPartner->verifyCksm = hasCorrectReserveSize;
|
||||
p->pPartner->computeCksm = hasCorrectReserveSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Read data from a cksm-file.
|
||||
*/
|
||||
static int cksmRead(
|
||||
sqlite3_file *pFile,
|
||||
void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
int rc;
|
||||
CksmFile *p = (CksmFile *)pFile;
|
||||
pFile = ORIGFILE(pFile);
|
||||
rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( iOfst==0 && iAmt>=100 && (
|
||||
memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0
|
||||
)){
|
||||
u8 *d = (u8*)zBuf;
|
||||
char hasCorrectReserveSize = (d[20]==8);
|
||||
cksmSetFlags(p, hasCorrectReserveSize);
|
||||
}
|
||||
/* Verify the checksum if
|
||||
** (1) the size indicates that we are dealing with a complete
|
||||
** database page
|
||||
** (2) checksum verification is enabled
|
||||
** (3) we are not in the middle of checkpoint
|
||||
*/
|
||||
if( iAmt>=512 && (iAmt & (iAmt-1))==0 /* (1) */
|
||||
&& p->verifyCksm /* (2) */
|
||||
&& !p->inCkpt /* (3) */
|
||||
){
|
||||
u8 cksum[8];
|
||||
cksmCompute((u8*)zBuf, iAmt-8, cksum);
|
||||
if( memcmp((u8*)zBuf+iAmt-8, cksum, 8)!=0 ){
|
||||
sqlite3_log(SQLITE_IOERR_DATA,
|
||||
"checksum fault offset %lld of \"%s\"",
|
||||
iOfst, p->zFName);
|
||||
rc = SQLITE_IOERR_DATA;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to a cksm-file.
|
||||
*/
|
||||
static int cksmWrite(
|
||||
sqlite3_file *pFile,
|
||||
const void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
CksmFile *p = (CksmFile *)pFile;
|
||||
pFile = ORIGFILE(pFile);
|
||||
if( iOfst==0 && iAmt>=100 && (
|
||||
memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0
|
||||
)){
|
||||
u8 *d = (u8*)zBuf;
|
||||
char hasCorrectReserveSize = (d[20]==8);
|
||||
cksmSetFlags(p, hasCorrectReserveSize);
|
||||
}
|
||||
/* If the write size is appropriate for a database page and if
|
||||
** checksums where ever enabled, then it will be safe to compute
|
||||
** the checksums. The reserve byte size might have increased, but
|
||||
** it will never decrease. And because it cannot decrease, the
|
||||
** checksum will not overwrite anything.
|
||||
*/
|
||||
if( iAmt>=512
|
||||
&& p->computeCksm
|
||||
&& !p->inCkpt
|
||||
){
|
||||
cksmCompute((u8*)zBuf, iAmt-8, ((u8*)zBuf)+iAmt-8);
|
||||
}
|
||||
return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst);
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate a cksm-file.
|
||||
*/
|
||||
static int cksmTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xTruncate(pFile, size);
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync a cksm-file.
|
||||
*/
|
||||
static int cksmSync(sqlite3_file *pFile, int flags){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xSync(pFile, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current file-size of a cksm-file.
|
||||
*/
|
||||
static int cksmFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
||||
CksmFile *p = (CksmFile *)pFile;
|
||||
pFile = ORIGFILE(p);
|
||||
return pFile->pMethods->xFileSize(pFile, pSize);
|
||||
}
|
||||
|
||||
/*
|
||||
** Lock a cksm-file.
|
||||
*/
|
||||
static int cksmLock(sqlite3_file *pFile, int eLock){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xLock(pFile, eLock);
|
||||
}
|
||||
|
||||
/*
|
||||
** Unlock a cksm-file.
|
||||
*/
|
||||
static int cksmUnlock(sqlite3_file *pFile, int eLock){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xUnlock(pFile, eLock);
|
||||
}
|
||||
|
||||
/*
|
||||
** Check if another file-handle holds a RESERVED lock on a cksm-file.
|
||||
*/
|
||||
static int cksmCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** File control method. For custom operations on a cksm-file.
|
||||
*/
|
||||
static int cksmFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
int rc;
|
||||
CksmFile *p = (CksmFile*)pFile;
|
||||
pFile = ORIGFILE(pFile);
|
||||
if( op==SQLITE_FCNTL_PRAGMA ){
|
||||
char **azArg = (char**)pArg;
|
||||
assert( azArg[1]!=0 );
|
||||
if( sqlite3_stricmp(azArg[1],"checksum_verification")==0 ){
|
||||
char *zArg = azArg[2];
|
||||
if( zArg!=0 ){
|
||||
if( (zArg[0]>='1' && zArg[0]<='9')
|
||||
|| sqlite3_strlike("enable%",zArg,0)==0
|
||||
|| sqlite3_stricmp("yes",zArg)==0
|
||||
|| sqlite3_stricmp("on",zArg)==0
|
||||
){
|
||||
p->verifyCksm = p->computeCksm;
|
||||
}else{
|
||||
p->verifyCksm = 0;
|
||||
}
|
||||
if( p->pPartner ) p->pPartner->verifyCksm = p->verifyCksm;
|
||||
}
|
||||
azArg[0] = sqlite3_mprintf("%d",p->verifyCksm);
|
||||
return SQLITE_OK;
|
||||
}else if( p->computeCksm && azArg[2]!=0
|
||||
&& sqlite3_stricmp(azArg[1], "page_size")==0 ){
|
||||
/* Do not allow page size changes on a checksum database */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}else if( op==SQLITE_FCNTL_CKPT_START || op==SQLITE_FCNTL_CKPT_DONE ){
|
||||
p->inCkpt = op==SQLITE_FCNTL_CKPT_START;
|
||||
if( p->pPartner ) p->pPartner->inCkpt = p->inCkpt;
|
||||
}else if( op==SQLITE_FCNTL_CKSM_FILE ){
|
||||
/* This VFS needs to obtain a pointer to the corresponding database
|
||||
** file handle from within xOpen() calls to open wal files. To do this,
|
||||
** it uses the sqlite3_database_file_object() API to obtain a pointer
|
||||
** to the file-handle used by SQLite to access the db file. This is
|
||||
** fine if cksmvfs happens to be the top-level VFS, but not if there
|
||||
** are one or more wrapper VFS. To handle this case, this file-control
|
||||
** is used to extract the cksmvfs file-handle from any wrapper file
|
||||
** handle. */
|
||||
sqlite3_file **ppFile = (sqlite3_file**)pArg;
|
||||
*ppFile = (sqlite3_file*)p;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
rc = pFile->pMethods->xFileControl(pFile, op, pArg);
|
||||
if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
|
||||
*(char**)pArg = sqlite3_mprintf("cksm/%z", *(char**)pArg);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the sector-size in bytes for a cksm-file.
|
||||
*/
|
||||
static int cksmSectorSize(sqlite3_file *pFile){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xSectorSize(pFile);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the device characteristic flags supported by a cksm-file.
|
||||
*/
|
||||
static int cksmDeviceCharacteristics(sqlite3_file *pFile){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xDeviceCharacteristics(pFile);
|
||||
}
|
||||
|
||||
/* Create a shared memory file mapping */
|
||||
static int cksmShmMap(
|
||||
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 cksmShmLock(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 cksmShmBarrier(sqlite3_file *pFile){
|
||||
pFile = ORIGFILE(pFile);
|
||||
pFile->pMethods->xShmBarrier(pFile);
|
||||
}
|
||||
|
||||
/* Unmap a shared memory segment */
|
||||
static int cksmShmUnmap(sqlite3_file *pFile, int deleteFlag){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xShmUnmap(pFile,deleteFlag);
|
||||
}
|
||||
|
||||
/* Fetch a page of a memory-mapped file */
|
||||
static int cksmFetch(
|
||||
sqlite3_file *pFile,
|
||||
sqlite3_int64 iOfst,
|
||||
int iAmt,
|
||||
void **pp
|
||||
){
|
||||
CksmFile *p = (CksmFile *)pFile;
|
||||
if( p->computeCksm ){
|
||||
*pp = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
pFile = ORIGFILE(pFile);
|
||||
if( pFile->pMethods->iVersion>2 && pFile->pMethods->xFetch ){
|
||||
return pFile->pMethods->xFetch(pFile, iOfst, iAmt, pp);
|
||||
}
|
||||
*pp = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Release a memory-mapped page */
|
||||
static int cksmUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
|
||||
pFile = ORIGFILE(pFile);
|
||||
if( pFile->pMethods->iVersion>2 && pFile->pMethods->xUnfetch ){
|
||||
return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a cksm file handle.
|
||||
*/
|
||||
static int cksmOpen(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName,
|
||||
sqlite3_file *pFile,
|
||||
int flags,
|
||||
int *pOutFlags
|
||||
){
|
||||
CksmFile *p;
|
||||
sqlite3_file *pSubFile;
|
||||
sqlite3_vfs *pSubVfs;
|
||||
int rc;
|
||||
pSubVfs = ORIGVFS(pVfs);
|
||||
if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){
|
||||
return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
|
||||
}
|
||||
p = (CksmFile*)pFile;
|
||||
memset(p, 0, sizeof(*p));
|
||||
pSubFile = ORIGFILE(pFile);
|
||||
pFile->pMethods = &cksm_io_methods;
|
||||
rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
|
||||
if( rc ) goto cksm_open_done;
|
||||
if( flags & SQLITE_OPEN_WAL ){
|
||||
sqlite3_file *pDb = sqlite3_database_file_object(zName);
|
||||
rc = pDb->pMethods->xFileControl(pDb, SQLITE_FCNTL_CKSM_FILE, (void*)&pDb);
|
||||
assert( rc==SQLITE_OK );
|
||||
p->pPartner = (CksmFile*)pDb;
|
||||
assert( p->pPartner->pPartner==0 );
|
||||
p->pPartner->pPartner = p;
|
||||
p->isWal = 1;
|
||||
p->computeCksm = p->pPartner->computeCksm;
|
||||
}else{
|
||||
p->isWal = 0;
|
||||
p->computeCksm = 0;
|
||||
}
|
||||
p->zFName = zName;
|
||||
cksm_open_done:
|
||||
if( rc ) pFile->pMethods = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** All other VFS methods are pass-thrus.
|
||||
*/
|
||||
static int cksmDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
||||
return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
|
||||
}
|
||||
static int cksmAccess(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int flags,
|
||||
int *pResOut
|
||||
){
|
||||
return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
|
||||
}
|
||||
static int cksmFullPathname(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int nOut,
|
||||
char *zOut
|
||||
){
|
||||
return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut);
|
||||
}
|
||||
static void *cksmDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
||||
return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
|
||||
}
|
||||
static void cksmDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
||||
ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
|
||||
}
|
||||
static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
||||
return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
|
||||
}
|
||||
static void cksmDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
|
||||
}
|
||||
static int cksmRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
||||
return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
|
||||
}
|
||||
static int cksmSleep(sqlite3_vfs *pVfs, int nMicro){
|
||||
return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
|
||||
}
|
||||
static int cksmCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||||
return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
|
||||
}
|
||||
static int cksmGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
||||
return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
|
||||
}
|
||||
static int cksmCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
||||
sqlite3_vfs *pOrig = ORIGVFS(pVfs);
|
||||
int rc;
|
||||
assert( pOrig->iVersion>=2 );
|
||||
if( pOrig->xCurrentTimeInt64 ){
|
||||
rc = pOrig->xCurrentTimeInt64(pOrig, p);
|
||||
}else{
|
||||
double r;
|
||||
rc = pOrig->xCurrentTime(pOrig, &r);
|
||||
*p = (sqlite3_int64)(r*86400000.0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
static int cksmSetSystemCall(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName,
|
||||
sqlite3_syscall_ptr pCall
|
||||
){
|
||||
return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall);
|
||||
}
|
||||
static sqlite3_syscall_ptr cksmGetSystemCall(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName
|
||||
){
|
||||
return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName);
|
||||
}
|
||||
static const char *cksmNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
|
||||
return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
|
||||
}
|
||||
|
||||
/* Register the verify_checksum() SQL function.
|
||||
*/
|
||||
static int cksmRegisterFunc(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc;
|
||||
if( db==0 ) return SQLITE_OK;
|
||||
rc = sqlite3_create_function(db, "verify_checksum", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
|
||||
0, cksmVerifyFunc, 0, 0);
|
||||
#ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME
|
||||
(void)sqlite3_create_function(db, SQLITE_CKSUMVFS_INIT_FUNCNAME, 1,
|
||||
SQLITE_UTF8|SQLITE_DIRECTONLY,
|
||||
0, cksmInitFunc, 0, 0);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the cksum VFS as the default VFS for the system.
|
||||
** Also make arrangements to automatically register the "verify_checksum()"
|
||||
** SQL function on each new database connection.
|
||||
*/
|
||||
static int cksmRegisterVfs(void){
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_vfs *pOrig;
|
||||
if( sqlite3_vfs_find("cksmvfs")!=0 ) return SQLITE_OK;
|
||||
pOrig = sqlite3_vfs_find(0);
|
||||
if( pOrig==0 ) return SQLITE_ERROR;
|
||||
cksm_vfs.iVersion = pOrig->iVersion;
|
||||
cksm_vfs.pAppData = pOrig;
|
||||
cksm_vfs.szOsFile = pOrig->szOsFile + sizeof(CksmFile);
|
||||
rc = sqlite3_vfs_register(&cksm_vfs, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_auto_extension((void(*)(void))cksmRegisterFunc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_CKSUMVFS_STATIC)
|
||||
/* This variant of the initializer runs when the extension is
|
||||
** statically linked.
|
||||
*/
|
||||
int sqlite3_register_cksumvfs(const char *NotUsed){
|
||||
(void)NotUsed;
|
||||
return cksmRegisterVfs();
|
||||
}
|
||||
int sqlite3_unregister_cksumvfs(void){
|
||||
if( sqlite3_vfs_find("cksmvfs") ){
|
||||
sqlite3_vfs_unregister(&cksm_vfs);
|
||||
sqlite3_cancel_auto_extension((void(*)(void))cksmRegisterFunc);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif /* defined(SQLITE_CKSUMVFS_STATIC */
|
||||
|
||||
#if !defined(SQLITE_CKSUMVFS_STATIC)
|
||||
/* This variant of the initializer function is used when the
|
||||
** extension is shared library to be loaded at run-time.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
/*
|
||||
** This routine is called by sqlite3_load_extension() when the
|
||||
** extension is first loaded.
|
||||
***/
|
||||
int sqlite3_cksumvfs_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* not used */
|
||||
rc = cksmRegisterFunc(db, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = cksmRegisterVfs();
|
||||
}
|
||||
if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
|
||||
return rc;
|
||||
}
|
||||
#endif /* !defined(SQLITE_CKSUMVFS_STATIC) */
|
||||
@@ -1,966 +0,0 @@
|
||||
/*
|
||||
** 2013-04-16
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains code for a virtual table that finds the transitive
|
||||
** closure of a parent/child relationship in a real table. The virtual
|
||||
** table is called "transitive_closure".
|
||||
**
|
||||
** A transitive_closure virtual table is created like this:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE x USING transitive_closure(
|
||||
** tablename=<tablename>, -- T
|
||||
** idcolumn=<columnname>, -- X
|
||||
** parentcolumn=<columnname> -- P
|
||||
** );
|
||||
**
|
||||
** When it is created, the new transitive_closure table may be supplied
|
||||
** with default values for the name of a table T and columns T.X and T.P.
|
||||
** The T.X and T.P columns must contain integers. The ideal case is for
|
||||
** T.X to be the INTEGER PRIMARY KEY. The T.P column should reference
|
||||
** the T.X column. The row referenced by T.P is the parent of the current row.
|
||||
**
|
||||
** The tablename, idcolumn, and parentcolumn supplied by the CREATE VIRTUAL
|
||||
** TABLE statement may be overridden in individual queries by including
|
||||
** terms like tablename='newtable', idcolumn='id2', or
|
||||
** parentcolumn='parent3' in the WHERE clause of the query.
|
||||
**
|
||||
** For efficiency, it is essential that there be an index on the P column:
|
||||
**
|
||||
** CREATE Tidx1 ON T(P)
|
||||
**
|
||||
** Suppose a specific instance of the closure table is as follows:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE ct1 USING transitive_closure(
|
||||
** tablename='group',
|
||||
** idcolumn='groupId',
|
||||
** parentcolumn='parentId'
|
||||
** );
|
||||
**
|
||||
** Such an instance of the transitive_closure virtual table would be
|
||||
** appropriate for walking a tree defined using a table like this, for example:
|
||||
**
|
||||
** CREATE TABLE group(
|
||||
** groupId INTEGER PRIMARY KEY,
|
||||
** parentId INTEGER REFERENCES group
|
||||
** );
|
||||
** CREATE INDEX group_idx1 ON group(parentId);
|
||||
**
|
||||
** The group table above would presumably have other application-specific
|
||||
** fields. The key point here is that rows of the group table form a
|
||||
** tree. The purpose of the ct1 virtual table is to easily extract
|
||||
** branches of that tree.
|
||||
**
|
||||
** Once it has been created, the ct1 virtual table can be queried
|
||||
** as follows:
|
||||
**
|
||||
** SELECT * FROM element
|
||||
** WHERE element.groupId IN (SELECT id FROM ct1 WHERE root=?1);
|
||||
**
|
||||
** The above query will return all elements that are part of group ?1
|
||||
** or children of group ?1 or grand-children of ?1 and so forth for all
|
||||
** descendents of group ?1. The same query can be formulated as a join:
|
||||
**
|
||||
** SELECT element.* FROM element, ct1
|
||||
** WHERE element.groupid=ct1.id
|
||||
** AND ct1.root=?1;
|
||||
**
|
||||
** The depth of the transitive_closure (the number of generations of
|
||||
** parent/child relations to follow) can be limited by setting "depth"
|
||||
** column in the WHERE clause. So, for example, the following query
|
||||
** finds only children and grandchildren but no further descendents:
|
||||
**
|
||||
** SELECT element.* FROM element, ct1
|
||||
** WHERE element.groupid=ct1.id
|
||||
** AND ct1.root=?1
|
||||
** AND ct1.depth<=2;
|
||||
**
|
||||
** The "ct1.depth<=2" term could be a strict equality "ct1.depth=2" in
|
||||
** order to find only the grandchildren of ?1, not ?1 itself or the
|
||||
** children of ?1.
|
||||
**
|
||||
** The root=?1 term must be supplied in WHERE clause or else the query
|
||||
** of the ct1 virtual table will return an empty set. The tablename,
|
||||
** idcolumn, and parentcolumn attributes can be overridden in the WHERE
|
||||
** clause if desired. So, for example, the ct1 table could be repurposed
|
||||
** to find ancestors rather than descendents by inverting the roles of
|
||||
** the idcolumn and parentcolumn:
|
||||
**
|
||||
** SELECT element.* FROM element, ct1
|
||||
** WHERE element.groupid=ct1.id
|
||||
** AND ct1.root=?1
|
||||
** AND ct1.idcolumn='parentId'
|
||||
** AND ct1.parentcolumn='groupId';
|
||||
**
|
||||
** Multiple calls to ct1 could be combined. For example, the following
|
||||
** query finds all elements that "cousins" of groupId ?1. That is to say
|
||||
** elements where the groupId is a grandchild of the grandparent of ?1.
|
||||
** (This definition of "cousins" also includes siblings and self.)
|
||||
**
|
||||
** SELECT element.* FROM element, ct1
|
||||
** WHERE element.groupId=ct1.id
|
||||
** AND ct1.depth=2
|
||||
** AND ct1.root IN (SELECT id FROM ct1
|
||||
** WHERE root=?1
|
||||
** AND depth=2
|
||||
** AND idcolumn='parentId'
|
||||
** AND parentcolumn='groupId');
|
||||
**
|
||||
** In our example, the group.groupId column is unique and thus the
|
||||
** subquery will return exactly one row. For that reason, the IN
|
||||
** operator could be replaced by "=" to get the same result. But
|
||||
** in the general case where the idcolumn is not unique, an IN operator
|
||||
** would be required for this kind of query.
|
||||
**
|
||||
** Note that because the tablename, idcolumn, and parentcolumn can
|
||||
** all be specified in the query, it is possible for an application
|
||||
** to define a single transitive_closure virtual table for use on lots
|
||||
** of different hierarchy tables. One might say:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE temp.closure USING transitive_closure;
|
||||
**
|
||||
** As each database connection is being opened. Then the application
|
||||
** would always have a "closure" virtual table handy to use for querying.
|
||||
**
|
||||
** SELECT element.* FROM element, closure
|
||||
** WHERE element.groupid=ct1.id
|
||||
** AND closure.root=?1
|
||||
** AND closure.tablename='group'
|
||||
** AND closure.idname='groupId'
|
||||
** AND closure.parentname='parentId';
|
||||
**
|
||||
** See the documentation at http://www.sqlite.org/loadext.html for information
|
||||
** on how to compile and use loadable extensions such as this one.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/*
|
||||
** Forward declaration of objects used by this implementation
|
||||
*/
|
||||
typedef struct closure_vtab closure_vtab;
|
||||
typedef struct closure_cursor closure_cursor;
|
||||
typedef struct closure_queue closure_queue;
|
||||
typedef struct closure_avl closure_avl;
|
||||
|
||||
/*****************************************************************************
|
||||
** AVL Tree implementation
|
||||
*/
|
||||
/*
|
||||
** Objects that want to be members of the AVL tree should embedded an
|
||||
** instance of this structure.
|
||||
*/
|
||||
struct closure_avl {
|
||||
sqlite3_int64 id; /* Id of this entry in the table */
|
||||
int iGeneration; /* Which generation is this entry part of */
|
||||
closure_avl *pList; /* A linked list of nodes */
|
||||
closure_avl *pBefore; /* Other elements less than id */
|
||||
closure_avl *pAfter; /* Other elements greater than id */
|
||||
closure_avl *pUp; /* Parent element */
|
||||
short int height; /* Height of this node. Leaf==1 */
|
||||
short int imbalance; /* Height difference between pBefore and pAfter */
|
||||
};
|
||||
|
||||
/* Recompute the closure_avl.height and closure_avl.imbalance fields for p.
|
||||
** Assume that the children of p have correct heights.
|
||||
*/
|
||||
static void closureAvlRecomputeHeight(closure_avl *p){
|
||||
short int hBefore = p->pBefore ? p->pBefore->height : 0;
|
||||
short int hAfter = p->pAfter ? p->pAfter->height : 0;
|
||||
p->imbalance = hBefore - hAfter; /* -: pAfter higher. +: pBefore higher */
|
||||
p->height = (hBefore>hAfter ? hBefore : hAfter)+1;
|
||||
}
|
||||
|
||||
/*
|
||||
** P B
|
||||
** / \ / \
|
||||
** B Z ==> X P
|
||||
** / \ / \
|
||||
** X Y Y Z
|
||||
**
|
||||
*/
|
||||
static closure_avl *closureAvlRotateBefore(closure_avl *pP){
|
||||
closure_avl *pB = pP->pBefore;
|
||||
closure_avl *pY = pB->pAfter;
|
||||
pB->pUp = pP->pUp;
|
||||
pB->pAfter = pP;
|
||||
pP->pUp = pB;
|
||||
pP->pBefore = pY;
|
||||
if( pY ) pY->pUp = pP;
|
||||
closureAvlRecomputeHeight(pP);
|
||||
closureAvlRecomputeHeight(pB);
|
||||
return pB;
|
||||
}
|
||||
|
||||
/*
|
||||
** P A
|
||||
** / \ / \
|
||||
** X A ==> P Z
|
||||
** / \ / \
|
||||
** Y Z X Y
|
||||
**
|
||||
*/
|
||||
static closure_avl *closureAvlRotateAfter(closure_avl *pP){
|
||||
closure_avl *pA = pP->pAfter;
|
||||
closure_avl *pY = pA->pBefore;
|
||||
pA->pUp = pP->pUp;
|
||||
pA->pBefore = pP;
|
||||
pP->pUp = pA;
|
||||
pP->pAfter = pY;
|
||||
if( pY ) pY->pUp = pP;
|
||||
closureAvlRecomputeHeight(pP);
|
||||
closureAvlRecomputeHeight(pA);
|
||||
return pA;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the pBefore or pAfter pointer in the parent
|
||||
** of p that points to p. Or if p is the root node, return pp.
|
||||
*/
|
||||
static closure_avl **closureAvlFromPtr(closure_avl *p, closure_avl **pp){
|
||||
closure_avl *pUp = p->pUp;
|
||||
if( pUp==0 ) return pp;
|
||||
if( pUp->pAfter==p ) return &pUp->pAfter;
|
||||
return &pUp->pBefore;
|
||||
}
|
||||
|
||||
/*
|
||||
** Rebalance all nodes starting with p and working up to the root.
|
||||
** Return the new root.
|
||||
*/
|
||||
static closure_avl *closureAvlBalance(closure_avl *p){
|
||||
closure_avl *pTop = p;
|
||||
closure_avl **pp;
|
||||
while( p ){
|
||||
closureAvlRecomputeHeight(p);
|
||||
if( p->imbalance>=2 ){
|
||||
closure_avl *pB = p->pBefore;
|
||||
if( pB->imbalance<0 ) p->pBefore = closureAvlRotateAfter(pB);
|
||||
pp = closureAvlFromPtr(p,&p);
|
||||
p = *pp = closureAvlRotateBefore(p);
|
||||
}else if( p->imbalance<=(-2) ){
|
||||
closure_avl *pA = p->pAfter;
|
||||
if( pA->imbalance>0 ) p->pAfter = closureAvlRotateBefore(pA);
|
||||
pp = closureAvlFromPtr(p,&p);
|
||||
p = *pp = closureAvlRotateAfter(p);
|
||||
}
|
||||
pTop = p;
|
||||
p = p->pUp;
|
||||
}
|
||||
return pTop;
|
||||
}
|
||||
|
||||
/* Search the tree rooted at p for an entry with id. Return a pointer
|
||||
** to the entry or return NULL.
|
||||
*/
|
||||
static closure_avl *closureAvlSearch(closure_avl *p, sqlite3_int64 id){
|
||||
while( p && id!=p->id ){
|
||||
p = (id<p->id) ? p->pBefore : p->pAfter;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Find the first node (the one with the smallest key).
|
||||
*/
|
||||
static closure_avl *closureAvlFirst(closure_avl *p){
|
||||
if( p ) while( p->pBefore ) p = p->pBefore;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Return the node with the next larger key after p.
|
||||
*/
|
||||
closure_avl *closureAvlNext(closure_avl *p){
|
||||
closure_avl *pPrev = 0;
|
||||
while( p && p->pAfter==pPrev ){
|
||||
pPrev = p;
|
||||
p = p->pUp;
|
||||
}
|
||||
if( p && pPrev==0 ){
|
||||
p = closureAvlFirst(p->pAfter);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Insert a new node pNew. Return NULL on success. If the key is not
|
||||
** unique, then do not perform the insert but instead leave pNew unchanged
|
||||
** and return a pointer to an existing node with the same key.
|
||||
*/
|
||||
static closure_avl *closureAvlInsert(
|
||||
closure_avl **ppHead, /* Head of the tree */
|
||||
closure_avl *pNew /* New node to be inserted */
|
||||
){
|
||||
closure_avl *p = *ppHead;
|
||||
if( p==0 ){
|
||||
p = pNew;
|
||||
pNew->pUp = 0;
|
||||
}else{
|
||||
while( p ){
|
||||
if( pNew->id<p->id ){
|
||||
if( p->pBefore ){
|
||||
p = p->pBefore;
|
||||
}else{
|
||||
p->pBefore = pNew;
|
||||
pNew->pUp = p;
|
||||
break;
|
||||
}
|
||||
}else if( pNew->id>p->id ){
|
||||
if( p->pAfter ){
|
||||
p = p->pAfter;
|
||||
}else{
|
||||
p->pAfter = pNew;
|
||||
pNew->pUp = p;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
pNew->pBefore = 0;
|
||||
pNew->pAfter = 0;
|
||||
pNew->height = 1;
|
||||
pNew->imbalance = 0;
|
||||
*ppHead = closureAvlBalance(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Walk the tree can call xDestroy on each node
|
||||
*/
|
||||
static void closureAvlDestroy(closure_avl *p, void (*xDestroy)(closure_avl*)){
|
||||
if( p ){
|
||||
closureAvlDestroy(p->pBefore, xDestroy);
|
||||
closureAvlDestroy(p->pAfter, xDestroy);
|
||||
xDestroy(p);
|
||||
}
|
||||
}
|
||||
/*
|
||||
** End of the AVL Tree implementation
|
||||
******************************************************************************/
|
||||
|
||||
/*
|
||||
** A closure virtual-table object
|
||||
*/
|
||||
struct closure_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
char *zDb; /* Name of database. (ex: "main") */
|
||||
char *zSelf; /* Name of this virtual table */
|
||||
char *zTableName; /* Name of table holding parent/child relation */
|
||||
char *zIdColumn; /* Name of ID column of zTableName */
|
||||
char *zParentColumn; /* Name of PARENT column in zTableName */
|
||||
sqlite3 *db; /* The database connection */
|
||||
int nCursor; /* Number of pending cursors */
|
||||
};
|
||||
|
||||
/* A closure cursor object */
|
||||
struct closure_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
closure_vtab *pVtab; /* The virtual table this cursor belongs to */
|
||||
char *zTableName; /* Name of table holding parent/child relation */
|
||||
char *zIdColumn; /* Name of ID column of zTableName */
|
||||
char *zParentColumn; /* Name of PARENT column in zTableName */
|
||||
closure_avl *pCurrent; /* Current element of output */
|
||||
closure_avl *pClosure; /* The complete closure tree */
|
||||
};
|
||||
|
||||
/* A queue of AVL nodes */
|
||||
struct closure_queue {
|
||||
closure_avl *pFirst; /* Oldest node on the queue */
|
||||
closure_avl *pLast; /* Youngest node on the queue */
|
||||
};
|
||||
|
||||
/*
|
||||
** Add a node to the end of the queue
|
||||
*/
|
||||
static void queuePush(closure_queue *pQueue, closure_avl *pNode){
|
||||
pNode->pList = 0;
|
||||
if( pQueue->pLast ){
|
||||
pQueue->pLast->pList = pNode;
|
||||
}else{
|
||||
pQueue->pFirst = pNode;
|
||||
}
|
||||
pQueue->pLast = pNode;
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract the oldest element (the front element) from the queue.
|
||||
*/
|
||||
static closure_avl *queuePull(closure_queue *pQueue){
|
||||
closure_avl *p = pQueue->pFirst;
|
||||
if( p ){
|
||||
pQueue->pFirst = p->pList;
|
||||
if( pQueue->pFirst==0 ) pQueue->pLast = 0;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function converts an SQL quoted string into an unquoted string
|
||||
** and returns a pointer to a buffer allocated using sqlite3_malloc()
|
||||
** containing the result. The caller should eventually free this buffer
|
||||
** using sqlite3_free.
|
||||
**
|
||||
** Examples:
|
||||
**
|
||||
** "abc" becomes abc
|
||||
** 'xyz' becomes xyz
|
||||
** [pqr] becomes pqr
|
||||
** `mno` becomes mno
|
||||
*/
|
||||
static char *closureDequote(const char *zIn){
|
||||
sqlite3_int64 nIn; /* Size of input string, in bytes */
|
||||
char *zOut; /* Output (dequoted) string */
|
||||
|
||||
nIn = strlen(zIn);
|
||||
zOut = sqlite3_malloc64(nIn+1);
|
||||
if( zOut ){
|
||||
char q = zIn[0]; /* Quote character (if any ) */
|
||||
|
||||
if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
|
||||
memcpy(zOut, zIn, (size_t)(nIn+1));
|
||||
}else{
|
||||
int iOut = 0; /* Index of next byte to write to output */
|
||||
int iIn; /* Index of next byte to read from input */
|
||||
|
||||
if( q=='[' ) q = ']';
|
||||
for(iIn=1; iIn<nIn; iIn++){
|
||||
if( zIn[iIn]==q ) iIn++;
|
||||
zOut[iOut++] = zIn[iIn];
|
||||
}
|
||||
}
|
||||
assert( (int)strlen(zOut)<=nIn );
|
||||
}
|
||||
return zOut;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate an closure_vtab object
|
||||
*/
|
||||
static void closureFree(closure_vtab *p){
|
||||
if( p ){
|
||||
sqlite3_free(p->zDb);
|
||||
sqlite3_free(p->zSelf);
|
||||
sqlite3_free(p->zTableName);
|
||||
sqlite3_free(p->zIdColumn);
|
||||
sqlite3_free(p->zParentColumn);
|
||||
memset(p, 0, sizeof(*p));
|
||||
sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** xDisconnect/xDestroy method for the closure module.
|
||||
*/
|
||||
static int closureDisconnect(sqlite3_vtab *pVtab){
|
||||
closure_vtab *p = (closure_vtab*)pVtab;
|
||||
assert( p->nCursor==0 );
|
||||
closureFree(p);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if the argument is of the form:
|
||||
**
|
||||
** KEY = VALUE
|
||||
**
|
||||
** If it is, return a pointer to the first character of VALUE.
|
||||
** If not, return NULL. Spaces around the = are ignored.
|
||||
*/
|
||||
static const char *closureValueOfKey(const char *zKey, const char *zStr){
|
||||
int nKey = (int)strlen(zKey);
|
||||
int nStr = (int)strlen(zStr);
|
||||
int i;
|
||||
if( nStr<nKey+1 ) return 0;
|
||||
if( memcmp(zStr, zKey, nKey)!=0 ) return 0;
|
||||
for(i=nKey; isspace((unsigned char)zStr[i]); i++){}
|
||||
if( zStr[i]!='=' ) return 0;
|
||||
i++;
|
||||
while( isspace((unsigned char)zStr[i]) ){ i++; }
|
||||
return zStr+i;
|
||||
}
|
||||
|
||||
/*
|
||||
** xConnect/xCreate method for the closure module. Arguments are:
|
||||
**
|
||||
** argv[0] -> module name ("transitive_closure")
|
||||
** argv[1] -> database name
|
||||
** argv[2] -> table name
|
||||
** argv[3...] -> arguments
|
||||
*/
|
||||
static int closureConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
closure_vtab *pNew = 0; /* New virtual table */
|
||||
const char *zDb = argv[1];
|
||||
const char *zVal;
|
||||
int i;
|
||||
|
||||
(void)pAux;
|
||||
*ppVtab = 0;
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->db = db;
|
||||
pNew->zDb = sqlite3_mprintf("%s", zDb);
|
||||
if( pNew->zDb==0 ) goto closureConnectError;
|
||||
pNew->zSelf = sqlite3_mprintf("%s", argv[2]);
|
||||
if( pNew->zSelf==0 ) goto closureConnectError;
|
||||
for(i=3; i<argc; i++){
|
||||
zVal = closureValueOfKey("tablename", argv[i]);
|
||||
if( zVal ){
|
||||
sqlite3_free(pNew->zTableName);
|
||||
pNew->zTableName = closureDequote(zVal);
|
||||
if( pNew->zTableName==0 ) goto closureConnectError;
|
||||
continue;
|
||||
}
|
||||
zVal = closureValueOfKey("idcolumn", argv[i]);
|
||||
if( zVal ){
|
||||
sqlite3_free(pNew->zIdColumn);
|
||||
pNew->zIdColumn = closureDequote(zVal);
|
||||
if( pNew->zIdColumn==0 ) goto closureConnectError;
|
||||
continue;
|
||||
}
|
||||
zVal = closureValueOfKey("parentcolumn", argv[i]);
|
||||
if( zVal ){
|
||||
sqlite3_free(pNew->zParentColumn);
|
||||
pNew->zParentColumn = closureDequote(zVal);
|
||||
if( pNew->zParentColumn==0 ) goto closureConnectError;
|
||||
continue;
|
||||
}
|
||||
*pzErr = sqlite3_mprintf("unrecognized argument: [%s]\n", argv[i]);
|
||||
closureFree(pNew);
|
||||
*ppVtab = 0;
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(id,depth,root HIDDEN,tablename HIDDEN,"
|
||||
"idcolumn HIDDEN,parentcolumn HIDDEN)"
|
||||
);
|
||||
#define CLOSURE_COL_ID 0
|
||||
#define CLOSURE_COL_DEPTH 1
|
||||
#define CLOSURE_COL_ROOT 2
|
||||
#define CLOSURE_COL_TABLENAME 3
|
||||
#define CLOSURE_COL_IDCOLUMN 4
|
||||
#define CLOSURE_COL_PARENTCOLUMN 5
|
||||
if( rc!=SQLITE_OK ){
|
||||
closureFree(pNew);
|
||||
}
|
||||
*ppVtab = &pNew->base;
|
||||
return rc;
|
||||
|
||||
closureConnectError:
|
||||
closureFree(pNew);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new closure cursor.
|
||||
*/
|
||||
static int closureOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
closure_vtab *p = (closure_vtab*)pVTab;
|
||||
closure_cursor *pCur;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->pVtab = p;
|
||||
*ppCursor = &pCur->base;
|
||||
p->nCursor++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free up all the memory allocated by a cursor. Set it rLimit to 0
|
||||
** to indicate that it is at EOF.
|
||||
*/
|
||||
static void closureClearCursor(closure_cursor *pCur){
|
||||
closureAvlDestroy(pCur->pClosure, (void(*)(closure_avl*))sqlite3_free);
|
||||
sqlite3_free(pCur->zTableName);
|
||||
sqlite3_free(pCur->zIdColumn);
|
||||
sqlite3_free(pCur->zParentColumn);
|
||||
pCur->zTableName = 0;
|
||||
pCur->zIdColumn = 0;
|
||||
pCur->zParentColumn = 0;
|
||||
pCur->pCurrent = 0;
|
||||
pCur->pClosure = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a closure cursor.
|
||||
*/
|
||||
static int closureClose(sqlite3_vtab_cursor *cur){
|
||||
closure_cursor *pCur = (closure_cursor *)cur;
|
||||
closureClearCursor(pCur);
|
||||
pCur->pVtab->nCursor--;
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Advance a cursor to its next row of output
|
||||
*/
|
||||
static int closureNext(sqlite3_vtab_cursor *cur){
|
||||
closure_cursor *pCur = (closure_cursor*)cur;
|
||||
pCur->pCurrent = closureAvlNext(pCur->pCurrent);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and insert a node
|
||||
*/
|
||||
static int closureInsertNode(
|
||||
closure_queue *pQueue, /* Add new node to this queue */
|
||||
closure_cursor *pCur, /* The cursor into which to add the node */
|
||||
sqlite3_int64 id, /* The node ID */
|
||||
int iGeneration /* The generation number for this node */
|
||||
){
|
||||
closure_avl *pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->id = id;
|
||||
pNew->iGeneration = iGeneration;
|
||||
closureAvlInsert(&pCur->pClosure, pNew);
|
||||
queuePush(pQueue, pNew);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to "rewind" a cursor back to the beginning so that
|
||||
** it starts its output over again. Always called at least once
|
||||
** prior to any closureColumn, closureRowid, or closureEof call.
|
||||
**
|
||||
** This routine actually computes the closure.
|
||||
**
|
||||
** See the comment at the beginning of closureBestIndex() for a
|
||||
** description of the meaning of idxNum. The idxStr parameter is
|
||||
** not used.
|
||||
*/
|
||||
static int closureFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
closure_cursor *pCur = (closure_cursor *)pVtabCursor;
|
||||
closure_vtab *pVtab = pCur->pVtab;
|
||||
sqlite3_int64 iRoot;
|
||||
int mxGen = 999999999;
|
||||
char *zSql;
|
||||
sqlite3_stmt *pStmt;
|
||||
closure_avl *pAvl;
|
||||
int rc = SQLITE_OK;
|
||||
const char *zTableName = pVtab->zTableName;
|
||||
const char *zIdColumn = pVtab->zIdColumn;
|
||||
const char *zParentColumn = pVtab->zParentColumn;
|
||||
closure_queue sQueue;
|
||||
|
||||
(void)idxStr; /* Unused parameter */
|
||||
(void)argc; /* Unused parameter */
|
||||
closureClearCursor(pCur);
|
||||
memset(&sQueue, 0, sizeof(sQueue));
|
||||
if( (idxNum & 1)==0 ){
|
||||
/* No root=$root in the WHERE clause. Return an empty set */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
iRoot = sqlite3_value_int64(argv[0]);
|
||||
if( (idxNum & 0x000f0)!=0 ){
|
||||
mxGen = sqlite3_value_int(argv[(idxNum>>4)&0x0f]);
|
||||
if( (idxNum & 0x00002)!=0 ) mxGen--;
|
||||
}
|
||||
if( (idxNum & 0x00f00)!=0 ){
|
||||
zTableName = (const char*)sqlite3_value_text(argv[(idxNum>>8)&0x0f]);
|
||||
pCur->zTableName = sqlite3_mprintf("%s", zTableName);
|
||||
}
|
||||
if( (idxNum & 0x0f000)!=0 ){
|
||||
zIdColumn = (const char*)sqlite3_value_text(argv[(idxNum>>12)&0x0f]);
|
||||
pCur->zIdColumn = sqlite3_mprintf("%s", zIdColumn);
|
||||
}
|
||||
if( (idxNum & 0x0f0000)!=0 ){
|
||||
zParentColumn = (const char*)sqlite3_value_text(argv[(idxNum>>16)&0x0f]);
|
||||
pCur->zParentColumn = sqlite3_mprintf("%s", zParentColumn);
|
||||
}
|
||||
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT \"%w\".\"%w\" FROM \"%w\" WHERE \"%w\".\"%w\"=?1",
|
||||
zTableName, zIdColumn, zTableName, zTableName, zParentColumn);
|
||||
if( zSql==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(pVtab->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ){
|
||||
sqlite3_free(pVtab->base.zErrMsg);
|
||||
pVtab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pVtab->db));
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = closureInsertNode(&sQueue, pCur, iRoot, 0);
|
||||
}
|
||||
while( (pAvl = queuePull(&sQueue))!=0 ){
|
||||
if( pAvl->iGeneration>=mxGen ) continue;
|
||||
sqlite3_bind_int64(pStmt, 1, pAvl->id);
|
||||
while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
if( sqlite3_column_type(pStmt,0)==SQLITE_INTEGER ){
|
||||
sqlite3_int64 iNew = sqlite3_column_int64(pStmt, 0);
|
||||
if( closureAvlSearch(pCur->pClosure, iNew)==0 ){
|
||||
rc = closureInsertNode(&sQueue, pCur, iNew, pAvl->iGeneration+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_reset(pStmt);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
if( rc==SQLITE_OK ){
|
||||
pCur->pCurrent = closureAvlFirst(pCur->pClosure);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Only the word and distance columns have values. All other columns
|
||||
** return NULL
|
||||
*/
|
||||
static int closureColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
closure_cursor *pCur = (closure_cursor*)cur;
|
||||
switch( i ){
|
||||
case CLOSURE_COL_ID: {
|
||||
sqlite3_result_int64(ctx, pCur->pCurrent->id);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_COL_DEPTH: {
|
||||
sqlite3_result_int(ctx, pCur->pCurrent->iGeneration);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_COL_ROOT: {
|
||||
sqlite3_result_null(ctx);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_COL_TABLENAME: {
|
||||
sqlite3_result_text(ctx,
|
||||
pCur->zTableName ? pCur->zTableName : pCur->pVtab->zTableName,
|
||||
-1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_COL_IDCOLUMN: {
|
||||
sqlite3_result_text(ctx,
|
||||
pCur->zIdColumn ? pCur->zIdColumn : pCur->pVtab->zIdColumn,
|
||||
-1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case CLOSURE_COL_PARENTCOLUMN: {
|
||||
sqlite3_result_text(ctx,
|
||||
pCur->zParentColumn ? pCur->zParentColumn : pCur->pVtab->zParentColumn,
|
||||
-1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The rowid. For the closure table, this is the same as the "id" column.
|
||||
*/
|
||||
static int closureRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
closure_cursor *pCur = (closure_cursor*)cur;
|
||||
*pRowid = pCur->pCurrent->id;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** EOF indicator
|
||||
*/
|
||||
static int closureEof(sqlite3_vtab_cursor *cur){
|
||||
closure_cursor *pCur = (closure_cursor*)cur;
|
||||
return pCur->pCurrent==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Search for terms of these forms:
|
||||
**
|
||||
** (A) root = $root
|
||||
** (B1) depth < $depth
|
||||
** (B2) depth <= $depth
|
||||
** (B3) depth = $depth
|
||||
** (C) tablename = $tablename
|
||||
** (D) idcolumn = $idcolumn
|
||||
** (E) parentcolumn = $parentcolumn
|
||||
**
|
||||
**
|
||||
**
|
||||
** idxNum meaning
|
||||
** ---------- ------------------------------------------------------
|
||||
** 0x00000001 Term of the form (A) found
|
||||
** 0x00000002 The term of bit-2 is like (B1)
|
||||
** 0x000000f0 Index in filter.argv[] of $depth. 0 if not used.
|
||||
** 0x00000f00 Index in filter.argv[] of $tablename. 0 if not used.
|
||||
** 0x0000f000 Index in filter.argv[] of $idcolumn. 0 if not used
|
||||
** 0x000f0000 Index in filter.argv[] of $parentcolumn. 0 if not used.
|
||||
**
|
||||
** There must be a term of type (A). If there is not, then the index type
|
||||
** is 0 and the query will return an empty set.
|
||||
*/
|
||||
static int closureBestIndex(
|
||||
sqlite3_vtab *pTab, /* The virtual table */
|
||||
sqlite3_index_info *pIdxInfo /* Information about the query */
|
||||
){
|
||||
int iPlan = 0;
|
||||
int i;
|
||||
int idx = 1;
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
closure_vtab *pVtab = (closure_vtab*)pTab;
|
||||
double rCost = 10000000.0;
|
||||
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
if( pConstraint->usable==0 ) continue;
|
||||
if( (iPlan & 1)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_ROOT
|
||||
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
){
|
||||
iPlan |= 1;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
rCost /= 100.0;
|
||||
}
|
||||
if( (iPlan & 0x0000f0)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_DEPTH
|
||||
&& (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT
|
||||
|| pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE
|
||||
|| pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ)
|
||||
){
|
||||
iPlan |= idx<<4;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||||
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ) iPlan |= 0x000002;
|
||||
rCost /= 5.0;
|
||||
}
|
||||
if( (iPlan & 0x000f00)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_TABLENAME
|
||||
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
){
|
||||
iPlan |= idx<<8;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
rCost /= 5.0;
|
||||
}
|
||||
if( (iPlan & 0x00f000)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_IDCOLUMN
|
||||
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
){
|
||||
iPlan |= idx<<12;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
}
|
||||
if( (iPlan & 0x0f0000)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_PARENTCOLUMN
|
||||
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
){
|
||||
iPlan |= idx<<16;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
}
|
||||
}
|
||||
if( (pVtab->zTableName==0 && (iPlan & 0x000f00)==0)
|
||||
|| (pVtab->zIdColumn==0 && (iPlan & 0x00f000)==0)
|
||||
|| (pVtab->zParentColumn==0 && (iPlan & 0x0f0000)==0)
|
||||
){
|
||||
/* All of tablename, idcolumn, and parentcolumn must be specified
|
||||
** in either the CREATE VIRTUAL TABLE or in the WHERE clause constraints
|
||||
** or else the result is an empty set. */
|
||||
iPlan = 0;
|
||||
}
|
||||
if( (iPlan&1)==0 ){
|
||||
/* If there is no usable "root=?" term, then set the index-type to 0.
|
||||
** Also clear any argvIndex variables already set. This is necessary
|
||||
** to prevent the core from throwing an "xBestIndex malfunction error"
|
||||
** error (because the argvIndex values are not contiguously assigned
|
||||
** starting from 1). */
|
||||
rCost *= 1e30;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = 0;
|
||||
}
|
||||
iPlan = 0;
|
||||
}
|
||||
pIdxInfo->idxNum = iPlan;
|
||||
if( pIdxInfo->nOrderBy==1
|
||||
&& pIdxInfo->aOrderBy[0].iColumn==CLOSURE_COL_ID
|
||||
&& pIdxInfo->aOrderBy[0].desc==0
|
||||
){
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
pIdxInfo->estimatedCost = rCost;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that implements the "transitive_closure".
|
||||
*/
|
||||
static sqlite3_module closureModule = {
|
||||
0, /* iVersion */
|
||||
closureConnect, /* xCreate */
|
||||
closureConnect, /* xConnect */
|
||||
closureBestIndex, /* xBestIndex */
|
||||
closureDisconnect, /* xDisconnect */
|
||||
closureDisconnect, /* xDestroy */
|
||||
closureOpen, /* xOpen - open a cursor */
|
||||
closureClose, /* xClose - close a cursor */
|
||||
closureFilter, /* xFilter - configure scan constraints */
|
||||
closureNext, /* xNext - advance a cursor */
|
||||
closureEof, /* xEof - check for end of scan */
|
||||
closureColumn, /* xColumn - read data */
|
||||
closureRowid, /* 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 */
|
||||
|
||||
/*
|
||||
** Register the closure virtual table
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_closure_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3_create_module(db, "transitive_closure", &closureModule, 0);
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
return rc;
|
||||
}
|
||||
@@ -1,502 +0,0 @@
|
||||
/*
|
||||
** 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
|
||||
|
||||
/* 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_malloc( 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_malloc( 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 */
|
||||
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;
|
||||
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);
|
||||
zSql = sqlite3_mprintf(
|
||||
"%z%s"
|
||||
"SELECT name FROM \"%w\".sqlite_schema",
|
||||
zSql, zSep, zDb
|
||||
);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
zSep = " UNION ";
|
||||
}
|
||||
sqlite3_finalize(pS2);
|
||||
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
iCol = 0;
|
||||
eNextPhase = COMPLETION_COLUMNS;
|
||||
break;
|
||||
}
|
||||
case COMPLETION_COLUMNS: {
|
||||
if( pCur->pStmt==0 ){
|
||||
sqlite3_stmt *pS2;
|
||||
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);
|
||||
zSql = sqlite3_mprintf(
|
||||
"%z%s"
|
||||
"SELECT pti.name FROM \"%w\".sqlite_schema AS sm"
|
||||
" JOIN pragma_table_xinfo(sm.name,%Q) AS pti"
|
||||
" WHERE sm.type='table'",
|
||||
zSql, zSep, zDb, zDb
|
||||
);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
zSep = " UNION ";
|
||||
}
|
||||
sqlite3_finalize(pS2);
|
||||
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
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 */
|
||||
sqlite3_finalize(pCur->pStmt);
|
||||
pCur->pStmt = 0;
|
||||
pCur->ePhase = eNextPhase;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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->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
|
||||
__declspec(dllexport)
|
||||
#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;
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
** 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 compression functions
|
||||
** compress() and uncompress() using ZLIB.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <zlib.h>
|
||||
|
||||
/*
|
||||
** Implementation of the "compress(X)" SQL function. The input X is
|
||||
** compressed using zLib and the output is returned.
|
||||
**
|
||||
** The output is a BLOB that begins with a variable-length integer that
|
||||
** is the input size in bytes (the size of X before compression). The
|
||||
** variable-length integer is implemented as 1 to 5 bytes. There are
|
||||
** seven bits per integer stored in the lower seven bits of each byte.
|
||||
** More significant bits occur first. The most significant bit (0x80)
|
||||
** is a flag to indicate the end of the integer.
|
||||
**
|
||||
** This function, SQLAR, and ZIP all use the same "deflate" compression
|
||||
** algorithm, but each is subtly different:
|
||||
**
|
||||
** * ZIP uses raw deflate.
|
||||
**
|
||||
** * SQLAR uses the "zlib format" which is raw deflate with a two-byte
|
||||
** algorithm-identification header and a four-byte checksum at the end.
|
||||
**
|
||||
** * This utility uses the "zlib format" like SQLAR, but adds the variable-
|
||||
** length integer uncompressed size value at the beginning.
|
||||
**
|
||||
** This function might be extended in the future to support compression
|
||||
** formats other than deflate, by providing a different algorithm-id
|
||||
** mark following the variable-length integer size parameter.
|
||||
*/
|
||||
static void compressFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const unsigned char *pIn;
|
||||
unsigned char *pOut;
|
||||
unsigned int nIn;
|
||||
unsigned long int nOut;
|
||||
unsigned char x[8];
|
||||
int rc;
|
||||
int i, j;
|
||||
|
||||
pIn = sqlite3_value_blob(argv[0]);
|
||||
nIn = sqlite3_value_bytes(argv[0]);
|
||||
nOut = 13 + nIn + (nIn+999)/1000;
|
||||
pOut = sqlite3_malloc( nOut+5 );
|
||||
for(i=4; i>=0; i--){
|
||||
x[i] = (nIn >> (7*(4-i)))&0x7f;
|
||||
}
|
||||
for(i=0; i<4 && x[i]==0; i++){}
|
||||
for(j=0; i<=4; i++, j++) pOut[j] = x[i];
|
||||
pOut[j-1] |= 0x80;
|
||||
rc = compress(&pOut[j], &nOut, pIn, nIn);
|
||||
if( rc==Z_OK ){
|
||||
sqlite3_result_blob(context, pOut, nOut+j, sqlite3_free);
|
||||
}else{
|
||||
sqlite3_free(pOut);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the "uncompress(X)" SQL function. The argument X
|
||||
** is a blob which was obtained from compress(Y). The output will be
|
||||
** the value Y.
|
||||
*/
|
||||
static void uncompressFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const unsigned char *pIn;
|
||||
unsigned char *pOut;
|
||||
unsigned int nIn;
|
||||
unsigned long int nOut;
|
||||
int rc;
|
||||
unsigned int i;
|
||||
|
||||
pIn = sqlite3_value_blob(argv[0]);
|
||||
nIn = sqlite3_value_bytes(argv[0]);
|
||||
nOut = 0;
|
||||
for(i=0; i<nIn && i<5; i++){
|
||||
nOut = (nOut<<7) | (pIn[i]&0x7f);
|
||||
if( (pIn[i]&0x80)!=0 ){ i++; break; }
|
||||
}
|
||||
pOut = sqlite3_malloc( nOut+1 );
|
||||
rc = uncompress(pOut, &nOut, &pIn[i], nIn-i);
|
||||
if( rc==Z_OK ){
|
||||
sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
|
||||
}else{
|
||||
sqlite3_free(pOut);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_compress_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, "compress", 1,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS,
|
||||
0, compressFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "uncompress", 1,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
|
||||
0, uncompressFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1,884 +0,0 @@
|
||||
/*
|
||||
** 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
|
||||
|
||||
|
||||
/* 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;
|
||||
|
||||
p = sqlite3_malloc( 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 ){
|
||||
p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
|
||||
if( p->a==0 ) goto new_from_text_failed;
|
||||
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 ){
|
||||
p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
|
||||
if( p->a==0 ) goto new_from_text_failed;
|
||||
memmove(p->a+iExp, p->a, p->nDigit);
|
||||
memset(p->a, 0, iExp);
|
||||
p->nDigit += iExp;
|
||||
p->nFrac += iExp;
|
||||
}
|
||||
}
|
||||
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_malloc( 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);
|
||||
}
|
||||
|
||||
/*
|
||||
** 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){
|
||||
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;
|
||||
}
|
||||
for(nDigit=p->nDigit; nDigit>0 && 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_malloc( 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(const Decimal *pA, const Decimal *pB){
|
||||
int nASig, nBSig, rc, n;
|
||||
if( pA->sign!=pB->sign ){
|
||||
return pA->sign ? -1 : +1;
|
||||
}
|
||||
if( pA->sign ){
|
||||
const 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;
|
||||
if( p==0 ) return;
|
||||
nAddFrac = nFrac - p->nFrac;
|
||||
nAddSig = (nDigit - p->nDigit) - nAddFrac;
|
||||
if( nAddFrac==0 && nAddSig==0 ) return;
|
||||
p->a = sqlite3_realloc64(p->a, nDigit+1);
|
||||
if( p->a==0 ){
|
||||
p->oom = 1;
|
||||
return;
|
||||
}
|
||||
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;
|
||||
|
||||
if( pA==0 || pA->oom || pA->isNull
|
||||
|| pB==0 || pB->oom || pB->isNull
|
||||
){
|
||||
goto mul_end;
|
||||
}
|
||||
acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 );
|
||||
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 ){
|
||||
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);
|
||||
UNUSED_PARAMETER(argc);
|
||||
if( p ){
|
||||
if( sqlite3_user_data(context)!=0 ){
|
||||
decimal_result_sci(context, p);
|
||||
}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 funcion: 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_malloc(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);
|
||||
decimal_free(pA);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#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_exp", 1, 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;
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
** 2014-11-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 SQLite extension implements SQL function eval() which runs
|
||||
** SQL statements recursively.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Structure used to accumulate the output
|
||||
*/
|
||||
struct EvalResult {
|
||||
char *z; /* Accumulated output */
|
||||
const char *zSep; /* Separator */
|
||||
int szSep; /* Size of the separator string */
|
||||
sqlite3_int64 nAlloc; /* Number of bytes allocated for z[] */
|
||||
sqlite3_int64 nUsed; /* Number of bytes of z[] actually used */
|
||||
};
|
||||
|
||||
/*
|
||||
** Callback from sqlite_exec() for the eval() function.
|
||||
*/
|
||||
static int callback(void *pCtx, int argc, char **argv, char **colnames){
|
||||
struct EvalResult *p = (struct EvalResult*)pCtx;
|
||||
int i;
|
||||
if( argv==0 ) return 0;
|
||||
for(i=0; i<argc; i++){
|
||||
const char *z = argv[i] ? argv[i] : "";
|
||||
size_t sz = strlen(z);
|
||||
if( (sqlite3_int64)sz+p->nUsed+p->szSep+1 > p->nAlloc ){
|
||||
char *zNew;
|
||||
p->nAlloc = p->nAlloc*2 + sz + p->szSep + 1;
|
||||
/* Using sqlite3_realloc64() would be better, but it is a recent
|
||||
** addition and will cause a segfault if loaded by an older version
|
||||
** of SQLite. */
|
||||
zNew = p->nAlloc<=0x7fffffff ? sqlite3_realloc64(p->z, p->nAlloc) : 0;
|
||||
if( zNew==0 ){
|
||||
sqlite3_free(p->z);
|
||||
memset(p, 0, sizeof(*p));
|
||||
return 1;
|
||||
}
|
||||
p->z = zNew;
|
||||
}
|
||||
if( p->nUsed>0 ){
|
||||
memcpy(&p->z[p->nUsed], p->zSep, p->szSep);
|
||||
p->nUsed += p->szSep;
|
||||
}
|
||||
memcpy(&p->z[p->nUsed], z, sz);
|
||||
p->nUsed += sz;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the eval(X) and eval(X,Y) SQL functions.
|
||||
**
|
||||
** Evaluate the SQL text in X. Return the results, using string
|
||||
** Y as the separator. If Y is omitted, use a single space character.
|
||||
*/
|
||||
static void sqlEvalFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSql;
|
||||
sqlite3 *db;
|
||||
char *zErr = 0;
|
||||
int rc;
|
||||
struct EvalResult x;
|
||||
|
||||
memset(&x, 0, sizeof(x));
|
||||
x.zSep = " ";
|
||||
zSql = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zSql==0 ) return;
|
||||
if( argc>1 ){
|
||||
x.zSep = (const char*)sqlite3_value_text(argv[1]);
|
||||
if( x.zSep==0 ) return;
|
||||
}
|
||||
x.szSep = (int)strlen(x.zSep);
|
||||
db = sqlite3_context_db_handle(context);
|
||||
rc = sqlite3_exec(db, zSql, callback, &x, &zErr);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_result_error(context, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
}else if( x.zSep==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
sqlite3_free(x.z);
|
||||
}else{
|
||||
sqlite3_result_text(context, x.z, (int)x.nUsed, sqlite3_free);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_eval_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, "eval", 1,
|
||||
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
|
||||
sqlEvalFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "eval", 2,
|
||||
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
|
||||
sqlEvalFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
/*
|
||||
** 2018-09-16
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file demonstrates an eponymous virtual table that returns the
|
||||
** EXPLAIN output from an SQL statement.
|
||||
**
|
||||
** Usage example:
|
||||
**
|
||||
** .load ./explain
|
||||
** SELECT p2 FROM explain('SELECT * FROM sqlite_schema')
|
||||
** WHERE opcode='OpenRead';
|
||||
**
|
||||
** This module was originally written to help simplify SQLite testing,
|
||||
** by providing an easier means of verifying certain patterns in the
|
||||
** generated bytecode.
|
||||
*/
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include "sqlite3ext.h"
|
||||
#endif
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/* explain_vtab is a subclass of sqlite3_vtab which will
|
||||
** serve as the underlying representation of a explain virtual table
|
||||
*/
|
||||
typedef struct explain_vtab explain_vtab;
|
||||
struct explain_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
sqlite3 *db; /* Database connection for this explain vtab */
|
||||
};
|
||||
|
||||
/* explain_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 from an EXPLAIN operation.
|
||||
*/
|
||||
typedef struct explain_cursor explain_cursor;
|
||||
struct explain_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
sqlite3 *db; /* Database connection for this cursor */
|
||||
char *zSql; /* Value for the EXPLN_COLUMN_SQL column */
|
||||
sqlite3_stmt *pExplain; /* Statement being explained */
|
||||
int rc; /* Result of last sqlite3_step() on pExplain */
|
||||
};
|
||||
|
||||
/*
|
||||
** The explainConnect() method is invoked to create a new
|
||||
** explain_vtab that describes the explain virtual table.
|
||||
**
|
||||
** Think of this routine as the constructor for explain_vtab objects.
|
||||
**
|
||||
** All this routine needs to do is:
|
||||
**
|
||||
** (1) Allocate the explain_vtab object and initialize all fields.
|
||||
**
|
||||
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
||||
** result set of queries against explain will look like.
|
||||
*/
|
||||
static int explainConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
explain_vtab *pNew;
|
||||
int rc;
|
||||
|
||||
/* Column numbers */
|
||||
#define EXPLN_COLUMN_ADDR 0 /* Instruction address */
|
||||
#define EXPLN_COLUMN_OPCODE 1 /* Opcode */
|
||||
#define EXPLN_COLUMN_P1 2 /* Operand 1 */
|
||||
#define EXPLN_COLUMN_P2 3 /* Operand 2 */
|
||||
#define EXPLN_COLUMN_P3 4 /* Operand 3 */
|
||||
#define EXPLN_COLUMN_P4 5 /* Operand 4 */
|
||||
#define EXPLN_COLUMN_P5 6 /* Operand 5 */
|
||||
#define EXPLN_COLUMN_COMMENT 7 /* Comment */
|
||||
#define EXPLN_COLUMN_SQL 8 /* SQL that is being explained */
|
||||
|
||||
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(addr,opcode,p1,p2,p3,p4,p5,comment,sql HIDDEN)");
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc( 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 explain_cursor objects.
|
||||
*/
|
||||
static int explainDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Constructor for a new explain_cursor object.
|
||||
*/
|
||||
static int explainOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
explain_cursor *pCur;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->db = ((explain_vtab*)p)->db;
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor for a explain_cursor.
|
||||
*/
|
||||
static int explainClose(sqlite3_vtab_cursor *cur){
|
||||
explain_cursor *pCur = (explain_cursor*)cur;
|
||||
sqlite3_finalize(pCur->pExplain);
|
||||
sqlite3_free(pCur->zSql);
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance a explain_cursor to its next row of output.
|
||||
*/
|
||||
static int explainNext(sqlite3_vtab_cursor *cur){
|
||||
explain_cursor *pCur = (explain_cursor*)cur;
|
||||
pCur->rc = sqlite3_step(pCur->pExplain);
|
||||
if( pCur->rc!=SQLITE_DONE && pCur->rc!=SQLITE_ROW ) return pCur->rc;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return values of columns for the row at which the explain_cursor
|
||||
** is currently pointing.
|
||||
*/
|
||||
static int explainColumn(
|
||||
sqlite3_vtab_cursor *cur, /* The cursor */
|
||||
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
||||
int i /* Which column to return */
|
||||
){
|
||||
explain_cursor *pCur = (explain_cursor*)cur;
|
||||
if( i==EXPLN_COLUMN_SQL ){
|
||||
sqlite3_result_text(ctx, pCur->zSql, -1, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
sqlite3_result_value(ctx, sqlite3_column_value(pCur->pExplain, i));
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the rowid for the current row. In this implementation, the
|
||||
** rowid is the same as the output value.
|
||||
*/
|
||||
static int explainRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
explain_cursor *pCur = (explain_cursor*)cur;
|
||||
*pRowid = sqlite3_column_int64(pCur->pExplain, 0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the cursor has been moved off of the last
|
||||
** row of output.
|
||||
*/
|
||||
static int explainEof(sqlite3_vtab_cursor *cur){
|
||||
explain_cursor *pCur = (explain_cursor*)cur;
|
||||
return pCur->rc!=SQLITE_ROW;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is called to "rewind" the explain_cursor object back
|
||||
** to the first row of output. This method is always called at least
|
||||
** once prior to any call to explainColumn() or explainRowid() or
|
||||
** explainEof().
|
||||
**
|
||||
** The argv[0] is the SQL statement that is to be explained.
|
||||
*/
|
||||
static int explainFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
explain_cursor *pCur = (explain_cursor *)pVtabCursor;
|
||||
char *zSql = 0;
|
||||
int rc;
|
||||
sqlite3_finalize(pCur->pExplain);
|
||||
pCur->pExplain = 0;
|
||||
if( sqlite3_value_type(argv[0])!=SQLITE_TEXT ){
|
||||
pCur->rc = SQLITE_DONE;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
sqlite3_free(pCur->zSql);
|
||||
pCur->zSql = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
|
||||
if( pCur->zSql ){
|
||||
zSql = sqlite3_mprintf("EXPLAIN %s", pCur->zSql);
|
||||
}
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pExplain, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
if( rc ){
|
||||
sqlite3_finalize(pCur->pExplain);
|
||||
pCur->pExplain = 0;
|
||||
sqlite3_free(pCur->zSql);
|
||||
pCur->zSql = 0;
|
||||
}else{
|
||||
pCur->rc = sqlite3_step(pCur->pExplain);
|
||||
rc = (pCur->rc==SQLITE_DONE || pCur->rc==SQLITE_ROW) ? SQLITE_OK : pCur->rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the explain virtual table. This routine needs to create
|
||||
** a query plan for each invocation and compute an estimated cost for that
|
||||
** plan.
|
||||
*/
|
||||
static int explainBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
int i; /* Loop counter */
|
||||
int idx = -1; /* Index of a usable == constraint against SQL */
|
||||
int unusable = 0; /* True if there are unusable constraints on SQL */
|
||||
|
||||
pIdxInfo->estimatedRows = 500;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++){
|
||||
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i];
|
||||
if( p->iColumn!=EXPLN_COLUMN_SQL ) continue;
|
||||
if( !p->usable ){
|
||||
unusable = 1;
|
||||
}else if( p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
if( idx>=0 ){
|
||||
/* There exists a usable == constraint against the SQL column */
|
||||
pIdxInfo->estimatedCost = 10.0;
|
||||
pIdxInfo->idxNum = 1;
|
||||
pIdxInfo->aConstraintUsage[idx].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[idx].omit = 1;
|
||||
}else if( unusable ){
|
||||
/* There are unusable constraints against the SQL column. Do not allow
|
||||
** this plan to continue forward. */
|
||||
return SQLITE_CONSTRAINT;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This following structure defines all the methods for the
|
||||
** explain virtual table.
|
||||
*/
|
||||
static sqlite3_module explainModule = {
|
||||
0, /* iVersion */
|
||||
0, /* xCreate */
|
||||
explainConnect, /* xConnect */
|
||||
explainBestIndex, /* xBestIndex */
|
||||
explainDisconnect, /* xDisconnect */
|
||||
0, /* xDestroy */
|
||||
explainOpen, /* xOpen - open a cursor */
|
||||
explainClose, /* xClose - close a cursor */
|
||||
explainFilter, /* xFilter - configure scan constraints */
|
||||
explainNext, /* xNext - advance a cursor */
|
||||
explainEof, /* xEof - check for end of scan */
|
||||
explainColumn, /* xColumn - read data */
|
||||
explainRowid, /* 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 sqlite3ExplainVtabInit(sqlite3 *db){
|
||||
int rc = SQLITE_OK;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3_create_module(db, "explain", &explainModule, 0);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_explain_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3ExplainVtabInit(db);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,324 +0,0 @@
|
||||
/*
|
||||
** 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'3ff0000000000000') -> 1.0
|
||||
** ieee754_to_blob(1.0) -> x'3ff0000000000000'
|
||||
**
|
||||
** 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'000fffffffffffff'),
|
||||
** ('mininum positive nornal value', x'0010000000000000'),
|
||||
** ('maximum value', x'7fefffffffffffff'))
|
||||
** 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{
|
||||
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 ){
|
||||
isNeg = 1;
|
||||
m = -m;
|
||||
if( m<0 ) return;
|
||||
}else if( m==0 && e>-1000 && e<1000 ){
|
||||
sqlite3_result_double(context, 0.0);
|
||||
return;
|
||||
}
|
||||
while( (m>>32)&0xffe00000 ){
|
||||
m >>= 1;
|
||||
e++;
|
||||
}
|
||||
while( m!=0 && ((m>>32)&0xfff00000)==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>0x7ff ){
|
||||
e = 0x7ff;
|
||||
}
|
||||
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&0xff;
|
||||
v >>= 8;
|
||||
}
|
||||
sqlite3_result_blob(context, a, sizeof(r), SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** 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
|
||||
__declspec(dllexport)
|
||||
#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_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;
|
||||
}
|
||||
@@ -1,429 +0,0 @@
|
||||
/*
|
||||
** 2018-09-27
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file demonstrates an eponymous virtual table that returns information
|
||||
** from sqlite3_status64() and sqlite3_db_status().
|
||||
**
|
||||
** Usage example:
|
||||
**
|
||||
** .load ./memstat
|
||||
** .mode quote
|
||||
** .header on
|
||||
** SELECT * FROM memstat;
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_MEMSTATVTAB)
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include "sqlite3ext.h"
|
||||
#endif
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/* memstat_vtab is a subclass of sqlite3_vtab which will
|
||||
** serve as the underlying representation of a memstat virtual table
|
||||
*/
|
||||
typedef struct memstat_vtab memstat_vtab;
|
||||
struct memstat_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
sqlite3 *db; /* Database connection for this memstat vtab */
|
||||
};
|
||||
|
||||
/* memstat_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 memstat_cursor memstat_cursor;
|
||||
struct memstat_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
sqlite3 *db; /* Database connection for this cursor */
|
||||
int iRowid; /* Current row in aMemstatColumn[] */
|
||||
int iDb; /* Which schema we are looking at */
|
||||
int nDb; /* Number of schemas */
|
||||
char **azDb; /* Names of all schemas */
|
||||
sqlite3_int64 aVal[2]; /* Result values */
|
||||
};
|
||||
|
||||
/*
|
||||
** The memstatConnect() method is invoked to create a new
|
||||
** memstat_vtab that describes the memstat virtual table.
|
||||
**
|
||||
** Think of this routine as the constructor for memstat_vtab objects.
|
||||
**
|
||||
** All this routine needs to do is:
|
||||
**
|
||||
** (1) Allocate the memstat_vtab object and initialize all fields.
|
||||
**
|
||||
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
||||
** result set of queries against memstat will look like.
|
||||
*/
|
||||
static int memstatConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
memstat_vtab *pNew;
|
||||
int rc;
|
||||
|
||||
/* Column numbers */
|
||||
#define MSV_COLUMN_NAME 0 /* Name of quantity being measured */
|
||||
#define MSV_COLUMN_SCHEMA 1 /* schema name */
|
||||
#define MSV_COLUMN_VALUE 2 /* Current value */
|
||||
#define MSV_COLUMN_HIWTR 3 /* Highwater mark */
|
||||
|
||||
rc = sqlite3_declare_vtab(db,"CREATE TABLE x(name,schema,value,hiwtr)");
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc( 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 memstat_cursor objects.
|
||||
*/
|
||||
static int memstatDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Constructor for a new memstat_cursor object.
|
||||
*/
|
||||
static int memstatOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
memstat_cursor *pCur;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->db = ((memstat_vtab*)p)->db;
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear all the schema names from a cursor
|
||||
*/
|
||||
static void memstatClearSchema(memstat_cursor *pCur){
|
||||
int i;
|
||||
if( pCur->azDb==0 ) return;
|
||||
for(i=0; i<pCur->nDb; i++){
|
||||
sqlite3_free(pCur->azDb[i]);
|
||||
}
|
||||
sqlite3_free(pCur->azDb);
|
||||
pCur->azDb = 0;
|
||||
pCur->nDb = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Fill in the azDb[] array for the cursor.
|
||||
*/
|
||||
static int memstatFindSchemas(memstat_cursor *pCur){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int rc;
|
||||
if( pCur->nDb ) return SQLITE_OK;
|
||||
rc = sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pStmt, 0);
|
||||
if( rc ){
|
||||
sqlite3_finalize(pStmt);
|
||||
return rc;
|
||||
}
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
char **az, *z;
|
||||
az = sqlite3_realloc64(pCur->azDb, sizeof(char*)*(pCur->nDb+1));
|
||||
if( az==0 ){
|
||||
memstatClearSchema(pCur);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pCur->azDb = az;
|
||||
z = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
|
||||
if( z==0 ){
|
||||
memstatClearSchema(pCur);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pCur->azDb[pCur->nDb] = z;
|
||||
pCur->nDb++;
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Destructor for a memstat_cursor.
|
||||
*/
|
||||
static int memstatClose(sqlite3_vtab_cursor *cur){
|
||||
memstat_cursor *pCur = (memstat_cursor*)cur;
|
||||
memstatClearSchema(pCur);
|
||||
sqlite3_free(cur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Allowed values for aMemstatColumn[].eType
|
||||
*/
|
||||
#define MSV_GSTAT 0 /* sqlite3_status64() information */
|
||||
#define MSV_DB 1 /* sqlite3_db_status() information */
|
||||
#define MSV_ZIPVFS 2 /* ZIPVFS file-control with 64-bit return */
|
||||
|
||||
/*
|
||||
** An array of quantities that can be measured and reported by
|
||||
** this virtual table
|
||||
*/
|
||||
static const struct MemstatColumns {
|
||||
const char *zName; /* Symbolic name */
|
||||
unsigned char eType; /* Type of interface */
|
||||
unsigned char mNull; /* Bitmask of which columns are NULL */
|
||||
/* 2: dbname, 4: current, 8: hiwtr */
|
||||
int eOp; /* Opcode */
|
||||
} aMemstatColumn[] = {
|
||||
{"MEMORY_USED", MSV_GSTAT, 2, SQLITE_STATUS_MEMORY_USED },
|
||||
{"MALLOC_SIZE", MSV_GSTAT, 6, SQLITE_STATUS_MALLOC_SIZE },
|
||||
{"MALLOC_COUNT", MSV_GSTAT, 2, SQLITE_STATUS_MALLOC_COUNT },
|
||||
{"PAGECACHE_USED", MSV_GSTAT, 2, SQLITE_STATUS_PAGECACHE_USED },
|
||||
{"PAGECACHE_OVERFLOW", MSV_GSTAT, 2, SQLITE_STATUS_PAGECACHE_OVERFLOW },
|
||||
{"PAGECACHE_SIZE", MSV_GSTAT, 6, SQLITE_STATUS_PAGECACHE_SIZE },
|
||||
{"PARSER_STACK", MSV_GSTAT, 6, SQLITE_STATUS_PARSER_STACK },
|
||||
{"DB_LOOKASIDE_USED", MSV_DB, 2, SQLITE_DBSTATUS_LOOKASIDE_USED },
|
||||
{"DB_LOOKASIDE_HIT", MSV_DB, 6, SQLITE_DBSTATUS_LOOKASIDE_HIT },
|
||||
{"DB_LOOKASIDE_MISS_SIZE", MSV_DB, 6, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE},
|
||||
{"DB_LOOKASIDE_MISS_FULL", MSV_DB, 6, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL},
|
||||
{"DB_CACHE_USED", MSV_DB, 10, SQLITE_DBSTATUS_CACHE_USED },
|
||||
#if SQLITE_VERSION_NUMBER >= 3140000
|
||||
{"DB_CACHE_USED_SHARED", MSV_DB, 10, SQLITE_DBSTATUS_CACHE_USED_SHARED },
|
||||
#endif
|
||||
{"DB_SCHEMA_USED", MSV_DB, 10, SQLITE_DBSTATUS_SCHEMA_USED },
|
||||
{"DB_STMT_USED", MSV_DB, 10, SQLITE_DBSTATUS_STMT_USED },
|
||||
{"DB_CACHE_HIT", MSV_DB, 10, SQLITE_DBSTATUS_CACHE_HIT },
|
||||
{"DB_CACHE_MISS", MSV_DB, 10, SQLITE_DBSTATUS_CACHE_MISS },
|
||||
{"DB_CACHE_WRITE", MSV_DB, 10, SQLITE_DBSTATUS_CACHE_WRITE },
|
||||
#if SQLITE_VERSION_NUMBER >= 3230000
|
||||
{"DB_CACHE_SPILL", MSV_DB, 10, SQLITE_DBSTATUS_CACHE_SPILL },
|
||||
#endif
|
||||
{"DB_DEFERRED_FKS", MSV_DB, 10, SQLITE_DBSTATUS_DEFERRED_FKS },
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
{"ZIPVFS_CACHE_USED", MSV_ZIPVFS, 8, 231454 },
|
||||
{"ZIPVFS_CACHE_HIT", MSV_ZIPVFS, 8, 231455 },
|
||||
{"ZIPVFS_CACHE_MISS", MSV_ZIPVFS, 8, 231456 },
|
||||
{"ZIPVFS_CACHE_WRITE", MSV_ZIPVFS, 8, 231457 },
|
||||
{"ZIPVFS_DIRECT_READ", MSV_ZIPVFS, 8, 231458 },
|
||||
{"ZIPVFS_DIRECT_BYTES", MSV_ZIPVFS, 8, 231459 },
|
||||
#endif /* SQLITE_ENABLE_ZIPVFS */
|
||||
};
|
||||
#define MSV_NROW (sizeof(aMemstatColumn)/sizeof(aMemstatColumn[0]))
|
||||
|
||||
/*
|
||||
** Advance a memstat_cursor to its next row of output.
|
||||
*/
|
||||
static int memstatNext(sqlite3_vtab_cursor *cur){
|
||||
memstat_cursor *pCur = (memstat_cursor*)cur;
|
||||
int i;
|
||||
assert( pCur->iRowid<=MSV_NROW );
|
||||
while(1){
|
||||
i = (int)pCur->iRowid - 1;
|
||||
if( i<0 || (aMemstatColumn[i].mNull & 2)!=0 || (++pCur->iDb)>=pCur->nDb ){
|
||||
pCur->iRowid++;
|
||||
if( pCur->iRowid>MSV_NROW ) return SQLITE_OK; /* End of the table */
|
||||
pCur->iDb = 0;
|
||||
i++;
|
||||
}
|
||||
pCur->aVal[0] = 0;
|
||||
pCur->aVal[1] = 0;
|
||||
switch( aMemstatColumn[i].eType ){
|
||||
case MSV_GSTAT: {
|
||||
if( sqlite3_libversion_number()>=3010000 ){
|
||||
sqlite3_status64(aMemstatColumn[i].eOp,
|
||||
&pCur->aVal[0], &pCur->aVal[1],0);
|
||||
}else{
|
||||
int xCur, xHiwtr;
|
||||
sqlite3_status(aMemstatColumn[i].eOp, &xCur, &xHiwtr, 0);
|
||||
pCur->aVal[0] = xCur;
|
||||
pCur->aVal[1] = xHiwtr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSV_DB: {
|
||||
int xCur, xHiwtr;
|
||||
sqlite3_db_status(pCur->db, aMemstatColumn[i].eOp, &xCur, &xHiwtr, 0);
|
||||
pCur->aVal[0] = xCur;
|
||||
pCur->aVal[1] = xHiwtr;
|
||||
break;
|
||||
}
|
||||
case MSV_ZIPVFS: {
|
||||
int rc;
|
||||
rc = sqlite3_file_control(pCur->db, pCur->azDb[pCur->iDb],
|
||||
aMemstatColumn[i].eOp, (void*)&pCur->aVal[0]);
|
||||
if( rc!=SQLITE_OK ) continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return values of columns for the row at which the memstat_cursor
|
||||
** is currently pointing.
|
||||
*/
|
||||
static int memstatColumn(
|
||||
sqlite3_vtab_cursor *cur, /* The cursor */
|
||||
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
||||
int iCol /* Which column to return */
|
||||
){
|
||||
memstat_cursor *pCur = (memstat_cursor*)cur;
|
||||
int i;
|
||||
assert( pCur->iRowid>0 && pCur->iRowid<=MSV_NROW );
|
||||
i = (int)pCur->iRowid - 1;
|
||||
if( (aMemstatColumn[i].mNull & (1<<iCol))!=0 ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
switch( iCol ){
|
||||
case MSV_COLUMN_NAME: {
|
||||
sqlite3_result_text(ctx, aMemstatColumn[i].zName, -1, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
case MSV_COLUMN_SCHEMA: {
|
||||
sqlite3_result_text(ctx, pCur->azDb[pCur->iDb], -1, 0);
|
||||
break;
|
||||
}
|
||||
case MSV_COLUMN_VALUE: {
|
||||
sqlite3_result_int64(ctx, pCur->aVal[0]);
|
||||
break;
|
||||
}
|
||||
case MSV_COLUMN_HIWTR: {
|
||||
sqlite3_result_int64(ctx, pCur->aVal[1]);
|
||||
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 memstatRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
memstat_cursor *pCur = (memstat_cursor*)cur;
|
||||
*pRowid = pCur->iRowid*1000 + pCur->iDb;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the cursor has been moved off of the last
|
||||
** row of output.
|
||||
*/
|
||||
static int memstatEof(sqlite3_vtab_cursor *cur){
|
||||
memstat_cursor *pCur = (memstat_cursor*)cur;
|
||||
return pCur->iRowid>MSV_NROW;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is called to "rewind" the memstat_cursor object back
|
||||
** to the first row of output. This method is always called at least
|
||||
** once prior to any call to memstatColumn() or memstatRowid() or
|
||||
** memstatEof().
|
||||
*/
|
||||
static int memstatFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
memstat_cursor *pCur = (memstat_cursor *)pVtabCursor;
|
||||
int rc = memstatFindSchemas(pCur);
|
||||
if( rc ) return rc;
|
||||
pCur->iRowid = 0;
|
||||
pCur->iDb = 0;
|
||||
return memstatNext(pVtabCursor);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the memstat virtual table. This routine needs to create
|
||||
** a query plan for each invocation and compute an estimated cost for that
|
||||
** plan.
|
||||
*/
|
||||
static int memstatBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
pIdxInfo->estimatedCost = (double)500;
|
||||
pIdxInfo->estimatedRows = 500;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This following structure defines all the methods for the
|
||||
** memstat virtual table.
|
||||
*/
|
||||
static sqlite3_module memstatModule = {
|
||||
0, /* iVersion */
|
||||
0, /* xCreate */
|
||||
memstatConnect, /* xConnect */
|
||||
memstatBestIndex, /* xBestIndex */
|
||||
memstatDisconnect, /* xDisconnect */
|
||||
0, /* xDestroy */
|
||||
memstatOpen, /* xOpen - open a cursor */
|
||||
memstatClose, /* xClose - close a cursor */
|
||||
memstatFilter, /* xFilter - configure scan constraints */
|
||||
memstatNext, /* xNext - advance a cursor */
|
||||
memstatEof, /* xEof - check for end of scan */
|
||||
memstatColumn, /* xColumn - read data */
|
||||
memstatRowid, /* 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 sqlite3MemstatVtabInit(sqlite3 *db){
|
||||
int rc = SQLITE_OK;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3_create_module(db, "sqlite_memstat", &memstatModule, 0);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_CORE
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_memstat_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3MemstatVtabInit(db);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_CORE */
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_MEMSTATVTAB) */
|
||||
@@ -1,314 +0,0 @@
|
||||
/*
|
||||
** 2013-02-28
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file contains code to implement the next_char(A,T,F,W,C) SQL function.
|
||||
**
|
||||
** The next_char(A,T,F,W,C) function finds all valid "next" characters for
|
||||
** string A given the vocabulary in T.F. If the W value exists and is a
|
||||
** non-empty string, then it is an SQL expression that limits the entries
|
||||
** in T.F that will be considered. If C exists and is a non-empty string,
|
||||
** then it is the name of the collating sequence to use for comparison. If
|
||||
**
|
||||
** Only the first three arguments are required. If the C parameter is
|
||||
** omitted or is NULL or is an empty string, then the default collating
|
||||
** sequence of T.F is used for comparision. If the W parameter is omitted
|
||||
** or is NULL or is an empty string, then no filtering of the output is
|
||||
** done.
|
||||
**
|
||||
** The T.F column should be indexed using collation C or else this routine
|
||||
** will be quite slow.
|
||||
**
|
||||
** For example, suppose an application has a dictionary like this:
|
||||
**
|
||||
** CREATE TABLE dictionary(word TEXT UNIQUE);
|
||||
**
|
||||
** Further suppose that for user keypad entry, it is desired to disable
|
||||
** (gray out) keys that are not valid as the next character. If the
|
||||
** the user has previously entered (say) 'cha' then to find all allowed
|
||||
** next characters (and thereby determine when keys should not be grayed
|
||||
** out) run the following query:
|
||||
**
|
||||
** SELECT next_char('cha','dictionary','word');
|
||||
**
|
||||
** IMPLEMENTATION NOTES:
|
||||
**
|
||||
** The next_char function is implemented using recursive SQL that makes
|
||||
** use of the table name and column name as part of a query. If either
|
||||
** the table name or column name are keywords or contain special characters,
|
||||
** then they should be escaped. For example:
|
||||
**
|
||||
** SELECT next_char('cha','[dictionary]','[word]');
|
||||
**
|
||||
** This also means that the table name can be a subquery:
|
||||
**
|
||||
** SELECT next_char('cha','(SELECT word AS w FROM dictionary)','w');
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** A structure to hold context of the next_char() computation across
|
||||
** nested function calls.
|
||||
*/
|
||||
typedef struct nextCharContext nextCharContext;
|
||||
struct nextCharContext {
|
||||
sqlite3 *db; /* Database connection */
|
||||
sqlite3_stmt *pStmt; /* Prepared statement used to query */
|
||||
const unsigned char *zPrefix; /* Prefix to scan */
|
||||
int nPrefix; /* Size of zPrefix in bytes */
|
||||
int nAlloc; /* Space allocated to aResult */
|
||||
int nUsed; /* Space used in aResult */
|
||||
unsigned int *aResult; /* Array of next characters */
|
||||
int mallocFailed; /* True if malloc fails */
|
||||
int otherError; /* True for any other failure */
|
||||
};
|
||||
|
||||
/*
|
||||
** Append a result character if the character is not already in the
|
||||
** result.
|
||||
*/
|
||||
static void nextCharAppend(nextCharContext *p, unsigned c){
|
||||
int i;
|
||||
for(i=0; i<p->nUsed; i++){
|
||||
if( p->aResult[i]==c ) return;
|
||||
}
|
||||
if( p->nUsed+1 > p->nAlloc ){
|
||||
unsigned int *aNew;
|
||||
int n = p->nAlloc*2 + 30;
|
||||
aNew = sqlite3_realloc64(p->aResult, n*sizeof(unsigned int));
|
||||
if( aNew==0 ){
|
||||
p->mallocFailed = 1;
|
||||
return;
|
||||
}else{
|
||||
p->aResult = aNew;
|
||||
p->nAlloc = n;
|
||||
}
|
||||
}
|
||||
p->aResult[p->nUsed++] = c;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write a character into z[] as UTF8. Return the number of bytes needed
|
||||
** to hold the character
|
||||
*/
|
||||
static int writeUtf8(unsigned char *z, unsigned c){
|
||||
if( c<0x00080 ){
|
||||
z[0] = (unsigned char)(c&0xff);
|
||||
return 1;
|
||||
}
|
||||
if( c<0x00800 ){
|
||||
z[0] = 0xC0 + (unsigned char)((c>>6)&0x1F);
|
||||
z[1] = 0x80 + (unsigned char)(c & 0x3F);
|
||||
return 2;
|
||||
}
|
||||
if( c<0x10000 ){
|
||||
z[0] = 0xE0 + (unsigned char)((c>>12)&0x0F);
|
||||
z[1] = 0x80 + (unsigned char)((c>>6) & 0x3F);
|
||||
z[2] = 0x80 + (unsigned char)(c & 0x3F);
|
||||
return 3;
|
||||
}
|
||||
z[0] = 0xF0 + (unsigned char)((c>>18) & 0x07);
|
||||
z[1] = 0x80 + (unsigned char)((c>>12) & 0x3F);
|
||||
z[2] = 0x80 + (unsigned char)((c>>6) & 0x3F);
|
||||
z[3] = 0x80 + (unsigned char)(c & 0x3F);
|
||||
return 4;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a UTF8 character out of z[] and write it into *pOut. Return
|
||||
** the number of bytes in z[] that were used to construct the character.
|
||||
*/
|
||||
static int readUtf8(const unsigned char *z, unsigned *pOut){
|
||||
static const unsigned char validBits[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
|
||||
};
|
||||
unsigned c = z[0];
|
||||
if( c<0xc0 ){
|
||||
*pOut = c;
|
||||
return 1;
|
||||
}else{
|
||||
int n = 1;
|
||||
c = validBits[c-0xc0];
|
||||
while( (z[n] & 0xc0)==0x80 ){
|
||||
c = (c<<6) + (0x3f & z[n++]);
|
||||
}
|
||||
if( c<0x80 || (c&0xFFFFF800)==0xD800 || (c&0xFFFFFFFE)==0xFFFE ){
|
||||
c = 0xFFFD;
|
||||
}
|
||||
*pOut = c;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The nextCharContext structure has been set up. Add all "next" characters
|
||||
** to the result set.
|
||||
*/
|
||||
static void findNextChars(nextCharContext *p){
|
||||
unsigned cPrev = 0;
|
||||
unsigned char zPrev[8];
|
||||
int n, rc;
|
||||
|
||||
for(;;){
|
||||
sqlite3_bind_text(p->pStmt, 1, (char*)p->zPrefix, p->nPrefix,
|
||||
SQLITE_STATIC);
|
||||
n = writeUtf8(zPrev, cPrev+1);
|
||||
sqlite3_bind_text(p->pStmt, 2, (char*)zPrev, n, SQLITE_STATIC);
|
||||
rc = sqlite3_step(p->pStmt);
|
||||
if( rc==SQLITE_DONE ){
|
||||
sqlite3_reset(p->pStmt);
|
||||
return;
|
||||
}else if( rc!=SQLITE_ROW ){
|
||||
p->otherError = rc;
|
||||
return;
|
||||
}else{
|
||||
const unsigned char *zOut = sqlite3_column_text(p->pStmt, 0);
|
||||
unsigned cNext;
|
||||
n = readUtf8(zOut+p->nPrefix, &cNext);
|
||||
sqlite3_reset(p->pStmt);
|
||||
nextCharAppend(p, cNext);
|
||||
cPrev = cNext;
|
||||
if( p->mallocFailed ) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** next_character(A,T,F,W)
|
||||
**
|
||||
** Return a string composted of all next possible characters after
|
||||
** A for elements of T.F. If W is supplied, then it is an SQL expression
|
||||
** that limits the elements in T.F that are considered.
|
||||
*/
|
||||
static void nextCharFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
nextCharContext c;
|
||||
const unsigned char *zTable = sqlite3_value_text(argv[1]);
|
||||
const unsigned char *zField = sqlite3_value_text(argv[2]);
|
||||
const unsigned char *zWhere;
|
||||
const unsigned char *zCollName;
|
||||
char *zWhereClause = 0;
|
||||
char *zColl = 0;
|
||||
char *zSql;
|
||||
int rc;
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.db = sqlite3_context_db_handle(context);
|
||||
c.zPrefix = sqlite3_value_text(argv[0]);
|
||||
c.nPrefix = sqlite3_value_bytes(argv[0]);
|
||||
if( zTable==0 || zField==0 || c.zPrefix==0 ) return;
|
||||
if( argc>=4
|
||||
&& (zWhere = sqlite3_value_text(argv[3]))!=0
|
||||
&& zWhere[0]!=0
|
||||
){
|
||||
zWhereClause = sqlite3_mprintf("AND (%s)", zWhere);
|
||||
if( zWhereClause==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
zWhereClause = "";
|
||||
}
|
||||
if( argc>=5
|
||||
&& (zCollName = sqlite3_value_text(argv[4]))!=0
|
||||
&& zCollName[0]!=0
|
||||
){
|
||||
zColl = sqlite3_mprintf("collate \"%w\"", zCollName);
|
||||
if( zColl==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
if( zWhereClause[0] ) sqlite3_free(zWhereClause);
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
zColl = "";
|
||||
}
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT %s FROM %s"
|
||||
" WHERE %s>=(?1 || ?2) %s"
|
||||
" AND %s<=(?1 || char(1114111)) %s" /* 1114111 == 0x10ffff */
|
||||
" %s"
|
||||
" ORDER BY 1 %s ASC LIMIT 1",
|
||||
zField, zTable, zField, zColl, zField, zColl, zWhereClause, zColl
|
||||
);
|
||||
if( zWhereClause[0] ) sqlite3_free(zWhereClause);
|
||||
if( zColl[0] ) sqlite3_free(zColl);
|
||||
if( zSql==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = sqlite3_prepare_v2(c.db, zSql, -1, &c.pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ){
|
||||
sqlite3_result_error(context, sqlite3_errmsg(c.db), -1);
|
||||
return;
|
||||
}
|
||||
findNextChars(&c);
|
||||
if( c.mallocFailed ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
unsigned char *pRes;
|
||||
pRes = sqlite3_malloc64( c.nUsed*4 + 1 );
|
||||
if( pRes==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
int i;
|
||||
int n = 0;
|
||||
for(i=0; i<c.nUsed; i++){
|
||||
n += writeUtf8(pRes+n, c.aResult[i]);
|
||||
}
|
||||
pRes[n] = 0;
|
||||
sqlite3_result_text(context, (const char*)pRes, n, sqlite3_free);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(c.pStmt);
|
||||
sqlite3_free(c.aResult);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_nextchar_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, "next_char", 3,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
nextCharFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "next_char", 4,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
nextCharFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "next_char", 5,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
nextCharFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
** 2020-01-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 a noop() function used for testing.
|
||||
**
|
||||
** Variants:
|
||||
**
|
||||
** noop(X) The default. Deterministic.
|
||||
** noop_i(X) Deterministic and innocuous.
|
||||
** noop_do(X) Deterministic and direct-only.
|
||||
** noop_nd(X) Non-deterministic.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Implementation of the noop() function.
|
||||
**
|
||||
** The function returns its argument, unchanged.
|
||||
*/
|
||||
static void noopfunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
assert( argc==1 );
|
||||
sqlite3_result_value(context, argv[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the multitype_text() function.
|
||||
**
|
||||
** The function returns its argument. The result will always have a
|
||||
** TEXT value. But if the original input is numeric, it will also
|
||||
** have that numeric value.
|
||||
*/
|
||||
static void multitypeTextFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
assert( argc==1 );
|
||||
(void)argc;
|
||||
(void)sqlite3_value_text(argv[0]);
|
||||
sqlite3_result_value(context, argv[0]);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_noop_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, "noop", 1,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
|
||||
0, noopfunc, 0, 0);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3_create_function(db, "noop_i", 1,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_INNOCUOUS,
|
||||
0, noopfunc, 0, 0);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3_create_function(db, "noop_do", 1,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_DIRECTONLY,
|
||||
0, noopfunc, 0, 0);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3_create_function(db, "noop_nd", 1,
|
||||
SQLITE_UTF8,
|
||||
0, noopfunc, 0, 0);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3_create_function(db, "multitype_text", 1,
|
||||
SQLITE_UTF8,
|
||||
0, multitypeTextFunc, 0, 0);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
/*
|
||||
** 2013-05-28
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file contains code to implement the percentile(Y,P) SQL function
|
||||
** as described below:
|
||||
**
|
||||
** (1) The percentile(Y,P) function is an aggregate function taking
|
||||
** exactly two arguments.
|
||||
**
|
||||
** (2) If the P argument to percentile(Y,P) is not the same for every
|
||||
** row in the aggregate then an error is thrown. The word "same"
|
||||
** in the previous sentence means that the value differ by less
|
||||
** than 0.001.
|
||||
**
|
||||
** (3) If the P argument to percentile(Y,P) evaluates to anything other
|
||||
** than a number in the range of 0.0 to 100.0 inclusive then an
|
||||
** error is thrown.
|
||||
**
|
||||
** (4) If any Y argument to percentile(Y,P) evaluates to a value that
|
||||
** is not NULL and is not numeric then an error is thrown.
|
||||
**
|
||||
** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
|
||||
** infinity then an error is thrown. (SQLite always interprets NaN
|
||||
** values as NULL.)
|
||||
**
|
||||
** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
|
||||
** including CASE WHEN expressions.
|
||||
**
|
||||
** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
|
||||
** one million (1,000,000) rows.
|
||||
**
|
||||
** (8) If there are no non-NULL values for Y, then percentile(Y,P)
|
||||
** returns NULL.
|
||||
**
|
||||
** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
|
||||
** returns the one Y value.
|
||||
**
|
||||
** (10) If there N non-NULL values of Y where N is two or more and
|
||||
** the Y values are ordered from least to greatest and a graph is
|
||||
** drawn from 0 to N-1 such that the height of the graph at J is
|
||||
** the J-th Y value and such that straight lines are drawn between
|
||||
** adjacent Y values, then the percentile(Y,P) function returns
|
||||
** the height of the graph at P*(N-1)/100.
|
||||
**
|
||||
** (11) The percentile(Y,P) function always returns either a floating
|
||||
** point number or NULL.
|
||||
**
|
||||
** (12) The percentile(Y,P) is implemented as a single C99 source-code
|
||||
** file that compiles into a shared-library or DLL that can be loaded
|
||||
** into SQLite using the sqlite3_load_extension() interface.
|
||||
**
|
||||
** (13) A separate median(Y) function is the equivalent percentile(Y,50).
|
||||
**
|
||||
** (14) A separate percentile_cond(Y,X) function is the equivalent of
|
||||
** percentile(Y,X*100.0).
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* The following object is the session context for a single percentile()
|
||||
** function. We have to remember all input Y values until the very end.
|
||||
** Those values are accumulated in the Percentile.a[] array.
|
||||
*/
|
||||
typedef struct Percentile Percentile;
|
||||
struct Percentile {
|
||||
unsigned nAlloc; /* Number of slots allocated for a[] */
|
||||
unsigned nUsed; /* Number of slots actually used in a[] */
|
||||
double rPct; /* 1.0 more than the value for P */
|
||||
double *a; /* Array of Y values */
|
||||
};
|
||||
|
||||
/*
|
||||
** Return TRUE if the input floating-point number is an infinity.
|
||||
*/
|
||||
static int isInfinity(double r){
|
||||
sqlite3_uint64 u;
|
||||
assert( sizeof(u)==sizeof(r) );
|
||||
memcpy(&u, &r, sizeof(u));
|
||||
return ((u>>52)&0x7ff)==0x7ff;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if two doubles differ by 0.001 or less
|
||||
*/
|
||||
static int sameValue(double a, double b){
|
||||
a -= b;
|
||||
return a>=-0.001 && a<=0.001;
|
||||
}
|
||||
|
||||
/*
|
||||
** The "step" function for percentile(Y,P) is called once for each
|
||||
** input row.
|
||||
*/
|
||||
static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
|
||||
Percentile *p;
|
||||
double rPct;
|
||||
int eType;
|
||||
double y;
|
||||
assert( argc==2 || argc==1 );
|
||||
|
||||
if( argc==1 ){
|
||||
/* Requirement 13: median(Y) is the same as percentile(Y,50). */
|
||||
rPct = 50.0;
|
||||
}else if( sqlite3_user_data(pCtx)==0 ){
|
||||
/* Requirement 3: P must be a number between 0 and 100 */
|
||||
eType = sqlite3_value_numeric_type(argv[1]);
|
||||
rPct = sqlite3_value_double(argv[1]);
|
||||
if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
|
||||
|| rPct<0.0 || rPct>100.0 ){
|
||||
sqlite3_result_error(pCtx, "2nd argument to percentile() is not "
|
||||
"a number between 0.0 and 100.0", -1);
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
/* Requirement 3: P must be a number between 0 and 1 */
|
||||
eType = sqlite3_value_numeric_type(argv[1]);
|
||||
rPct = sqlite3_value_double(argv[1]);
|
||||
if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
|
||||
|| rPct<0.0 || rPct>1.0 ){
|
||||
sqlite3_result_error(pCtx, "2nd argument to percentile_cont() is not "
|
||||
"a number between 0.0 and 1.0", -1);
|
||||
return;
|
||||
}
|
||||
rPct *= 100.0;
|
||||
}
|
||||
|
||||
/* Allocate the session context. */
|
||||
p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
||||
if( p==0 ) return;
|
||||
|
||||
/* Remember the P value. Throw an error if the P value is different
|
||||
** from any prior row, per Requirement (2). */
|
||||
if( p->rPct==0.0 ){
|
||||
p->rPct = rPct+1.0;
|
||||
}else if( !sameValue(p->rPct,rPct+1.0) ){
|
||||
sqlite3_result_error(pCtx, "2nd argument to percentile() is not the "
|
||||
"same for all input rows", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore rows for which Y is NULL */
|
||||
eType = sqlite3_value_type(argv[0]);
|
||||
if( eType==SQLITE_NULL ) return;
|
||||
|
||||
/* If not NULL, then Y must be numeric. Otherwise throw an error.
|
||||
** Requirement 4 */
|
||||
if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
|
||||
sqlite3_result_error(pCtx, "1st argument to percentile() is not "
|
||||
"numeric", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Throw an error if the Y value is infinity or NaN */
|
||||
y = sqlite3_value_double(argv[0]);
|
||||
if( isInfinity(y) ){
|
||||
sqlite3_result_error(pCtx, "Inf input to percentile()", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate and store the Y */
|
||||
if( p->nUsed>=p->nAlloc ){
|
||||
unsigned n = p->nAlloc*2 + 250;
|
||||
double *a = sqlite3_realloc64(p->a, sizeof(double)*n);
|
||||
if( a==0 ){
|
||||
sqlite3_free(p->a);
|
||||
memset(p, 0, sizeof(*p));
|
||||
sqlite3_result_error_nomem(pCtx);
|
||||
return;
|
||||
}
|
||||
p->nAlloc = n;
|
||||
p->a = a;
|
||||
}
|
||||
p->a[p->nUsed++] = y;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sort an array of doubles.
|
||||
*/
|
||||
static void sortDoubles(double *a, int n){
|
||||
int iLt; /* Entries with index less than iLt are less than rPivot */
|
||||
int iGt; /* Entries with index iGt or more are greater than rPivot */
|
||||
int i; /* Loop counter */
|
||||
double rPivot; /* The pivot value */
|
||||
double rTmp; /* Temporary used to swap two values */
|
||||
|
||||
if( n<2 ) return;
|
||||
if( n>5 ){
|
||||
rPivot = (a[0] + a[n/2] + a[n-1])/3.0;
|
||||
}else{
|
||||
rPivot = a[n/2];
|
||||
}
|
||||
iLt = i = 0;
|
||||
iGt = n;
|
||||
while( i<iGt ){
|
||||
if( a[i]<rPivot ){
|
||||
if( i>iLt ){
|
||||
rTmp = a[i];
|
||||
a[i] = a[iLt];
|
||||
a[iLt] = rTmp;
|
||||
}
|
||||
iLt++;
|
||||
i++;
|
||||
}else if( a[i]>rPivot ){
|
||||
do{
|
||||
iGt--;
|
||||
}while( iGt>i && a[iGt]>rPivot );
|
||||
rTmp = a[i];
|
||||
a[i] = a[iGt];
|
||||
a[iGt] = rTmp;
|
||||
}else{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if( iLt>=2 ) sortDoubles(a, iLt);
|
||||
if( n-iGt>=2 ) sortDoubles(a+iGt, n-iGt);
|
||||
|
||||
/* Uncomment for testing */
|
||||
#if 0
|
||||
for(i=0; i<n-1; i++){
|
||||
assert( a[i]<=a[i+1] );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to compute the final output of percentile() and to clean
|
||||
** up all allocated memory.
|
||||
*/
|
||||
static void percentFinal(sqlite3_context *pCtx){
|
||||
Percentile *p;
|
||||
unsigned i1, i2;
|
||||
double v1, v2;
|
||||
double ix, vx;
|
||||
p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
|
||||
if( p==0 ) return;
|
||||
if( p->a==0 ) return;
|
||||
if( p->nUsed ){
|
||||
sortDoubles(p->a, p->nUsed);
|
||||
ix = (p->rPct-1.0)*(p->nUsed-1)*0.01;
|
||||
i1 = (unsigned)ix;
|
||||
i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
|
||||
v1 = p->a[i1];
|
||||
v2 = p->a[i2];
|
||||
vx = v1 + (v2-v1)*(ix-i1);
|
||||
sqlite3_result_double(pCtx, vx);
|
||||
}
|
||||
sqlite3_free(p->a);
|
||||
memset(p, 0, sizeof(*p));
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_percentile_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, "percentile", 2,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
0, percentStep, percentFinal);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "median", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
0, percentStep, percentFinal);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "percentile_cont", 2,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS, &percentStep,
|
||||
0, percentStep, percentFinal);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
/*
|
||||
** 2018-04-19
|
||||
**
|
||||
** 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 table-valued function:
|
||||
**
|
||||
** prefixes('abcdefg')
|
||||
**
|
||||
** The function has a single (non-HIDDEN) column named prefix that takes
|
||||
** on all prefixes of the string in its argument, including an empty string
|
||||
** and the input string itself. The order of prefixes is from longest
|
||||
** to shortest.
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || !defined(SQLITE_OMIT_VIRTUALTABLE)
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include "sqlite3ext.h"
|
||||
#endif
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* prefixes_vtab is a subclass of sqlite3_vtab which is
|
||||
** underlying representation of the virtual table
|
||||
*/
|
||||
typedef struct prefixes_vtab prefixes_vtab;
|
||||
struct prefixes_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
/* No additional fields are necessary */
|
||||
};
|
||||
|
||||
/* prefixes_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 prefixes_cursor prefixes_cursor;
|
||||
struct prefixes_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
sqlite3_int64 iRowid; /* The rowid */
|
||||
char *zStr; /* Original string to be prefixed */
|
||||
int nStr; /* Length of the string in bytes */
|
||||
};
|
||||
|
||||
/*
|
||||
** The prefixesConnect() method is invoked to create a new
|
||||
** template virtual table.
|
||||
**
|
||||
** Think of this routine as the constructor for prefixes_vtab objects.
|
||||
**
|
||||
** All this routine needs to do is:
|
||||
**
|
||||
** (1) Allocate the prefixes_vtab object and initialize all fields.
|
||||
**
|
||||
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
||||
** result set of queries against the virtual table will look like.
|
||||
*/
|
||||
static int prefixesConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
prefixes_vtab *pNew;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE prefixes(prefix TEXT, original_string TEXT HIDDEN)"
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)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 prefixes_vtab objects.
|
||||
*/
|
||||
static int prefixesDisconnect(sqlite3_vtab *pVtab){
|
||||
prefixes_vtab *p = (prefixes_vtab*)pVtab;
|
||||
sqlite3_free(p);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Constructor for a new prefixes_cursor object.
|
||||
*/
|
||||
static int prefixesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
prefixes_cursor *pCur;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor for a prefixes_cursor.
|
||||
*/
|
||||
static int prefixesClose(sqlite3_vtab_cursor *cur){
|
||||
prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
||||
sqlite3_free(pCur->zStr);
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance a prefixes_cursor to its next row of output.
|
||||
*/
|
||||
static int prefixesNext(sqlite3_vtab_cursor *cur){
|
||||
prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
||||
pCur->iRowid++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return values of columns for the row at which the prefixes_cursor
|
||||
** is currently pointing.
|
||||
*/
|
||||
static int prefixesColumn(
|
||||
sqlite3_vtab_cursor *cur, /* The cursor */
|
||||
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
||||
int i /* Which column to return */
|
||||
){
|
||||
prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
||||
switch( i ){
|
||||
case 0:
|
||||
sqlite3_result_text(ctx, pCur->zStr, pCur->nStr - (int)pCur->iRowid,
|
||||
0);
|
||||
break;
|
||||
default:
|
||||
sqlite3_result_text(ctx, pCur->zStr, pCur->nStr, 0);
|
||||
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 prefixesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
prefixes_cursor *pCur = (prefixes_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 prefixesEof(sqlite3_vtab_cursor *cur){
|
||||
prefixes_cursor *pCur = (prefixes_cursor*)cur;
|
||||
return pCur->iRowid>pCur->nStr;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is called to "rewind" the prefixes_cursor object back
|
||||
** to the first row of output. This method is always called at least
|
||||
** once prior to any call to prefixesColumn() or prefixesRowid() or
|
||||
** prefixesEof().
|
||||
*/
|
||||
static int prefixesFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
prefixes_cursor *pCur = (prefixes_cursor *)pVtabCursor;
|
||||
sqlite3_free(pCur->zStr);
|
||||
if( argc>0 ){
|
||||
pCur->zStr = sqlite3_mprintf("%s", sqlite3_value_text(argv[0]));
|
||||
pCur->nStr = pCur->zStr ? (int)strlen(pCur->zStr) : 0;
|
||||
}else{
|
||||
pCur->zStr = 0;
|
||||
pCur->nStr = 0;
|
||||
}
|
||||
pCur->iRowid = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the virtual table. This routine needs to create
|
||||
** a query plan for each invocation and compute an estimated cost for that
|
||||
** plan.
|
||||
*/
|
||||
static int prefixesBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
/* Search for a usable equality constraint against column 1
|
||||
** (original_string) and use it if at all possible */
|
||||
int i;
|
||||
const struct sqlite3_index_constraint *p;
|
||||
|
||||
for(i=0, p=pIdxInfo->aConstraint; i<pIdxInfo->nConstraint; i++, p++){
|
||||
if( p->iColumn!=1 ) continue;
|
||||
if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
||||
if( !p->usable ) continue;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
pIdxInfo->estimatedCost = (double)10;
|
||||
pIdxInfo->estimatedRows = 10;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
pIdxInfo->estimatedCost = (double)1000000000;
|
||||
pIdxInfo->estimatedRows = 1000000000;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This following structure defines all the methods for the
|
||||
** virtual table.
|
||||
*/
|
||||
static sqlite3_module prefixesModule = {
|
||||
/* iVersion */ 0,
|
||||
/* xCreate */ 0,
|
||||
/* xConnect */ prefixesConnect,
|
||||
/* xBestIndex */ prefixesBestIndex,
|
||||
/* xDisconnect */ prefixesDisconnect,
|
||||
/* xDestroy */ 0,
|
||||
/* xOpen */ prefixesOpen,
|
||||
/* xClose */ prefixesClose,
|
||||
/* xFilter */ prefixesFilter,
|
||||
/* xNext */ prefixesNext,
|
||||
/* xEof */ prefixesEof,
|
||||
/* xColumn */ prefixesColumn,
|
||||
/* xRowid */ prefixesRowid,
|
||||
/* xUpdate */ 0,
|
||||
/* xBegin */ 0,
|
||||
/* xSync */ 0,
|
||||
/* xCommit */ 0,
|
||||
/* xRollback */ 0,
|
||||
/* xFindMethod */ 0,
|
||||
/* xRename */ 0,
|
||||
/* xSavepoint */ 0,
|
||||
/* xRelease */ 0,
|
||||
/* xRollbackTo */ 0,
|
||||
/* xShadowName */ 0,
|
||||
/* xIntegrity */ 0
|
||||
};
|
||||
|
||||
/*
|
||||
** This is a copy of the SQLITE_SKIP_UTF8(zIn) macro in sqliteInt.h.
|
||||
**
|
||||
** Assuming zIn points to the first byte of a UTF-8 character,
|
||||
** advance zIn to point to the first byte of the next UTF-8 character.
|
||||
*/
|
||||
#define PREFIX_SKIP_UTF8(zIn) { \
|
||||
if( (*(zIn++))>=0xc0 ){ \
|
||||
while( (*zIn & 0xc0)==0x80 ){ zIn++; } \
|
||||
} \
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of function prefix_length(). This function accepts two
|
||||
** strings as arguments and returns the length in characters (not bytes),
|
||||
** of the longest prefix shared by the two strings. For example:
|
||||
**
|
||||
** prefix_length('abcdxxx', 'abcyy') == 3
|
||||
** prefix_length('abcdxxx', 'bcyyy') == 0
|
||||
** prefix_length('abcdxxx', 'ab') == 2
|
||||
** prefix_length('ab', 'abcd') == 2
|
||||
**
|
||||
** This function assumes the input is well-formed utf-8. If it is not,
|
||||
** it is possible for this function to return -1.
|
||||
*/
|
||||
static void prefixLengthFunc(
|
||||
sqlite3_context *ctx,
|
||||
int nVal,
|
||||
sqlite3_value **apVal
|
||||
){
|
||||
int nByte; /* Number of bytes to compare */
|
||||
int nRet = 0; /* Return value */
|
||||
const unsigned char *zL = sqlite3_value_text(apVal[0]);
|
||||
const unsigned char *zR = sqlite3_value_text(apVal[1]);
|
||||
int nL = sqlite3_value_bytes(apVal[0]);
|
||||
int nR = sqlite3_value_bytes(apVal[1]);
|
||||
int i;
|
||||
|
||||
nByte = (nL > nR ? nL : nR);
|
||||
for(i=0; i<nByte; i++){
|
||||
if( zL[i]!=zR[i] ) break;
|
||||
if( (zL[i] & 0xC0)!=0x80 ) nRet++;
|
||||
}
|
||||
|
||||
if( (zL[i] & 0xC0)==0x80 ) nRet--;
|
||||
sqlite3_result_int(ctx, nRet);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_prefixes_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
rc = sqlite3_create_module(db, "prefixes", &prefixesModule, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(
|
||||
db, "prefix_length", 2, SQLITE_UTF8, 0, prefixLengthFunc, 0, 0
|
||||
);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif /* !defined(SQLITE_CORE) || !defined(SQLITE_OMIT_VIRTUALTABLE) */
|
||||
@@ -1,462 +0,0 @@
|
||||
/*
|
||||
** 2022-01-19
|
||||
**
|
||||
** 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 that returns information about
|
||||
** how the query planner called the xBestIndex method. This virtual table
|
||||
** is intended for testing and debugging only.
|
||||
**
|
||||
** The schema of the virtual table is this:
|
||||
**
|
||||
** CREATE TABLE qpvtab(
|
||||
** vn TEXT, -- Name of an sqlite3_index_info field
|
||||
** ix INTEGER, -- Array index or value
|
||||
** cn TEXT, -- Column name
|
||||
** op INTEGER, -- operator
|
||||
** ux BOOLEAN, -- "usable" field
|
||||
** rhs TEXT, -- sqlite3_vtab_rhs_value()
|
||||
**
|
||||
** a, b, c, d, e, -- Extra columns to attach constraints to
|
||||
**
|
||||
** flags INTEGER HIDDEN -- control flags
|
||||
** );
|
||||
**
|
||||
** The virtual table returns a description of the sqlite3_index_info object
|
||||
** that was provided to the (successful) xBestIndex method. There is one
|
||||
** row in the result table for each field in the sqlite3_index_info object.
|
||||
**
|
||||
** The values of the "a" through "e" columns are one of:
|
||||
**
|
||||
** 1. TEXT - the same as the column name
|
||||
** 2. INTEGER - 1 for "a", 2 for "b", and so forth
|
||||
**
|
||||
** Option 1 is the default behavior. 2 is use if there is a usable
|
||||
** constraint on "flags" with an integer right-hand side that where the
|
||||
** value of the right-hand side has its 0x001 bit set.
|
||||
**
|
||||
** All constraints on columns "a" through "e" are marked as "omit".
|
||||
**
|
||||
** If there is a usable constraint on "flags" that has a RHS value that
|
||||
** is an integer and that integer has its 0x02 bit set, then the
|
||||
** orderByConsumed flag is set.
|
||||
**
|
||||
** FLAGS SUMMARY:
|
||||
**
|
||||
** 0x001 Columns 'a' through 'e' have INT values
|
||||
** 0x002 orderByConsumed is set
|
||||
** 0x004 OFFSET and LIMIT have omit set
|
||||
**
|
||||
** COMPILE:
|
||||
**
|
||||
** gcc -Wall -g -shared -fPIC -I. qpvtab.c -o qqvtab.so
|
||||
**
|
||||
** EXAMPLE USAGE:
|
||||
**
|
||||
** .load ./qpvtab
|
||||
** SELECT rowid, *, flags FROM qpvtab(102)
|
||||
** WHERE a=19
|
||||
** AND b BETWEEN 4.5 and 'hello'
|
||||
** AND c<>x'aabbcc'
|
||||
** ORDER BY d, e DESC;
|
||||
*/
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include "sqlite3ext.h"
|
||||
#endif
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE)
|
||||
|
||||
/* qpvtab_vtab is a subclass of sqlite3_vtab which is
|
||||
** underlying representation of the virtual table
|
||||
*/
|
||||
typedef struct qpvtab_vtab qpvtab_vtab;
|
||||
struct qpvtab_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
};
|
||||
|
||||
/* qpvtab_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 qpvtab_cursor qpvtab_cursor;
|
||||
struct qpvtab_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
sqlite3_int64 iRowid; /* The rowid */
|
||||
const char *zData; /* Data to return */
|
||||
int nData; /* Number of bytes of data */
|
||||
int flags; /* Flags value */
|
||||
};
|
||||
|
||||
/*
|
||||
** Names of columns
|
||||
*/
|
||||
static const char *azColname[] = {
|
||||
"vn",
|
||||
"ix",
|
||||
"cn",
|
||||
"op",
|
||||
"ux",
|
||||
"rhs",
|
||||
"a", "b", "c", "d", "e",
|
||||
"flags",
|
||||
""
|
||||
};
|
||||
|
||||
/*
|
||||
** The qpvtabConnect() method is invoked to create a new
|
||||
** qpvtab virtual table.
|
||||
*/
|
||||
static int qpvtabConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
qpvtab_vtab *pNew;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x("
|
||||
" vn TEXT,"
|
||||
" ix INT,"
|
||||
" cn TEXT,"
|
||||
" op INT,"
|
||||
" ux BOOLEAN,"
|
||||
" rhs TEXT,"
|
||||
" a, b, c, d, e,"
|
||||
" flags INT HIDDEN)"
|
||||
);
|
||||
#define QPVTAB_VN 0
|
||||
#define QPVTAB_IX 1
|
||||
#define QPVTAB_CN 2
|
||||
#define QPVTAB_OP 3
|
||||
#define QPVTAB_UX 4
|
||||
#define QPVTAB_RHS 5
|
||||
#define QPVTAB_A 6
|
||||
#define QPVTAB_B 7
|
||||
#define QPVTAB_C 8
|
||||
#define QPVTAB_D 9
|
||||
#define QPVTAB_E 10
|
||||
#define QPVTAB_FLAGS 11
|
||||
#define QPVTAB_NONE 12
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is the destructor for qpvtab_vtab objects.
|
||||
*/
|
||||
static int qpvtabDisconnect(sqlite3_vtab *pVtab){
|
||||
qpvtab_vtab *p = (qpvtab_vtab*)pVtab;
|
||||
sqlite3_free(p);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Constructor for a new qpvtab_cursor object.
|
||||
*/
|
||||
static int qpvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
qpvtab_cursor *pCur;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor for a qpvtab_cursor.
|
||||
*/
|
||||
static int qpvtabClose(sqlite3_vtab_cursor *cur){
|
||||
qpvtab_cursor *pCur = (qpvtab_cursor*)cur;
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance a qpvtab_cursor to its next row of output.
|
||||
*/
|
||||
static int qpvtabNext(sqlite3_vtab_cursor *cur){
|
||||
qpvtab_cursor *pCur = (qpvtab_cursor*)cur;
|
||||
if( pCur->iRowid<pCur->nData ){
|
||||
const char *z = &pCur->zData[pCur->iRowid];
|
||||
const char *zEnd = strchr(z, '\n');
|
||||
if( zEnd ) zEnd++;
|
||||
pCur->iRowid = (int)(zEnd - pCur->zData);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return values of columns for the row at which the qpvtab_cursor
|
||||
** is currently pointing.
|
||||
*/
|
||||
static int qpvtabColumn(
|
||||
sqlite3_vtab_cursor *cur, /* The cursor */
|
||||
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
||||
int i /* Which column to return */
|
||||
){
|
||||
qpvtab_cursor *pCur = (qpvtab_cursor*)cur;
|
||||
if( i>=QPVTAB_VN && i<=QPVTAB_RHS && pCur->iRowid<pCur->nData ){
|
||||
const char *z = &pCur->zData[pCur->iRowid];
|
||||
const char *zEnd;
|
||||
int j;
|
||||
j = QPVTAB_VN;
|
||||
while(1){
|
||||
zEnd = strchr(z, j==QPVTAB_RHS ? '\n' : ',');
|
||||
if( j==i || zEnd==0 ) break;
|
||||
z = zEnd+1;
|
||||
j++;
|
||||
}
|
||||
if( zEnd==z ){
|
||||
sqlite3_result_null(ctx);
|
||||
}else if( i==QPVTAB_IX || i==QPVTAB_OP || i==QPVTAB_UX ){
|
||||
sqlite3_result_int(ctx, atoi(z));
|
||||
}else{
|
||||
sqlite3_result_text64(ctx, z, zEnd-z, SQLITE_TRANSIENT, SQLITE_UTF8);
|
||||
}
|
||||
}else if( i>=QPVTAB_A && i<=QPVTAB_E ){
|
||||
if( pCur->flags & 0x001 ){
|
||||
sqlite3_result_int(ctx, i-QPVTAB_A+1);
|
||||
}else{
|
||||
char x = 'a'+i-QPVTAB_A;
|
||||
sqlite3_result_text64(ctx, &x, 1, SQLITE_TRANSIENT, SQLITE_UTF8);
|
||||
}
|
||||
}else if( i==QPVTAB_FLAGS ){
|
||||
sqlite3_result_int(ctx, pCur->flags);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the rowid for the current row. In this implementation, the
|
||||
** rowid is the same as the output value.
|
||||
*/
|
||||
static int qpvtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
qpvtab_cursor *pCur = (qpvtab_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 qpvtabEof(sqlite3_vtab_cursor *cur){
|
||||
qpvtab_cursor *pCur = (qpvtab_cursor*)cur;
|
||||
return pCur->iRowid>=pCur->nData;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is called to "rewind" the qpvtab_cursor object back
|
||||
** to the first row of output. This method is always called at least
|
||||
** once prior to any call to qpvtabColumn() or qpvtabRowid() or
|
||||
** qpvtabEof().
|
||||
*/
|
||||
static int qpvtabFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
qpvtab_cursor *pCur = (qpvtab_cursor *)pVtabCursor;
|
||||
pCur->iRowid = 0;
|
||||
pCur->zData = idxStr;
|
||||
pCur->nData = (int)strlen(idxStr);
|
||||
pCur->flags = idxNum;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Append the text of a value to pStr
|
||||
*/
|
||||
static void qpvtabStrAppendValue(
|
||||
sqlite3_str *pStr,
|
||||
sqlite3_value *pVal
|
||||
){
|
||||
switch( sqlite3_value_type(pVal) ){
|
||||
case SQLITE_NULL:
|
||||
sqlite3_str_appendf(pStr, "NULL");
|
||||
break;
|
||||
case SQLITE_INTEGER:
|
||||
sqlite3_str_appendf(pStr, "%lld", sqlite3_value_int64(pVal));
|
||||
break;
|
||||
case SQLITE_FLOAT:
|
||||
sqlite3_str_appendf(pStr, "%!f", sqlite3_value_double(pVal));
|
||||
break;
|
||||
case SQLITE_TEXT: {
|
||||
int i;
|
||||
const char *a = (const char*)sqlite3_value_text(pVal);
|
||||
int n = sqlite3_value_bytes(pVal);
|
||||
sqlite3_str_append(pStr, "'", 1);
|
||||
for(i=0; i<n; i++){
|
||||
char c = a[i];
|
||||
if( c=='\n' ) c = ' ';
|
||||
sqlite3_str_append(pStr, &c, 1);
|
||||
if( c=='\'' ) sqlite3_str_append(pStr, &c, 1);
|
||||
}
|
||||
sqlite3_str_append(pStr, "'", 1);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int i;
|
||||
const unsigned char *a = sqlite3_value_blob(pVal);
|
||||
int n = sqlite3_value_bytes(pVal);
|
||||
sqlite3_str_append(pStr, "x'", 2);
|
||||
for(i=0; i<n; i++){
|
||||
sqlite3_str_appendf(pStr, "%02x", a[i]);
|
||||
}
|
||||
sqlite3_str_append(pStr, "'", 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the virtual table. This routine needs to create
|
||||
** a query plan for each invocation and compute an estimated cost for that
|
||||
** plan.
|
||||
*/
|
||||
static int qpvtabBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
sqlite3_str *pStr = sqlite3_str_new(0);
|
||||
int i, k = 0;
|
||||
int rc;
|
||||
sqlite3_str_appendf(pStr, "nConstraint,%d,,,,\n", pIdxInfo->nConstraint);
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++){
|
||||
sqlite3_value *pVal;
|
||||
int iCol = pIdxInfo->aConstraint[i].iColumn;
|
||||
int op = pIdxInfo->aConstraint[i].op;
|
||||
if( iCol==QPVTAB_FLAGS && pIdxInfo->aConstraint[i].usable ){
|
||||
pVal = 0;
|
||||
rc = sqlite3_vtab_rhs_value(pIdxInfo, i, &pVal);
|
||||
assert( rc==SQLITE_OK || pVal==0 );
|
||||
if( pVal ){
|
||||
pIdxInfo->idxNum = sqlite3_value_int(pVal);
|
||||
if( pIdxInfo->idxNum & 0x002 ) pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
}
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_LIMIT
|
||||
|| op==SQLITE_INDEX_CONSTRAINT_OFFSET
|
||||
){
|
||||
iCol = QPVTAB_NONE;
|
||||
}
|
||||
sqlite3_str_appendf(pStr,"aConstraint,%d,%s,%d,%d,",
|
||||
i,
|
||||
azColname[iCol],
|
||||
op,
|
||||
pIdxInfo->aConstraint[i].usable);
|
||||
pVal = 0;
|
||||
rc = sqlite3_vtab_rhs_value(pIdxInfo, i, &pVal);
|
||||
assert( rc==SQLITE_OK || pVal==0 );
|
||||
if( pVal ){
|
||||
qpvtabStrAppendValue(pStr, pVal);
|
||||
}
|
||||
sqlite3_str_append(pStr, "\n", 1);
|
||||
}
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++){
|
||||
int iCol = pIdxInfo->aConstraint[i].iColumn;
|
||||
int op = pIdxInfo->aConstraint[i].op;
|
||||
if( op==SQLITE_INDEX_CONSTRAINT_LIMIT
|
||||
|| op==SQLITE_INDEX_CONSTRAINT_OFFSET
|
||||
){
|
||||
iCol = QPVTAB_NONE;
|
||||
}
|
||||
if( iCol>=QPVTAB_A && pIdxInfo->aConstraint[i].usable ){
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = ++k;
|
||||
if( iCol<=QPVTAB_FLAGS || (pIdxInfo->idxNum & 0x004)!=0 ){
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_str_appendf(pStr, "nOrderBy,%d,,,,\n", pIdxInfo->nOrderBy);
|
||||
for(i=0; i<pIdxInfo->nOrderBy; i++){
|
||||
int iCol = pIdxInfo->aOrderBy[i].iColumn;
|
||||
sqlite3_str_appendf(pStr, "aOrderBy,%d,%s,%d,,\n",i,
|
||||
iCol>=0 ? azColname[iCol] : "rowid",
|
||||
pIdxInfo->aOrderBy[i].desc
|
||||
);
|
||||
}
|
||||
sqlite3_str_appendf(pStr, "sqlite3_vtab_distinct,%d,,,,\n",
|
||||
sqlite3_vtab_distinct(pIdxInfo));
|
||||
sqlite3_str_appendf(pStr, "idxFlags,%d,,,,\n", pIdxInfo->idxFlags);
|
||||
sqlite3_str_appendf(pStr, "colUsed,%d,,,,\n", (int)pIdxInfo->colUsed);
|
||||
pIdxInfo->estimatedCost = (double)10;
|
||||
pIdxInfo->estimatedRows = 10;
|
||||
sqlite3_str_appendf(pStr, "idxNum,%d,,,,\n", pIdxInfo->idxNum);
|
||||
sqlite3_str_appendf(pStr, "orderByConsumed,%d,,,,\n",
|
||||
pIdxInfo->orderByConsumed);
|
||||
pIdxInfo->idxStr = sqlite3_str_finish(pStr);
|
||||
pIdxInfo->needToFreeIdxStr = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This following structure defines all the methods for the
|
||||
** virtual table.
|
||||
*/
|
||||
static sqlite3_module qpvtabModule = {
|
||||
/* iVersion */ 0,
|
||||
/* xCreate */ 0,
|
||||
/* xConnect */ qpvtabConnect,
|
||||
/* xBestIndex */ qpvtabBestIndex,
|
||||
/* xDisconnect */ qpvtabDisconnect,
|
||||
/* xDestroy */ 0,
|
||||
/* xOpen */ qpvtabOpen,
|
||||
/* xClose */ qpvtabClose,
|
||||
/* xFilter */ qpvtabFilter,
|
||||
/* xNext */ qpvtabNext,
|
||||
/* xEof */ qpvtabEof,
|
||||
/* xColumn */ qpvtabColumn,
|
||||
/* xRowid */ qpvtabRowid,
|
||||
/* xUpdate */ 0,
|
||||
/* xBegin */ 0,
|
||||
/* xSync */ 0,
|
||||
/* xCommit */ 0,
|
||||
/* xRollback */ 0,
|
||||
/* xFindMethod */ 0,
|
||||
/* xRename */ 0,
|
||||
/* xSavepoint */ 0,
|
||||
/* xRelease */ 0,
|
||||
/* xRollbackTo */ 0,
|
||||
/* xShadowName */ 0,
|
||||
/* xIntegrity */ 0
|
||||
};
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_qpvtab_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3_create_module(db, "qpvtab", &qpvtabModule, 0);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
** 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 SQLite extension implements a the random_json(SEED) and
|
||||
** random_json5(SEED) functions. Given a numeric SEED value, these
|
||||
** routines generate pseudo-random JSON or JSON5, respectively. The
|
||||
** same value is always generated for the same seed.
|
||||
**
|
||||
** These SQL functions are intended for testing. They do not have any
|
||||
** practical real-world use, that we know of.
|
||||
**
|
||||
** COMPILE:
|
||||
**
|
||||
** gcc --shared -fPIC -o randomjson.so -I. ext/misc/randomjson.c
|
||||
**
|
||||
** USING FROM THE CLI:
|
||||
**
|
||||
** .load ./randomjson
|
||||
** SELECT random_json(1);
|
||||
** SELECT random_json5(1);
|
||||
*/
|
||||
#ifdef SQLITE_STATIC_RANDOMJSON
|
||||
# include "sqlite3.h"
|
||||
#else
|
||||
# include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Pseudo-random number generator */
|
||||
typedef struct Prng {
|
||||
unsigned int x, y;
|
||||
} Prng;
|
||||
|
||||
/* Reseed the PRNG */
|
||||
static void prngSeed(Prng *p, unsigned int iSeed){
|
||||
p->x = iSeed | 1;
|
||||
p->y = iSeed;
|
||||
}
|
||||
|
||||
/* Extract a random number */
|
||||
static unsigned int prngInt(Prng *p){
|
||||
p->x = (p->x>>1) ^ ((1+~(p->x&1)) & 0xd0000001);
|
||||
p->y = p->y*1103515245 + 12345;
|
||||
return p->x ^ p->y;
|
||||
}
|
||||
|
||||
static char *azJsonAtoms[] = {
|
||||
/* JSON JSON-5 */
|
||||
"0", "0",
|
||||
"1", "1",
|
||||
"-1", "-1",
|
||||
"2", "+2",
|
||||
"3DDDD", "3DDDD",
|
||||
"2.5DD", "2.5DD",
|
||||
"0.75", ".75",
|
||||
"-4.0e2", "-4.e2",
|
||||
"5.0e-3", "+5e-3",
|
||||
"6.DDe+0DD", "6.DDe+0DD",
|
||||
"0", "0x0",
|
||||
"512", "0x200",
|
||||
"256", "+0x100",
|
||||
"-2748", "-0xabc",
|
||||
"true", "true",
|
||||
"false", "false",
|
||||
"null", "null",
|
||||
"9.0e999", "Infinity",
|
||||
"-9.0e999", "-Infinity",
|
||||
"9.0e999", "+Infinity",
|
||||
"null", "NaN",
|
||||
"-0.0005DD", "-0.0005DD",
|
||||
"4.35e-3", "+4.35e-3",
|
||||
"\"gem\\\"hay\"", "\"gem\\\"hay\"",
|
||||
"\"icy'joy\"", "'icy\\'joy\'",
|
||||
"\"keylog\"", "\"key\\\nlog\"",
|
||||
"\"mix\\\\\\tnet\"", "\"mix\\\\\\tnet\"",
|
||||
"\"oat\\r\\n\"", "\"oat\\r\\n\"",
|
||||
"\"\\fpan\\b\"", "\"\\fpan\\b\"",
|
||||
"{}", "{}",
|
||||
"[]", "[]",
|
||||
"[]", "[/*empty*/]",
|
||||
"{}", "{//empty\n}",
|
||||
"\"ask\"", "\"ask\"",
|
||||
"\"bag\"", "\"bag\"",
|
||||
"\"can\"", "\"can\"",
|
||||
"\"day\"", "\"day\"",
|
||||
"\"end\"", "'end'",
|
||||
"\"fly\"", "\"fly\"",
|
||||
"\"\\u00XX\\u00XX\"", "\"\\xXX\\xXX\"",
|
||||
"\"y\\uXXXXz\"", "\"y\\uXXXXz\"",
|
||||
"\"\"", "\"\"",
|
||||
};
|
||||
static char *azJsonTemplate[] = {
|
||||
/* JSON JSON-5 */
|
||||
"{\"a\":%,\"b\":%,\"cDD\":%}", "{a:%,b:%,cDD:%}",
|
||||
"{\"a\":%,\"b\":%,\"c\":%,\"d\":%,\"e\":%}", "{a:%,b:%,c:%,d:%,e:%}",
|
||||
"{\"a\":%,\"b\":%,\"c\":%,\"d\":%,\"\":%}", "{a:%,b:%,c:%,d:%,'':%}",
|
||||
"{\"d\":%}", "{d:%}",
|
||||
"{\"eeee\":%, \"ffff\":%}", "{eeee:% /*and*/, ffff:%}",
|
||||
"{\"$g\":%,\"_h_\":%,\"a b c d\":%}", "{$g:%,_h_:%,\"a b c d\":%}",
|
||||
"{\"x\":%,\n \"y\":%}", "{\"x\":%,\n \"y\":%}",
|
||||
"{\"\\u00XX\":%,\"\\uXXXX\":%}", "{\"\\xXX\":%,\"\\uXXXX\":%}",
|
||||
"{\"Z\":%}", "{Z:%,}",
|
||||
"[%]", "[%,]",
|
||||
"[%,%]", "[%,%]",
|
||||
"[%,%,%]", "[%,%,%,]",
|
||||
"[%,%,%,%]", "[%,%,%,%]",
|
||||
"[%,%,%,%,%]", "[%,%,%,%,%]",
|
||||
};
|
||||
|
||||
#define count(X) (sizeof(X)/sizeof(X[0]))
|
||||
|
||||
#define STRSZ 10000
|
||||
|
||||
static void jsonExpand(
|
||||
const char *zSrc,
|
||||
char *zDest,
|
||||
Prng *p,
|
||||
int eType, /* 0 for JSON, 1 for JSON5 */
|
||||
unsigned int r /* Growth probability 0..1000. 0 means no growth */
|
||||
){
|
||||
unsigned int i, j, k;
|
||||
char *z;
|
||||
char *zX;
|
||||
size_t n;
|
||||
char zBuf[200];
|
||||
|
||||
j = 0;
|
||||
if( zSrc==0 ) zSrc = "%";
|
||||
if( strlen(zSrc)>=STRSZ/10 ) r = 0;
|
||||
for(i=0; zSrc[i]; i++){
|
||||
if( zSrc[i]!='%' ){
|
||||
if( j<STRSZ ) zDest[j++] = zSrc[i];
|
||||
continue;
|
||||
}
|
||||
if( r==0 || (r<1000 && (prngInt(p)%1000)<=r) ){
|
||||
/* Fill in without values without any new % */
|
||||
k = prngInt(p)%(count(azJsonAtoms)/2);
|
||||
k = k*2 + eType;
|
||||
z = azJsonAtoms[k];
|
||||
}else{
|
||||
/* Add new % terms */
|
||||
k = prngInt(p)%(count(azJsonTemplate)/2);
|
||||
k = k*2 + eType;
|
||||
z = azJsonTemplate[k];
|
||||
}
|
||||
n = strlen(z);
|
||||
if( (zX = strstr(z,"XX"))!=0 ){
|
||||
unsigned int y = prngInt(p);
|
||||
if( (y&0xff)==((y>>8)&0xff) ) y += 0x100;
|
||||
while( (y&0xff)==((y>>16)&0xff) || ((y>>8)&0xff)==((y>>16)&0xff) ){
|
||||
y += 0x10000;
|
||||
}
|
||||
memcpy(zBuf, z, n+1);
|
||||
z = zBuf;
|
||||
zX = strstr(z,"XX");
|
||||
while( zX!=0 ){
|
||||
zX[0] = "0123456789abcdef"[y%16]; y /= 16;
|
||||
zX[1] = "0123456789abcdef"[y%16]; y /= 16;
|
||||
zX = strstr(zX, "XX");
|
||||
}
|
||||
}else if( (zX = strstr(z,"DD"))!=0 ){
|
||||
unsigned int y = prngInt(p);
|
||||
memcpy(zBuf, z, n+1);
|
||||
z = zBuf;
|
||||
zX = strstr(z,"DD");
|
||||
while( zX!=0 ){
|
||||
zX[0] = "0123456789"[y%10]; y /= 10;
|
||||
zX[1] = "0123456789"[y%10]; y /= 10;
|
||||
zX = strstr(zX, "DD");
|
||||
}
|
||||
}
|
||||
assert( strstr(z, "XX")==0 );
|
||||
assert( strstr(z, "DD")==0 );
|
||||
if( j+n<STRSZ ){
|
||||
memcpy(&zDest[j], z, n);
|
||||
j += (int)n;
|
||||
}
|
||||
}
|
||||
zDest[STRSZ-1] = 0;
|
||||
if( j<STRSZ ) zDest[j] = 0;
|
||||
}
|
||||
|
||||
static void randJsonFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned int iSeed;
|
||||
int eType = *(int*)sqlite3_user_data(context);
|
||||
Prng prng;
|
||||
char z1[STRSZ+1], z2[STRSZ+1];
|
||||
|
||||
iSeed = (unsigned int)sqlite3_value_int(argv[0]);
|
||||
prngSeed(&prng, iSeed);
|
||||
jsonExpand(0, z2, &prng, eType, 1000);
|
||||
jsonExpand(z2, z1, &prng, eType, 1000);
|
||||
jsonExpand(z1, z2, &prng, eType, 100);
|
||||
jsonExpand(z2, z1, &prng, eType, 0);
|
||||
sqlite3_result_text(context, z1, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_randomjson_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
static int cOne = 1;
|
||||
static int cZero = 0;
|
||||
int rc = SQLITE_OK;
|
||||
#ifdef SQLITE_STATIC_RANDOMJSON
|
||||
(void)pApi; /* Unused parameter */
|
||||
#else
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
#endif
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "random_json", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
|
||||
&cZero, randJsonFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "random_json5", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
|
||||
&cOne, randJsonFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1,881 +0,0 @@
|
||||
/*
|
||||
** 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.
|
||||
*/
|
||||
#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 0xfffffff /* 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 */
|
||||
|
||||
#if defined(SQLITE_DEBUG)
|
||||
/* Opcode names used for symbolic debugging */
|
||||
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",
|
||||
};
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
|
||||
/* 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[] */
|
||||
};
|
||||
|
||||
/* 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>=0x80 ){
|
||||
if( (c&0xe0)==0xc0 && p->i<p->mx && (p->z[p->i]&0xc0)==0x80 ){
|
||||
c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f);
|
||||
if( c<0x80 ) c = 0xfffd;
|
||||
}else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
|
||||
&& (p->z[p->i+1]&0xc0)==0x80 ){
|
||||
c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
|
||||
p->i += 2;
|
||||
if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
|
||||
}else if( (c&0xf8)==0xf0 && p->i+2<p->mx && (p->z[p->i]&0xc0)==0x80
|
||||
&& (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
|
||||
c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
|
||||
| (p->z[p->i+2]&0x3f);
|
||||
p->i += 3;
|
||||
if( c<=0xffff || c>0x10ffff ) c = 0xfffd;
|
||||
}else{
|
||||
c = 0xfffd;
|
||||
}
|
||||
}
|
||||
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, int N){
|
||||
char *aOp;
|
||||
int *aArg;
|
||||
aOp = sqlite3_realloc64(p->aOp, N*sizeof(p->aOp[0]));
|
||||
if( aOp==0 ) return 1;
|
||||
p->aOp = aOp;
|
||||
aArg = sqlite3_realloc64(p->aArg, N*sizeof(p->aArg[0]));
|
||||
if( aArg==0 ) 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, 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 & 0xff);
|
||||
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 '{': {
|
||||
int m = 0, n = 0;
|
||||
int sz, j;
|
||||
if( iPrev<0 ) return "'{m,n}' without operand";
|
||||
while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
|
||||
n = m;
|
||||
if( c==',' ){
|
||||
p->sIn.i++;
|
||||
n = 0;
|
||||
while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
|
||||
}
|
||||
if( c!='}' ) return "unmatched '{'";
|
||||
if( n>0 && 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, -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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** 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, const char *zIn, int noCase){
|
||||
ReCompiled *pRe;
|
||||
const char *zErr;
|
||||
int i, j;
|
||||
|
||||
*ppRe = 0;
|
||||
pRe = sqlite3_malloc( sizeof(*pRe) );
|
||||
if( pRe==0 ){
|
||||
return "out of memory";
|
||||
}
|
||||
memset(pRe, 0, sizeof(*pRe));
|
||||
pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
|
||||
if( re_resize(pRe, 30) ){
|
||||
re_free(pRe);
|
||||
return "out of memory";
|
||||
}
|
||||
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<=0x7f ){
|
||||
pRe->zInit[j++] = (unsigned char)x;
|
||||
}else if( x<=0x7ff ){
|
||||
pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6));
|
||||
pRe->zInit[j++] = 0x80 | (x&0x3f);
|
||||
}else if( x<=0xffff ){
|
||||
pRe->zInit[j++] = (unsigned char)(0xe0 | (x>>12));
|
||||
pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
|
||||
pRe->zInit[j++] = 0x80 | (x&0x3f);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( j>0 && pRe->zInit[j-1]==0 ) j--;
|
||||
pRe->nInit = j;
|
||||
}
|
||||
return pRe->zErr;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 ){
|
||||
zPattern = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zPattern==0 ) return;
|
||||
zErr = re_compile(&pRe, zPattern, 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, (void(*)(void*))re_free);
|
||||
}
|
||||
}
|
||||
|
||||
#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;
|
||||
(void)argc;
|
||||
|
||||
zPattern = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zPattern==0 ) return;
|
||||
zErr = re_compile(&pRe, zPattern, 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
|
||||
__declspec(dllexport)
|
||||
#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;
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
** 2016-08-09
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file demonstrates how to create an SQL function that is a pass-through
|
||||
** for integer values (it returns a copy of its argument) but also saves the
|
||||
** value that is passed through into a C-language variable. The address of
|
||||
** the C-language variable is supplied as the second argument.
|
||||
**
|
||||
** This allows, for example, a counter to incremented and the original
|
||||
** value retrieved, atomically, using a single statement:
|
||||
**
|
||||
** UPDATE counterTab SET cnt=remember(cnt,$PTR)+1 WHERE id=$ID
|
||||
**
|
||||
** Prepare the above statement once. Then to use it, bind the address
|
||||
** of the output variable to $PTR using sqlite3_bind_pointer() with a
|
||||
** pointer type of "carray" and bind the id of the counter to $ID and
|
||||
** run the prepared statement.
|
||||
**
|
||||
** This implementation of the remember() function uses a "carray"
|
||||
** pointer so that it can share pointers with the carray() extension.
|
||||
**
|
||||
** One can imagine doing similar things with floating-point values and
|
||||
** strings, but this demonstration extension will stick to using just
|
||||
** integers.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
** remember(V,PTR)
|
||||
**
|
||||
** Return the integer value V. Also save the value of V in a
|
||||
** C-language variable whose address is PTR.
|
||||
*/
|
||||
static void rememberFunc(
|
||||
sqlite3_context *pCtx,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3_int64 v;
|
||||
sqlite3_int64 *ptr;
|
||||
assert( argc==2 );
|
||||
v = sqlite3_value_int64(argv[0]);
|
||||
ptr = sqlite3_value_pointer(argv[1], "carray");
|
||||
if( ptr ) *ptr = v;
|
||||
sqlite3_result_int64(pCtx, v);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_remember_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
rc = sqlite3_create_function(db, "remember", 2, SQLITE_UTF8, 0,
|
||||
rememberFunc, 0, 0);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
** 2013-05-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 SQLite extension implements a rot13() function and a rot13
|
||||
** collating sequence.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Perform rot13 encoding on a single ASCII character.
|
||||
*/
|
||||
static unsigned char rot13(unsigned char c){
|
||||
if( c>='a' && c<='z' ){
|
||||
c += 13;
|
||||
if( c>'z' ) c -= 26;
|
||||
}else if( c>='A' && c<='Z' ){
|
||||
c += 13;
|
||||
if( c>'Z' ) c -= 26;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the rot13() function.
|
||||
**
|
||||
** Rotate ASCII alphabetic characters by 13 character positions.
|
||||
** Non-ASCII characters are unchanged. rot13(rot13(X)) should always
|
||||
** equal X.
|
||||
*/
|
||||
static void rot13func(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const unsigned char *zIn;
|
||||
int nIn;
|
||||
unsigned char *zOut;
|
||||
unsigned char *zToFree = 0;
|
||||
int i;
|
||||
unsigned char zTemp[100];
|
||||
assert( argc==1 );
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
zIn = (const unsigned char*)sqlite3_value_text(argv[0]);
|
||||
nIn = sqlite3_value_bytes(argv[0]);
|
||||
if( nIn<sizeof(zTemp)-1 ){
|
||||
zOut = zTemp;
|
||||
}else{
|
||||
zOut = zToFree = (unsigned char*)sqlite3_malloc64( nIn+1 );
|
||||
if( zOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for(i=0; i<nIn; i++) zOut[i] = rot13(zIn[i]);
|
||||
zOut[i] = 0;
|
||||
sqlite3_result_text(context, (char*)zOut, i, SQLITE_TRANSIENT);
|
||||
sqlite3_free(zToFree);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implement the rot13 collating sequence so that if
|
||||
**
|
||||
** x=y COLLATE rot13
|
||||
**
|
||||
** Then
|
||||
**
|
||||
** rot13(x)=rot13(y) COLLATE binary
|
||||
*/
|
||||
static int rot13CollFunc(
|
||||
void *notUsed,
|
||||
int nKey1, const void *pKey1,
|
||||
int nKey2, const void *pKey2
|
||||
){
|
||||
const char *zA = (const char*)pKey1;
|
||||
const char *zB = (const char*)pKey2;
|
||||
int i, x;
|
||||
for(i=0; i<nKey1 && i<nKey2; i++){
|
||||
x = (int)rot13(zA[i]) - (int)rot13(zB[i]);
|
||||
if( x!=0 ) return x;
|
||||
}
|
||||
return nKey1 - nKey2;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_rot_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, "rot13", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
|
||||
0, rot13func, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_collation(db, "rot13", SQLITE_UTF8, 0, rot13CollFunc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1,641 +0,0 @@
|
||||
/*
|
||||
** 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 0xffffffff)
|
||||
** 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.
|
||||
**
|
||||
** SELECT * FROM generate_series(0,100);
|
||||
**
|
||||
** Integers from 0 through 100 with a step size of 1.
|
||||
**
|
||||
** SELECT * FROM generate_series(20) LIMIT 10;
|
||||
**
|
||||
** Integers 20 through 29.
|
||||
**
|
||||
** SELECT * FROM generate_series(0,-100,-5);
|
||||
**
|
||||
** Integers 0 -5 -10 ... -100.
|
||||
**
|
||||
** 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, logically equivalent to n+1 where
|
||||
** "n" is the ascending integer in the aforesaid production definition.
|
||||
**
|
||||
** 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.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/*
|
||||
** Return that member of a generate_series(...) sequence whose 0-based
|
||||
** index is ix. The 0th member is given by smBase. The sequence members
|
||||
** progress per ix increment by smStep.
|
||||
*/
|
||||
static sqlite3_int64 genSeqMember(
|
||||
sqlite3_int64 smBase,
|
||||
sqlite3_int64 smStep,
|
||||
sqlite3_uint64 ix
|
||||
){
|
||||
static const sqlite3_uint64 mxI64 =
|
||||
((sqlite3_uint64)0x7fffffff)<<32 | 0xffffffff;
|
||||
if( ix>=mxI64 ){
|
||||
/* Get ix into signed i64 range. */
|
||||
ix -= mxI64;
|
||||
/* With 2's complement ALU, this next can be 1 step, but is split into
|
||||
* 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
|
||||
smBase += (mxI64/2) * smStep;
|
||||
smBase += (mxI64 - mxI64/2) * smStep;
|
||||
}
|
||||
/* Under UBSAN (or on 1's complement machines), must do this last term
|
||||
* in steps to avoid the dreaded (and harmless) signed multiply overlow. */
|
||||
if( ix>=2 ){
|
||||
sqlite3_int64 ix2 = (sqlite3_int64)ix/2;
|
||||
smBase += ix2*smStep;
|
||||
ix -= ix2;
|
||||
}
|
||||
return smBase + ((sqlite3_int64)ix)*smStep;
|
||||
}
|
||||
|
||||
typedef unsigned char u8;
|
||||
|
||||
typedef struct SequenceSpec {
|
||||
sqlite3_int64 iBase; /* Starting value ("start") */
|
||||
sqlite3_int64 iTerm; /* Given terminal value ("stop") */
|
||||
sqlite3_int64 iStep; /* Increment ("step") */
|
||||
sqlite3_uint64 uSeqIndexMax; /* maximum sequence index (aka "n") */
|
||||
sqlite3_uint64 uSeqIndexNow; /* Current index during generation */
|
||||
sqlite3_int64 iValueNow; /* Current value during generation */
|
||||
u8 isNotEOF; /* Sequence generation not exhausted */
|
||||
u8 isReversing; /* Sequence is being reverse generated */
|
||||
} SequenceSpec;
|
||||
|
||||
/*
|
||||
** Prepare a SequenceSpec for use in generating an integer series
|
||||
** given initialized iBase, iTerm and iStep values. Sequence is
|
||||
** initialized per given isReversing. Other members are computed.
|
||||
*/
|
||||
static void setupSequence( SequenceSpec *pss ){
|
||||
int bSameSigns;
|
||||
pss->uSeqIndexMax = 0;
|
||||
pss->isNotEOF = 0;
|
||||
bSameSigns = (pss->iBase < 0)==(pss->iTerm < 0);
|
||||
if( pss->iTerm < pss->iBase ){
|
||||
sqlite3_uint64 nuspan = 0;
|
||||
if( bSameSigns ){
|
||||
nuspan = (sqlite3_uint64)(pss->iBase - pss->iTerm);
|
||||
}else{
|
||||
/* Under UBSAN (or on 1's complement machines), must do this in steps.
|
||||
* In this clause, iBase>=0 and iTerm<0 . */
|
||||
nuspan = 1;
|
||||
nuspan += pss->iBase;
|
||||
nuspan += -(pss->iTerm+1);
|
||||
}
|
||||
if( pss->iStep<0 ){
|
||||
pss->isNotEOF = 1;
|
||||
if( nuspan==ULONG_MAX ){
|
||||
pss->uSeqIndexMax = ( pss->iStep>LLONG_MIN )? nuspan/-pss->iStep : 1;
|
||||
}else if( pss->iStep>LLONG_MIN ){
|
||||
pss->uSeqIndexMax = nuspan/-pss->iStep;
|
||||
}
|
||||
}
|
||||
}else if( pss->iTerm > pss->iBase ){
|
||||
sqlite3_uint64 puspan = 0;
|
||||
if( bSameSigns ){
|
||||
puspan = (sqlite3_uint64)(pss->iTerm - pss->iBase);
|
||||
}else{
|
||||
/* Under UBSAN (or on 1's complement machines), must do this in steps.
|
||||
* In this clause, iTerm>=0 and iBase<0 . */
|
||||
puspan = 1;
|
||||
puspan += pss->iTerm;
|
||||
puspan += -(pss->iBase+1);
|
||||
}
|
||||
if( pss->iStep>0 ){
|
||||
pss->isNotEOF = 1;
|
||||
pss->uSeqIndexMax = puspan/pss->iStep;
|
||||
}
|
||||
}else if( pss->iTerm == pss->iBase ){
|
||||
pss->isNotEOF = 1;
|
||||
pss->uSeqIndexMax = 0;
|
||||
}
|
||||
pss->uSeqIndexNow = (pss->isReversing)? pss->uSeqIndexMax : 0;
|
||||
pss->iValueNow = (pss->isReversing)
|
||||
? genSeqMember(pss->iBase, pss->iStep, pss->uSeqIndexMax)
|
||||
: pss->iBase;
|
||||
}
|
||||
|
||||
/*
|
||||
** Progress sequence generator to yield next value, if any.
|
||||
** Leave its state to either yield next value or be at EOF.
|
||||
** Return whether there is a next value, or 0 at EOF.
|
||||
*/
|
||||
static int progressSequence( SequenceSpec *pss ){
|
||||
if( !pss->isNotEOF ) return 0;
|
||||
if( pss->isReversing ){
|
||||
if( pss->uSeqIndexNow > 0 ){
|
||||
pss->uSeqIndexNow--;
|
||||
pss->iValueNow -= pss->iStep;
|
||||
}else{
|
||||
pss->isNotEOF = 0;
|
||||
}
|
||||
}else{
|
||||
if( pss->uSeqIndexNow < pss->uSeqIndexMax ){
|
||||
pss->uSeqIndexNow++;
|
||||
pss->iValueNow += pss->iStep;
|
||||
}else{
|
||||
pss->isNotEOF = 0;
|
||||
}
|
||||
}
|
||||
return pss->isNotEOF;
|
||||
}
|
||||
|
||||
/* 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
|
||||
*/
|
||||
typedef struct series_cursor series_cursor;
|
||||
struct series_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
SequenceSpec ss; /* (this) Derived class data */
|
||||
};
|
||||
|
||||
/*
|
||||
** 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_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_malloc( 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_malloc( 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;
|
||||
progressSequence( & pCur->ss );
|
||||
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->ss.iBase; break;
|
||||
case SERIES_COLUMN_STOP: x = pCur->ss.iTerm; break;
|
||||
case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break;
|
||||
default: x = pCur->ss.iValueNow; break;
|
||||
}
|
||||
sqlite3_result_int64(ctx, x);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifndef LARGEST_UINT64
|
||||
#define LARGEST_UINT64 (0xffffffff|(((sqlite3_uint64)0xffffffff)<<32))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return the rowid for the current row, logically equivalent to n+1 where
|
||||
** "n" is the ascending integer in the aforesaid production definition.
|
||||
*/
|
||||
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
series_cursor *pCur = (series_cursor*)cur;
|
||||
sqlite3_uint64 n = pCur->ss.uSeqIndexNow;
|
||||
*pRowid = (sqlite3_int64)((n<LARGEST_UINT64)? n+1 : 0);
|
||||
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->ss.isNotEOF;
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
||||
/*
|
||||
** 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:
|
||||
**
|
||||
** 0x01: start=VALUE
|
||||
** 0x02: stop=VALUE
|
||||
** 0x04: step=VALUE
|
||||
** 0x08: descending order
|
||||
** 0x10: ascending order
|
||||
** 0x20: LIMIT VALUE
|
||||
** 0x40: OFFSET 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 i = 0;
|
||||
(void)idxStrUnused;
|
||||
if( idxNum & 0x01 ){
|
||||
pCur->ss.iBase = sqlite3_value_int64(argv[i++]);
|
||||
}else{
|
||||
pCur->ss.iBase = 0;
|
||||
}
|
||||
if( idxNum & 0x02 ){
|
||||
pCur->ss.iTerm = sqlite3_value_int64(argv[i++]);
|
||||
}else{
|
||||
pCur->ss.iTerm = 0xffffffff;
|
||||
}
|
||||
if( idxNum & 0x04 ){
|
||||
pCur->ss.iStep = sqlite3_value_int64(argv[i++]);
|
||||
if( pCur->ss.iStep==0 ){
|
||||
pCur->ss.iStep = 1;
|
||||
}else if( pCur->ss.iStep<0 ){
|
||||
if( (idxNum & 0x10)==0 ) idxNum |= 0x08;
|
||||
}
|
||||
}else{
|
||||
pCur->ss.iStep = 1;
|
||||
}
|
||||
if( idxNum & 0x20 ){
|
||||
sqlite3_int64 iLimit = sqlite3_value_int64(argv[i++]);
|
||||
sqlite3_int64 iTerm;
|
||||
if( idxNum & 0x40 ){
|
||||
sqlite3_int64 iOffset = sqlite3_value_int64(argv[i++]);
|
||||
if( iOffset>0 ){
|
||||
pCur->ss.iBase += pCur->ss.iStep*iOffset;
|
||||
}
|
||||
}
|
||||
if( iLimit>=0 ){
|
||||
iTerm = pCur->ss.iBase + (iLimit - 1)*pCur->ss.iStep;
|
||||
if( pCur->ss.iStep<0 ){
|
||||
if( iTerm>pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
|
||||
}else{
|
||||
if( iTerm<pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i=0; i<argc; i++){
|
||||
if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
|
||||
/* If any of the constraints have a NULL value, then return no rows.
|
||||
** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */
|
||||
pCur->ss.iBase = 1;
|
||||
pCur->ss.iTerm = 0;
|
||||
pCur->ss.iStep = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( idxNum & 0x08 ){
|
||||
pCur->ss.isReversing = pCur->ss.iStep > 0;
|
||||
}else{
|
||||
pCur->ss.isReversing = pCur->ss.iStep < 0;
|
||||
}
|
||||
setupSequence( &pCur->ss );
|
||||
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:
|
||||
**
|
||||
** 0x01 start = $value -- constraint exists
|
||||
** 0x02 stop = $value -- constraint exists
|
||||
** 0x04 step = $value -- constraint exists
|
||||
** 0x08 output is in descending order
|
||||
** 0x10 output is in ascending order
|
||||
** 0x20 LIMIT $value -- constraint exists
|
||||
** 0x40 OFFSET $value -- constraint exists
|
||||
*/
|
||||
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[5]; /* Constraints on start, stop, step, LIMIT, OFFSET */
|
||||
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] = -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 |= 0x20;
|
||||
}else{
|
||||
assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET );
|
||||
aIdx[4] = i;
|
||||
idxNum |= 0x40;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if( pConstraint->iColumn<SERIES_COLUMN_START ) 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 &= ~0x60;
|
||||
aIdx[4] = 0;
|
||||
}
|
||||
for(i=0; i<5; 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 & 0x03)==0x03 ){
|
||||
/* 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 |= 0x08;
|
||||
}else{
|
||||
idxNum |= 0x10;
|
||||
}
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
}else if( (idxNum & 0x21)==0x21 ){
|
||||
/* 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;
|
||||
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
|
||||
__declspec(dllexport)
|
||||
#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;
|
||||
}
|
||||
@@ -1,393 +0,0 @@
|
||||
/*
|
||||
** 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)&0xFF00FF00) \
|
||||
|(rol(block[i],8)&0x00FF00FF))
|
||||
#define blk0be(i) block[i]
|
||||
#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
|
||||
^block[(i+2)&15]^block[i&15],1))
|
||||
|
||||
/*
|
||||
* (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
|
||||
*
|
||||
* Rl0() for little-endian and Rb0() for big-endian. Endianness is
|
||||
* determined at run-time.
|
||||
*/
|
||||
#define Rl0(v,w,x,y,z,i) \
|
||||
z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2);
|
||||
#define Rb0(v,w,x,y,z,i) \
|
||||
z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2);
|
||||
#define R1(v,w,x,y,z,i) \
|
||||
z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2);
|
||||
#define R2(v,w,x,y,z,i) \
|
||||
z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2);
|
||||
#define R3(v,w,x,y,z,i) \
|
||||
z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2);
|
||||
#define R4(v,w,x,y,z,i) \
|
||||
z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2);
|
||||
|
||||
/*
|
||||
* Hash a single 512-bit block. This is the core of the algorithm.
|
||||
*/
|
||||
static void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]){
|
||||
unsigned int qq[5]; /* a, b, c, d, e; */
|
||||
static int one = 1;
|
||||
unsigned int block[16];
|
||||
memcpy(block, buffer, 64);
|
||||
memcpy(qq,state,5*sizeof(unsigned int));
|
||||
|
||||
#define a qq[0]
|
||||
#define b qq[1]
|
||||
#define c qq[2]
|
||||
#define d qq[3]
|
||||
#define e qq[4]
|
||||
|
||||
/* Copy p->state[] to working vars */
|
||||
/*
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
*/
|
||||
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
if( 1 == *(unsigned char*)&one ){
|
||||
Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3);
|
||||
Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7);
|
||||
Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11);
|
||||
Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15);
|
||||
}else{
|
||||
Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3);
|
||||
Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7);
|
||||
Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11);
|
||||
Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15);
|
||||
}
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
|
||||
#undef a
|
||||
#undef b
|
||||
#undef c
|
||||
#undef d
|
||||
#undef e
|
||||
}
|
||||
|
||||
|
||||
/* Initialize a SHA1 context */
|
||||
static void hash_init(SHA1Context *p){
|
||||
/* SHA1 initialization constants */
|
||||
p->state[0] = 0x67452301;
|
||||
p->state[1] = 0xEFCDAB89;
|
||||
p->state[2] = 0x98BADCFE;
|
||||
p->state[3] = 0x10325476;
|
||||
p->state[4] = 0xC3D2E1F0;
|
||||
p->count[0] = p->count[1] = 0;
|
||||
}
|
||||
|
||||
/* Add new content to the SHA1 hash */
|
||||
static void hash_step(
|
||||
SHA1Context *p, /* Add content to this context */
|
||||
const unsigned char *data, /* Data to be added */
|
||||
unsigned int len /* Number of bytes in data */
|
||||
){
|
||||
unsigned int i, j;
|
||||
|
||||
j = p->count[0];
|
||||
if( (p->count[0] += len << 3) < j ){
|
||||
p->count[1] += (len>>29)+1;
|
||||
}
|
||||
j = (j >> 3) & 63;
|
||||
if( (j + len) > 63 ){
|
||||
(void)memcpy(&p->buffer[j], data, (i = 64-j));
|
||||
SHA1Transform(p->state, p->buffer);
|
||||
for(; i + 63 < len; i += 64){
|
||||
SHA1Transform(p->state, &data[i]);
|
||||
}
|
||||
j = 0;
|
||||
}else{
|
||||
i = 0;
|
||||
}
|
||||
(void)memcpy(&p->buffer[j], &data[i], len - i);
|
||||
}
|
||||
|
||||
/* Compute a string using sqlite3_vsnprintf() and hash it */
|
||||
static void hash_step_vformat(
|
||||
SHA1Context *p, /* Add content to this context */
|
||||
const char *zFormat,
|
||||
...
|
||||
){
|
||||
va_list ap;
|
||||
int n;
|
||||
char zBuf[50];
|
||||
va_start(ap, zFormat);
|
||||
sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap);
|
||||
va_end(ap);
|
||||
n = (int)strlen(zBuf);
|
||||
hash_step(p, (unsigned char*)zBuf, n);
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and compute the message digest. Render the
|
||||
** message digest as lower-case hexadecimal and put it into
|
||||
** zOut[]. zOut[] must be at least 41 bytes long. */
|
||||
static void hash_finish(
|
||||
SHA1Context *p, /* The SHA1 context to finish and render */
|
||||
char *zOut /* Store hexadecimal hash here */
|
||||
){
|
||||
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);
|
||||
}
|
||||
for(i=0; i<20; i++){
|
||||
zOut[i*2] = zEncode[(digest[i]>>4)&0xf];
|
||||
zOut[i*2+1] = zEncode[digest[i] & 0xf];
|
||||
}
|
||||
zOut[i*2]= 0;
|
||||
}
|
||||
/* End of the hashing logic
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
** Implementation of the sha1(X) function.
|
||||
**
|
||||
** Return a lower-case hexadecimal rendering of the SHA1 hash of the
|
||||
** argument X. If X is a BLOB, it is hashed as is. For all other
|
||||
** types of input, X is converted into a UTF-8 string and the string
|
||||
** is hash without the trailing 0x00 terminator. The hash of a NULL
|
||||
** value is NULL.
|
||||
*/
|
||||
static void sha1Func(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
SHA1Context cx;
|
||||
int eType = sqlite3_value_type(argv[0]);
|
||||
int nByte = sqlite3_value_bytes(argv[0]);
|
||||
char zOut[44];
|
||||
|
||||
assert( argc==1 );
|
||||
if( eType==SQLITE_NULL ) return;
|
||||
hash_init(&cx);
|
||||
if( eType==SQLITE_BLOB ){
|
||||
hash_step(&cx, sqlite3_value_blob(argv[0]), nByte);
|
||||
}else{
|
||||
hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
|
||||
}
|
||||
hash_finish(&cx, zOut);
|
||||
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);
|
||||
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 & 0xff;
|
||||
u >>= 8;
|
||||
}
|
||||
x[0] = 'I';
|
||||
hash_step(&cx, x, 9);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_uint64 u;
|
||||
int j;
|
||||
unsigned char x[9];
|
||||
double r = sqlite3_column_double(pStmt,i);
|
||||
memcpy(&u, &r, 8);
|
||||
for(j=8; j>=1; j--){
|
||||
x[j] = u & 0xff;
|
||||
u >>= 8;
|
||||
}
|
||||
x[0] = 'F';
|
||||
hash_step(&cx,x,9);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
int n2 = sqlite3_column_bytes(pStmt, i);
|
||||
const unsigned char *z2 = sqlite3_column_text(pStmt, i);
|
||||
hash_step_vformat(&cx,"T%d:",n2);
|
||||
hash_step(&cx, z2, n2);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n2 = sqlite3_column_bytes(pStmt, i);
|
||||
const unsigned char *z2 = sqlite3_column_blob(pStmt, i);
|
||||
hash_step_vformat(&cx,"B%d:",n2);
|
||||
hash_step(&cx, z2, n2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
hash_finish(&cx, zOut);
|
||||
sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_sha_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, "sha1", 1,
|
||||
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
|
||||
0, 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;
|
||||
}
|
||||
@@ -1,854 +0,0 @@
|
||||
/*
|
||||
** 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.
|
||||
** Two 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 mimimum-length decimal
|
||||
** representation of the byte-length of the blob.
|
||||
**
|
||||
** According to the rules above, all of the following SELECT statements
|
||||
** should return TRUE:
|
||||
**
|
||||
** SELECT sha3(1) = sha3('1');
|
||||
**
|
||||
** SELECT sha3('hello') = sha3(x'68656c6c6f');
|
||||
**
|
||||
** WITH a(x) AS (VALUES('xyzzy'))
|
||||
** SELECT sha3_agg(x) = sha3('T5:xyzzy') FROM a;
|
||||
**
|
||||
** WITH a(x) AS (VALUES(x'010203'))
|
||||
** SELECT sha3_agg(x) = sha3(x'42333a010203') FROM a;
|
||||
**
|
||||
** WITH a(x) AS (VALUES(0x123456))
|
||||
** SELECT sha3_agg(x) = sha3(x'490000000000123456') FROM a;
|
||||
**
|
||||
** WITH a(x) AS (VALUES(100.015625))
|
||||
** SELECT sha3_agg(x) = sha3(x'464059010000000000') FROM a;
|
||||
**
|
||||
** WITH a(x) AS (VALUES(NULL))
|
||||
** SELECT sha3_agg(x) = sha3('N') FROM a;
|
||||
**
|
||||
**
|
||||
** In sha3_query(), individual column values are encoded as with
|
||||
** sha3_agg(), but with the addition that a single "R" character is
|
||||
** inserted at the start of each row.
|
||||
**
|
||||
** Note that sha3_agg() hashes rows for which Y is NULL. Add a FILTER
|
||||
** clause if NULL rows should be excluded:
|
||||
**
|
||||
** SELECT sha3_agg(x ORDER BY rowid) FILTER(WHERE x NOT NULL) FROM t1;
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#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[] = {
|
||||
0x0000000000000001ULL, 0x0000000000008082ULL,
|
||||
0x800000000000808aULL, 0x8000000080008000ULL,
|
||||
0x000000000000808bULL, 0x0000000080000001ULL,
|
||||
0x8000000080008081ULL, 0x8000000000008009ULL,
|
||||
0x000000000000008aULL, 0x0000000000000088ULL,
|
||||
0x0000000080008009ULL, 0x000000008000000aULL,
|
||||
0x000000008000808bULL, 0x800000000000008bULL,
|
||||
0x8000000000008089ULL, 0x8000000000008003ULL,
|
||||
0x8000000000008002ULL, 0x8000000000000080ULL,
|
||||
0x000000000000800aULL, 0x800000008000000aULL,
|
||||
0x8000000080008081ULL, 0x8000000000008080ULL,
|
||||
0x0000000080000001ULL, 0x8000000080008008ULL
|
||||
};
|
||||
# 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^0x07] ^= 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 = 0x86;
|
||||
SHA3Update(p, &c1, 1);
|
||||
}else{
|
||||
const unsigned char c2 = 0x06;
|
||||
const unsigned char c3 = 0x80;
|
||||
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 0x00 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 & 0xff;
|
||||
u >>= 8;
|
||||
}
|
||||
x[0] = 'I';
|
||||
SHA3Update(p, x, 9);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_uint64 u;
|
||||
int j;
|
||||
unsigned char x[9];
|
||||
double r = sqlite3_value_double(pVal);
|
||||
memcpy(&u, &r, 8);
|
||||
for(j=8; j>=1; j--){
|
||||
x[j] = u & 0xff;
|
||||
u >>= 8;
|
||||
}
|
||||
x[0] = 'F';
|
||||
SHA3Update(p,x,9);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
int n2 = sqlite3_value_bytes(pVal);
|
||||
const unsigned char *z2 = sqlite3_value_text(pVal);
|
||||
sha3_step_vformat(p,"T%d:",n2);
|
||||
SHA3Update(p, z2, n2);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n2 = sqlite3_value_bytes(pVal);
|
||||
const unsigned char *z2 = sqlite3_value_blob(pVal);
|
||||
sha3_step_vformat(p,"B%d:",n2);
|
||||
SHA3Update(p, z2, n2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sha3_query(SQL,SIZE) function.
|
||||
**
|
||||
** 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
|
||||
__declspec(dllexport)
|
||||
#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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
** 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_malloc(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_int(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_malloc(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
|
||||
__declspec(dllexport)
|
||||
#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;
|
||||
}
|
||||
@@ -1,347 +0,0 @@
|
||||
/*
|
||||
** 2017-05-31
|
||||
**
|
||||
** 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 an eponymous virtual table that returns information
|
||||
** about all prepared statements for the database connection.
|
||||
**
|
||||
** Usage example:
|
||||
**
|
||||
** .load ./stmt
|
||||
** .mode line
|
||||
** .header on
|
||||
** SELECT * FROM stmt;
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB)
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include "sqlite3ext.h"
|
||||
#endif
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
|
||||
#define STMT_NUM_INTEGER_COLUMN 10
|
||||
typedef struct StmtRow StmtRow;
|
||||
struct StmtRow {
|
||||
sqlite3_int64 iRowid; /* Rowid value */
|
||||
char *zSql; /* column "sql" */
|
||||
int aCol[STMT_NUM_INTEGER_COLUMN+1]; /* all other column values */
|
||||
StmtRow *pNext; /* Next row to return */
|
||||
};
|
||||
|
||||
/* stmt_vtab is a subclass of sqlite3_vtab which will
|
||||
** serve as the underlying representation of a stmt virtual table
|
||||
*/
|
||||
typedef struct stmt_vtab stmt_vtab;
|
||||
struct stmt_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
sqlite3 *db; /* Database connection for this stmt vtab */
|
||||
};
|
||||
|
||||
/* stmt_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 stmt_cursor stmt_cursor;
|
||||
struct stmt_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
sqlite3 *db; /* Database connection for this cursor */
|
||||
StmtRow *pRow; /* Current row */
|
||||
};
|
||||
|
||||
/*
|
||||
** The stmtConnect() method is invoked to create a new
|
||||
** stmt_vtab that describes the stmt virtual table.
|
||||
**
|
||||
** Think of this routine as the constructor for stmt_vtab objects.
|
||||
**
|
||||
** All this routine needs to do is:
|
||||
**
|
||||
** (1) Allocate the stmt_vtab object and initialize all fields.
|
||||
**
|
||||
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
||||
** result set of queries against stmt will look like.
|
||||
*/
|
||||
static int stmtConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
stmt_vtab *pNew;
|
||||
int rc;
|
||||
|
||||
/* Column numbers */
|
||||
#define STMT_COLUMN_SQL 0 /* SQL for the statement */
|
||||
#define STMT_COLUMN_NCOL 1 /* Number of result columns */
|
||||
#define STMT_COLUMN_RO 2 /* True if read-only */
|
||||
#define STMT_COLUMN_BUSY 3 /* True if currently busy */
|
||||
#define STMT_COLUMN_NSCAN 4 /* SQLITE_STMTSTATUS_FULLSCAN_STEP */
|
||||
#define STMT_COLUMN_NSORT 5 /* SQLITE_STMTSTATUS_SORT */
|
||||
#define STMT_COLUMN_NAIDX 6 /* SQLITE_STMTSTATUS_AUTOINDEX */
|
||||
#define STMT_COLUMN_NSTEP 7 /* SQLITE_STMTSTATUS_VM_STEP */
|
||||
#define STMT_COLUMN_REPREP 8 /* SQLITE_STMTSTATUS_REPREPARE */
|
||||
#define STMT_COLUMN_RUN 9 /* SQLITE_STMTSTATUS_RUN */
|
||||
#define STMT_COLUMN_MEM 10 /* SQLITE_STMTSTATUS_MEMUSED */
|
||||
|
||||
|
||||
(void)pAux;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
(void)pzErr;
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(sql,ncol,ro,busy,nscan,nsort,naidx,nstep,"
|
||||
"reprep,run,mem)");
|
||||
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 stmt_cursor objects.
|
||||
*/
|
||||
static int stmtDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Constructor for a new stmt_cursor object.
|
||||
*/
|
||||
static int stmtOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
stmt_cursor *pCur;
|
||||
pCur = sqlite3_malloc64( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->db = ((stmt_vtab*)p)->db;
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static void stmtCsrReset(stmt_cursor *pCur){
|
||||
StmtRow *pRow = 0;
|
||||
StmtRow *pNext = 0;
|
||||
for(pRow=pCur->pRow; pRow; pRow=pNext){
|
||||
pNext = pRow->pNext;
|
||||
sqlite3_free(pRow);
|
||||
}
|
||||
pCur->pRow = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor for a stmt_cursor.
|
||||
*/
|
||||
static int stmtClose(sqlite3_vtab_cursor *cur){
|
||||
stmtCsrReset((stmt_cursor*)cur);
|
||||
sqlite3_free(cur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance a stmt_cursor to its next row of output.
|
||||
*/
|
||||
static int stmtNext(sqlite3_vtab_cursor *cur){
|
||||
stmt_cursor *pCur = (stmt_cursor*)cur;
|
||||
StmtRow *pNext = pCur->pRow->pNext;
|
||||
sqlite3_free(pCur->pRow);
|
||||
pCur->pRow = pNext;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return values of columns for the row at which the stmt_cursor
|
||||
** is currently pointing.
|
||||
*/
|
||||
static int stmtColumn(
|
||||
sqlite3_vtab_cursor *cur, /* The cursor */
|
||||
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
||||
int i /* Which column to return */
|
||||
){
|
||||
stmt_cursor *pCur = (stmt_cursor*)cur;
|
||||
StmtRow *pRow = pCur->pRow;
|
||||
if( i==STMT_COLUMN_SQL ){
|
||||
sqlite3_result_text(ctx, pRow->zSql, -1, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
sqlite3_result_int(ctx, pRow->aCol[i]);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the rowid for the current row. In this implementation, the
|
||||
** rowid is the same as the output value.
|
||||
*/
|
||||
static int stmtRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
stmt_cursor *pCur = (stmt_cursor*)cur;
|
||||
*pRowid = pCur->pRow->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the cursor has been moved off of the last
|
||||
** row of output.
|
||||
*/
|
||||
static int stmtEof(sqlite3_vtab_cursor *cur){
|
||||
stmt_cursor *pCur = (stmt_cursor*)cur;
|
||||
return pCur->pRow==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is called to "rewind" the stmt_cursor object back
|
||||
** to the first row of output. This method is always called at least
|
||||
** once prior to any call to stmtColumn() or stmtRowid() or
|
||||
** stmtEof().
|
||||
*/
|
||||
static int stmtFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
stmt_cursor *pCur = (stmt_cursor *)pVtabCursor;
|
||||
sqlite3_stmt *p = 0;
|
||||
sqlite3_int64 iRowid = 1;
|
||||
StmtRow **ppRow = 0;
|
||||
|
||||
(void)idxNum;
|
||||
(void)idxStr;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
stmtCsrReset(pCur);
|
||||
ppRow = &pCur->pRow;
|
||||
for(p=sqlite3_next_stmt(pCur->db, 0); p; p=sqlite3_next_stmt(pCur->db, p)){
|
||||
const char *zSql = sqlite3_sql(p);
|
||||
sqlite3_int64 nSql = zSql ? strlen(zSql)+1 : 0;
|
||||
StmtRow *pNew = (StmtRow*)sqlite3_malloc64(sizeof(StmtRow) + nSql);
|
||||
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(StmtRow));
|
||||
if( zSql ){
|
||||
pNew->zSql = (char*)&pNew[1];
|
||||
memcpy(pNew->zSql, zSql, nSql);
|
||||
}
|
||||
pNew->aCol[STMT_COLUMN_NCOL] = sqlite3_column_count(p);
|
||||
pNew->aCol[STMT_COLUMN_RO] = sqlite3_stmt_readonly(p);
|
||||
pNew->aCol[STMT_COLUMN_BUSY] = sqlite3_stmt_busy(p);
|
||||
pNew->aCol[STMT_COLUMN_NSCAN] = sqlite3_stmt_status(
|
||||
p, SQLITE_STMTSTATUS_FULLSCAN_STEP, 0
|
||||
);
|
||||
pNew->aCol[STMT_COLUMN_NSORT] = sqlite3_stmt_status(
|
||||
p, SQLITE_STMTSTATUS_SORT, 0
|
||||
);
|
||||
pNew->aCol[STMT_COLUMN_NAIDX] = sqlite3_stmt_status(
|
||||
p, SQLITE_STMTSTATUS_AUTOINDEX, 0
|
||||
);
|
||||
pNew->aCol[STMT_COLUMN_NSTEP] = sqlite3_stmt_status(
|
||||
p, SQLITE_STMTSTATUS_VM_STEP, 0
|
||||
);
|
||||
pNew->aCol[STMT_COLUMN_REPREP] = sqlite3_stmt_status(
|
||||
p, SQLITE_STMTSTATUS_REPREPARE, 0
|
||||
);
|
||||
pNew->aCol[STMT_COLUMN_RUN] = sqlite3_stmt_status(
|
||||
p, SQLITE_STMTSTATUS_RUN, 0
|
||||
);
|
||||
pNew->aCol[STMT_COLUMN_MEM] = sqlite3_stmt_status(
|
||||
p, SQLITE_STMTSTATUS_MEMUSED, 0
|
||||
);
|
||||
pNew->iRowid = iRowid++;
|
||||
*ppRow = pNew;
|
||||
ppRow = &pNew->pNext;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the stmt virtual table. This routine needs to create
|
||||
** a query plan for each invocation and compute an estimated cost for that
|
||||
** plan.
|
||||
*/
|
||||
static int stmtBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
(void)tab;
|
||||
pIdxInfo->estimatedCost = (double)500;
|
||||
pIdxInfo->estimatedRows = 500;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This following structure defines all the methods for the
|
||||
** stmt virtual table.
|
||||
*/
|
||||
static sqlite3_module stmtModule = {
|
||||
0, /* iVersion */
|
||||
0, /* xCreate */
|
||||
stmtConnect, /* xConnect */
|
||||
stmtBestIndex, /* xBestIndex */
|
||||
stmtDisconnect, /* xDisconnect */
|
||||
0, /* xDestroy */
|
||||
stmtOpen, /* xOpen - open a cursor */
|
||||
stmtClose, /* xClose - close a cursor */
|
||||
stmtFilter, /* xFilter - configure scan constraints */
|
||||
stmtNext, /* xNext - advance a cursor */
|
||||
stmtEof, /* xEof - check for end of scan */
|
||||
stmtColumn, /* xColumn - read data */
|
||||
stmtRowid, /* 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 sqlite3StmtVtabInit(sqlite3 *db){
|
||||
int rc = SQLITE_OK;
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3_create_module(db, "sqlite_stmt", &stmtModule, 0);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_CORE
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_stmt_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3StmtVtabInit(db);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_CORE */
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
** 2024-05-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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** An SQL function that return pseudo-random non-negative integers.
|
||||
**
|
||||
** SELECT stmtrand(123);
|
||||
**
|
||||
** A special feature of this function is that the same sequence of random
|
||||
** integers is returned for each invocation of the statement. This makes
|
||||
** the results repeatable, and hence useful for testing. The argument is
|
||||
** an integer which is the seed for the random number sequence. The seed
|
||||
** is used by the first invocation of this function only and is ignored
|
||||
** for all subsequent calls within the same statement.
|
||||
**
|
||||
** Resetting a statement (sqlite3_reset()) also resets the random number
|
||||
** sequence.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/* State of the pseudo-random number generator */
|
||||
typedef struct Stmtrand {
|
||||
unsigned int x, y;
|
||||
} Stmtrand;
|
||||
|
||||
/* auxdata key */
|
||||
#define STMTRAND_KEY (-4418371)
|
||||
|
||||
/*
|
||||
** Function: stmtrand(SEED)
|
||||
**
|
||||
** Return a pseudo-random number.
|
||||
*/
|
||||
static void stmtrandFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Stmtrand *p;
|
||||
|
||||
p = (Stmtrand*)sqlite3_get_auxdata(context, STMTRAND_KEY);
|
||||
if( p==0 ){
|
||||
unsigned int seed;
|
||||
p = sqlite3_malloc( sizeof(*p) );
|
||||
if( p==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
if( argc>=1 ){
|
||||
seed = (unsigned int)sqlite3_value_int(argv[0]);
|
||||
}else{
|
||||
seed = 0;
|
||||
}
|
||||
p->x = seed | 1;
|
||||
p->y = seed;
|
||||
sqlite3_set_auxdata(context, STMTRAND_KEY, p, sqlite3_free);
|
||||
p = (Stmtrand*)sqlite3_get_auxdata(context, STMTRAND_KEY);
|
||||
if( p==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
p->x = (p->x>>1) ^ ((1+~(p->x&1)) & 0xd0000001);
|
||||
p->y = p->y*1103515245 + 12345;
|
||||
sqlite3_result_int(context, (int)((p->x ^ p->y)&0x7fffffff));
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_stmtrand_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, "stmtrand", 1, SQLITE_UTF8, 0,
|
||||
stmtrandFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "stmtrand", 0, SQLITE_UTF8, 0,
|
||||
stmtrandFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
/*
|
||||
** 2018-04-19
|
||||
**
|
||||
** 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 template virtual-table.
|
||||
** Developers can make a copy of this file as a baseline for writing
|
||||
** new virtual tables and/or table-valued functions.
|
||||
**
|
||||
** Steps for writing a new virtual table implementation:
|
||||
**
|
||||
** (1) Make a copy of this file. Perhaps call it "mynewvtab.c"
|
||||
**
|
||||
** (2) Replace this header comment with something appropriate for
|
||||
** the new virtual table
|
||||
**
|
||||
** (3) Change every occurrence of "templatevtab" to some other string
|
||||
** appropriate for the new virtual table. Ideally, the new string
|
||||
** should be the basename of the source file: "mynewvtab". Also
|
||||
** globally change "TEMPLATEVTAB" to "MYNEWVTAB".
|
||||
**
|
||||
** (4) Run a test compilation to make sure the unmodified virtual
|
||||
** table works.
|
||||
**
|
||||
** (5) Begin making incremental changes, testing as you go, to evolve
|
||||
** the new virtual table to do what you want it to do.
|
||||
**
|
||||
** This template is minimal, in the sense that it uses only the required
|
||||
** methods on the sqlite3_module object. As a result, templatevtab is
|
||||
** a read-only and eponymous-only table. Those limitation can be removed
|
||||
** by adding new methods.
|
||||
**
|
||||
** This template implements an eponymous-only virtual table with a rowid and
|
||||
** two columns named "a" and "b". The table as 10 rows with fixed integer
|
||||
** values. Usage example:
|
||||
**
|
||||
** SELECT rowid, a, b FROM templatevtab;
|
||||
*/
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include "sqlite3ext.h"
|
||||
#endif
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* templatevtab_vtab is a subclass of sqlite3_vtab which is
|
||||
** underlying representation of the virtual table
|
||||
*/
|
||||
typedef struct templatevtab_vtab templatevtab_vtab;
|
||||
struct templatevtab_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
/* Add new fields here, as necessary */
|
||||
};
|
||||
|
||||
/* templatevtab_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 templatevtab_cursor templatevtab_cursor;
|
||||
struct templatevtab_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
/* Insert new fields here. For this templatevtab we only keep track
|
||||
** of the rowid */
|
||||
sqlite3_int64 iRowid; /* The rowid */
|
||||
};
|
||||
|
||||
/*
|
||||
** The templatevtabConnect() method is invoked to create a new
|
||||
** template virtual table.
|
||||
**
|
||||
** Think of this routine as the constructor for templatevtab_vtab objects.
|
||||
**
|
||||
** All this routine needs to do is:
|
||||
**
|
||||
** (1) Allocate the templatevtab_vtab object and initialize all fields.
|
||||
**
|
||||
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
||||
** result set of queries against the virtual table will look like.
|
||||
*/
|
||||
static int templatevtabConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
templatevtab_vtab *pNew;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(a,b)"
|
||||
);
|
||||
/* For convenience, define symbolic names for the index to each column. */
|
||||
#define TEMPLATEVTAB_A 0
|
||||
#define TEMPLATEVTAB_B 1
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is the destructor for templatevtab_vtab objects.
|
||||
*/
|
||||
static int templatevtabDisconnect(sqlite3_vtab *pVtab){
|
||||
templatevtab_vtab *p = (templatevtab_vtab*)pVtab;
|
||||
sqlite3_free(p);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Constructor for a new templatevtab_cursor object.
|
||||
*/
|
||||
static int templatevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
templatevtab_cursor *pCur;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor for a templatevtab_cursor.
|
||||
*/
|
||||
static int templatevtabClose(sqlite3_vtab_cursor *cur){
|
||||
templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance a templatevtab_cursor to its next row of output.
|
||||
*/
|
||||
static int templatevtabNext(sqlite3_vtab_cursor *cur){
|
||||
templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
|
||||
pCur->iRowid++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return values of columns for the row at which the templatevtab_cursor
|
||||
** is currently pointing.
|
||||
*/
|
||||
static int templatevtabColumn(
|
||||
sqlite3_vtab_cursor *cur, /* The cursor */
|
||||
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
||||
int i /* Which column to return */
|
||||
){
|
||||
templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
|
||||
switch( i ){
|
||||
case TEMPLATEVTAB_A:
|
||||
sqlite3_result_int(ctx, 1000 + pCur->iRowid);
|
||||
break;
|
||||
default:
|
||||
assert( i==TEMPLATEVTAB_B );
|
||||
sqlite3_result_int(ctx, 2000 + pCur->iRowid);
|
||||
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 templatevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
templatevtab_cursor *pCur = (templatevtab_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 templatevtabEof(sqlite3_vtab_cursor *cur){
|
||||
templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
|
||||
return pCur->iRowid>=10;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is called to "rewind" the templatevtab_cursor object back
|
||||
** to the first row of output. This method is always called at least
|
||||
** once prior to any call to templatevtabColumn() or templatevtabRowid() or
|
||||
** templatevtabEof().
|
||||
*/
|
||||
static int templatevtabFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
templatevtab_cursor *pCur = (templatevtab_cursor *)pVtabCursor;
|
||||
pCur->iRowid = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the virtual table. This routine needs to create
|
||||
** a query plan for each invocation and compute an estimated cost for that
|
||||
** plan.
|
||||
*/
|
||||
static int templatevtabBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
pIdxInfo->estimatedCost = (double)10;
|
||||
pIdxInfo->estimatedRows = 10;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This following structure defines all the methods for the
|
||||
** virtual table.
|
||||
*/
|
||||
static sqlite3_module templatevtabModule = {
|
||||
/* iVersion */ 0,
|
||||
/* xCreate */ 0,
|
||||
/* xConnect */ templatevtabConnect,
|
||||
/* xBestIndex */ templatevtabBestIndex,
|
||||
/* xDisconnect */ templatevtabDisconnect,
|
||||
/* xDestroy */ 0,
|
||||
/* xOpen */ templatevtabOpen,
|
||||
/* xClose */ templatevtabClose,
|
||||
/* xFilter */ templatevtabFilter,
|
||||
/* xNext */ templatevtabNext,
|
||||
/* xEof */ templatevtabEof,
|
||||
/* xColumn */ templatevtabColumn,
|
||||
/* xRowid */ templatevtabRowid,
|
||||
/* xUpdate */ 0,
|
||||
/* xBegin */ 0,
|
||||
/* xSync */ 0,
|
||||
/* xCommit */ 0,
|
||||
/* xRollback */ 0,
|
||||
/* xFindMethod */ 0,
|
||||
/* xRename */ 0,
|
||||
/* xSavepoint */ 0,
|
||||
/* xRelease */ 0,
|
||||
/* xRollbackTo */ 0,
|
||||
/* xShadowName */ 0,
|
||||
/* xIntegrity */ 0
|
||||
};
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_templatevtab_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
rc = sqlite3_create_module(db, "templatevtab", &templatevtabModule, 0);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,528 +0,0 @@
|
||||
/*
|
||||
** 2013-10-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 functions tointeger(X) and toreal(X).
|
||||
**
|
||||
** If X is an integer, real, or string value that can be
|
||||
** losslessly represented as an integer, then tointeger(X)
|
||||
** returns the corresponding integer value.
|
||||
** If X is an 8-byte BLOB then that blob is interpreted as
|
||||
** a signed two-compliment little-endian encoding of an integer
|
||||
** and tointeger(X) returns the corresponding integer value.
|
||||
** Otherwise tointeger(X) return NULL.
|
||||
**
|
||||
** If X is an integer, real, or string value that can be
|
||||
** convert into a real number, preserving at least 15 digits
|
||||
** of precision, then toreal(X) returns the corresponding real value.
|
||||
** If X is an 8-byte BLOB then that blob is interpreted as
|
||||
** a 64-bit IEEE754 big-endian floating point value
|
||||
** and toreal(X) returns the corresponding real value.
|
||||
** Otherwise toreal(X) return NULL.
|
||||
**
|
||||
** Note that tointeger(X) of an 8-byte BLOB assumes a little-endian
|
||||
** encoding whereas toreal(X) of an 8-byte BLOB assumes a big-endian
|
||||
** encoding.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Determine if this is running on a big-endian or little-endian
|
||||
** processor
|
||||
*/
|
||||
#if defined(i386) || defined(__i386__) || defined(_M_IX86)\
|
||||
|| defined(__x86_64) || defined(__x86_64__)
|
||||
# define TOTYPE_BIGENDIAN 0
|
||||
# define TOTYPE_LITTLEENDIAN 1
|
||||
#else
|
||||
const int totype_one = 1;
|
||||
# define TOTYPE_BIGENDIAN (*(char *)(&totype_one)==0)
|
||||
# define TOTYPE_LITTLEENDIAN (*(char *)(&totype_one)==1)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Constants for the largest and smallest possible 64-bit signed integers.
|
||||
** These macros are designed to work correctly on both 32-bit and 64-bit
|
||||
** compilers.
|
||||
*/
|
||||
#ifndef LARGEST_INT64
|
||||
# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
|
||||
#endif
|
||||
|
||||
#ifndef SMALLEST_INT64
|
||||
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return TRUE if character c is a whitespace character
|
||||
*/
|
||||
static int totypeIsspace(unsigned char c){
|
||||
return c==' ' || c=='\t' || c=='\n' || c=='\v' || c=='\f' || c=='\r';
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if character c is a digit
|
||||
*/
|
||||
static int totypeIsdigit(unsigned char c){
|
||||
return c>='0' && c<='9';
|
||||
}
|
||||
|
||||
/*
|
||||
** Compare the 19-character string zNum against the text representation
|
||||
** value 2^63: 9223372036854775808. Return negative, zero, or positive
|
||||
** if zNum is less than, equal to, or greater than the string.
|
||||
** Note that zNum must contain exactly 19 characters.
|
||||
**
|
||||
** Unlike memcmp() this routine is guaranteed to return the difference
|
||||
** in the values of the last digit if the only difference is in the
|
||||
** last digit. So, for example,
|
||||
**
|
||||
** totypeCompare2pow63("9223372036854775800")
|
||||
**
|
||||
** will return -8.
|
||||
*/
|
||||
static int totypeCompare2pow63(const char *zNum){
|
||||
int c = 0;
|
||||
int i;
|
||||
/* 012345678901234567 */
|
||||
const char *pow63 = "922337203685477580";
|
||||
for(i=0; c==0 && i<18; i++){
|
||||
c = (zNum[i]-pow63[i])*10;
|
||||
}
|
||||
if( c==0 ){
|
||||
c = zNum[18] - '8';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert zNum to a 64-bit signed integer.
|
||||
**
|
||||
** If the zNum value is representable as a 64-bit twos-complement
|
||||
** integer, then write that value into *pNum and return 0.
|
||||
**
|
||||
** If zNum is exactly 9223372036854665808, return 2. This special
|
||||
** case is broken out because while 9223372036854665808 cannot be a
|
||||
** signed 64-bit integer, its negative -9223372036854665808 can be.
|
||||
**
|
||||
** If zNum is too big for a 64-bit integer and is not
|
||||
** 9223372036854665808 or if zNum contains any non-numeric text,
|
||||
** then return 1.
|
||||
**
|
||||
** The string is not necessarily zero-terminated.
|
||||
*/
|
||||
static int totypeAtoi64(const char *zNum, sqlite3_int64 *pNum, int length){
|
||||
sqlite3_uint64 u = 0;
|
||||
int neg = 0; /* assume positive */
|
||||
int i;
|
||||
int c = 0;
|
||||
int nonNum = 0;
|
||||
const char *zStart;
|
||||
const char *zEnd = zNum + length;
|
||||
|
||||
while( zNum<zEnd && totypeIsspace(*zNum) ) zNum++;
|
||||
if( zNum<zEnd ){
|
||||
if( *zNum=='-' ){
|
||||
neg = 1;
|
||||
zNum++;
|
||||
}else if( *zNum=='+' ){
|
||||
zNum++;
|
||||
}
|
||||
}
|
||||
zStart = zNum;
|
||||
while( zNum<zEnd && zNum[0]=='0' ){ zNum++; } /* Skip leading zeros. */
|
||||
for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i++){
|
||||
u = u*10 + c - '0';
|
||||
}
|
||||
if( u>LARGEST_INT64 ){
|
||||
*pNum = SMALLEST_INT64;
|
||||
}else if( neg ){
|
||||
*pNum = -(sqlite3_int64)u;
|
||||
}else{
|
||||
*pNum = (sqlite3_int64)u;
|
||||
}
|
||||
if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19 || nonNum ){
|
||||
/* zNum is empty or contains non-numeric text or is longer
|
||||
** than 19 digits (thus guaranteeing that it is too large) */
|
||||
return 1;
|
||||
}else if( i<19 ){
|
||||
/* Less than 19 digits, so we know that it fits in 64 bits */
|
||||
assert( u<=LARGEST_INT64 );
|
||||
return 0;
|
||||
}else{
|
||||
/* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */
|
||||
c = totypeCompare2pow63(zNum);
|
||||
if( c<0 ){
|
||||
/* zNum is less than 9223372036854775808 so it fits */
|
||||
assert( u<=LARGEST_INT64 );
|
||||
return 0;
|
||||
}else if( c>0 ){
|
||||
/* zNum is greater than 9223372036854775808 so it overflows */
|
||||
return 1;
|
||||
}else{
|
||||
/* zNum is exactly 9223372036854775808. Fits if negative. The
|
||||
** special case 2 overflow if positive */
|
||||
assert( u-1==LARGEST_INT64 );
|
||||
assert( (*pNum)==SMALLEST_INT64 );
|
||||
return neg ? 0 : 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The string z[] is an text representation of a real number.
|
||||
** Convert this string to a double and write it into *pResult.
|
||||
**
|
||||
** The string is not necessarily zero-terminated.
|
||||
**
|
||||
** Return TRUE if the result is a valid real number (or integer) and FALSE
|
||||
** if the string is empty or contains extraneous text. Valid numbers
|
||||
** are in one of these formats:
|
||||
**
|
||||
** [+-]digits[E[+-]digits]
|
||||
** [+-]digits.[digits][E[+-]digits]
|
||||
** [+-].digits[E[+-]digits]
|
||||
**
|
||||
** Leading and trailing whitespace is ignored for the purpose of determining
|
||||
** validity.
|
||||
**
|
||||
** If some prefix of the input string is a valid number, this routine
|
||||
** returns FALSE but it still converts the prefix and writes the result
|
||||
** into *pResult.
|
||||
*/
|
||||
static int totypeAtoF(const char *z, double *pResult, int length){
|
||||
const char *zEnd = z + length;
|
||||
/* sign * significand * (10 ^ (esign * exponent)) */
|
||||
int sign = 1; /* sign of significand */
|
||||
sqlite3_int64 s = 0; /* significand */
|
||||
int d = 0; /* adjust exponent for shifting decimal point */
|
||||
int esign = 1; /* sign of exponent */
|
||||
int e = 0; /* exponent */
|
||||
int eValid = 1; /* True exponent is either not used or is well-formed */
|
||||
double result;
|
||||
int nDigits = 0;
|
||||
int nonNum = 0;
|
||||
|
||||
*pResult = 0.0; /* Default return value, in case of an error */
|
||||
|
||||
/* skip leading spaces */
|
||||
while( z<zEnd && totypeIsspace(*z) ) z++;
|
||||
if( z>=zEnd ) return 0;
|
||||
|
||||
/* get sign of significand */
|
||||
if( *z=='-' ){
|
||||
sign = -1;
|
||||
z++;
|
||||
}else if( *z=='+' ){
|
||||
z++;
|
||||
}
|
||||
|
||||
/* skip leading zeroes */
|
||||
while( z<zEnd && z[0]=='0' ) z++, nDigits++;
|
||||
|
||||
/* copy max significant digits to significand */
|
||||
while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
||||
s = s*10 + (*z - '0');
|
||||
z++, nDigits++;
|
||||
}
|
||||
|
||||
/* skip non-significant significand digits
|
||||
** (increase exponent by d to shift decimal left) */
|
||||
while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++, d++;
|
||||
if( z>=zEnd ) goto totype_atof_calc;
|
||||
|
||||
/* if decimal point is present */
|
||||
if( *z=='.' ){
|
||||
z++;
|
||||
/* copy digits from after decimal to significand
|
||||
** (decrease exponent by d to shift decimal right) */
|
||||
while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
||||
s = s*10 + (*z - '0');
|
||||
z++, nDigits++, d--;
|
||||
}
|
||||
/* skip non-significant digits */
|
||||
while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++;
|
||||
}
|
||||
if( z>=zEnd ) goto totype_atof_calc;
|
||||
|
||||
/* if exponent is present */
|
||||
if( *z=='e' || *z=='E' ){
|
||||
z++;
|
||||
eValid = 0;
|
||||
if( z>=zEnd ) goto totype_atof_calc;
|
||||
/* get sign of exponent */
|
||||
if( *z=='-' ){
|
||||
esign = -1;
|
||||
z++;
|
||||
}else if( *z=='+' ){
|
||||
z++;
|
||||
}
|
||||
/* copy digits to exponent */
|
||||
while( z<zEnd && totypeIsdigit(*z) ){
|
||||
e = e<10000 ? (e*10 + (*z - '0')) : 10000;
|
||||
z++;
|
||||
eValid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* skip trailing spaces */
|
||||
if( nDigits && eValid ){
|
||||
while( z<zEnd && totypeIsspace(*z) ) z++;
|
||||
}
|
||||
|
||||
totype_atof_calc:
|
||||
/* adjust exponent by d, and update sign */
|
||||
e = (e*esign) + d;
|
||||
if( e<0 ) {
|
||||
esign = -1;
|
||||
e *= -1;
|
||||
} else {
|
||||
esign = 1;
|
||||
}
|
||||
|
||||
/* if 0 significand */
|
||||
if( !s ) {
|
||||
/* In the IEEE 754 standard, zero is signed.
|
||||
** Add the sign if we've seen at least one digit */
|
||||
result = (sign<0 && nDigits) ? -(double)0 : (double)0;
|
||||
} else {
|
||||
/* attempt to reduce exponent */
|
||||
if( esign>0 ){
|
||||
while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10;
|
||||
}else{
|
||||
while( !(s%10) && e>0 ) e--,s/=10;
|
||||
}
|
||||
|
||||
/* adjust the sign of significand */
|
||||
s = sign<0 ? -s : s;
|
||||
|
||||
/* if exponent, scale significand as appropriate
|
||||
** and store in result. */
|
||||
if( e ){
|
||||
double scale = 1.0;
|
||||
/* attempt to handle extremely small/large numbers better */
|
||||
if( e>307 && e<342 ){
|
||||
while( e%308 ) { scale *= 1.0e+1; e -= 1; }
|
||||
if( esign<0 ){
|
||||
result = s / scale;
|
||||
result /= 1.0e+308;
|
||||
}else{
|
||||
result = s * scale;
|
||||
result *= 1.0e+308;
|
||||
}
|
||||
}else if( e>=342 ){
|
||||
if( esign<0 ){
|
||||
result = 0.0*s;
|
||||
}else{
|
||||
result = 1e308*1e308*s; /* Infinity */
|
||||
}
|
||||
}else{
|
||||
/* 1.0e+22 is the largest power of 10 than can be
|
||||
** represented exactly. */
|
||||
while( e%22 ) { scale *= 1.0e+1; e -= 1; }
|
||||
while( e>0 ) { scale *= 1.0e+22; e -= 22; }
|
||||
if( esign<0 ){
|
||||
result = s / scale;
|
||||
}else{
|
||||
result = s * scale;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = (double)s;
|
||||
}
|
||||
}
|
||||
|
||||
/* store the result */
|
||||
*pResult = result;
|
||||
|
||||
/* return true if number and no extra non-whitespace chracters after */
|
||||
return z>=zEnd && nDigits>0 && eValid && nonNum==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a floating point value to an integer. Or, if this cannot be
|
||||
** done in a way that avoids 'outside the range of representable values'
|
||||
** warnings from UBSAN, return 0.
|
||||
**
|
||||
** This function is a modified copy of internal SQLite function
|
||||
** sqlite3RealToI64().
|
||||
*/
|
||||
static sqlite3_int64 totypeDoubleToInt(double r){
|
||||
if( r<-9223372036854774784.0 ) return 0;
|
||||
if( r>+9223372036854774784.0 ) return 0;
|
||||
return (sqlite3_int64)r;
|
||||
}
|
||||
|
||||
/*
|
||||
** tointeger(X): If X is any value (integer, double, blob, or string) that
|
||||
** can be losslessly converted into an integer, then make the conversion and
|
||||
** return the result. Otherwise, return NULL.
|
||||
*/
|
||||
static void tointegerFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
assert( argc==1 );
|
||||
(void)argc;
|
||||
switch( sqlite3_value_type(argv[0]) ){
|
||||
case SQLITE_FLOAT: {
|
||||
double rVal = sqlite3_value_double(argv[0]);
|
||||
sqlite3_int64 iVal = totypeDoubleToInt(rVal);
|
||||
if( rVal==(double)iVal ){
|
||||
sqlite3_result_int64(context, iVal);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_result_int64(context, sqlite3_value_int64(argv[0]));
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
|
||||
if( zBlob ){
|
||||
int nBlob = sqlite3_value_bytes(argv[0]);
|
||||
if( nBlob==sizeof(sqlite3_int64) ){
|
||||
sqlite3_int64 iVal;
|
||||
if( TOTYPE_BIGENDIAN ){
|
||||
int i;
|
||||
unsigned char zBlobRev[sizeof(sqlite3_int64)];
|
||||
for(i=0; i<sizeof(sqlite3_int64); i++){
|
||||
zBlobRev[i] = zBlob[sizeof(sqlite3_int64)-1-i];
|
||||
}
|
||||
memcpy(&iVal, zBlobRev, sizeof(sqlite3_int64));
|
||||
}else{
|
||||
memcpy(&iVal, zBlob, sizeof(sqlite3_int64));
|
||||
}
|
||||
sqlite3_result_int64(context, iVal);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
const unsigned char *zStr = sqlite3_value_text(argv[0]);
|
||||
if( zStr ){
|
||||
int nStr = sqlite3_value_bytes(argv[0]);
|
||||
if( nStr && !totypeIsspace(zStr[0]) ){
|
||||
sqlite3_int64 iVal;
|
||||
if( !totypeAtoi64((const char*)zStr, &iVal, nStr) ){
|
||||
sqlite3_result_int64(context, iVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** toreal(X): If X is any value (integer, double, blob, or string) that can
|
||||
** be losslessly converted into a real number, then do so and return that
|
||||
** real number. Otherwise return NULL.
|
||||
*/
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable: 4748)
|
||||
#pragma optimize("", off)
|
||||
#endif
|
||||
static void torealFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
assert( argc==1 );
|
||||
(void)argc;
|
||||
switch( sqlite3_value_type(argv[0]) ){
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_result_double(context, sqlite3_value_double(argv[0]));
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_int64 iVal = sqlite3_value_int64(argv[0]);
|
||||
double rVal = (double)iVal;
|
||||
if( iVal==totypeDoubleToInt(rVal) ){
|
||||
sqlite3_result_double(context, rVal);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
|
||||
if( zBlob ){
|
||||
int nBlob = sqlite3_value_bytes(argv[0]);
|
||||
if( nBlob==sizeof(double) ){
|
||||
double rVal;
|
||||
if( TOTYPE_LITTLEENDIAN ){
|
||||
int i;
|
||||
unsigned char zBlobRev[sizeof(double)];
|
||||
for(i=0; i<sizeof(double); i++){
|
||||
zBlobRev[i] = zBlob[sizeof(double)-1-i];
|
||||
}
|
||||
memcpy(&rVal, zBlobRev, sizeof(double));
|
||||
}else{
|
||||
memcpy(&rVal, zBlob, sizeof(double));
|
||||
}
|
||||
sqlite3_result_double(context, rVal);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
const unsigned char *zStr = sqlite3_value_text(argv[0]);
|
||||
if( zStr ){
|
||||
int nStr = sqlite3_value_bytes(argv[0]);
|
||||
if( nStr && !totypeIsspace(zStr[0]) && !totypeIsspace(zStr[nStr-1]) ){
|
||||
double rVal;
|
||||
if( totypeAtoF((const char*)zStr, &rVal, nStr) ){
|
||||
sqlite3_result_double(context, rVal);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if defined(_MSC_VER)
|
||||
#pragma optimize("", on)
|
||||
#pragma warning(default: 4748)
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_totype_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, "tointeger", 1,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_INNOCUOUS, 0,
|
||||
tointegerFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "toreal", 1,
|
||||
SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_INNOCUOUS, 0,
|
||||
torealFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
** 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 maginitude 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
|
||||
__declspec(dllexport)
|
||||
#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);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
** 2020-01-11
|
||||
**
|
||||
** 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 various SQL functions used to access
|
||||
** the following SQLite C-language APIs:
|
||||
**
|
||||
** sqlite3_uri_parameter()
|
||||
** sqlite3_uri_boolean()
|
||||
** sqlite3_uri_int64()
|
||||
** sqlite3_uri_key()
|
||||
** sqlite3_filename_database()
|
||||
** sqlite3_filename_journal()
|
||||
** sqlite3_filename_wal()
|
||||
** sqlite3_db_filename()
|
||||
**
|
||||
** These SQL functions are for testing and demonstration purposes only.
|
||||
**
|
||||
**
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_db_filename(SCHEMA)
|
||||
**
|
||||
** Return the filename corresponding to SCHEMA.
|
||||
*/
|
||||
static void func_db_filename(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
sqlite3_result_text(context, zFile, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_uri_parameter(SCHEMA,NAME)
|
||||
**
|
||||
** Return the value of the NAME query parameter to the database for SCHEMA
|
||||
*/
|
||||
static void func_uri_parameter(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zName = (const char*)sqlite3_value_text(argv[1]);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
const char *zRes = sqlite3_uri_parameter(zFile, zName);
|
||||
sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_uri_boolean(SCHEMA,NAME,DEFAULT)
|
||||
**
|
||||
** Return the boolean value of the NAME query parameter to
|
||||
** the database for SCHEMA
|
||||
*/
|
||||
static void func_uri_boolean(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zName = (const char*)sqlite3_value_text(argv[1]);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
int iDflt = sqlite3_value_int(argv[2]);
|
||||
int iRes = sqlite3_uri_boolean(zFile, zName, iDflt);
|
||||
sqlite3_result_int(context, iRes);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_uri_key(SCHEMA,N)
|
||||
**
|
||||
** Return the name of the Nth query parameter
|
||||
*/
|
||||
static void func_uri_key(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
int N = sqlite3_value_int(argv[1]);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
const char *zRes = sqlite3_uri_key(zFile, N);
|
||||
sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_uri_int64(SCHEMA,NAME,DEFAULT)
|
||||
**
|
||||
** Return the int64 value of the NAME query parameter to
|
||||
** the database for SCHEMA
|
||||
*/
|
||||
static void func_uri_int64(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zName = (const char*)sqlite3_value_text(argv[1]);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
sqlite3_int64 iDflt = sqlite3_value_int64(argv[2]);
|
||||
sqlite3_int64 iRes = sqlite3_uri_int64(zFile, zName, iDflt);
|
||||
sqlite3_result_int64(context, iRes);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_filename_database(SCHEMA)
|
||||
**
|
||||
** Return the database filename for SCHEMA
|
||||
*/
|
||||
static void func_filename_database(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
const char *zRes = zFile ? sqlite3_filename_database(zFile) : 0;
|
||||
sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_filename_journal(SCHEMA)
|
||||
**
|
||||
** Return the rollback journal filename for SCHEMA
|
||||
*/
|
||||
static void func_filename_journal(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
const char *zRes = zFile ? sqlite3_filename_journal(zFile) : 0;
|
||||
sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: sqlite3_filename_wal(SCHEMA)
|
||||
**
|
||||
** Return the WAL filename for SCHEMA
|
||||
*/
|
||||
static void func_filename_wal(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zFile = sqlite3_db_filename(db, zSchema);
|
||||
const char *zRes = zFile ? sqlite3_filename_wal(zFile) : 0;
|
||||
sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_urifuncs_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
static const struct {
|
||||
const char *zFuncName;
|
||||
int nArg;
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} aFunc[] = {
|
||||
{ "sqlite3_db_filename", 1, func_db_filename },
|
||||
{ "sqlite3_uri_parameter", 2, func_uri_parameter },
|
||||
{ "sqlite3_uri_boolean", 3, func_uri_boolean },
|
||||
{ "sqlite3_uri_int64", 3, func_uri_int64 },
|
||||
{ "sqlite3_uri_key", 2, func_uri_key },
|
||||
{ "sqlite3_filename_database", 1, func_filename_database },
|
||||
{ "sqlite3_filename_journal", 1, func_filename_journal },
|
||||
{ "sqlite3_filename_wal", 1, func_filename_wal },
|
||||
};
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
for(i=0; rc==SQLITE_OK && i<sizeof(aFunc)/sizeof(aFunc[0]); i++){
|
||||
rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg,
|
||||
SQLITE_UTF8, 0,
|
||||
aFunc[i].xFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
** 2019-10-23
|
||||
**
|
||||
** 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 handling RFC-4122 UUIDs
|
||||
** Three SQL functions are implemented:
|
||||
**
|
||||
** uuid() - generate a version 4 UUID as a string
|
||||
** uuid_str(X) - convert a UUID X into a well-formed UUID string
|
||||
** uuid_blob(X) - convert a UUID X into a 16-byte blob
|
||||
**
|
||||
** The output from uuid() and uuid_str(X) are always well-formed RFC-4122
|
||||
** UUID strings in this format:
|
||||
**
|
||||
** xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
|
||||
**
|
||||
** All of the 'x', 'M', and 'N' values are lower-case hexadecimal digits.
|
||||
** The M digit indicates the "version". For uuid()-generated UUIDs, the
|
||||
** version is always "4" (a random UUID). The upper three bits of N digit
|
||||
** are the "variant". This library only supports variant 1 (indicated
|
||||
** by values of N between '8' and 'b') as those are overwhelming the most
|
||||
** common. Other variants are for legacy compatibility only.
|
||||
**
|
||||
** The output of uuid_blob(X) is always a 16-byte blob. The UUID input
|
||||
** string is converted in network byte order (big-endian) in accordance
|
||||
** with RFC-4122 specifications for variant-1 UUIDs. Note that network
|
||||
** byte order is *always* used, even if the input self-identifies as a
|
||||
** variant-2 UUID.
|
||||
**
|
||||
** The input X to the uuid_str() and uuid_blob() functions can be either
|
||||
** a string or a BLOB. If it is a BLOB it must be exactly 16 bytes in
|
||||
** length or else a NULL is returned. If the input is a string it must
|
||||
** consist of 32 hexadecimal digits, upper or lower case, optionally
|
||||
** surrounded by {...} and with optional "-" characters interposed in the
|
||||
** middle. The flexibility of input is inspired by the PostgreSQL
|
||||
** implementation of UUID functions that accept in all of the following
|
||||
** formats:
|
||||
**
|
||||
** A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11
|
||||
** {a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}
|
||||
** a0eebc999c0b4ef8bb6d6bb9bd380a11
|
||||
** a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11
|
||||
** {a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}
|
||||
**
|
||||
** If any of the above inputs are passed into uuid_str(), the output will
|
||||
** always be in the canonical RFC-4122 format:
|
||||
**
|
||||
** a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11
|
||||
**
|
||||
** If the X input string has too few or too many digits or contains
|
||||
** stray characters other than {, }, or -, then NULL is returned.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#if !defined(SQLITE_ASCII) && !defined(SQLITE_EBCDIC)
|
||||
# define SQLITE_ASCII 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Translate a single byte of Hex into an integer.
|
||||
** This routine only works if h really is a valid hexadecimal
|
||||
** character: 0..9a..fA..F
|
||||
*/
|
||||
static unsigned char sqlite3UuidHexToInt(int h){
|
||||
assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
|
||||
#ifdef SQLITE_ASCII
|
||||
h += 9*(1&(h>>6));
|
||||
#endif
|
||||
#ifdef SQLITE_EBCDIC
|
||||
h += 9*(1&~(h>>4));
|
||||
#endif
|
||||
return (unsigned char)(h & 0xf);
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a 16-byte BLOB into a well-formed RFC-4122 UUID. The output
|
||||
** buffer zStr should be at least 37 bytes in length. The output will
|
||||
** be zero-terminated.
|
||||
*/
|
||||
static void sqlite3UuidBlobToStr(
|
||||
const unsigned char *aBlob, /* Input blob */
|
||||
unsigned char *zStr /* Write the answer here */
|
||||
){
|
||||
static const char zDigits[] = "0123456789abcdef";
|
||||
int i, k;
|
||||
unsigned char x;
|
||||
k = 0;
|
||||
for(i=0, k=0x550; i<16; i++, k=k>>1){
|
||||
if( k&1 ){
|
||||
zStr[0] = '-';
|
||||
zStr++;
|
||||
}
|
||||
x = aBlob[i];
|
||||
zStr[0] = zDigits[x>>4];
|
||||
zStr[1] = zDigits[x&0xf];
|
||||
zStr += 2;
|
||||
}
|
||||
*zStr = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to parse a zero-terminated input string zStr into a binary
|
||||
** UUID. Return 0 on success, or non-zero if the input string is not
|
||||
** parsable.
|
||||
*/
|
||||
static int sqlite3UuidStrToBlob(
|
||||
const unsigned char *zStr, /* Input string */
|
||||
unsigned char *aBlob /* Write results here */
|
||||
){
|
||||
int i;
|
||||
if( zStr[0]=='{' ) zStr++;
|
||||
for(i=0; i<16; i++){
|
||||
if( zStr[0]=='-' ) zStr++;
|
||||
if( isxdigit(zStr[0]) && isxdigit(zStr[1]) ){
|
||||
aBlob[i] = (sqlite3UuidHexToInt(zStr[0])<<4)
|
||||
+ sqlite3UuidHexToInt(zStr[1]);
|
||||
zStr += 2;
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if( zStr[0]=='}' ) zStr++;
|
||||
return zStr[0]!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Render sqlite3_value pIn as a 16-byte UUID blob. Return a pointer
|
||||
** to the blob, or NULL if the input is not well-formed.
|
||||
*/
|
||||
static const unsigned char *sqlite3UuidInputToBlob(
|
||||
sqlite3_value *pIn, /* Input text */
|
||||
unsigned char *pBuf /* output buffer */
|
||||
){
|
||||
switch( sqlite3_value_type(pIn) ){
|
||||
case SQLITE_TEXT: {
|
||||
const unsigned char *z = sqlite3_value_text(pIn);
|
||||
if( sqlite3UuidStrToBlob(z, pBuf) ) return 0;
|
||||
return pBuf;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n = sqlite3_value_bytes(pIn);
|
||||
return n==16 ? sqlite3_value_blob(pIn) : 0;
|
||||
}
|
||||
default: {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Implementation of uuid() */
|
||||
static void sqlite3UuidFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char aBlob[16];
|
||||
unsigned char zStr[37];
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
sqlite3_randomness(16, aBlob);
|
||||
aBlob[6] = (aBlob[6]&0x0f) + 0x40;
|
||||
aBlob[8] = (aBlob[8]&0x3f) + 0x80;
|
||||
sqlite3UuidBlobToStr(aBlob, zStr);
|
||||
sqlite3_result_text(context, (char*)zStr, 36, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/* Implementation of uuid_str() */
|
||||
static void sqlite3UuidStrFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char aBlob[16];
|
||||
unsigned char zStr[37];
|
||||
const unsigned char *pBlob;
|
||||
(void)argc;
|
||||
pBlob = sqlite3UuidInputToBlob(argv[0], aBlob);
|
||||
if( pBlob==0 ) return;
|
||||
sqlite3UuidBlobToStr(pBlob, zStr);
|
||||
sqlite3_result_text(context, (char*)zStr, 36, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/* Implementation of uuid_blob() */
|
||||
static void sqlite3UuidBlobFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
unsigned char aBlob[16];
|
||||
const unsigned char *pBlob;
|
||||
(void)argc;
|
||||
pBlob = sqlite3UuidInputToBlob(argv[0], aBlob);
|
||||
if( pBlob==0 ) return;
|
||||
sqlite3_result_blob(context, pBlob, 16, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_uuid_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, "uuid", 0, SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
|
||||
sqlite3UuidFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "uuid_str", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
|
||||
0, sqlite3UuidStrFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "uuid_blob", 1,
|
||||
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
|
||||
0, sqlite3UuidBlobFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1,625 +0,0 @@
|
||||
/*
|
||||
** 2017-08-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 a virtual table that prints diagnostic information
|
||||
** on stdout when its key interfaces are called. This is intended for
|
||||
** interactive analysis and debugging of virtual table interfaces.
|
||||
**
|
||||
** Usage example:
|
||||
**
|
||||
** .load ./vtablog
|
||||
** CREATE VIRTUAL TABLE temp.log USING vtablog(
|
||||
** schema='CREATE TABLE x(a,b,c)',
|
||||
** rows=25
|
||||
** );
|
||||
** SELECT * FROM log;
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
/* vtablog_vtab is a subclass of sqlite3_vtab which will
|
||||
** serve as the underlying representation of a vtablog virtual table
|
||||
*/
|
||||
typedef struct vtablog_vtab vtablog_vtab;
|
||||
struct vtablog_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
char *zDb; /* Schema name. argv[1] of xConnect/xCreate */
|
||||
char *zName; /* Table name. argv[2] of xConnect/xCreate */
|
||||
int nRow; /* Number of rows in the table */
|
||||
int nCursor; /* Number of cursors created */
|
||||
};
|
||||
|
||||
/* vtablog_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 vtablog_cursor vtablog_cursor;
|
||||
struct vtablog_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
int iCursor; /* Cursor number */
|
||||
sqlite3_int64 iRowid; /* The rowid */
|
||||
};
|
||||
|
||||
/* Skip leading whitespace. Return a pointer to the first non-whitespace
|
||||
** character, or to the zero terminator if the string has only whitespace */
|
||||
static const char *vtablog_skip_whitespace(const char *z){
|
||||
while( isspace((unsigned char)z[0]) ) z++;
|
||||
return z;
|
||||
}
|
||||
|
||||
/* Remove trailing whitespace from the end of string z[] */
|
||||
static void vtablog_trim_whitespace(char *z){
|
||||
size_t n = strlen(z);
|
||||
while( n>0 && isspace((unsigned char)z[n]) ) n--;
|
||||
z[n] = 0;
|
||||
}
|
||||
|
||||
/* Dequote the string */
|
||||
static void vtablog_dequote(char *z){
|
||||
int j;
|
||||
char cQuote = z[0];
|
||||
size_t i, n;
|
||||
|
||||
if( cQuote!='\'' && cQuote!='"' ) return;
|
||||
n = strlen(z);
|
||||
if( n<2 || z[n-1]!=z[0] ) return;
|
||||
for(i=1, j=0; i<n-1; i++){
|
||||
if( z[i]==cQuote && z[i+1]==cQuote ) i++;
|
||||
z[j++] = z[i];
|
||||
}
|
||||
z[j] = 0;
|
||||
}
|
||||
|
||||
/* Check to see if the string is of the form: "TAG = VALUE" with optional
|
||||
** whitespace before and around tokens. If it is, return a pointer to the
|
||||
** first character of VALUE. If it is not, return NULL.
|
||||
*/
|
||||
static const char *vtablog_parameter(const char *zTag, int nTag, const char *z){
|
||||
z = vtablog_skip_whitespace(z);
|
||||
if( strncmp(zTag, z, nTag)!=0 ) return 0;
|
||||
z = vtablog_skip_whitespace(z+nTag);
|
||||
if( z[0]!='=' ) return 0;
|
||||
return vtablog_skip_whitespace(z+1);
|
||||
}
|
||||
|
||||
/* Decode a parameter that requires a dequoted string.
|
||||
**
|
||||
** Return non-zero on an error.
|
||||
*/
|
||||
static int vtablog_string_parameter(
|
||||
char **pzErr, /* Leave the error message here, if there is one */
|
||||
const char *zParam, /* Parameter we are checking for */
|
||||
const char *zArg, /* Raw text of the virtual table argment */
|
||||
char **pzVal /* Write the dequoted string value here */
|
||||
){
|
||||
const char *zValue;
|
||||
zValue = vtablog_parameter(zParam,(int)strlen(zParam),zArg);
|
||||
if( zValue==0 ) return 0;
|
||||
if( *pzVal ){
|
||||
*pzErr = sqlite3_mprintf("more than one '%s' parameter", zParam);
|
||||
return 1;
|
||||
}
|
||||
*pzVal = sqlite3_mprintf("%s", zValue);
|
||||
if( *pzVal==0 ){
|
||||
*pzErr = sqlite3_mprintf("out of memory");
|
||||
return 1;
|
||||
}
|
||||
vtablog_trim_whitespace(*pzVal);
|
||||
vtablog_dequote(*pzVal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0 /* not used - yet */
|
||||
/* Return 0 if the argument is false and 1 if it is true. Return -1 if
|
||||
** we cannot really tell.
|
||||
*/
|
||||
static int vtablog_boolean(const char *z){
|
||||
if( sqlite3_stricmp("yes",z)==0
|
||||
|| sqlite3_stricmp("on",z)==0
|
||||
|| sqlite3_stricmp("true",z)==0
|
||||
|| (z[0]=='1' && z[1]==0)
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3_stricmp("no",z)==0
|
||||
|| sqlite3_stricmp("off",z)==0
|
||||
|| sqlite3_stricmp("false",z)==0
|
||||
|| (z[0]=='0' && z[1]==0)
|
||||
){
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The vtablogConnect() method is invoked to create a new
|
||||
** vtablog_vtab that describes the vtablog virtual table.
|
||||
**
|
||||
** Think of this routine as the constructor for vtablog_vtab objects.
|
||||
**
|
||||
** All this routine needs to do is:
|
||||
**
|
||||
** (1) Allocate the vtablog_vtab object and initialize all fields.
|
||||
**
|
||||
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
|
||||
** result set of queries against vtablog will look like.
|
||||
*/
|
||||
static int vtablogConnectCreate(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr,
|
||||
int isCreate
|
||||
){
|
||||
vtablog_vtab *pNew;
|
||||
int i;
|
||||
int rc;
|
||||
char *zSchema = 0;
|
||||
char *zNRow = 0;
|
||||
|
||||
printf("%s.%s.%s():\n", argv[1], argv[2],
|
||||
isCreate ? "xCreate" : "xConnect");
|
||||
printf(" argc=%d\n", argc);
|
||||
for(i=0; i<argc; i++){
|
||||
printf(" argv[%d] = ", i);
|
||||
if( argv[i] ){
|
||||
printf("[%s]\n", argv[i]);
|
||||
}else{
|
||||
printf("NULL\n");
|
||||
}
|
||||
}
|
||||
|
||||
for(i=3; i<argc; i++){
|
||||
const char *z = argv[i];
|
||||
if( vtablog_string_parameter(pzErr, "schema", z, &zSchema) ){
|
||||
rc = SQLITE_ERROR;
|
||||
goto vtablog_end_connect;
|
||||
}
|
||||
if( vtablog_string_parameter(pzErr, "rows", z, &zNRow) ){
|
||||
rc = SQLITE_ERROR;
|
||||
goto vtablog_end_connect;
|
||||
}
|
||||
}
|
||||
if( zSchema==0 ){
|
||||
zSchema = sqlite3_mprintf("%s","CREATE TABLE x(a,b);");
|
||||
}
|
||||
printf(" schema = '%s'\n", zSchema);
|
||||
rc = sqlite3_declare_vtab(db, zSchema);
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->nRow = 10;
|
||||
if( zNRow ) pNew->nRow = atoi(zNRow);
|
||||
printf(" nrow = %d\n", pNew->nRow);
|
||||
pNew->zDb = sqlite3_mprintf("%s", argv[1]);
|
||||
pNew->zName = sqlite3_mprintf("%s", argv[2]);
|
||||
}
|
||||
|
||||
vtablog_end_connect:
|
||||
sqlite3_free(zSchema);
|
||||
sqlite3_free(zNRow);
|
||||
return rc;
|
||||
}
|
||||
static int vtablogCreate(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,1);
|
||||
}
|
||||
static int vtablogConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This method is the destructor for vtablog_cursor objects.
|
||||
*/
|
||||
static int vtablogDisconnect(sqlite3_vtab *pVtab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
|
||||
printf("%s.%s.xDisconnect()\n", pTab->zDb, pTab->zName);
|
||||
sqlite3_free(pTab->zDb);
|
||||
sqlite3_free(pTab->zName);
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is the destructor for vtablog_cursor objects.
|
||||
*/
|
||||
static int vtablogDestroy(sqlite3_vtab *pVtab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
|
||||
printf("%s.%s.xDestroy()\n", pTab->zDb, pTab->zName);
|
||||
sqlite3_free(pTab->zDb);
|
||||
sqlite3_free(pTab->zName);
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Constructor for a new vtablog_cursor object.
|
||||
*/
|
||||
static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)p;
|
||||
vtablog_cursor *pCur;
|
||||
printf("%s.%s.xOpen(cursor=%d)\n", pTab->zDb, pTab->zName,
|
||||
++pTab->nCursor);
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->iCursor = pTab->nCursor;
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor for a vtablog_cursor.
|
||||
*/
|
||||
static int vtablogClose(sqlite3_vtab_cursor *cur){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
printf("%s.%s.xClose(cursor=%d)\n", pTab->zDb, pTab->zName, pCur->iCursor);
|
||||
sqlite3_free(cur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance a vtablog_cursor to its next row of output.
|
||||
*/
|
||||
static int vtablogNext(sqlite3_vtab_cursor *cur){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
printf("%s.%s.xNext(cursor=%d) rowid %d -> %d\n",
|
||||
pTab->zDb, pTab->zName, pCur->iCursor,
|
||||
(int)pCur->iRowid, (int)pCur->iRowid+1);
|
||||
pCur->iRowid++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return values of columns for the row at which the vtablog_cursor
|
||||
** is currently pointing.
|
||||
*/
|
||||
static int vtablogColumn(
|
||||
sqlite3_vtab_cursor *cur, /* The cursor */
|
||||
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
||||
int i /* Which column to return */
|
||||
){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
char zVal[50];
|
||||
|
||||
if( i<26 ){
|
||||
sqlite3_snprintf(sizeof(zVal),zVal,"%c%d",
|
||||
"abcdefghijklmnopqrstuvwyz"[i], pCur->iRowid);
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid);
|
||||
}
|
||||
printf("%s.%s.xColumn(cursor=%d, i=%d): [%s]\n",
|
||||
pTab->zDb, pTab->zName, pCur->iCursor, i, zVal);
|
||||
sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the rowid for the current row. In this implementation, the
|
||||
** rowid is the same as the output value.
|
||||
*/
|
||||
static int vtablogRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
printf("%s.%s.xRowid(cursor=%d): %d\n",
|
||||
pTab->zDb, pTab->zName, pCur->iCursor, (int)pCur->iRowid);
|
||||
*pRowid = pCur->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the cursor has been moved off of the last
|
||||
** row of output.
|
||||
*/
|
||||
static int vtablogEof(sqlite3_vtab_cursor *cur){
|
||||
vtablog_cursor *pCur = (vtablog_cursor*)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
int rc = pCur->iRowid >= pTab->nRow;
|
||||
printf("%s.%s.xEof(cursor=%d): %d\n",
|
||||
pTab->zDb, pTab->zName, pCur->iCursor, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Output an sqlite3_value object's value as an SQL literal.
|
||||
*/
|
||||
static void vtablogQuote(sqlite3_value *p){
|
||||
char z[50];
|
||||
switch( sqlite3_value_type(p) ){
|
||||
case SQLITE_NULL: {
|
||||
printf("NULL");
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_snprintf(50,z,"%lld", sqlite3_value_int64(p));
|
||||
printf("%s", z);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_snprintf(50,z,"%!.20g", sqlite3_value_double(p));
|
||||
printf("%s", z);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n = sqlite3_value_bytes(p);
|
||||
const unsigned char *z = (const unsigned char*)sqlite3_value_blob(p);
|
||||
int i;
|
||||
printf("x'");
|
||||
for(i=0; i<n; i++) printf("%02x", z[i]);
|
||||
printf("'");
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
const char *z = (const char*)sqlite3_value_text(p);
|
||||
int i;
|
||||
char c;
|
||||
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
|
||||
if( c==0 ){
|
||||
printf("'%s'",z);
|
||||
}else{
|
||||
printf("'");
|
||||
while( *z ){
|
||||
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
|
||||
if( c=='\'' ) i++;
|
||||
if( i ){
|
||||
printf("%.*s", i, z);
|
||||
z += i;
|
||||
}
|
||||
if( c=='\'' ){
|
||||
printf("'");
|
||||
continue;
|
||||
}
|
||||
if( c==0 ){
|
||||
break;
|
||||
}
|
||||
z++;
|
||||
}
|
||||
printf("'");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This method is called to "rewind" the vtablog_cursor object back
|
||||
** to the first row of output. This method is always called at least
|
||||
** once prior to any call to vtablogColumn() or vtablogRowid() or
|
||||
** vtablogEof().
|
||||
*/
|
||||
static int vtablogFilter(
|
||||
sqlite3_vtab_cursor *cur,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
vtablog_cursor *pCur = (vtablog_cursor *)cur;
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab;
|
||||
printf("%s.%s.xFilter(cursor=%d):\n", pTab->zDb, pTab->zName, pCur->iCursor);
|
||||
pCur->iRowid = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the vtablog virtual table. This routine needs to create
|
||||
** a query plan for each invocation and compute an estimated cost for that
|
||||
** plan.
|
||||
*/
|
||||
static int vtablogBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *p
|
||||
){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
int i;
|
||||
printf("%s.%s.xBestIndex():\n", pTab->zDb, pTab->zName);
|
||||
printf(" colUsed: 0x%016llx\n", p->colUsed);
|
||||
printf(" nConstraint: %d\n", p->nConstraint);
|
||||
for(i=0; i<p->nConstraint; i++){
|
||||
printf(
|
||||
" constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n",
|
||||
i,
|
||||
p->aConstraint[i].iColumn,
|
||||
p->aConstraint[i].iTermOffset,
|
||||
p->aConstraint[i].op,
|
||||
p->aConstraint[i].usable,
|
||||
sqlite3_vtab_collation(p,i));
|
||||
}
|
||||
printf(" nOrderBy: %d\n", p->nOrderBy);
|
||||
for(i=0; i<p->nOrderBy; i++){
|
||||
printf(" orderby[%d]: col=%d desc=%d\n",
|
||||
i,
|
||||
p->aOrderBy[i].iColumn,
|
||||
p->aOrderBy[i].desc);
|
||||
}
|
||||
p->estimatedCost = (double)500;
|
||||
p->estimatedRows = 500;
|
||||
printf(" idxNum=%d\n", p->idxNum);
|
||||
printf(" idxStr=NULL\n");
|
||||
printf(" orderByConsumed=%d\n", p->orderByConsumed);
|
||||
printf(" estimatedCost=%g\n", p->estimatedCost);
|
||||
printf(" estimatedRows=%lld\n", p->estimatedRows);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite invokes this method to INSERT, UPDATE, or DELETE content from
|
||||
** the table.
|
||||
**
|
||||
** This implementation does not actually make any changes to the table
|
||||
** content. It merely logs the fact that the method was invoked
|
||||
*/
|
||||
static int vtablogUpdate(
|
||||
sqlite3_vtab *tab,
|
||||
int argc,
|
||||
sqlite3_value **argv,
|
||||
sqlite_int64 *pRowid
|
||||
){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
int i;
|
||||
printf("%s.%s.xUpdate():\n", pTab->zDb, pTab->zName);
|
||||
printf(" argc=%d\n", argc);
|
||||
for(i=0; i<argc; i++){
|
||||
printf(" argv[%d]=", i);
|
||||
vtablogQuote(argv[i]);
|
||||
printf("\n");
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int vtablogBegin(sqlite3_vtab *tab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xBegin()\n", pTab->zDb, pTab->zName);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogSync(sqlite3_vtab *tab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xSync()\n", pTab->zDb, pTab->zName);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogCommit(sqlite3_vtab *tab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xCommit()\n", pTab->zDb, pTab->zName);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogRollback(sqlite3_vtab *tab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xRollback()\n", pTab->zDb, pTab->zName);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogSavepoint(sqlite3_vtab *tab, int N){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xSavepoint(%d)\n", pTab->zDb, pTab->zName, N);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogRelease(sqlite3_vtab *tab, int N){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xRelease(%d)\n", pTab->zDb, pTab->zName, N);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogRollbackTo(sqlite3_vtab *tab, int N){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xRollbackTo(%d)\n", pTab->zDb, pTab->zName, N);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int vtablogFindMethod(
|
||||
sqlite3_vtab *tab,
|
||||
int nArg,
|
||||
const char *zName,
|
||||
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void **ppArg
|
||||
){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xFindMethod(nArg=%d, zName=%s)\n",
|
||||
pTab->zDb, pTab->zName, nArg, zName);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
static int vtablogRename(sqlite3_vtab *tab, const char *zNew){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xRename('%s')\n", pTab->zDb, pTab->zName, zNew);
|
||||
sqlite3_free(pTab->zName);
|
||||
pTab->zName = sqlite3_mprintf("%s", zNew);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Any table name that contains the text "shadow" is seen as a
|
||||
** shadow table. Nothing else is.
|
||||
*/
|
||||
static int vtablogShadowName(const char *zName){
|
||||
printf("vtablog.xShadowName('%s')\n", zName);
|
||||
return sqlite3_strglob("*shadow*", zName)==0;
|
||||
}
|
||||
|
||||
static int vtablogIntegrity(
|
||||
sqlite3_vtab *tab,
|
||||
const char *zSchema,
|
||||
const char *zTabName,
|
||||
int mFlags,
|
||||
char **pzErr
|
||||
){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)tab;
|
||||
printf("%s.%s.xIntegrity(mFlags=0x%x)\n", pTab->zDb, pTab->zName, mFlags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This following structure defines all the methods for the
|
||||
** vtablog virtual table.
|
||||
*/
|
||||
static sqlite3_module vtablogModule = {
|
||||
4, /* iVersion */
|
||||
vtablogCreate, /* xCreate */
|
||||
vtablogConnect, /* xConnect */
|
||||
vtablogBestIndex, /* xBestIndex */
|
||||
vtablogDisconnect, /* xDisconnect */
|
||||
vtablogDestroy, /* xDestroy */
|
||||
vtablogOpen, /* xOpen - open a cursor */
|
||||
vtablogClose, /* xClose - close a cursor */
|
||||
vtablogFilter, /* xFilter - configure scan constraints */
|
||||
vtablogNext, /* xNext - advance a cursor */
|
||||
vtablogEof, /* xEof - check for end of scan */
|
||||
vtablogColumn, /* xColumn - read data */
|
||||
vtablogRowid, /* xRowid - read data */
|
||||
vtablogUpdate, /* xUpdate */
|
||||
vtablogBegin, /* xBegin */
|
||||
vtablogSync, /* xSync */
|
||||
vtablogCommit, /* xCommit */
|
||||
vtablogRollback, /* xRollback */
|
||||
vtablogFindMethod, /* xFindMethod */
|
||||
vtablogRename, /* xRename */
|
||||
vtablogSavepoint, /* xSavepoint */
|
||||
vtablogRelease, /* xRelease */
|
||||
vtablogRollbackTo, /* xRollbackTo */
|
||||
vtablogShadowName, /* xShadowName */
|
||||
vtablogIntegrity /* xIntegrity */
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_vtablog_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
rc = sqlite3_create_module(db, "vtablog", &vtablogModule, 0);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,553 +0,0 @@
|
||||
/*
|
||||
** 2013-06-12
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** A shim that sits between the SQLite virtual table interface and
|
||||
** runtimes with garbage collector based memory management.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/* Forward references */
|
||||
typedef struct vtshim_aux vtshim_aux;
|
||||
typedef struct vtshim_vtab vtshim_vtab;
|
||||
typedef struct vtshim_cursor vtshim_cursor;
|
||||
|
||||
|
||||
/* The vtshim_aux argument is the auxiliary parameter that is passed
|
||||
** into sqlite3_create_module_v2().
|
||||
*/
|
||||
struct vtshim_aux {
|
||||
void *pChildAux; /* pAux for child virtual tables */
|
||||
void (*xChildDestroy)(void*); /* Destructor for pChildAux */
|
||||
sqlite3_module *pMod; /* Methods for child virtual tables */
|
||||
sqlite3 *db; /* The database to which we are attached */
|
||||
char *zName; /* Name of the module */
|
||||
int bDisposed; /* True if disposed */
|
||||
vtshim_vtab *pAllVtab; /* List of all vtshim_vtab objects */
|
||||
sqlite3_module sSelf; /* Methods used by this shim */
|
||||
};
|
||||
|
||||
/* A vtshim virtual table object */
|
||||
struct vtshim_vtab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
sqlite3_vtab *pChild; /* Child virtual table */
|
||||
vtshim_aux *pAux; /* Pointer to vtshim_aux object */
|
||||
vtshim_cursor *pAllCur; /* List of all cursors */
|
||||
vtshim_vtab **ppPrev; /* Previous on list */
|
||||
vtshim_vtab *pNext; /* Next on list */
|
||||
};
|
||||
|
||||
/* A vtshim cursor object */
|
||||
struct vtshim_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
sqlite3_vtab_cursor *pChild; /* Cursor generated by the managed subclass */
|
||||
vtshim_cursor **ppPrev; /* Previous on list of all cursors */
|
||||
vtshim_cursor *pNext; /* Next on list of all cursors */
|
||||
};
|
||||
|
||||
/* Macro used to copy the child vtable error message to outer vtable */
|
||||
#define VTSHIM_COPY_ERRMSG() \
|
||||
do { \
|
||||
sqlite3_free(pVtab->base.zErrMsg); \
|
||||
pVtab->base.zErrMsg = sqlite3_mprintf("%s", pVtab->pChild->zErrMsg); \
|
||||
} while (0)
|
||||
|
||||
/* Methods for the vtshim module */
|
||||
static int vtshimCreate(
|
||||
sqlite3 *db,
|
||||
void *ppAux,
|
||||
int argc,
|
||||
const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
vtshim_aux *pAux = (vtshim_aux*)ppAux;
|
||||
vtshim_vtab *pNew;
|
||||
int rc;
|
||||
|
||||
assert( db==pAux->db );
|
||||
if( pAux->bDisposed ){
|
||||
if( pzErr ){
|
||||
*pzErr = sqlite3_mprintf("virtual table was disposed: \"%s\"",
|
||||
pAux->zName);
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
rc = pAux->pMod->xCreate(db, pAux->pChildAux, argc, argv,
|
||||
&pNew->pChild, pzErr);
|
||||
if( rc ){
|
||||
sqlite3_free(pNew);
|
||||
*ppVtab = 0;
|
||||
return rc;
|
||||
}
|
||||
pNew->pAux = pAux;
|
||||
pNew->ppPrev = &pAux->pAllVtab;
|
||||
pNew->pNext = pAux->pAllVtab;
|
||||
if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext;
|
||||
pAux->pAllVtab = pNew;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimConnect(
|
||||
sqlite3 *db,
|
||||
void *ppAux,
|
||||
int argc,
|
||||
const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
vtshim_aux *pAux = (vtshim_aux*)ppAux;
|
||||
vtshim_vtab *pNew;
|
||||
int rc;
|
||||
|
||||
assert( db==pAux->db );
|
||||
if( pAux->bDisposed ){
|
||||
if( pzErr ){
|
||||
*pzErr = sqlite3_mprintf("virtual table was disposed: \"%s\"",
|
||||
pAux->zName);
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pNew = sqlite3_malloc( sizeof(*pNew) );
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
rc = pAux->pMod->xConnect(db, pAux->pChildAux, argc, argv,
|
||||
&pNew->pChild, pzErr);
|
||||
if( rc ){
|
||||
sqlite3_free(pNew);
|
||||
*ppVtab = 0;
|
||||
return rc;
|
||||
}
|
||||
pNew->pAux = pAux;
|
||||
pNew->ppPrev = &pAux->pAllVtab;
|
||||
pNew->pNext = pAux->pAllVtab;
|
||||
if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext;
|
||||
pAux->pAllVtab = pNew;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimBestIndex(
|
||||
sqlite3_vtab *pBase,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xBestIndex(pVtab->pChild, pIdxInfo);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimDisconnect(sqlite3_vtab *pBase){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc = SQLITE_OK;
|
||||
if( !pAux->bDisposed ){
|
||||
rc = pAux->pMod->xDisconnect(pVtab->pChild);
|
||||
}
|
||||
if( pVtab->pNext ) pVtab->pNext->ppPrev = pVtab->ppPrev;
|
||||
*pVtab->ppPrev = pVtab->pNext;
|
||||
sqlite3_free(pVtab);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimDestroy(sqlite3_vtab *pBase){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc = SQLITE_OK;
|
||||
if( !pAux->bDisposed ){
|
||||
rc = pAux->pMod->xDestroy(pVtab->pChild);
|
||||
}
|
||||
if( pVtab->pNext ) pVtab->pNext->ppPrev = pVtab->ppPrev;
|
||||
*pVtab->ppPrev = pVtab->pNext;
|
||||
sqlite3_free(pVtab);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimOpen(sqlite3_vtab *pBase, sqlite3_vtab_cursor **ppCursor){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
vtshim_cursor *pCur;
|
||||
int rc;
|
||||
*ppCursor = 0;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
rc = pAux->pMod->xOpen(pVtab->pChild, &pCur->pChild);
|
||||
if( rc ){
|
||||
sqlite3_free(pCur);
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
return rc;
|
||||
}
|
||||
pCur->pChild->pVtab = pVtab->pChild;
|
||||
*ppCursor = &pCur->base;
|
||||
pCur->ppPrev = &pVtab->pAllCur;
|
||||
if( pVtab->pAllCur ) pVtab->pAllCur->ppPrev = &pCur->pNext;
|
||||
pCur->pNext = pVtab->pAllCur;
|
||||
pVtab->pAllCur = pCur;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int vtshimClose(sqlite3_vtab_cursor *pX){
|
||||
vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc = SQLITE_OK;
|
||||
if( !pAux->bDisposed ){
|
||||
rc = pAux->pMod->xClose(pCur->pChild);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
}
|
||||
if( pCur->pNext ) pCur->pNext->ppPrev = pCur->ppPrev;
|
||||
*pCur->ppPrev = pCur->pNext;
|
||||
sqlite3_free(pCur);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimFilter(
|
||||
sqlite3_vtab_cursor *pX,
|
||||
int idxNum,
|
||||
const char *idxStr,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xFilter(pCur->pChild, idxNum, idxStr, argc, argv);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimNext(sqlite3_vtab_cursor *pX){
|
||||
vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xNext(pCur->pChild);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimEof(sqlite3_vtab_cursor *pX){
|
||||
vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return 1;
|
||||
rc = pAux->pMod->xEof(pCur->pChild);
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimColumn(sqlite3_vtab_cursor *pX, sqlite3_context *ctx, int i){
|
||||
vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xColumn(pCur->pChild, ctx, i);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimRowid(sqlite3_vtab_cursor *pX, sqlite3_int64 *pRowid){
|
||||
vtshim_cursor *pCur = (vtshim_cursor*)pX;
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xRowid(pCur->pChild, pRowid);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimUpdate(
|
||||
sqlite3_vtab *pBase,
|
||||
int argc,
|
||||
sqlite3_value **argv,
|
||||
sqlite3_int64 *pRowid
|
||||
){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xUpdate(pVtab->pChild, argc, argv, pRowid);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimBegin(sqlite3_vtab *pBase){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xBegin(pVtab->pChild);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimSync(sqlite3_vtab *pBase){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xSync(pVtab->pChild);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimCommit(sqlite3_vtab *pBase){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xCommit(pVtab->pChild);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimRollback(sqlite3_vtab *pBase){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xRollback(pVtab->pChild);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimFindFunction(
|
||||
sqlite3_vtab *pBase,
|
||||
int nArg,
|
||||
const char *zName,
|
||||
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void **ppArg
|
||||
){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return 0;
|
||||
rc = pAux->pMod->xFindFunction(pVtab->pChild, nArg, zName, pxFunc, ppArg);
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimRename(sqlite3_vtab *pBase, const char *zNewName){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xRename(pVtab->pChild, zNewName);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimSavepoint(sqlite3_vtab *pBase, int n){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xSavepoint(pVtab->pChild, n);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimRelease(sqlite3_vtab *pBase, int n){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xRelease(pVtab->pChild, n);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vtshimRollbackTo(sqlite3_vtab *pBase, int n){
|
||||
vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
|
||||
vtshim_aux *pAux = pVtab->pAux;
|
||||
int rc;
|
||||
if( pAux->bDisposed ) return SQLITE_ERROR;
|
||||
rc = pAux->pMod->xRollbackTo(pVtab->pChild, n);
|
||||
if( rc!=SQLITE_OK ){
|
||||
VTSHIM_COPY_ERRMSG();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* The destructor function for a disposible module */
|
||||
static void vtshimAuxDestructor(void *pXAux){
|
||||
vtshim_aux *pAux = (vtshim_aux*)pXAux;
|
||||
assert( pAux->pAllVtab==0 );
|
||||
if( !pAux->bDisposed && pAux->xChildDestroy ){
|
||||
pAux->xChildDestroy(pAux->pChildAux);
|
||||
pAux->xChildDestroy = 0;
|
||||
}
|
||||
sqlite3_free(pAux->zName);
|
||||
sqlite3_free(pAux->pMod);
|
||||
sqlite3_free(pAux);
|
||||
}
|
||||
|
||||
static int vtshimCopyModule(
|
||||
const sqlite3_module *pMod, /* Source module to be copied */
|
||||
sqlite3_module **ppMod /* Destination for copied module */
|
||||
){
|
||||
sqlite3_module *p;
|
||||
if( !pMod || !ppMod ) return SQLITE_ERROR;
|
||||
p = sqlite3_malloc( sizeof(*p) );
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
memcpy(p, pMod, sizeof(*p));
|
||||
*ppMod = p;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
void *sqlite3_create_disposable_module(
|
||||
sqlite3 *db, /* SQLite connection to register module with */
|
||||
const char *zName, /* Name of the module */
|
||||
const sqlite3_module *p, /* Methods for the module */
|
||||
void *pClientData, /* Client data for xCreate/xConnect */
|
||||
void(*xDestroy)(void*) /* Module destructor function */
|
||||
){
|
||||
vtshim_aux *pAux;
|
||||
sqlite3_module *pMod;
|
||||
int rc;
|
||||
pAux = sqlite3_malloc( sizeof(*pAux) );
|
||||
if( pAux==0 ){
|
||||
if( xDestroy ) xDestroy(pClientData);
|
||||
return 0;
|
||||
}
|
||||
rc = vtshimCopyModule(p, &pMod);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free(pAux);
|
||||
return 0;
|
||||
}
|
||||
pAux->pChildAux = pClientData;
|
||||
pAux->xChildDestroy = xDestroy;
|
||||
pAux->pMod = pMod;
|
||||
pAux->db = db;
|
||||
pAux->zName = sqlite3_mprintf("%s", zName);
|
||||
pAux->bDisposed = 0;
|
||||
pAux->pAllVtab = 0;
|
||||
pAux->sSelf.iVersion = p->iVersion<=2 ? p->iVersion : 2;
|
||||
pAux->sSelf.xCreate = p->xCreate ? vtshimCreate : 0;
|
||||
pAux->sSelf.xConnect = p->xConnect ? vtshimConnect : 0;
|
||||
pAux->sSelf.xBestIndex = p->xBestIndex ? vtshimBestIndex : 0;
|
||||
pAux->sSelf.xDisconnect = p->xDisconnect ? vtshimDisconnect : 0;
|
||||
pAux->sSelf.xDestroy = p->xDestroy ? vtshimDestroy : 0;
|
||||
pAux->sSelf.xOpen = p->xOpen ? vtshimOpen : 0;
|
||||
pAux->sSelf.xClose = p->xClose ? vtshimClose : 0;
|
||||
pAux->sSelf.xFilter = p->xFilter ? vtshimFilter : 0;
|
||||
pAux->sSelf.xNext = p->xNext ? vtshimNext : 0;
|
||||
pAux->sSelf.xEof = p->xEof ? vtshimEof : 0;
|
||||
pAux->sSelf.xColumn = p->xColumn ? vtshimColumn : 0;
|
||||
pAux->sSelf.xRowid = p->xRowid ? vtshimRowid : 0;
|
||||
pAux->sSelf.xUpdate = p->xUpdate ? vtshimUpdate : 0;
|
||||
pAux->sSelf.xBegin = p->xBegin ? vtshimBegin : 0;
|
||||
pAux->sSelf.xSync = p->xSync ? vtshimSync : 0;
|
||||
pAux->sSelf.xCommit = p->xCommit ? vtshimCommit : 0;
|
||||
pAux->sSelf.xRollback = p->xRollback ? vtshimRollback : 0;
|
||||
pAux->sSelf.xFindFunction = p->xFindFunction ? vtshimFindFunction : 0;
|
||||
pAux->sSelf.xRename = p->xRename ? vtshimRename : 0;
|
||||
if( p->iVersion>=2 ){
|
||||
pAux->sSelf.xSavepoint = p->xSavepoint ? vtshimSavepoint : 0;
|
||||
pAux->sSelf.xRelease = p->xRelease ? vtshimRelease : 0;
|
||||
pAux->sSelf.xRollbackTo = p->xRollbackTo ? vtshimRollbackTo : 0;
|
||||
}else{
|
||||
pAux->sSelf.xSavepoint = 0;
|
||||
pAux->sSelf.xRelease = 0;
|
||||
pAux->sSelf.xRollbackTo = 0;
|
||||
}
|
||||
rc = sqlite3_create_module_v2(db, zName, &pAux->sSelf,
|
||||
pAux, vtshimAuxDestructor);
|
||||
return rc==SQLITE_OK ? (void*)pAux : 0;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
void sqlite3_dispose_module(void *pX){
|
||||
vtshim_aux *pAux = (vtshim_aux*)pX;
|
||||
if( !pAux->bDisposed ){
|
||||
vtshim_vtab *pVtab;
|
||||
vtshim_cursor *pCur;
|
||||
for(pVtab=pAux->pAllVtab; pVtab; pVtab=pVtab->pNext){
|
||||
for(pCur=pVtab->pAllCur; pCur; pCur=pCur->pNext){
|
||||
pAux->pMod->xClose(pCur->pChild);
|
||||
}
|
||||
pAux->pMod->xDisconnect(pVtab->pChild);
|
||||
}
|
||||
pAux->bDisposed = 1;
|
||||
if( pAux->xChildDestroy ){
|
||||
pAux->xChildDestroy(pAux->pChildAux);
|
||||
pAux->xChildDestroy = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_vtshim_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
/*
|
||||
** 2011 April 02
|
||||
**
|
||||
** 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 that returns the whole numbers
|
||||
** between 1 and 4294967295, inclusive.
|
||||
**
|
||||
** Example:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE nums USING wholenumber;
|
||||
** SELECT value FROM nums WHERE value<10;
|
||||
**
|
||||
** Results in:
|
||||
**
|
||||
** 1 2 3 4 5 6 7 8 9
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
|
||||
/* A wholenumber cursor object */
|
||||
typedef struct wholenumber_cursor wholenumber_cursor;
|
||||
struct wholenumber_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
sqlite3_int64 iValue; /* Current value */
|
||||
sqlite3_int64 mxValue; /* Maximum value */
|
||||
};
|
||||
|
||||
/* Methods for the wholenumber module */
|
||||
static int wholenumberConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
sqlite3_vtab *pNew;
|
||||
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
sqlite3_declare_vtab(db, "CREATE TABLE x(value)");
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/* Note that for this virtual table, the xCreate and xConnect
|
||||
** methods are identical. */
|
||||
|
||||
static int wholenumberDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/* The xDisconnect and xDestroy methods are also the same */
|
||||
|
||||
|
||||
/*
|
||||
** Open a new wholenumber cursor.
|
||||
*/
|
||||
static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
wholenumber_cursor *pCur;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a wholenumber cursor.
|
||||
*/
|
||||
static int wholenumberClose(sqlite3_vtab_cursor *cur){
|
||||
sqlite3_free(cur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance a cursor to its next row of output
|
||||
*/
|
||||
static int wholenumberNext(sqlite3_vtab_cursor *cur){
|
||||
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
||||
pCur->iValue++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the value associated with a wholenumber.
|
||||
*/
|
||||
static int wholenumberColumn(
|
||||
sqlite3_vtab_cursor *cur,
|
||||
sqlite3_context *ctx,
|
||||
int i
|
||||
){
|
||||
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
||||
sqlite3_result_int64(ctx, pCur->iValue);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The rowid.
|
||||
*/
|
||||
static int wholenumberRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
||||
*pRowid = pCur->iValue;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** When the wholenumber_cursor.rLimit value is 0 or less, that is a signal
|
||||
** that the cursor has nothing more to output.
|
||||
*/
|
||||
static int wholenumberEof(sqlite3_vtab_cursor *cur){
|
||||
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
|
||||
return pCur->iValue>pCur->mxValue || pCur->iValue==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to "rewind" a cursor back to the beginning so that
|
||||
** it starts its output over again. Always called at least once
|
||||
** prior to any wholenumberColumn, wholenumberRowid, or wholenumberEof call.
|
||||
**
|
||||
** idxNum Constraints
|
||||
** ------ ---------------------
|
||||
** 0 (none)
|
||||
** 1 value > $argv0
|
||||
** 2 value >= $argv0
|
||||
** 4 value < $argv0
|
||||
** 8 value <= $argv0
|
||||
**
|
||||
** 5 value > $argv0 AND value < $argv1
|
||||
** 6 value >= $argv0 AND value < $argv1
|
||||
** 9 value > $argv0 AND value <= $argv1
|
||||
** 10 value >= $argv0 AND value <= $argv1
|
||||
*/
|
||||
static int wholenumberFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
wholenumber_cursor *pCur = (wholenumber_cursor *)pVtabCursor;
|
||||
sqlite3_int64 v;
|
||||
int i = 0;
|
||||
pCur->iValue = 1;
|
||||
pCur->mxValue = 0xffffffff; /* 4294967295 */
|
||||
if( idxNum & 3 ){
|
||||
v = sqlite3_value_int64(argv[0]) + (idxNum&1);
|
||||
if( v>pCur->iValue && v<=pCur->mxValue ) pCur->iValue = v;
|
||||
i++;
|
||||
}
|
||||
if( idxNum & 12 ){
|
||||
v = sqlite3_value_int64(argv[i]) - ((idxNum>>2)&1);
|
||||
if( v>=pCur->iValue && v<pCur->mxValue ) pCur->mxValue = v;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Search for terms of these forms:
|
||||
**
|
||||
** (1) value > $value
|
||||
** (2) value >= $value
|
||||
** (4) value < $value
|
||||
** (8) value <= $value
|
||||
**
|
||||
** idxNum is an ORed combination of 1 or 2 with 4 or 8.
|
||||
*/
|
||||
static int wholenumberBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
int i;
|
||||
int idxNum = 0;
|
||||
int argvIdx = 1;
|
||||
int ltIdx = -1;
|
||||
int gtIdx = -1;
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
if( pConstraint->usable==0 ) continue;
|
||||
if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GT ){
|
||||
idxNum |= 1;
|
||||
ltIdx = i;
|
||||
}
|
||||
if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE ){
|
||||
idxNum |= 2;
|
||||
ltIdx = i;
|
||||
}
|
||||
if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
|
||||
idxNum |= 4;
|
||||
gtIdx = i;
|
||||
}
|
||||
if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
|
||||
idxNum |= 8;
|
||||
gtIdx = i;
|
||||
}
|
||||
}
|
||||
pIdxInfo->idxNum = idxNum;
|
||||
if( ltIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[ltIdx].argvIndex = argvIdx++;
|
||||
pIdxInfo->aConstraintUsage[ltIdx].omit = 1;
|
||||
}
|
||||
if( gtIdx>=0 ){
|
||||
pIdxInfo->aConstraintUsage[gtIdx].argvIndex = argvIdx;
|
||||
pIdxInfo->aConstraintUsage[gtIdx].omit = 1;
|
||||
}
|
||||
if( pIdxInfo->nOrderBy==1
|
||||
&& pIdxInfo->aOrderBy[0].desc==0
|
||||
){
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
if( (idxNum & 12)==0 ){
|
||||
pIdxInfo->estimatedCost = 1e99;
|
||||
}else if( (idxNum & 3)==0 ){
|
||||
pIdxInfo->estimatedCost = (double)5;
|
||||
}else{
|
||||
pIdxInfo->estimatedCost = (double)1;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that provides read-only access to a
|
||||
** Tcl global variable namespace.
|
||||
*/
|
||||
static sqlite3_module wholenumberModule = {
|
||||
0, /* iVersion */
|
||||
wholenumberConnect,
|
||||
wholenumberConnect,
|
||||
wholenumberBestIndex,
|
||||
wholenumberDisconnect,
|
||||
wholenumberDisconnect,
|
||||
wholenumberOpen, /* xOpen - open a cursor */
|
||||
wholenumberClose, /* xClose - close a cursor */
|
||||
wholenumberFilter, /* xFilter - configure scan constraints */
|
||||
wholenumberNext, /* xNext - advance a cursor */
|
||||
wholenumberEof, /* xEof - check for end of scan */
|
||||
wholenumberColumn, /* xColumn - read data */
|
||||
wholenumberRowid, /* 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
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_wholenumber_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
rc = sqlite3_create_module(db, "wholenumber", &wholenumberModule, 0);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
** 2018-02-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.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** SQL functions for z-order (Morton code) transformations.
|
||||
**
|
||||
** zorder(X0,X0,..,xN) Generate an N+1 dimension Morton code
|
||||
**
|
||||
** unzorder(Z,N,I) Extract the I-th dimension from N-dimensional
|
||||
** Morton code Z.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Functions: zorder(X0,X1,....)
|
||||
**
|
||||
** Convert integers X0, X1, ... into morton code.
|
||||
**
|
||||
** The output is a signed 64-bit integer. If any argument is too large,
|
||||
** an error is thrown.
|
||||
*/
|
||||
static void zorderFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3_int64 z, x[63];
|
||||
int i, j;
|
||||
z = 0;
|
||||
for(i=0; i<argc; i++){
|
||||
x[i] = sqlite3_value_int64(argv[i]);
|
||||
}
|
||||
if( argc>0 ){
|
||||
for(i=0; i<63; i++){
|
||||
j = i%argc;
|
||||
z |= (x[j]&1)<<i;
|
||||
x[j] >>= 1;
|
||||
}
|
||||
}
|
||||
sqlite3_result_int64(context, z);
|
||||
for(i=0; i<argc; i++){
|
||||
if( x[i] ){
|
||||
sqlite3_result_error(context, "parameter too large", -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Functions: unzorder(Z,N,I)
|
||||
**
|
||||
** Assuming that Z is an N-dimensional Morton code, extract the I-th
|
||||
** dimension.
|
||||
*/
|
||||
static void unzorderFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3_int64 z, n, i, x;
|
||||
int j, k;
|
||||
z = sqlite3_value_int64(argv[0]);
|
||||
n = sqlite3_value_int64(argv[1]);
|
||||
i = sqlite3_value_int64(argv[2]);
|
||||
x = 0;
|
||||
for(k=0, j=i; j<63; j+=n, k++){
|
||||
x |= ((z>>j)&1)<<k;
|
||||
}
|
||||
sqlite3_result_int64(context, x);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_zorder_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, "zorder", -1, SQLITE_UTF8, 0,
|
||||
zorderFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "unzorder", 3, SQLITE_UTF8, 0,
|
||||
unzorderFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
13374
extensions/src/sqlite3.h
13374
extensions/src/sqlite3.h
File diff suppressed because it is too large
Load Diff
@@ -1,719 +0,0 @@
|
||||
/*
|
||||
** 2006 June 7
|
||||
**
|
||||
** 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 defines the SQLite interface for use by
|
||||
** shared libraries that want to be imported as extensions into
|
||||
** an SQLite instance. Shared libraries that intend to be loaded
|
||||
** as extensions by SQLite should #include this file instead of
|
||||
** sqlite3.h.
|
||||
*/
|
||||
#ifndef SQLITE3EXT_H
|
||||
#define SQLITE3EXT_H
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** The following structure holds pointers to all of the SQLite API
|
||||
** routines.
|
||||
**
|
||||
** WARNING: In order to maintain backwards compatibility, add new
|
||||
** interfaces to the end of this structure only. If you insert new
|
||||
** interfaces in the middle of this structure, then older different
|
||||
** versions of SQLite will not be able to load each other's shared
|
||||
** libraries!
|
||||
*/
|
||||
struct sqlite3_api_routines {
|
||||
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
||||
int (*aggregate_count)(sqlite3_context*);
|
||||
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
||||
int (*bind_double)(sqlite3_stmt*,int,double);
|
||||
int (*bind_int)(sqlite3_stmt*,int,int);
|
||||
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
||||
int (*bind_null)(sqlite3_stmt*,int);
|
||||
int (*bind_parameter_count)(sqlite3_stmt*);
|
||||
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
||||
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
||||
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
||||
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
||||
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
||||
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
||||
int (*busy_timeout)(sqlite3*,int ms);
|
||||
int (*changes)(sqlite3*);
|
||||
int (*close)(sqlite3*);
|
||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const char*));
|
||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const void*));
|
||||
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_count)(sqlite3_stmt*pStmt);
|
||||
const char * (*column_database_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_decltype)(sqlite3_stmt*,int i);
|
||||
const void * (*column_decltype16)(sqlite3_stmt*,int);
|
||||
double (*column_double)(sqlite3_stmt*,int iCol);
|
||||
int (*column_int)(sqlite3_stmt*,int iCol);
|
||||
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
|
||||
const char * (*column_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_origin_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_origin_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_table_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_table_name16)(sqlite3_stmt*,int);
|
||||
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
|
||||
const void * (*column_text16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_type)(sqlite3_stmt*,int iCol);
|
||||
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
|
||||
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
||||
int (*complete)(const char*sql);
|
||||
int (*complete16)(const void*sql);
|
||||
int (*create_collation)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_collation16)(sqlite3*,const void*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
||||
int (*data_count)(sqlite3_stmt*pStmt);
|
||||
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
||||
int (*declare_vtab)(sqlite3*,const char*);
|
||||
int (*enable_shared_cache)(int);
|
||||
int (*errcode)(sqlite3*db);
|
||||
const char * (*errmsg)(sqlite3*);
|
||||
const void * (*errmsg16)(sqlite3*);
|
||||
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
|
||||
int (*expired)(sqlite3_stmt*);
|
||||
int (*finalize)(sqlite3_stmt*pStmt);
|
||||
void (*free)(void*);
|
||||
void (*free_table)(char**result);
|
||||
int (*get_autocommit)(sqlite3*);
|
||||
void * (*get_auxdata)(sqlite3_context*,int);
|
||||
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
||||
int (*global_recover)(void);
|
||||
void (*interruptx)(sqlite3*);
|
||||
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
||||
const char * (*libversion)(void);
|
||||
int (*libversion_number)(void);
|
||||
void *(*malloc)(int);
|
||||
char * (*mprintf)(const char*,...);
|
||||
int (*open)(const char*,sqlite3**);
|
||||
int (*open16)(const void*,sqlite3**);
|
||||
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
|
||||
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
||||
void *(*realloc)(void*,int);
|
||||
int (*reset)(sqlite3_stmt*pStmt);
|
||||
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_double)(sqlite3_context*,double);
|
||||
void (*result_error)(sqlite3_context*,const char*,int);
|
||||
void (*result_error16)(sqlite3_context*,const void*,int);
|
||||
void (*result_int)(sqlite3_context*,int);
|
||||
void (*result_int64)(sqlite3_context*,sqlite_int64);
|
||||
void (*result_null)(sqlite3_context*);
|
||||
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
|
||||
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
||||
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
||||
const char*,const char*),void*);
|
||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||
char * (*xsnprintf)(int,char*,const char*,...);
|
||||
int (*step)(sqlite3_stmt*);
|
||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
||||
char const**,char const**,int*,int*,int*);
|
||||
void (*thread_cleanup)(void);
|
||||
int (*total_changes)(sqlite3*);
|
||||
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
||||
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
||||
sqlite_int64),void*);
|
||||
void * (*user_data)(sqlite3_context*);
|
||||
const void * (*value_blob)(sqlite3_value*);
|
||||
int (*value_bytes)(sqlite3_value*);
|
||||
int (*value_bytes16)(sqlite3_value*);
|
||||
double (*value_double)(sqlite3_value*);
|
||||
int (*value_int)(sqlite3_value*);
|
||||
sqlite_int64 (*value_int64)(sqlite3_value*);
|
||||
int (*value_numeric_type)(sqlite3_value*);
|
||||
const unsigned char * (*value_text)(sqlite3_value*);
|
||||
const void * (*value_text16)(sqlite3_value*);
|
||||
const void * (*value_text16be)(sqlite3_value*);
|
||||
const void * (*value_text16le)(sqlite3_value*);
|
||||
int (*value_type)(sqlite3_value*);
|
||||
char *(*vmprintf)(const char*,va_list);
|
||||
/* Added ??? */
|
||||
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
|
||||
/* Added by 3.3.13 */
|
||||
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
int (*clear_bindings)(sqlite3_stmt*);
|
||||
/* Added by 3.4.1 */
|
||||
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
|
||||
void (*xDestroy)(void *));
|
||||
/* Added by 3.5.0 */
|
||||
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
|
||||
int (*blob_bytes)(sqlite3_blob*);
|
||||
int (*blob_close)(sqlite3_blob*);
|
||||
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
|
||||
int,sqlite3_blob**);
|
||||
int (*blob_read)(sqlite3_blob*,void*,int,int);
|
||||
int (*blob_write)(sqlite3_blob*,const void*,int,int);
|
||||
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*),
|
||||
void(*)(void*));
|
||||
int (*file_control)(sqlite3*,const char*,int,void*);
|
||||
sqlite3_int64 (*memory_highwater)(int);
|
||||
sqlite3_int64 (*memory_used)(void);
|
||||
sqlite3_mutex *(*mutex_alloc)(int);
|
||||
void (*mutex_enter)(sqlite3_mutex*);
|
||||
void (*mutex_free)(sqlite3_mutex*);
|
||||
void (*mutex_leave)(sqlite3_mutex*);
|
||||
int (*mutex_try)(sqlite3_mutex*);
|
||||
int (*open_v2)(const char*,sqlite3**,int,const char*);
|
||||
int (*release_memory)(int);
|
||||
void (*result_error_nomem)(sqlite3_context*);
|
||||
void (*result_error_toobig)(sqlite3_context*);
|
||||
int (*sleep)(int);
|
||||
void (*soft_heap_limit)(int);
|
||||
sqlite3_vfs *(*vfs_find)(const char*);
|
||||
int (*vfs_register)(sqlite3_vfs*,int);
|
||||
int (*vfs_unregister)(sqlite3_vfs*);
|
||||
int (*xthreadsafe)(void);
|
||||
void (*result_zeroblob)(sqlite3_context*,int);
|
||||
void (*result_error_code)(sqlite3_context*,int);
|
||||
int (*test_control)(int, ...);
|
||||
void (*randomness)(int,void*);
|
||||
sqlite3 *(*context_db_handle)(sqlite3_context*);
|
||||
int (*extended_result_codes)(sqlite3*,int);
|
||||
int (*limit)(sqlite3*,int,int);
|
||||
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
|
||||
const char *(*sql)(sqlite3_stmt*);
|
||||
int (*status)(int,int*,int*,int);
|
||||
int (*backup_finish)(sqlite3_backup*);
|
||||
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
|
||||
int (*backup_pagecount)(sqlite3_backup*);
|
||||
int (*backup_remaining)(sqlite3_backup*);
|
||||
int (*backup_step)(sqlite3_backup*,int);
|
||||
const char *(*compileoption_get)(int);
|
||||
int (*compileoption_used)(const char*);
|
||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void(*xDestroy)(void*));
|
||||
int (*db_config)(sqlite3*,int,...);
|
||||
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
||||
int (*db_status)(sqlite3*,int,int*,int*,int);
|
||||
int (*extended_errcode)(sqlite3*);
|
||||
void (*log)(int,const char*,...);
|
||||
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
|
||||
const char *(*sourceid)(void);
|
||||
int (*stmt_status)(sqlite3_stmt*,int,int);
|
||||
int (*strnicmp)(const char*,const char*,int);
|
||||
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
|
||||
int (*wal_autocheckpoint)(sqlite3*,int);
|
||||
int (*wal_checkpoint)(sqlite3*,const char*);
|
||||
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
||||
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
|
||||
int (*vtab_config)(sqlite3*,int op,...);
|
||||
int (*vtab_on_conflict)(sqlite3*);
|
||||
/* Version 3.7.16 and later */
|
||||
int (*close_v2)(sqlite3*);
|
||||
const char *(*db_filename)(sqlite3*,const char*);
|
||||
int (*db_readonly)(sqlite3*,const char*);
|
||||
int (*db_release_memory)(sqlite3*);
|
||||
const char *(*errstr)(int);
|
||||
int (*stmt_busy)(sqlite3_stmt*);
|
||||
int (*stmt_readonly)(sqlite3_stmt*);
|
||||
int (*stricmp)(const char*,const char*);
|
||||
int (*uri_boolean)(const char*,const char*,int);
|
||||
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
||||
const char *(*uri_parameter)(const char*,const char*);
|
||||
char *(*xvsnprintf)(int,char*,const char*,va_list);
|
||||
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
||||
/* Version 3.8.7 and later */
|
||||
int (*auto_extension)(void(*)(void));
|
||||
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
|
||||
void(*)(void*));
|
||||
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
|
||||
void(*)(void*),unsigned char);
|
||||
int (*cancel_auto_extension)(void(*)(void));
|
||||
int (*load_extension)(sqlite3*,const char*,const char*,char**);
|
||||
void *(*malloc64)(sqlite3_uint64);
|
||||
sqlite3_uint64 (*msize)(void*);
|
||||
void *(*realloc64)(void*,sqlite3_uint64);
|
||||
void (*reset_auto_extension)(void);
|
||||
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
|
||||
void(*)(void*));
|
||||
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
|
||||
void(*)(void*), unsigned char);
|
||||
int (*strglob)(const char*,const char*);
|
||||
/* Version 3.8.11 and later */
|
||||
sqlite3_value *(*value_dup)(const sqlite3_value*);
|
||||
void (*value_free)(sqlite3_value*);
|
||||
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
|
||||
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
|
||||
/* Version 3.9.0 and later */
|
||||
unsigned int (*value_subtype)(sqlite3_value*);
|
||||
void (*result_subtype)(sqlite3_context*,unsigned int);
|
||||
/* Version 3.10.0 and later */
|
||||
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
|
||||
int (*strlike)(const char*,const char*,unsigned int);
|
||||
int (*db_cacheflush)(sqlite3*);
|
||||
/* Version 3.12.0 and later */
|
||||
int (*system_errno)(sqlite3*);
|
||||
/* Version 3.14.0 and later */
|
||||
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
|
||||
char *(*expanded_sql)(sqlite3_stmt*);
|
||||
/* Version 3.18.0 and later */
|
||||
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
|
||||
/* Version 3.20.0 and later */
|
||||
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
|
||||
sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
|
||||
sqlite3_stmt**,const void**);
|
||||
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
|
||||
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
|
||||
void *(*value_pointer)(sqlite3_value*,const char*);
|
||||
int (*vtab_nochange)(sqlite3_context*);
|
||||
int (*value_nochange)(sqlite3_value*);
|
||||
const char *(*vtab_collation)(sqlite3_index_info*,int);
|
||||
/* Version 3.24.0 and later */
|
||||
int (*keyword_count)(void);
|
||||
int (*keyword_name)(int,const char**,int*);
|
||||
int (*keyword_check)(const char*,int);
|
||||
sqlite3_str *(*str_new)(sqlite3*);
|
||||
char *(*str_finish)(sqlite3_str*);
|
||||
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
|
||||
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
|
||||
void (*str_append)(sqlite3_str*, const char *zIn, int N);
|
||||
void (*str_appendall)(sqlite3_str*, const char *zIn);
|
||||
void (*str_appendchar)(sqlite3_str*, int N, char C);
|
||||
void (*str_reset)(sqlite3_str*);
|
||||
int (*str_errcode)(sqlite3_str*);
|
||||
int (*str_length)(sqlite3_str*);
|
||||
char *(*str_value)(sqlite3_str*);
|
||||
/* Version 3.25.0 and later */
|
||||
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void (*xValue)(sqlite3_context*),
|
||||
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
|
||||
void(*xDestroy)(void*));
|
||||
/* Version 3.26.0 and later */
|
||||
const char *(*normalized_sql)(sqlite3_stmt*);
|
||||
/* Version 3.28.0 and later */
|
||||
int (*stmt_isexplain)(sqlite3_stmt*);
|
||||
int (*value_frombind)(sqlite3_value*);
|
||||
/* Version 3.30.0 and later */
|
||||
int (*drop_modules)(sqlite3*,const char**);
|
||||
/* Version 3.31.0 and later */
|
||||
sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
|
||||
const char *(*uri_key)(const char*,int);
|
||||
const char *(*filename_database)(const char*);
|
||||
const char *(*filename_journal)(const char*);
|
||||
const char *(*filename_wal)(const char*);
|
||||
/* Version 3.32.0 and later */
|
||||
const char *(*create_filename)(const char*,const char*,const char*,
|
||||
int,const char**);
|
||||
void (*free_filename)(const char*);
|
||||
sqlite3_file *(*database_file_object)(const char*);
|
||||
/* Version 3.34.0 and later */
|
||||
int (*txn_state)(sqlite3*,const char*);
|
||||
/* Version 3.36.1 and later */
|
||||
sqlite3_int64 (*changes64)(sqlite3*);
|
||||
sqlite3_int64 (*total_changes64)(sqlite3*);
|
||||
/* Version 3.37.0 and later */
|
||||
int (*autovacuum_pages)(sqlite3*,
|
||||
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
|
||||
void*, void(*)(void*));
|
||||
/* Version 3.38.0 and later */
|
||||
int (*error_offset)(sqlite3*);
|
||||
int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
|
||||
int (*vtab_distinct)(sqlite3_index_info*);
|
||||
int (*vtab_in)(sqlite3_index_info*,int,int);
|
||||
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
|
||||
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
|
||||
/* Version 3.39.0 and later */
|
||||
int (*deserialize)(sqlite3*,const char*,unsigned char*,
|
||||
sqlite3_int64,sqlite3_int64,unsigned);
|
||||
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
|
||||
unsigned int);
|
||||
const char *(*db_name)(sqlite3*,int);
|
||||
/* Version 3.40.0 and later */
|
||||
int (*value_encoding)(sqlite3_value*);
|
||||
/* Version 3.41.0 and later */
|
||||
int (*is_interrupted)(sqlite3*);
|
||||
/* Version 3.43.0 and later */
|
||||
int (*stmt_explain)(sqlite3_stmt*,int);
|
||||
/* Version 3.44.0 and later */
|
||||
void *(*get_clientdata)(sqlite3*,const char*);
|
||||
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
||||
};
|
||||
|
||||
/*
|
||||
** This is the function signature used for all extension entry points. It
|
||||
** is also defined in the file "loadext.c".
|
||||
*/
|
||||
typedef int (*sqlite3_loadext_entry)(
|
||||
sqlite3 *db, /* Handle to the database. */
|
||||
char **pzErrMsg, /* Used to set error string on failure. */
|
||||
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
|
||||
);
|
||||
|
||||
/*
|
||||
** The following macros redefine the API routines so that they are
|
||||
** redirected through the global sqlite3_api structure.
|
||||
**
|
||||
** This header file is also used by the loadext.c source file
|
||||
** (part of the main SQLite library - not an extension) so that
|
||||
** it can get access to the sqlite3_api_routines structure
|
||||
** definition. But the main library does not want to redefine
|
||||
** the API. So the redefinition macros are only valid if the
|
||||
** SQLITE_CORE macros is undefined.
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
||||
#endif
|
||||
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
||||
#define sqlite3_bind_double sqlite3_api->bind_double
|
||||
#define sqlite3_bind_int sqlite3_api->bind_int
|
||||
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
||||
#define sqlite3_bind_null sqlite3_api->bind_null
|
||||
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
||||
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
||||
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
||||
#define sqlite3_bind_text sqlite3_api->bind_text
|
||||
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
||||
#define sqlite3_bind_value sqlite3_api->bind_value
|
||||
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
||||
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
||||
#define sqlite3_changes sqlite3_api->changes
|
||||
#define sqlite3_close sqlite3_api->close
|
||||
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
||||
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
||||
#define sqlite3_column_blob sqlite3_api->column_blob
|
||||
#define sqlite3_column_bytes sqlite3_api->column_bytes
|
||||
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
|
||||
#define sqlite3_column_count sqlite3_api->column_count
|
||||
#define sqlite3_column_database_name sqlite3_api->column_database_name
|
||||
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
|
||||
#define sqlite3_column_decltype sqlite3_api->column_decltype
|
||||
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
|
||||
#define sqlite3_column_double sqlite3_api->column_double
|
||||
#define sqlite3_column_int sqlite3_api->column_int
|
||||
#define sqlite3_column_int64 sqlite3_api->column_int64
|
||||
#define sqlite3_column_name sqlite3_api->column_name
|
||||
#define sqlite3_column_name16 sqlite3_api->column_name16
|
||||
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
|
||||
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
|
||||
#define sqlite3_column_table_name sqlite3_api->column_table_name
|
||||
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
|
||||
#define sqlite3_column_text sqlite3_api->column_text
|
||||
#define sqlite3_column_text16 sqlite3_api->column_text16
|
||||
#define sqlite3_column_type sqlite3_api->column_type
|
||||
#define sqlite3_column_value sqlite3_api->column_value
|
||||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
||||
#define sqlite3_complete sqlite3_api->complete
|
||||
#define sqlite3_complete16 sqlite3_api->complete16
|
||||
#define sqlite3_create_collation sqlite3_api->create_collation
|
||||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
||||
#define sqlite3_create_function sqlite3_api->create_function
|
||||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
||||
#define sqlite3_create_module sqlite3_api->create_module
|
||||
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
||||
#define sqlite3_data_count sqlite3_api->data_count
|
||||
#define sqlite3_db_handle sqlite3_api->db_handle
|
||||
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
|
||||
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
|
||||
#define sqlite3_errcode sqlite3_api->errcode
|
||||
#define sqlite3_errmsg sqlite3_api->errmsg
|
||||
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
||||
#define sqlite3_exec sqlite3_api->exec
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_expired sqlite3_api->expired
|
||||
#endif
|
||||
#define sqlite3_finalize sqlite3_api->finalize
|
||||
#define sqlite3_free sqlite3_api->free
|
||||
#define sqlite3_free_table sqlite3_api->free_table
|
||||
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
||||
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
||||
#define sqlite3_get_table sqlite3_api->get_table
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
||||
#endif
|
||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
||||
#define sqlite3_libversion sqlite3_api->libversion
|
||||
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
||||
#define sqlite3_malloc sqlite3_api->malloc
|
||||
#define sqlite3_mprintf sqlite3_api->mprintf
|
||||
#define sqlite3_open sqlite3_api->open
|
||||
#define sqlite3_open16 sqlite3_api->open16
|
||||
#define sqlite3_prepare sqlite3_api->prepare
|
||||
#define sqlite3_prepare16 sqlite3_api->prepare16
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_profile sqlite3_api->profile
|
||||
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
||||
#define sqlite3_realloc sqlite3_api->realloc
|
||||
#define sqlite3_reset sqlite3_api->reset
|
||||
#define sqlite3_result_blob sqlite3_api->result_blob
|
||||
#define sqlite3_result_double sqlite3_api->result_double
|
||||
#define sqlite3_result_error sqlite3_api->result_error
|
||||
#define sqlite3_result_error16 sqlite3_api->result_error16
|
||||
#define sqlite3_result_int sqlite3_api->result_int
|
||||
#define sqlite3_result_int64 sqlite3_api->result_int64
|
||||
#define sqlite3_result_null sqlite3_api->result_null
|
||||
#define sqlite3_result_text sqlite3_api->result_text
|
||||
#define sqlite3_result_text16 sqlite3_api->result_text16
|
||||
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
||||
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
||||
#define sqlite3_result_value sqlite3_api->result_value
|
||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
||||
#define sqlite3_snprintf sqlite3_api->xsnprintf
|
||||
#define sqlite3_step sqlite3_api->step
|
||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||
#define sqlite3_total_changes sqlite3_api->total_changes
|
||||
#define sqlite3_trace sqlite3_api->trace
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
||||
#endif
|
||||
#define sqlite3_update_hook sqlite3_api->update_hook
|
||||
#define sqlite3_user_data sqlite3_api->user_data
|
||||
#define sqlite3_value_blob sqlite3_api->value_blob
|
||||
#define sqlite3_value_bytes sqlite3_api->value_bytes
|
||||
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
|
||||
#define sqlite3_value_double sqlite3_api->value_double
|
||||
#define sqlite3_value_int sqlite3_api->value_int
|
||||
#define sqlite3_value_int64 sqlite3_api->value_int64
|
||||
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
|
||||
#define sqlite3_value_text sqlite3_api->value_text
|
||||
#define sqlite3_value_text16 sqlite3_api->value_text16
|
||||
#define sqlite3_value_text16be sqlite3_api->value_text16be
|
||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||
#define sqlite3_value_type sqlite3_api->value_type
|
||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
|
||||
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
|
||||
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
|
||||
#define sqlite3_blob_close sqlite3_api->blob_close
|
||||
#define sqlite3_blob_open sqlite3_api->blob_open
|
||||
#define sqlite3_blob_read sqlite3_api->blob_read
|
||||
#define sqlite3_blob_write sqlite3_api->blob_write
|
||||
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
|
||||
#define sqlite3_file_control sqlite3_api->file_control
|
||||
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
|
||||
#define sqlite3_memory_used sqlite3_api->memory_used
|
||||
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
|
||||
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
|
||||
#define sqlite3_mutex_free sqlite3_api->mutex_free
|
||||
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
|
||||
#define sqlite3_mutex_try sqlite3_api->mutex_try
|
||||
#define sqlite3_open_v2 sqlite3_api->open_v2
|
||||
#define sqlite3_release_memory sqlite3_api->release_memory
|
||||
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
||||
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
||||
#define sqlite3_sleep sqlite3_api->sleep
|
||||
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
||||
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
||||
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
||||
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
||||
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
||||
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
||||
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
||||
#define sqlite3_test_control sqlite3_api->test_control
|
||||
#define sqlite3_randomness sqlite3_api->randomness
|
||||
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
|
||||
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
|
||||
#define sqlite3_limit sqlite3_api->limit
|
||||
#define sqlite3_next_stmt sqlite3_api->next_stmt
|
||||
#define sqlite3_sql sqlite3_api->sql
|
||||
#define sqlite3_status sqlite3_api->status
|
||||
#define sqlite3_backup_finish sqlite3_api->backup_finish
|
||||
#define sqlite3_backup_init sqlite3_api->backup_init
|
||||
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
|
||||
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
|
||||
#define sqlite3_backup_step sqlite3_api->backup_step
|
||||
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
|
||||
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
|
||||
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
|
||||
#define sqlite3_db_config sqlite3_api->db_config
|
||||
#define sqlite3_db_mutex sqlite3_api->db_mutex
|
||||
#define sqlite3_db_status sqlite3_api->db_status
|
||||
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
|
||||
#define sqlite3_log sqlite3_api->log
|
||||
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
|
||||
#define sqlite3_sourceid sqlite3_api->sourceid
|
||||
#define sqlite3_stmt_status sqlite3_api->stmt_status
|
||||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
||||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
||||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||||
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
||||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
||||
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
|
||||
/* Version 3.7.16 and later */
|
||||
#define sqlite3_close_v2 sqlite3_api->close_v2
|
||||
#define sqlite3_db_filename sqlite3_api->db_filename
|
||||
#define sqlite3_db_readonly sqlite3_api->db_readonly
|
||||
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
|
||||
#define sqlite3_errstr sqlite3_api->errstr
|
||||
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
|
||||
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
|
||||
#define sqlite3_stricmp sqlite3_api->stricmp
|
||||
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
||||
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
||||
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
||||
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
||||
/* Version 3.8.7 and later */
|
||||
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
||||
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
|
||||
#define sqlite3_bind_text64 sqlite3_api->bind_text64
|
||||
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
|
||||
#define sqlite3_load_extension sqlite3_api->load_extension
|
||||
#define sqlite3_malloc64 sqlite3_api->malloc64
|
||||
#define sqlite3_msize sqlite3_api->msize
|
||||
#define sqlite3_realloc64 sqlite3_api->realloc64
|
||||
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
|
||||
#define sqlite3_result_blob64 sqlite3_api->result_blob64
|
||||
#define sqlite3_result_text64 sqlite3_api->result_text64
|
||||
#define sqlite3_strglob sqlite3_api->strglob
|
||||
/* Version 3.8.11 and later */
|
||||
#define sqlite3_value_dup sqlite3_api->value_dup
|
||||
#define sqlite3_value_free sqlite3_api->value_free
|
||||
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
|
||||
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
|
||||
/* Version 3.9.0 and later */
|
||||
#define sqlite3_value_subtype sqlite3_api->value_subtype
|
||||
#define sqlite3_result_subtype sqlite3_api->result_subtype
|
||||
/* Version 3.10.0 and later */
|
||||
#define sqlite3_status64 sqlite3_api->status64
|
||||
#define sqlite3_strlike sqlite3_api->strlike
|
||||
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
|
||||
/* Version 3.12.0 and later */
|
||||
#define sqlite3_system_errno sqlite3_api->system_errno
|
||||
/* Version 3.14.0 and later */
|
||||
#define sqlite3_trace_v2 sqlite3_api->trace_v2
|
||||
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
|
||||
/* Version 3.18.0 and later */
|
||||
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
|
||||
/* Version 3.20.0 and later */
|
||||
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
|
||||
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
|
||||
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
|
||||
#define sqlite3_result_pointer sqlite3_api->result_pointer
|
||||
#define sqlite3_value_pointer sqlite3_api->value_pointer
|
||||
/* Version 3.22.0 and later */
|
||||
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
|
||||
#define sqlite3_value_nochange sqlite3_api->value_nochange
|
||||
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
|
||||
/* Version 3.24.0 and later */
|
||||
#define sqlite3_keyword_count sqlite3_api->keyword_count
|
||||
#define sqlite3_keyword_name sqlite3_api->keyword_name
|
||||
#define sqlite3_keyword_check sqlite3_api->keyword_check
|
||||
#define sqlite3_str_new sqlite3_api->str_new
|
||||
#define sqlite3_str_finish sqlite3_api->str_finish
|
||||
#define sqlite3_str_appendf sqlite3_api->str_appendf
|
||||
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
|
||||
#define sqlite3_str_append sqlite3_api->str_append
|
||||
#define sqlite3_str_appendall sqlite3_api->str_appendall
|
||||
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
|
||||
#define sqlite3_str_reset sqlite3_api->str_reset
|
||||
#define sqlite3_str_errcode sqlite3_api->str_errcode
|
||||
#define sqlite3_str_length sqlite3_api->str_length
|
||||
#define sqlite3_str_value sqlite3_api->str_value
|
||||
/* Version 3.25.0 and later */
|
||||
#define sqlite3_create_window_function sqlite3_api->create_window_function
|
||||
/* Version 3.26.0 and later */
|
||||
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
|
||||
/* Version 3.28.0 and later */
|
||||
#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
|
||||
#define sqlite3_value_frombind sqlite3_api->value_frombind
|
||||
/* Version 3.30.0 and later */
|
||||
#define sqlite3_drop_modules sqlite3_api->drop_modules
|
||||
/* Version 3.31.0 and later */
|
||||
#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
|
||||
#define sqlite3_uri_key sqlite3_api->uri_key
|
||||
#define sqlite3_filename_database sqlite3_api->filename_database
|
||||
#define sqlite3_filename_journal sqlite3_api->filename_journal
|
||||
#define sqlite3_filename_wal sqlite3_api->filename_wal
|
||||
/* Version 3.32.0 and later */
|
||||
#define sqlite3_create_filename sqlite3_api->create_filename
|
||||
#define sqlite3_free_filename sqlite3_api->free_filename
|
||||
#define sqlite3_database_file_object sqlite3_api->database_file_object
|
||||
/* Version 3.34.0 and later */
|
||||
#define sqlite3_txn_state sqlite3_api->txn_state
|
||||
/* Version 3.36.1 and later */
|
||||
#define sqlite3_changes64 sqlite3_api->changes64
|
||||
#define sqlite3_total_changes64 sqlite3_api->total_changes64
|
||||
/* Version 3.37.0 and later */
|
||||
#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
|
||||
/* Version 3.38.0 and later */
|
||||
#define sqlite3_error_offset sqlite3_api->error_offset
|
||||
#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
|
||||
#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
|
||||
#define sqlite3_vtab_in sqlite3_api->vtab_in
|
||||
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
|
||||
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
|
||||
/* Version 3.39.0 and later */
|
||||
#ifndef SQLITE_OMIT_DESERIALIZE
|
||||
#define sqlite3_deserialize sqlite3_api->deserialize
|
||||
#define sqlite3_serialize sqlite3_api->serialize
|
||||
#endif
|
||||
#define sqlite3_db_name sqlite3_api->db_name
|
||||
/* Version 3.40.0 and later */
|
||||
#define sqlite3_value_encoding sqlite3_api->value_encoding
|
||||
/* Version 3.41.0 and later */
|
||||
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
|
||||
/* Version 3.43.0 and later */
|
||||
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
|
||||
/* Version 3.44.0 and later */
|
||||
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
||||
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
/* This case when the file really is being compiled as a loadable
|
||||
** extension */
|
||||
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
|
||||
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
|
||||
# define SQLITE_EXTENSION_INIT3 \
|
||||
extern const sqlite3_api_routines *sqlite3_api;
|
||||
#else
|
||||
/* This case when the file is being statically linked into the
|
||||
** application */
|
||||
# define SQLITE_EXTENSION_INIT1 /*no-op*/
|
||||
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
|
||||
# define SQLITE_EXTENSION_INIT3 /*no-op*/
|
||||
#endif
|
||||
|
||||
#endif /* SQLITE3EXT_H */
|
||||
Reference in New Issue
Block a user