mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Enable sqlite zipfile module in redbean
This change also breaks out a bunch of extension files that the SQLite authors inlined into a shell.c amalgamation.
This commit is contained in:
parent
2c7f865b12
commit
34e39ad027
18 changed files with 9640 additions and 9822 deletions
680
third_party/sqlite3/appendvfs.c
vendored
Normal file
680
third_party/sqlite3/appendvfs.c
vendored
Normal file
|
@ -0,0 +1,680 @@
|
|||
/*
|
||||
** 2017-10-20
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This file implements a VFS shim that allows an SQLite database to be
|
||||
** appended onto the end of some other file, such as an executable.
|
||||
**
|
||||
** A special record must appear at the end of the file that identifies the
|
||||
** file as an appended database and provides the offset to the first page
|
||||
** of the exposed content. (Or, it is the length of the content prefix.)
|
||||
** For best performance page 1 should be located at a disk page boundary,
|
||||
** though that is not required.
|
||||
**
|
||||
** When opening a database using this VFS, the connection might treat
|
||||
** the file as an ordinary SQLite database, or it might treat it as a
|
||||
** database appended onto some other file. The decision is made by
|
||||
** applying the following rules in order:
|
||||
**
|
||||
** (1) An empty file is an ordinary database.
|
||||
**
|
||||
** (2) If the file ends with the appendvfs trailer string
|
||||
** "Start-Of-SQLite3-NNNNNNNN" that file is an appended database.
|
||||
**
|
||||
** (3) If the file begins with the standard SQLite prefix string
|
||||
** "SQLite format 3", that file is an ordinary database.
|
||||
**
|
||||
** (4) If none of the above apply and the SQLITE_OPEN_CREATE flag is
|
||||
** set, then a new database is appended to the already existing file.
|
||||
**
|
||||
** (5) Otherwise, SQLITE_CANTOPEN is returned.
|
||||
**
|
||||
** To avoid unnecessary complications with the PENDING_BYTE, the size of
|
||||
** the file containing the database is limited to 1GiB. (1073741824 bytes)
|
||||
** This VFS will not read or write past the 1GiB mark. This restriction
|
||||
** might be lifted in future versions. For now, if you need a larger
|
||||
** database, then keep it in a separate file.
|
||||
**
|
||||
** If the file being opened is a plain database (not an appended one), then
|
||||
** this shim is a pass-through into the default underlying VFS. (rule 3)
|
||||
**/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/sqlite3/sqlite3ext.h"
|
||||
// clang-format off
|
||||
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
/* The append mark at the end of the database is:
|
||||
**
|
||||
** Start-Of-SQLite3-NNNNNNNN
|
||||
** 123456789 123456789 12345
|
||||
**
|
||||
** The NNNNNNNN represents a 64-bit big-endian unsigned integer which is
|
||||
** the offset to page 1, and also the length of the prefix content.
|
||||
*/
|
||||
#define APND_MARK_PREFIX "Start-Of-SQLite3-"
|
||||
#define APND_MARK_PREFIX_SZ 17
|
||||
#define APND_MARK_FOS_SZ 8
|
||||
#define APND_MARK_SIZE (APND_MARK_PREFIX_SZ+APND_MARK_FOS_SZ)
|
||||
|
||||
/*
|
||||
** Maximum size of the combined prefix + database + append-mark. This
|
||||
** must be less than 0x40000000 to avoid locking issues on Windows.
|
||||
*/
|
||||
#define APND_MAX_SIZE (0x40000000)
|
||||
|
||||
/*
|
||||
** Try to align the database to an even multiple of APND_ROUNDUP bytes.
|
||||
*/
|
||||
#ifndef APND_ROUNDUP
|
||||
#define APND_ROUNDUP 4096
|
||||
#endif
|
||||
#define APND_ALIGN_MASK ((sqlite3_int64)(APND_ROUNDUP-1))
|
||||
#define APND_START_ROUNDUP(fsz) (((fsz)+APND_ALIGN_MASK) & ~APND_ALIGN_MASK)
|
||||
|
||||
/*
|
||||
** Forward declaration of objects used by this utility
|
||||
*/
|
||||
typedef struct sqlite3_vfs ApndVfs;
|
||||
typedef struct ApndFile ApndFile;
|
||||
|
||||
/* Access to a lower-level VFS that (might) implement dynamic loading,
|
||||
** access to randomness, etc.
|
||||
*/
|
||||
#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
|
||||
#define ORIGFILE(p) ((sqlite3_file*)(((ApndFile*)(p))+1))
|
||||
|
||||
/* An open appendvfs file
|
||||
**
|
||||
** An instance of this structure describes the appended database file.
|
||||
** A separate sqlite3_file object is always appended. The appended
|
||||
** sqlite3_file object (which can be accessed using ORIGFILE()) describes
|
||||
** the entire file, including the prefix, the database, and the
|
||||
** append-mark.
|
||||
**
|
||||
** The structure of an AppendVFS database is like this:
|
||||
**
|
||||
** +-------------+---------+----------+-------------+
|
||||
** | prefix-file | padding | database | append-mark |
|
||||
** +-------------+---------+----------+-------------+
|
||||
** ^ ^
|
||||
** | |
|
||||
** iPgOne iMark
|
||||
**
|
||||
**
|
||||
** "prefix file" - file onto which the database has been appended.
|
||||
** "padding" - zero or more bytes inserted so that "database"
|
||||
** starts on an APND_ROUNDUP boundary
|
||||
** "database" - The SQLite database file
|
||||
** "append-mark" - The 25-byte "Start-Of-SQLite3-NNNNNNNN" that indicates
|
||||
** the offset from the start of prefix-file to the start
|
||||
** of "database".
|
||||
**
|
||||
** The size of the database is iMark - iPgOne.
|
||||
**
|
||||
** The NNNNNNNN in the "Start-Of-SQLite3-NNNNNNNN" suffix is the value
|
||||
** of iPgOne stored as a big-ending 64-bit integer.
|
||||
**
|
||||
** iMark will be the size of the underlying file minus 25 (APND_MARKSIZE).
|
||||
** Or, iMark is -1 to indicate that it has not yet been written.
|
||||
*/
|
||||
struct ApndFile {
|
||||
sqlite3_file base; /* Subclass. MUST BE FIRST! */
|
||||
sqlite3_int64 iPgOne; /* Offset to the start of the database */
|
||||
sqlite3_int64 iMark; /* Offset of the append mark. -1 if unwritten */
|
||||
/* Always followed by another sqlite3_file that describes the whole file */
|
||||
};
|
||||
|
||||
/*
|
||||
** Methods for ApndFile
|
||||
*/
|
||||
static int apndClose(sqlite3_file*);
|
||||
static int apndRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
||||
static int apndWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
||||
static int apndTruncate(sqlite3_file*, sqlite3_int64 size);
|
||||
static int apndSync(sqlite3_file*, int flags);
|
||||
static int apndFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
||||
static int apndLock(sqlite3_file*, int);
|
||||
static int apndUnlock(sqlite3_file*, int);
|
||||
static int apndCheckReservedLock(sqlite3_file*, int *pResOut);
|
||||
static int apndFileControl(sqlite3_file*, int op, void *pArg);
|
||||
static int apndSectorSize(sqlite3_file*);
|
||||
static int apndDeviceCharacteristics(sqlite3_file*);
|
||||
static int apndShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
|
||||
static int apndShmLock(sqlite3_file*, int offset, int n, int flags);
|
||||
static void apndShmBarrier(sqlite3_file*);
|
||||
static int apndShmUnmap(sqlite3_file*, int deleteFlag);
|
||||
static int apndFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
|
||||
static int apndUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
|
||||
|
||||
/*
|
||||
** Methods for ApndVfs
|
||||
*/
|
||||
static int apndOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
||||
static int apndDelete(sqlite3_vfs*, const char *zName, int syncDir);
|
||||
static int apndAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
||||
static int apndFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
||||
static void *apndDlOpen(sqlite3_vfs*, const char *zFilename);
|
||||
static void apndDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
||||
static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
||||
static void apndDlClose(sqlite3_vfs*, void*);
|
||||
static int apndRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
||||
static int apndSleep(sqlite3_vfs*, int microseconds);
|
||||
static int apndCurrentTime(sqlite3_vfs*, double*);
|
||||
static int apndGetLastError(sqlite3_vfs*, int, char *);
|
||||
static int apndCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
||||
static int apndSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr);
|
||||
static sqlite3_syscall_ptr apndGetSystemCall(sqlite3_vfs*, const char *z);
|
||||
static const char *apndNextSystemCall(sqlite3_vfs*, const char *zName);
|
||||
|
||||
static sqlite3_vfs apnd_vfs = {
|
||||
3, /* iVersion (set when registered) */
|
||||
0, /* szOsFile (set when registered) */
|
||||
1024, /* mxPathname */
|
||||
0, /* pNext */
|
||||
"apndvfs", /* zName */
|
||||
0, /* pAppData (set when registered) */
|
||||
apndOpen, /* xOpen */
|
||||
apndDelete, /* xDelete */
|
||||
apndAccess, /* xAccess */
|
||||
apndFullPathname, /* xFullPathname */
|
||||
apndDlOpen, /* xDlOpen */
|
||||
apndDlError, /* xDlError */
|
||||
apndDlSym, /* xDlSym */
|
||||
apndDlClose, /* xDlClose */
|
||||
apndRandomness, /* xRandomness */
|
||||
apndSleep, /* xSleep */
|
||||
apndCurrentTime, /* xCurrentTime */
|
||||
apndGetLastError, /* xGetLastError */
|
||||
apndCurrentTimeInt64, /* xCurrentTimeInt64 */
|
||||
apndSetSystemCall, /* xSetSystemCall */
|
||||
apndGetSystemCall, /* xGetSystemCall */
|
||||
apndNextSystemCall /* xNextSystemCall */
|
||||
};
|
||||
|
||||
static const sqlite3_io_methods apnd_io_methods = {
|
||||
3, /* iVersion */
|
||||
apndClose, /* xClose */
|
||||
apndRead, /* xRead */
|
||||
apndWrite, /* xWrite */
|
||||
apndTruncate, /* xTruncate */
|
||||
apndSync, /* xSync */
|
||||
apndFileSize, /* xFileSize */
|
||||
apndLock, /* xLock */
|
||||
apndUnlock, /* xUnlock */
|
||||
apndCheckReservedLock, /* xCheckReservedLock */
|
||||
apndFileControl, /* xFileControl */
|
||||
apndSectorSize, /* xSectorSize */
|
||||
apndDeviceCharacteristics, /* xDeviceCharacteristics */
|
||||
apndShmMap, /* xShmMap */
|
||||
apndShmLock, /* xShmLock */
|
||||
apndShmBarrier, /* xShmBarrier */
|
||||
apndShmUnmap, /* xShmUnmap */
|
||||
apndFetch, /* xFetch */
|
||||
apndUnfetch /* xUnfetch */
|
||||
};
|
||||
|
||||
/*
|
||||
** Close an apnd-file.
|
||||
*/
|
||||
static int apndClose(sqlite3_file *pFile){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xClose(pFile);
|
||||
}
|
||||
|
||||
/*
|
||||
** Read data from an apnd-file.
|
||||
*/
|
||||
static int apndRead(
|
||||
sqlite3_file *pFile,
|
||||
void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
ApndFile *paf = (ApndFile *)pFile;
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xRead(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
|
||||
}
|
||||
|
||||
/*
|
||||
** Add the append-mark onto what should become the end of the file.
|
||||
* If and only if this succeeds, internal ApndFile.iMark is updated.
|
||||
* Parameter iWriteEnd is the appendvfs-relative offset of the new mark.
|
||||
*/
|
||||
static int apndWriteMark(
|
||||
ApndFile *paf,
|
||||
sqlite3_file *pFile,
|
||||
sqlite_int64 iWriteEnd
|
||||
){
|
||||
sqlite_int64 iPgOne = paf->iPgOne;
|
||||
unsigned char a[APND_MARK_SIZE];
|
||||
int i = APND_MARK_FOS_SZ;
|
||||
int rc;
|
||||
assert(pFile == ORIGFILE(paf));
|
||||
memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
|
||||
while( --i >= 0 ){
|
||||
a[APND_MARK_PREFIX_SZ+i] = (unsigned char)(iPgOne & 0xff);
|
||||
iPgOne >>= 8;
|
||||
}
|
||||
iWriteEnd += paf->iPgOne;
|
||||
if( SQLITE_OK==(rc = pFile->pMethods->xWrite
|
||||
(pFile, a, APND_MARK_SIZE, iWriteEnd)) ){
|
||||
paf->iMark = iWriteEnd;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to an apnd-file.
|
||||
*/
|
||||
static int apndWrite(
|
||||
sqlite3_file *pFile,
|
||||
const void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
ApndFile *paf = (ApndFile *)pFile;
|
||||
sqlite_int64 iWriteEnd = iOfst + iAmt;
|
||||
if( iWriteEnd>=APND_MAX_SIZE ) return SQLITE_FULL;
|
||||
pFile = ORIGFILE(pFile);
|
||||
/* If append-mark is absent or will be overwritten, write it. */
|
||||
if( paf->iMark < 0 || paf->iPgOne + iWriteEnd > paf->iMark ){
|
||||
int rc = apndWriteMark(paf, pFile, iWriteEnd);
|
||||
if( SQLITE_OK!=rc ) return rc;
|
||||
}
|
||||
return pFile->pMethods->xWrite(pFile, zBuf, iAmt, paf->iPgOne+iOfst);
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate an apnd-file.
|
||||
*/
|
||||
static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
||||
ApndFile *paf = (ApndFile *)pFile;
|
||||
pFile = ORIGFILE(pFile);
|
||||
/* The append mark goes out first so truncate failure does not lose it. */
|
||||
if( SQLITE_OK!=apndWriteMark(paf, pFile, size) ) return SQLITE_IOERR;
|
||||
/* Truncate underlying file just past append mark */
|
||||
return pFile->pMethods->xTruncate(pFile, paf->iMark+APND_MARK_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync an apnd-file.
|
||||
*/
|
||||
static int apndSync(sqlite3_file *pFile, int flags){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xSync(pFile, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current file-size of an apnd-file.
|
||||
** If the append mark is not yet there, the file-size is 0.
|
||||
*/
|
||||
static int apndFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
||||
ApndFile *paf = (ApndFile *)pFile;
|
||||
*pSize = ( paf->iMark >= 0 )? (paf->iMark - paf->iPgOne) : 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Lock an apnd-file.
|
||||
*/
|
||||
static int apndLock(sqlite3_file *pFile, int eLock){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xLock(pFile, eLock);
|
||||
}
|
||||
|
||||
/*
|
||||
** Unlock an apnd-file.
|
||||
*/
|
||||
static int apndUnlock(sqlite3_file *pFile, int eLock){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xUnlock(pFile, eLock);
|
||||
}
|
||||
|
||||
/*
|
||||
** Check if another file-handle holds a RESERVED lock on an apnd-file.
|
||||
*/
|
||||
static int apndCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** File control method. For custom operations on an apnd-file.
|
||||
*/
|
||||
static int apndFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
ApndFile *paf = (ApndFile *)pFile;
|
||||
int rc;
|
||||
pFile = ORIGFILE(pFile);
|
||||
if( op==SQLITE_FCNTL_SIZE_HINT ) *(sqlite3_int64*)pArg += paf->iPgOne;
|
||||
rc = pFile->pMethods->xFileControl(pFile, op, pArg);
|
||||
if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
|
||||
*(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", paf->iPgOne,*(char**)pArg);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the sector-size in bytes for an apnd-file.
|
||||
*/
|
||||
static int apndSectorSize(sqlite3_file *pFile){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xSectorSize(pFile);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the device characteristic flags supported by an apnd-file.
|
||||
*/
|
||||
static int apndDeviceCharacteristics(sqlite3_file *pFile){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xDeviceCharacteristics(pFile);
|
||||
}
|
||||
|
||||
/* Create a shared memory file mapping */
|
||||
static int apndShmMap(
|
||||
sqlite3_file *pFile,
|
||||
int iPg,
|
||||
int pgsz,
|
||||
int bExtend,
|
||||
void volatile **pp
|
||||
){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp);
|
||||
}
|
||||
|
||||
/* Perform locking on a shared-memory segment */
|
||||
static int apndShmLock(sqlite3_file *pFile, int offset, int n, int flags){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xShmLock(pFile,offset,n,flags);
|
||||
}
|
||||
|
||||
/* Memory barrier operation on shared memory */
|
||||
static void apndShmBarrier(sqlite3_file *pFile){
|
||||
pFile = ORIGFILE(pFile);
|
||||
pFile->pMethods->xShmBarrier(pFile);
|
||||
}
|
||||
|
||||
/* Unmap a shared memory segment */
|
||||
static int apndShmUnmap(sqlite3_file *pFile, int deleteFlag){
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xShmUnmap(pFile,deleteFlag);
|
||||
}
|
||||
|
||||
/* Fetch a page of a memory-mapped file */
|
||||
static int apndFetch(
|
||||
sqlite3_file *pFile,
|
||||
sqlite3_int64 iOfst,
|
||||
int iAmt,
|
||||
void **pp
|
||||
){
|
||||
ApndFile *p = (ApndFile *)pFile;
|
||||
if( p->iMark < 0 || iOfst+iAmt > p->iMark ){
|
||||
return SQLITE_IOERR; /* Cannot read what is not yet there. */
|
||||
}
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xFetch(pFile, iOfst+p->iPgOne, iAmt, pp);
|
||||
}
|
||||
|
||||
/* Release a memory-mapped page */
|
||||
static int apndUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
|
||||
ApndFile *p = (ApndFile *)pFile;
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xUnfetch(pFile, iOfst+p->iPgOne, pPage);
|
||||
}
|
||||
|
||||
/*
|
||||
** Try to read the append-mark off the end of a file. Return the
|
||||
** start of the appended database if the append-mark is present.
|
||||
** If there is no valid append-mark, return -1;
|
||||
**
|
||||
** An append-mark is only valid if the NNNNNNNN start-of-database offset
|
||||
** indicates that the appended database contains at least one page. The
|
||||
** start-of-database value must be a multiple of 512.
|
||||
*/
|
||||
static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){
|
||||
int rc, i;
|
||||
sqlite3_int64 iMark;
|
||||
int msbs = 8 * (APND_MARK_FOS_SZ-1);
|
||||
unsigned char a[APND_MARK_SIZE];
|
||||
|
||||
if( APND_MARK_SIZE!=(sz & 0x1ff) ) return -1;
|
||||
rc = pFile->pMethods->xRead(pFile, a, APND_MARK_SIZE, sz-APND_MARK_SIZE);
|
||||
if( rc ) return -1;
|
||||
if( memcmp(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ)!=0 ) return -1;
|
||||
iMark = ((sqlite3_int64)(a[APND_MARK_PREFIX_SZ] & 0x7f)) << msbs;
|
||||
for(i=1; i<8; i++){
|
||||
msbs -= 8;
|
||||
iMark |= (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<msbs;
|
||||
}
|
||||
if( iMark > (sz - APND_MARK_SIZE - 512) ) return -1;
|
||||
if( iMark & 0x1ff ) return -1;
|
||||
return iMark;
|
||||
}
|
||||
|
||||
static const char apvfsSqliteHdr[] = "SQLite format 3";
|
||||
/*
|
||||
** Check to see if the file is an appendvfs SQLite database file.
|
||||
** Return true iff it is such. Parameter sz is the file's size.
|
||||
*/
|
||||
static int apndIsAppendvfsDatabase(sqlite3_int64 sz, sqlite3_file *pFile){
|
||||
int rc;
|
||||
char zHdr[16];
|
||||
sqlite3_int64 iMark = apndReadMark(sz, pFile);
|
||||
if( iMark>=0 ){
|
||||
/* If file has the correct end-marker, the expected odd size, and the
|
||||
** SQLite DB type marker where the end-marker puts it, then it
|
||||
** is an appendvfs database.
|
||||
*/
|
||||
rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), iMark);
|
||||
if( SQLITE_OK==rc
|
||||
&& memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))==0
|
||||
&& (sz & 0x1ff) == APND_MARK_SIZE
|
||||
&& sz>=512+APND_MARK_SIZE
|
||||
){
|
||||
return 1; /* It's an appendvfs database */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if the file is an ordinary SQLite database file.
|
||||
** Return true iff so. Parameter sz is the file's size.
|
||||
*/
|
||||
static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
|
||||
char zHdr[16];
|
||||
if( apndIsAppendvfsDatabase(sz, pFile) /* rule 2 */
|
||||
|| (sz & 0x1ff) != 0
|
||||
|| SQLITE_OK!=pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0)
|
||||
|| memcmp(zHdr, apvfsSqliteHdr, sizeof(zHdr))!=0
|
||||
){
|
||||
return 0;
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Open an apnd file handle.
|
||||
*/
|
||||
static int apndOpen(
|
||||
sqlite3_vfs *pApndVfs,
|
||||
const char *zName,
|
||||
sqlite3_file *pFile,
|
||||
int flags,
|
||||
int *pOutFlags
|
||||
){
|
||||
ApndFile *pApndFile = (ApndFile*)pFile;
|
||||
sqlite3_file *pBaseFile = ORIGFILE(pFile);
|
||||
sqlite3_vfs *pBaseVfs = ORIGVFS(pApndVfs);
|
||||
int rc;
|
||||
sqlite3_int64 sz = 0;
|
||||
if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
|
||||
/* The appendvfs is not to be used for transient or temporary databases.
|
||||
** Just use the base VFS open to initialize the given file object and
|
||||
** open the underlying file. (Appendvfs is then unused for this file.)
|
||||
*/
|
||||
return pBaseVfs->xOpen(pBaseVfs, zName, pFile, flags, pOutFlags);
|
||||
}
|
||||
memset(pApndFile, 0, sizeof(ApndFile));
|
||||
pFile->pMethods = &apnd_io_methods;
|
||||
pApndFile->iMark = -1; /* Append mark not yet written */
|
||||
|
||||
rc = pBaseVfs->xOpen(pBaseVfs, zName, pBaseFile, flags, pOutFlags);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pBaseFile->pMethods->xFileSize(pBaseFile, &sz);
|
||||
}
|
||||
if( rc ){
|
||||
pBaseFile->pMethods->xClose(pBaseFile);
|
||||
pFile->pMethods = 0;
|
||||
return rc;
|
||||
}
|
||||
if( apndIsOrdinaryDatabaseFile(sz, pBaseFile) ){
|
||||
/* The file being opened appears to be just an ordinary DB. Copy
|
||||
** the base dispatch-table so this instance mimics the base VFS.
|
||||
*/
|
||||
memmove(pApndFile, pBaseFile, pBaseVfs->szOsFile);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
pApndFile->iPgOne = apndReadMark(sz, pFile);
|
||||
if( pApndFile->iPgOne>=0 ){
|
||||
pApndFile->iMark = sz - APND_MARK_SIZE; /* Append mark found */
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( (flags & SQLITE_OPEN_CREATE)==0 ){
|
||||
pBaseFile->pMethods->xClose(pBaseFile);
|
||||
rc = SQLITE_CANTOPEN;
|
||||
pFile->pMethods = 0;
|
||||
}else{
|
||||
/* Round newly added appendvfs location to #define'd page boundary.
|
||||
** Note that nothing has yet been written to the underlying file.
|
||||
** The append mark will be written along with first content write.
|
||||
** Until then, paf->iMark value indicates it is not yet written.
|
||||
*/
|
||||
pApndFile->iPgOne = APND_START_ROUNDUP(sz);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete an apnd file.
|
||||
** For an appendvfs, this could mean delete the appendvfs portion,
|
||||
** leaving the appendee as it was before it gained an appendvfs.
|
||||
** For now, this code deletes the underlying file too.
|
||||
*/
|
||||
static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
||||
return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
|
||||
}
|
||||
|
||||
/*
|
||||
** All other VFS methods are pass-thrus.
|
||||
*/
|
||||
static int apndAccess(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int flags,
|
||||
int *pResOut
|
||||
){
|
||||
return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
|
||||
}
|
||||
|
||||
static int apndFullPathname(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int nOut,
|
||||
char *zOut
|
||||
){
|
||||
return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut);
|
||||
}
|
||||
|
||||
static void *apndDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
||||
return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
|
||||
}
|
||||
|
||||
static void apndDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
||||
ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
|
||||
}
|
||||
|
||||
static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
||||
return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
|
||||
}
|
||||
|
||||
static void apndDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
|
||||
}
|
||||
|
||||
static int apndRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
||||
return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
|
||||
}
|
||||
|
||||
static int apndSleep(sqlite3_vfs *pVfs, int nMicro){
|
||||
return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
|
||||
}
|
||||
|
||||
static int apndCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||||
return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
|
||||
}
|
||||
|
||||
static int apndGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
||||
return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
|
||||
}
|
||||
|
||||
static int apndCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
||||
return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
|
||||
}
|
||||
|
||||
static int apndSetSystemCall(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName,
|
||||
sqlite3_syscall_ptr pCall
|
||||
){
|
||||
return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall);
|
||||
}
|
||||
|
||||
static sqlite3_syscall_ptr apndGetSystemCall(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName
|
||||
){
|
||||
return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName);
|
||||
}
|
||||
|
||||
static const char *apndNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
|
||||
return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called when the extension is loaded.
|
||||
** Register the new VFS.
|
||||
*/
|
||||
int sqlite3_appendvfs_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_vfs *pOrig;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg;
|
||||
(void)db;
|
||||
pOrig = sqlite3_vfs_find(0);
|
||||
apnd_vfs.iVersion = pOrig->iVersion;
|
||||
apnd_vfs.pAppData = pOrig;
|
||||
apnd_vfs.szOsFile = pOrig->szOsFile + sizeof(ApndFile);
|
||||
rc = sqlite3_vfs_register(&apnd_vfs, 0);
|
||||
#ifdef APPENDVFS_TEST
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_auto_extension((void(*)(void))apndvfsRegister);
|
||||
}
|
||||
#endif
|
||||
if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
|
||||
return rc;
|
||||
}
|
499
third_party/sqlite3/completion.c
vendored
Normal file
499
third_party/sqlite3/completion.c
vendored
Normal file
|
@ -0,0 +1,499 @@
|
|||
/*
|
||||
** 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 "libc/assert.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/sqlite3/sqlite3ext.h"
|
||||
// clang-format off
|
||||
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
#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_info(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 */
|
||||
};
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
846
third_party/sqlite3/dbdata.c
vendored
Normal file
846
third_party/sqlite3/dbdata.c
vendored
Normal file
|
@ -0,0 +1,846 @@
|
|||
/*
|
||||
** 2019-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 file contains an implementation of two eponymous virtual tables,
|
||||
** "sqlite_dbdata" and "sqlite_dbptr". Both modules require that the
|
||||
** "sqlite_dbpage" eponymous virtual table be available.
|
||||
**
|
||||
** SQLITE_DBDATA:
|
||||
** sqlite_dbdata is used to extract data directly from a database b-tree
|
||||
** page and its associated overflow pages, bypassing the b-tree layer.
|
||||
** The table schema is equivalent to:
|
||||
**
|
||||
** CREATE TABLE sqlite_dbdata(
|
||||
** pgno INTEGER,
|
||||
** cell INTEGER,
|
||||
** field INTEGER,
|
||||
** value ANY,
|
||||
** schema TEXT HIDDEN
|
||||
** );
|
||||
**
|
||||
** IMPORTANT: THE VIRTUAL TABLE SCHEMA ABOVE IS SUBJECT TO CHANGE. IN THE
|
||||
** FUTURE NEW NON-HIDDEN COLUMNS MAY BE ADDED BETWEEN "value" AND
|
||||
** "schema".
|
||||
**
|
||||
** Each page of the database is inspected. If it cannot be interpreted as
|
||||
** a b-tree page, or if it is a b-tree page containing 0 entries, the
|
||||
** sqlite_dbdata table contains no rows for that page. Otherwise, the
|
||||
** table contains one row for each field in the record associated with
|
||||
** each cell on the page. For intkey b-trees, the key value is stored in
|
||||
** field -1.
|
||||
**
|
||||
** For example, for the database:
|
||||
**
|
||||
** CREATE TABLE t1(a, b); -- root page is page 2
|
||||
** INSERT INTO t1(rowid, a, b) VALUES(5, 'v', 'five');
|
||||
** INSERT INTO t1(rowid, a, b) VALUES(10, 'x', 'ten');
|
||||
**
|
||||
** the sqlite_dbdata table contains, as well as from entries related to
|
||||
** page 1, content equivalent to:
|
||||
**
|
||||
** INSERT INTO sqlite_dbdata(pgno, cell, field, value) VALUES
|
||||
** (2, 0, -1, 5 ),
|
||||
** (2, 0, 0, 'v' ),
|
||||
** (2, 0, 1, 'five'),
|
||||
** (2, 1, -1, 10 ),
|
||||
** (2, 1, 0, 'x' ),
|
||||
** (2, 1, 1, 'ten' );
|
||||
**
|
||||
** If database corruption is encountered, this module does not report an
|
||||
** error. Instead, it attempts to extract as much data as possible and
|
||||
** ignores the corruption.
|
||||
**
|
||||
** SQLITE_DBPTR:
|
||||
** The sqlite_dbptr table has the following schema:
|
||||
**
|
||||
** CREATE TABLE sqlite_dbptr(
|
||||
** pgno INTEGER,
|
||||
** child INTEGER,
|
||||
** schema TEXT HIDDEN
|
||||
** );
|
||||
**
|
||||
** It contains one entry for each b-tree pointer between a parent and
|
||||
** child page in the database.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/sqlite3/sqlite3ext.h"
|
||||
// clang-format off
|
||||
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
#define DBDATA_PADDING_BYTES 100
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef struct DbdataTable DbdataTable;
|
||||
typedef struct DbdataCursor DbdataCursor;
|
||||
|
||||
/* Cursor object */
|
||||
struct DbdataCursor {
|
||||
sqlite3_vtab_cursor base; /* Base class. Must be first */
|
||||
sqlite3_stmt *pStmt; /* For fetching database pages */
|
||||
|
||||
int iPgno; /* Current page number */
|
||||
u8 *aPage; /* Buffer containing page */
|
||||
int nPage; /* Size of aPage[] in bytes */
|
||||
int nCell; /* Number of cells on aPage[] */
|
||||
int iCell; /* Current cell number */
|
||||
int bOnePage; /* True to stop after one page */
|
||||
int szDb;
|
||||
sqlite3_int64 iRowid;
|
||||
|
||||
/* Only for the sqlite_dbdata table */
|
||||
u8 *pRec; /* Buffer containing current record */
|
||||
int nRec; /* Size of pRec[] in bytes */
|
||||
int nHdr; /* Size of header in bytes */
|
||||
int iField; /* Current field number */
|
||||
u8 *pHdrPtr;
|
||||
u8 *pPtr;
|
||||
|
||||
sqlite3_int64 iIntkey; /* Integer key value */
|
||||
};
|
||||
|
||||
/* Table object */
|
||||
struct DbdataTable {
|
||||
sqlite3_vtab base; /* Base class. Must be first */
|
||||
sqlite3 *db; /* The database connection */
|
||||
sqlite3_stmt *pStmt; /* For fetching database pages */
|
||||
int bPtr; /* True for sqlite3_dbptr table */
|
||||
};
|
||||
|
||||
/* Column and schema definitions for sqlite_dbdata */
|
||||
#define DBDATA_COLUMN_PGNO 0
|
||||
#define DBDATA_COLUMN_CELL 1
|
||||
#define DBDATA_COLUMN_FIELD 2
|
||||
#define DBDATA_COLUMN_VALUE 3
|
||||
#define DBDATA_COLUMN_SCHEMA 4
|
||||
#define DBDATA_SCHEMA \
|
||||
"CREATE TABLE x(" \
|
||||
" pgno INTEGER," \
|
||||
" cell INTEGER," \
|
||||
" field INTEGER," \
|
||||
" value ANY," \
|
||||
" schema TEXT HIDDEN" \
|
||||
")"
|
||||
|
||||
/* Column and schema definitions for sqlite_dbptr */
|
||||
#define DBPTR_COLUMN_PGNO 0
|
||||
#define DBPTR_COLUMN_CHILD 1
|
||||
#define DBPTR_COLUMN_SCHEMA 2
|
||||
#define DBPTR_SCHEMA \
|
||||
"CREATE TABLE x(" \
|
||||
" pgno INTEGER," \
|
||||
" child INTEGER," \
|
||||
" schema TEXT HIDDEN" \
|
||||
")"
|
||||
|
||||
/*
|
||||
** Connect to an sqlite_dbdata (pAux==0) or sqlite_dbptr (pAux!=0) virtual
|
||||
** table.
|
||||
*/
|
||||
static int dbdataConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
DbdataTable *pTab = 0;
|
||||
int rc = sqlite3_declare_vtab(db, pAux ? DBPTR_SCHEMA : DBDATA_SCHEMA);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab = (DbdataTable*)sqlite3_malloc64(sizeof(DbdataTable));
|
||||
if( pTab==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pTab, 0, sizeof(DbdataTable));
|
||||
pTab->db = db;
|
||||
pTab->bPtr = (pAux!=0);
|
||||
}
|
||||
}
|
||||
|
||||
*ppVtab = (sqlite3_vtab*)pTab;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Disconnect from or destroy a sqlite_dbdata or sqlite_dbptr virtual table.
|
||||
*/
|
||||
static int dbdataDisconnect(sqlite3_vtab *pVtab){
|
||||
DbdataTable *pTab = (DbdataTable*)pVtab;
|
||||
if( pTab ){
|
||||
sqlite3_finalize(pTab->pStmt);
|
||||
sqlite3_free(pVtab);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function interprets two types of constraints:
|
||||
**
|
||||
** schema=?
|
||||
** pgno=?
|
||||
**
|
||||
** If neither are present, idxNum is set to 0. If schema=? is present,
|
||||
** the 0x01 bit in idxNum is set. If pgno=? is present, the 0x02 bit
|
||||
** in idxNum is set.
|
||||
**
|
||||
** If both parameters are present, schema is in position 0 and pgno in
|
||||
** position 1.
|
||||
*/
|
||||
static int dbdataBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdx){
|
||||
DbdataTable *pTab = (DbdataTable*)tab;
|
||||
int i;
|
||||
int iSchema = -1;
|
||||
int iPgno = -1;
|
||||
int colSchema = (pTab->bPtr ? DBPTR_COLUMN_SCHEMA : DBDATA_COLUMN_SCHEMA);
|
||||
|
||||
for(i=0; i<pIdx->nConstraint; i++){
|
||||
struct sqlite3_index_constraint *p = &pIdx->aConstraint[i];
|
||||
if( p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
if( p->iColumn==colSchema ){
|
||||
if( p->usable==0 ) return SQLITE_CONSTRAINT;
|
||||
iSchema = i;
|
||||
}
|
||||
if( p->iColumn==DBDATA_COLUMN_PGNO && p->usable ){
|
||||
iPgno = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( iSchema>=0 ){
|
||||
pIdx->aConstraintUsage[iSchema].argvIndex = 1;
|
||||
pIdx->aConstraintUsage[iSchema].omit = 1;
|
||||
}
|
||||
if( iPgno>=0 ){
|
||||
pIdx->aConstraintUsage[iPgno].argvIndex = 1 + (iSchema>=0);
|
||||
pIdx->aConstraintUsage[iPgno].omit = 1;
|
||||
pIdx->estimatedCost = 100;
|
||||
pIdx->estimatedRows = 50;
|
||||
|
||||
if( pTab->bPtr==0 && pIdx->nOrderBy && pIdx->aOrderBy[0].desc==0 ){
|
||||
int iCol = pIdx->aOrderBy[0].iColumn;
|
||||
if( pIdx->nOrderBy==1 ){
|
||||
pIdx->orderByConsumed = (iCol==0 || iCol==1);
|
||||
}else if( pIdx->nOrderBy==2 && pIdx->aOrderBy[1].desc==0 && iCol==0 ){
|
||||
pIdx->orderByConsumed = (pIdx->aOrderBy[1].iColumn==1);
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
pIdx->estimatedCost = 100000000;
|
||||
pIdx->estimatedRows = 1000000000;
|
||||
}
|
||||
pIdx->idxNum = (iSchema>=0 ? 0x01 : 0x00) | (iPgno>=0 ? 0x02 : 0x00);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new sqlite_dbdata or sqlite_dbptr cursor.
|
||||
*/
|
||||
static int dbdataOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
DbdataCursor *pCsr;
|
||||
|
||||
pCsr = (DbdataCursor*)sqlite3_malloc64(sizeof(DbdataCursor));
|
||||
if( pCsr==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pCsr, 0, sizeof(DbdataCursor));
|
||||
pCsr->base.pVtab = pVTab;
|
||||
}
|
||||
|
||||
*ppCursor = (sqlite3_vtab_cursor *)pCsr;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Restore a cursor object to the state it was in when first allocated
|
||||
** by dbdataOpen().
|
||||
*/
|
||||
static void dbdataResetCursor(DbdataCursor *pCsr){
|
||||
DbdataTable *pTab = (DbdataTable*)(pCsr->base.pVtab);
|
||||
if( pTab->pStmt==0 ){
|
||||
pTab->pStmt = pCsr->pStmt;
|
||||
}else{
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
}
|
||||
pCsr->pStmt = 0;
|
||||
pCsr->iPgno = 1;
|
||||
pCsr->iCell = 0;
|
||||
pCsr->iField = 0;
|
||||
pCsr->bOnePage = 0;
|
||||
sqlite3_free(pCsr->aPage);
|
||||
sqlite3_free(pCsr->pRec);
|
||||
pCsr->pRec = 0;
|
||||
pCsr->aPage = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close an sqlite_dbdata or sqlite_dbptr cursor.
|
||||
*/
|
||||
static int dbdataClose(sqlite3_vtab_cursor *pCursor){
|
||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||
dbdataResetCursor(pCsr);
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Utility methods to decode 16 and 32-bit big-endian unsigned integers.
|
||||
*/
|
||||
static unsigned int get_uint16(unsigned char *a){
|
||||
return (a[0]<<8)|a[1];
|
||||
}
|
||||
static unsigned int get_uint32(unsigned char *a){
|
||||
return ((unsigned int)a[0]<<24)
|
||||
| ((unsigned int)a[1]<<16)
|
||||
| ((unsigned int)a[2]<<8)
|
||||
| ((unsigned int)a[3]);
|
||||
}
|
||||
|
||||
/*
|
||||
** Load page pgno from the database via the sqlite_dbpage virtual table.
|
||||
** If successful, set (*ppPage) to point to a buffer containing the page
|
||||
** data, (*pnPage) to the size of that buffer in bytes and return
|
||||
** SQLITE_OK. In this case it is the responsibility of the caller to
|
||||
** eventually free the buffer using sqlite3_free().
|
||||
**
|
||||
** Or, if an error occurs, set both (*ppPage) and (*pnPage) to 0 and
|
||||
** return an SQLite error code.
|
||||
*/
|
||||
static int dbdataLoadPage(
|
||||
DbdataCursor *pCsr, /* Cursor object */
|
||||
unsigned int pgno, /* Page number of page to load */
|
||||
u8 **ppPage, /* OUT: pointer to page buffer */
|
||||
int *pnPage /* OUT: Size of (*ppPage) in bytes */
|
||||
){
|
||||
int rc2;
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt *pStmt = pCsr->pStmt;
|
||||
|
||||
*ppPage = 0;
|
||||
*pnPage = 0;
|
||||
sqlite3_bind_int64(pStmt, 2, pgno);
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
int nCopy = sqlite3_column_bytes(pStmt, 0);
|
||||
if( nCopy>0 ){
|
||||
u8 *pPage;
|
||||
pPage = (u8*)sqlite3_malloc64(nCopy + DBDATA_PADDING_BYTES);
|
||||
if( pPage==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
const u8 *pCopy = sqlite3_column_blob(pStmt, 0);
|
||||
memcpy(pPage, pCopy, nCopy);
|
||||
memset(&pPage[nCopy], 0, DBDATA_PADDING_BYTES);
|
||||
}
|
||||
*ppPage = pPage;
|
||||
*pnPage = nCopy;
|
||||
}
|
||||
}
|
||||
rc2 = sqlite3_reset(pStmt);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a varint. Put the value in *pVal and return the number of bytes.
|
||||
*/
|
||||
static int dbdataGetVarint(const u8 *z, sqlite3_int64 *pVal){
|
||||
sqlite3_int64 v = 0;
|
||||
int i;
|
||||
for(i=0; i<8; i++){
|
||||
v = (v<<7) + (z[i]&0x7f);
|
||||
if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; }
|
||||
}
|
||||
v = (v<<8) + (z[i]&0xff);
|
||||
*pVal = v;
|
||||
return 9;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of bytes of space used by an SQLite value of type
|
||||
** eType.
|
||||
*/
|
||||
static int dbdataValueBytes(int eType){
|
||||
switch( eType ){
|
||||
case 0: case 8: case 9:
|
||||
case 10: case 11:
|
||||
return 0;
|
||||
case 1:
|
||||
return 1;
|
||||
case 2:
|
||||
return 2;
|
||||
case 3:
|
||||
return 3;
|
||||
case 4:
|
||||
return 4;
|
||||
case 5:
|
||||
return 6;
|
||||
case 6:
|
||||
case 7:
|
||||
return 8;
|
||||
default:
|
||||
if( eType>0 ){
|
||||
return ((eType-12) / 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Load a value of type eType from buffer pData and use it to set the
|
||||
** result of context object pCtx.
|
||||
*/
|
||||
static void dbdataValue(
|
||||
sqlite3_context *pCtx,
|
||||
int eType,
|
||||
u8 *pData,
|
||||
int nData
|
||||
){
|
||||
if( eType>=0 && dbdataValueBytes(eType)<=nData ){
|
||||
switch( eType ){
|
||||
case 0:
|
||||
case 10:
|
||||
case 11:
|
||||
sqlite3_result_null(pCtx);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
sqlite3_result_int(pCtx, 0);
|
||||
break;
|
||||
case 9:
|
||||
sqlite3_result_int(pCtx, 1);
|
||||
break;
|
||||
|
||||
case 1: case 2: case 3: case 4: case 5: case 6: case 7: {
|
||||
sqlite3_uint64 v = (signed char)pData[0];
|
||||
pData++;
|
||||
switch( eType ){
|
||||
case 7:
|
||||
case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
|
||||
case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
|
||||
case 4: v = (v<<8) + pData[0]; pData++;
|
||||
case 3: v = (v<<8) + pData[0]; pData++;
|
||||
case 2: v = (v<<8) + pData[0]; pData++;
|
||||
}
|
||||
|
||||
if( eType==7 ){
|
||||
double r;
|
||||
memcpy(&r, &v, sizeof(r));
|
||||
sqlite3_result_double(pCtx, r);
|
||||
}else{
|
||||
sqlite3_result_int64(pCtx, (sqlite3_int64)v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
int n = ((eType-12) / 2);
|
||||
if( eType % 2 ){
|
||||
sqlite3_result_text(pCtx, (const char*)pData, n, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Move an sqlite_dbdata or sqlite_dbptr cursor to the next entry.
|
||||
*/
|
||||
static int dbdataNext(sqlite3_vtab_cursor *pCursor){
|
||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||
DbdataTable *pTab = (DbdataTable*)pCursor->pVtab;
|
||||
|
||||
pCsr->iRowid++;
|
||||
while( 1 ){
|
||||
int rc;
|
||||
int iOff = (pCsr->iPgno==1 ? 100 : 0);
|
||||
int bNextPage = 0;
|
||||
|
||||
if( pCsr->aPage==0 ){
|
||||
while( 1 ){
|
||||
if( pCsr->bOnePage==0 && pCsr->iPgno>pCsr->szDb ) return SQLITE_OK;
|
||||
rc = dbdataLoadPage(pCsr, pCsr->iPgno, &pCsr->aPage, &pCsr->nPage);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
if( pCsr->aPage ) break;
|
||||
pCsr->iPgno++;
|
||||
}
|
||||
pCsr->iCell = pTab->bPtr ? -2 : 0;
|
||||
pCsr->nCell = get_uint16(&pCsr->aPage[iOff+3]);
|
||||
}
|
||||
|
||||
if( pTab->bPtr ){
|
||||
if( pCsr->aPage[iOff]!=0x02 && pCsr->aPage[iOff]!=0x05 ){
|
||||
pCsr->iCell = pCsr->nCell;
|
||||
}
|
||||
pCsr->iCell++;
|
||||
if( pCsr->iCell>=pCsr->nCell ){
|
||||
sqlite3_free(pCsr->aPage);
|
||||
pCsr->aPage = 0;
|
||||
if( pCsr->bOnePage ) return SQLITE_OK;
|
||||
pCsr->iPgno++;
|
||||
}else{
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}else{
|
||||
/* If there is no record loaded, load it now. */
|
||||
if( pCsr->pRec==0 ){
|
||||
int bHasRowid = 0;
|
||||
int nPointer = 0;
|
||||
sqlite3_int64 nPayload = 0;
|
||||
sqlite3_int64 nHdr = 0;
|
||||
int iHdr;
|
||||
int U, X;
|
||||
int nLocal;
|
||||
|
||||
switch( pCsr->aPage[iOff] ){
|
||||
case 0x02:
|
||||
nPointer = 4;
|
||||
break;
|
||||
case 0x0a:
|
||||
break;
|
||||
case 0x0d:
|
||||
bHasRowid = 1;
|
||||
break;
|
||||
default:
|
||||
/* This is not a b-tree page with records on it. Continue. */
|
||||
pCsr->iCell = pCsr->nCell;
|
||||
break;
|
||||
}
|
||||
|
||||
if( pCsr->iCell>=pCsr->nCell ){
|
||||
bNextPage = 1;
|
||||
}else{
|
||||
|
||||
iOff += 8 + nPointer + pCsr->iCell*2;
|
||||
if( iOff>pCsr->nPage ){
|
||||
bNextPage = 1;
|
||||
}else{
|
||||
iOff = get_uint16(&pCsr->aPage[iOff]);
|
||||
}
|
||||
|
||||
/* For an interior node cell, skip past the child-page number */
|
||||
iOff += nPointer;
|
||||
|
||||
/* Load the "byte of payload including overflow" field */
|
||||
if( bNextPage || iOff>pCsr->nPage ){
|
||||
bNextPage = 1;
|
||||
}else{
|
||||
iOff += dbdataGetVarint(&pCsr->aPage[iOff], &nPayload);
|
||||
}
|
||||
|
||||
/* If this is a leaf intkey cell, load the rowid */
|
||||
if( bHasRowid && !bNextPage && iOff<pCsr->nPage ){
|
||||
iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey);
|
||||
}
|
||||
|
||||
/* Figure out how much data to read from the local page */
|
||||
U = pCsr->nPage;
|
||||
if( bHasRowid ){
|
||||
X = U-35;
|
||||
}else{
|
||||
X = ((U-12)*64/255)-23;
|
||||
}
|
||||
if( nPayload<=X ){
|
||||
nLocal = nPayload;
|
||||
}else{
|
||||
int M, K;
|
||||
M = ((U-12)*32/255)-23;
|
||||
K = M+((nPayload-M)%(U-4));
|
||||
if( K<=X ){
|
||||
nLocal = K;
|
||||
}else{
|
||||
nLocal = M;
|
||||
}
|
||||
}
|
||||
|
||||
if( bNextPage || nLocal+iOff>pCsr->nPage ){
|
||||
bNextPage = 1;
|
||||
}else{
|
||||
|
||||
/* Allocate space for payload. And a bit more to catch small buffer
|
||||
** overruns caused by attempting to read a varint or similar from
|
||||
** near the end of a corrupt record. */
|
||||
pCsr->pRec = (u8*)sqlite3_malloc64(nPayload+DBDATA_PADDING_BYTES);
|
||||
if( pCsr->pRec==0 ) return SQLITE_NOMEM;
|
||||
memset(pCsr->pRec, 0, nPayload+DBDATA_PADDING_BYTES);
|
||||
pCsr->nRec = nPayload;
|
||||
|
||||
/* Load the nLocal bytes of payload */
|
||||
memcpy(pCsr->pRec, &pCsr->aPage[iOff], nLocal);
|
||||
iOff += nLocal;
|
||||
|
||||
/* Load content from overflow pages */
|
||||
if( nPayload>nLocal ){
|
||||
sqlite3_int64 nRem = nPayload - nLocal;
|
||||
unsigned int pgnoOvfl = get_uint32(&pCsr->aPage[iOff]);
|
||||
while( nRem>0 ){
|
||||
u8 *aOvfl = 0;
|
||||
int nOvfl = 0;
|
||||
int nCopy;
|
||||
rc = dbdataLoadPage(pCsr, pgnoOvfl, &aOvfl, &nOvfl);
|
||||
assert( rc!=SQLITE_OK || aOvfl==0 || nOvfl==pCsr->nPage );
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
if( aOvfl==0 ) break;
|
||||
|
||||
nCopy = U-4;
|
||||
if( nCopy>nRem ) nCopy = nRem;
|
||||
memcpy(&pCsr->pRec[nPayload-nRem], &aOvfl[4], nCopy);
|
||||
nRem -= nCopy;
|
||||
|
||||
pgnoOvfl = get_uint32(aOvfl);
|
||||
sqlite3_free(aOvfl);
|
||||
}
|
||||
}
|
||||
|
||||
iHdr = dbdataGetVarint(pCsr->pRec, &nHdr);
|
||||
pCsr->nHdr = nHdr;
|
||||
pCsr->pHdrPtr = &pCsr->pRec[iHdr];
|
||||
pCsr->pPtr = &pCsr->pRec[pCsr->nHdr];
|
||||
pCsr->iField = (bHasRowid ? -1 : 0);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
pCsr->iField++;
|
||||
if( pCsr->iField>0 ){
|
||||
sqlite3_int64 iType;
|
||||
if( pCsr->pHdrPtr>&pCsr->pRec[pCsr->nRec] ){
|
||||
bNextPage = 1;
|
||||
}else{
|
||||
pCsr->pHdrPtr += dbdataGetVarint(pCsr->pHdrPtr, &iType);
|
||||
pCsr->pPtr += dbdataValueBytes(iType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( bNextPage ){
|
||||
sqlite3_free(pCsr->aPage);
|
||||
sqlite3_free(pCsr->pRec);
|
||||
pCsr->aPage = 0;
|
||||
pCsr->pRec = 0;
|
||||
if( pCsr->bOnePage ) return SQLITE_OK;
|
||||
pCsr->iPgno++;
|
||||
}else{
|
||||
if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->pRec[pCsr->nHdr] ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Advance to the next cell. The next iteration of the loop will load
|
||||
** the record and so on. */
|
||||
sqlite3_free(pCsr->pRec);
|
||||
pCsr->pRec = 0;
|
||||
pCsr->iCell++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert( !"can't get here" );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the cursor is at EOF.
|
||||
*/
|
||||
static int dbdataEof(sqlite3_vtab_cursor *pCursor){
|
||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||
return pCsr->aPage==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Determine the size in pages of database zSchema (where zSchema is
|
||||
** "main", "temp" or the name of an attached database) and set
|
||||
** pCsr->szDb accordingly. If successful, return SQLITE_OK. Otherwise,
|
||||
** an SQLite error code.
|
||||
*/
|
||||
static int dbdataDbsize(DbdataCursor *pCsr, const char *zSchema){
|
||||
DbdataTable *pTab = (DbdataTable*)pCsr->base.pVtab;
|
||||
char *zSql = 0;
|
||||
int rc, rc2;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
|
||||
zSql = sqlite3_mprintf("PRAGMA %Q.page_count", zSchema);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
pCsr->szDb = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
rc2 = sqlite3_finalize(pStmt);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** xFilter method for sqlite_dbdata and sqlite_dbptr.
|
||||
*/
|
||||
static int dbdataFilter(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||
DbdataTable *pTab = (DbdataTable*)pCursor->pVtab;
|
||||
int rc = SQLITE_OK;
|
||||
const char *zSchema = "main";
|
||||
|
||||
dbdataResetCursor(pCsr);
|
||||
assert( pCsr->iPgno==1 );
|
||||
if( idxNum & 0x01 ){
|
||||
zSchema = (const char*)sqlite3_value_text(argv[0]);
|
||||
}
|
||||
if( idxNum & 0x02 ){
|
||||
pCsr->iPgno = sqlite3_value_int(argv[(idxNum & 0x01)]);
|
||||
pCsr->bOnePage = 1;
|
||||
}else{
|
||||
pCsr->nPage = dbdataDbsize(pCsr, zSchema);
|
||||
rc = dbdataDbsize(pCsr, zSchema);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pTab->pStmt ){
|
||||
pCsr->pStmt = pTab->pStmt;
|
||||
pTab->pStmt = 0;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(pTab->db,
|
||||
"SELECT data FROM sqlite_dbpage(?) WHERE pgno=?", -1,
|
||||
&pCsr->pStmt, 0
|
||||
);
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_bind_text(pCsr->pStmt, 1, zSchema, -1, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = dbdataNext(pCursor);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a column for the sqlite_dbdata or sqlite_dbptr table.
|
||||
*/
|
||||
static int dbdataColumn(
|
||||
sqlite3_vtab_cursor *pCursor,
|
||||
sqlite3_context *ctx,
|
||||
int i
|
||||
){
|
||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||
DbdataTable *pTab = (DbdataTable*)pCursor->pVtab;
|
||||
if( pTab->bPtr ){
|
||||
switch( i ){
|
||||
case DBPTR_COLUMN_PGNO:
|
||||
sqlite3_result_int64(ctx, pCsr->iPgno);
|
||||
break;
|
||||
case DBPTR_COLUMN_CHILD: {
|
||||
int iOff = pCsr->iPgno==1 ? 100 : 0;
|
||||
if( pCsr->iCell<0 ){
|
||||
iOff += 8;
|
||||
}else{
|
||||
iOff += 12 + pCsr->iCell*2;
|
||||
if( iOff>pCsr->nPage ) return SQLITE_OK;
|
||||
iOff = get_uint16(&pCsr->aPage[iOff]);
|
||||
}
|
||||
if( iOff<=pCsr->nPage ){
|
||||
sqlite3_result_int64(ctx, get_uint32(&pCsr->aPage[iOff]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
switch( i ){
|
||||
case DBDATA_COLUMN_PGNO:
|
||||
sqlite3_result_int64(ctx, pCsr->iPgno);
|
||||
break;
|
||||
case DBDATA_COLUMN_CELL:
|
||||
sqlite3_result_int(ctx, pCsr->iCell);
|
||||
break;
|
||||
case DBDATA_COLUMN_FIELD:
|
||||
sqlite3_result_int(ctx, pCsr->iField);
|
||||
break;
|
||||
case DBDATA_COLUMN_VALUE: {
|
||||
if( pCsr->iField<0 ){
|
||||
sqlite3_result_int64(ctx, pCsr->iIntkey);
|
||||
}else{
|
||||
sqlite3_int64 iType;
|
||||
dbdataGetVarint(pCsr->pHdrPtr, &iType);
|
||||
dbdataValue(
|
||||
ctx, iType, pCsr->pPtr, &pCsr->pRec[pCsr->nRec] - pCsr->pPtr
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the rowid for an sqlite_dbdata or sqlite_dptr table.
|
||||
*/
|
||||
static int dbdataRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
|
||||
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
||||
*pRowid = pCsr->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Invoke this routine to register the "sqlite_dbdata" virtual table module
|
||||
*/
|
||||
static int sqlite3DbdataRegister(sqlite3 *db){
|
||||
static sqlite3_module dbdata_module = {
|
||||
0, /* iVersion */
|
||||
0, /* xCreate */
|
||||
dbdataConnect, /* xConnect */
|
||||
dbdataBestIndex, /* xBestIndex */
|
||||
dbdataDisconnect, /* xDisconnect */
|
||||
0, /* xDestroy */
|
||||
dbdataOpen, /* xOpen - open a cursor */
|
||||
dbdataClose, /* xClose - close a cursor */
|
||||
dbdataFilter, /* xFilter - configure scan constraints */
|
||||
dbdataNext, /* xNext - advance a cursor */
|
||||
dbdataEof, /* xEof - check for end of scan */
|
||||
dbdataColumn, /* xColumn - read data */
|
||||
dbdataRowid, /* 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 */
|
||||
};
|
||||
|
||||
int rc = sqlite3_create_module(db, "sqlite_dbdata", &dbdata_module, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_module(db, "sqlite_dbptr", &dbdata_module, (void*)1);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3_dbdata_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
return sqlite3DbdataRegister(db);
|
||||
}
|
620
third_party/sqlite3/decimal.c
vendored
Normal file
620
third_party/sqlite3/decimal.c
vendored
Normal file
|
@ -0,0 +1,620 @@
|
|||
/*
|
||||
** 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 "libc/assert.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/sqlite3/sqlite3ext.h"
|
||||
// clang-format off
|
||||
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
/* 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. Initialize it to the number given
|
||||
** by the input string.
|
||||
*/
|
||||
static Decimal *decimal_new(
|
||||
sqlite3_context *pCtx,
|
||||
sqlite3_value *pIn,
|
||||
int nAlt,
|
||||
const unsigned char *zAlt
|
||||
){
|
||||
Decimal *p;
|
||||
int n, i;
|
||||
const unsigned char *zIn;
|
||||
int iExp = 0;
|
||||
p = sqlite3_malloc( sizeof(*p) );
|
||||
if( p==0 ) goto new_no_mem;
|
||||
p->sign = 0;
|
||||
p->oom = 0;
|
||||
p->isInit = 1;
|
||||
p->isNull = 0;
|
||||
p->nDigit = 0;
|
||||
p->nFrac = 0;
|
||||
if( zAlt ){
|
||||
n = nAlt,
|
||||
zIn = zAlt;
|
||||
}else{
|
||||
if( sqlite3_value_type(pIn)==SQLITE_NULL ){
|
||||
p->a = 0;
|
||||
p->isNull = 1;
|
||||
return p;
|
||||
}
|
||||
n = sqlite3_value_bytes(pIn);
|
||||
zIn = sqlite3_value_text(pIn);
|
||||
}
|
||||
p->a = sqlite3_malloc64( n+1 );
|
||||
if( p->a==0 ) goto new_no_mem;
|
||||
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_no_mem;
|
||||
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_no_mem;
|
||||
memmove(p->a+iExp, p->a, p->nDigit);
|
||||
memset(p->a, 0, iExp);
|
||||
p->nDigit += iExp;
|
||||
p->nFrac += iExp;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
|
||||
new_no_mem:
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL Function: decimal(X)
|
||||
**
|
||||
** Convert input X into decimal and then back into text
|
||||
*/
|
||||
static void decimalFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *p = decimal_new(context, argv[0], 0, 0);
|
||||
decimal_result(context, p);
|
||||
decimal_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** 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;
|
||||
|
||||
pA = decimal_new(context, argv[0], 0, 0);
|
||||
if( pA==0 || pA->isNull ) goto cmp_done;
|
||||
pB = decimal_new(context, argv[1], 0, 0);
|
||||
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.
|
||||
**
|
||||
** 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 = decimal_new(0, 0, nKey1, zA);
|
||||
Decimal *pB = decimal_new(0, 0, nKey2, zB);
|
||||
int rc;
|
||||
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], 0, 0);
|
||||
Decimal *pB = decimal_new(context, argv[1], 0, 0);
|
||||
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], 0, 0);
|
||||
Decimal *pB = decimal_new(context, argv[1], 0, 0);
|
||||
if( pB==0 ) return;
|
||||
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;
|
||||
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], 0, 0);
|
||||
decimal_add(p, pArg);
|
||||
decimal_free(pArg);
|
||||
}
|
||||
|
||||
static void decimalSumInverse(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *p;
|
||||
Decimal *pArg;
|
||||
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], 0, 0);
|
||||
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.
|
||||
**
|
||||
** 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 decimalMulFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
Decimal *pA = decimal_new(context, argv[0], 0, 0);
|
||||
Decimal *pB = decimal_new(context, argv[1], 0, 0);
|
||||
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 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
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--;
|
||||
}
|
||||
decimal_result(context, pA);
|
||||
|
||||
mul_end:
|
||||
sqlite3_free(acc);
|
||||
decimal_free(pA);
|
||||
decimal_free(pB);
|
||||
}
|
||||
|
||||
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;
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} aFunc[] = {
|
||||
{ "decimal", 1, decimalFunc },
|
||||
{ "decimal_cmp", 2, decimalCmpFunc },
|
||||
{ "decimal_add", 2, decimalAddFunc },
|
||||
{ "decimal_sub", 2, decimalSubFunc },
|
||||
{ "decimal_mul", 2, decimalMulFunc },
|
||||
};
|
||||
unsigned int i;
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
|
||||
for(i=0; i<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,
|
||||
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;
|
||||
}
|
25
third_party/sqlite3/extensions.h
vendored
Normal file
25
third_party/sqlite3/extensions.h
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef COSMOPOLITAN_THIRD_PARTY_SQLITE3_EXTENSIONS_H_
|
||||
#define COSMOPOLITAN_THIRD_PARTY_SQLITE3_EXTENSIONS_H_
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "third_party/sqlite3/sqlite3.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int sqlite3MemTraceActivate(FILE *);
|
||||
int sqlite3MemTraceDeactivate(void);
|
||||
|
||||
int sqlite3_appendvfs_init(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
int sqlite3_completion_init(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
int sqlite3_dbdata_init(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
int sqlite3_decimal_init(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
int sqlite3_fileio_init(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
int sqlite3_ieee_init(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
int sqlite3_series_init(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
int sqlite3_shathree_init(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
int sqlite3_sqlar_init(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
int sqlite3_uint_init(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
int sqlite3_zipfile_init(sqlite3 *, char **, const sqlite3_api_routines *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_SQLITE3_EXTENSIONS_H_ */
|
873
third_party/sqlite3/fileio.c
vendored
Normal file
873
third_party/sqlite3/fileio.c
vendored
Normal file
|
@ -0,0 +1,873 @@
|
|||
/*
|
||||
** 2014-06-13
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
******************************************************************************
|
||||
**
|
||||
** This SQLite extension implements SQL functions readfile() and
|
||||
** writefile(), and eponymous virtual type "fsdir".
|
||||
**
|
||||
** WRITEFILE(FILE, DATA [, MODE [, MTIME]]):
|
||||
**
|
||||
** If neither of the optional arguments is present, then this UDF
|
||||
** function writes blob DATA to file FILE. If successful, the number
|
||||
** of bytes written is returned. If an error occurs, NULL is returned.
|
||||
**
|
||||
** If the first option argument - MODE - is present, then it must
|
||||
** be passed an integer value that corresponds to a POSIX mode
|
||||
** value (file type + permissions, as returned in the stat.st_mode
|
||||
** field by the stat() system call). Three types of files may
|
||||
** be written/created:
|
||||
**
|
||||
** regular files: (mode & 0170000)==0100000
|
||||
** symbolic links: (mode & 0170000)==0120000
|
||||
** directories: (mode & 0170000)==0040000
|
||||
**
|
||||
** For a directory, the DATA is ignored. For a symbolic link, it is
|
||||
** interpreted as text and used as the target of the link. For a
|
||||
** regular file, it is interpreted as a blob and written into the
|
||||
** named file. Regardless of the type of file, its permissions are
|
||||
** set to (mode & 0777) before returning.
|
||||
**
|
||||
** If the optional MTIME argument is present, then it is interpreted
|
||||
** as an integer - the number of seconds since the unix epoch. The
|
||||
** modification-time of the target file is set to this value before
|
||||
** returning.
|
||||
**
|
||||
** If three or more arguments are passed to this function and an
|
||||
** error is encountered, an exception is raised.
|
||||
**
|
||||
** READFILE(FILE):
|
||||
**
|
||||
** Read and return the contents of file FILE (type blob) from disk.
|
||||
**
|
||||
** FSDIR:
|
||||
**
|
||||
** Used as follows:
|
||||
**
|
||||
** SELECT * FROM fsdir($path [, $dir]);
|
||||
**
|
||||
** Parameter $path is an absolute or relative pathname. If the file that it
|
||||
** refers to does not exist, it is an error. If the path refers to a regular
|
||||
** file or symbolic link, it returns a single row. Or, if the path refers
|
||||
** to a directory, it returns one row for the directory, and one row for each
|
||||
** file within the hierarchy rooted at $path.
|
||||
**
|
||||
** Each row has the following columns:
|
||||
**
|
||||
** name: Path to file or directory (text value).
|
||||
** mode: Value of stat.st_mode for directory entry (an integer).
|
||||
** mtime: Value of stat.st_mtime for directory entry (an integer).
|
||||
** data: For a regular file, a blob containing the file data. For a
|
||||
** symlink, a text value containing the text of the link. For a
|
||||
** directory, NULL.
|
||||
**
|
||||
** If a non-NULL value is specified for the optional $dir parameter and
|
||||
** $path is a relative path, then $path is interpreted relative to $dir.
|
||||
** And the paths returned in the "name" column of the table are also
|
||||
** relative to directory $dir.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/dirent.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/struct/stat.macros.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/isystem/unistd.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "third_party/sqlite3/sqlite3ext.h"
|
||||
// clang-format off
|
||||
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
/*
|
||||
** Structure of the fsdir() table-valued function
|
||||
*/
|
||||
/* 0 1 2 3 4 5 */
|
||||
#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
|
||||
#define FSDIR_COLUMN_NAME 0 /* Name of the file */
|
||||
#define FSDIR_COLUMN_MODE 1 /* Access mode */
|
||||
#define FSDIR_COLUMN_MTIME 2 /* Last modification time */
|
||||
#define FSDIR_COLUMN_DATA 3 /* File content */
|
||||
#define FSDIR_COLUMN_PATH 4 /* Path to top of search */
|
||||
#define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */
|
||||
|
||||
|
||||
/*
|
||||
** Set the result stored by context ctx to a blob containing the
|
||||
** contents of file zName. Or, leave the result unchanged (NULL)
|
||||
** if the file does not exist or is unreadable.
|
||||
**
|
||||
** If the file exceeds the SQLite blob size limit, through an
|
||||
** SQLITE_TOOBIG error.
|
||||
**
|
||||
** Throw an SQLITE_IOERR if there are difficulties pulling the file
|
||||
** off of disk.
|
||||
*/
|
||||
static void readFileContents(sqlite3_context *ctx, const char *zName){
|
||||
FILE *in;
|
||||
sqlite3_int64 nIn;
|
||||
void *pBuf;
|
||||
sqlite3 *db;
|
||||
int mxBlob;
|
||||
|
||||
in = fopen(zName, "rb");
|
||||
if( in==0 ){
|
||||
/* File does not exist or is unreadable. Leave the result set to NULL. */
|
||||
return;
|
||||
}
|
||||
fseek(in, 0, SEEK_END);
|
||||
nIn = ftell(in);
|
||||
rewind(in);
|
||||
db = sqlite3_context_db_handle(ctx);
|
||||
mxBlob = sqlite3_limit(db, SQLITE_LIMIT_LENGTH, -1);
|
||||
if( nIn>mxBlob ){
|
||||
sqlite3_result_error_code(ctx, SQLITE_TOOBIG);
|
||||
fclose(in);
|
||||
return;
|
||||
}
|
||||
pBuf = sqlite3_malloc64( nIn ? nIn : 1 );
|
||||
if( pBuf==0 ){
|
||||
sqlite3_result_error_nomem(ctx);
|
||||
fclose(in);
|
||||
return;
|
||||
}
|
||||
if( nIn==(sqlite3_int64)fread(pBuf, 1, (size_t)nIn, in) ){
|
||||
sqlite3_result_blob64(ctx, pBuf, nIn, sqlite3_free);
|
||||
}else{
|
||||
sqlite3_result_error_code(ctx, SQLITE_IOERR);
|
||||
sqlite3_free(pBuf);
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the "readfile(X)" SQL function. The entire content
|
||||
** of the file named X is read and returned as a BLOB. NULL is returned
|
||||
** if the file does not exist or is unreadable.
|
||||
*/
|
||||
static void readfileFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zName;
|
||||
(void)(argc); /* Unused parameter */
|
||||
zName = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zName==0 ) return;
|
||||
readFileContents(context, zName);
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the error message contained in context ctx to the results of
|
||||
** vprintf(zFmt, ...).
|
||||
*/
|
||||
static void ctxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
|
||||
char *zMsg = 0;
|
||||
va_list ap;
|
||||
va_start(ap, zFmt);
|
||||
zMsg = sqlite3_vmprintf(zFmt, ap);
|
||||
sqlite3_result_error(ctx, zMsg, -1);
|
||||
sqlite3_free(zMsg);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used in place of stat(). On Windows, special handling
|
||||
** is required in order for the included time to be returned as UTC. On all
|
||||
** other systems, this function simply calls stat().
|
||||
*/
|
||||
static int fileStat(
|
||||
const char *zPath,
|
||||
struct stat *pStatBuf
|
||||
){
|
||||
return stat(zPath, pStatBuf);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used in place of lstat(). On Windows, special handling
|
||||
** is required in order for the included time to be returned as UTC. On all
|
||||
** other systems, this function simply calls lstat().
|
||||
*/
|
||||
static int fileLinkStat(
|
||||
const char *zPath,
|
||||
struct stat *pStatBuf
|
||||
){
|
||||
#if defined(_WIN32)
|
||||
int rc = lstat(zPath, pStatBuf);
|
||||
if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
|
||||
return rc;
|
||||
#else
|
||||
return lstat(zPath, pStatBuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument zFile is the name of a file that will be created and/or written
|
||||
** by SQL function writefile(). This function ensures that the directory
|
||||
** zFile will be written to exists, creating it if required. The permissions
|
||||
** for any path components created by this function are set in accordance
|
||||
** with the current umask.
|
||||
**
|
||||
** If an OOM condition is encountered, SQLITE_NOMEM is returned. Otherwise,
|
||||
** SQLITE_OK is returned if the directory is successfully created, or
|
||||
** SQLITE_ERROR otherwise.
|
||||
*/
|
||||
static int makeDirectory(
|
||||
const char *zFile
|
||||
){
|
||||
char *zCopy = sqlite3_mprintf("%s", zFile);
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( zCopy==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
int nCopy = (int)strlen(zCopy);
|
||||
int i = 1;
|
||||
|
||||
while( rc==SQLITE_OK ){
|
||||
struct stat sStat;
|
||||
int rc2;
|
||||
|
||||
for(; zCopy[i]!='/' && i<nCopy; i++);
|
||||
if( i==nCopy ) break;
|
||||
zCopy[i] = '\0';
|
||||
|
||||
rc2 = fileStat(zCopy, &sStat);
|
||||
if( rc2!=0 ){
|
||||
if( mkdir(zCopy, 0777) ) rc = SQLITE_ERROR;
|
||||
}else{
|
||||
if( !S_ISDIR(sStat.st_mode) ) rc = SQLITE_ERROR;
|
||||
}
|
||||
zCopy[i] = '/';
|
||||
i++;
|
||||
}
|
||||
|
||||
sqlite3_free(zCopy);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function does the work for the writefile() UDF. Refer to
|
||||
** header comments at the top of this file for details.
|
||||
*/
|
||||
static int writeFile(
|
||||
sqlite3_context *pCtx, /* Context to return bytes written in */
|
||||
const char *zFile, /* File to write */
|
||||
sqlite3_value *pData, /* Data to write */
|
||||
mode_t mode, /* MODE parameter passed to writefile() */
|
||||
sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */
|
||||
){
|
||||
if( S_ISLNK(mode) ){
|
||||
const char *zTo = (const char*)sqlite3_value_text(pData);
|
||||
if( symlink(zTo, zFile)<0 ) return 1;
|
||||
}else
|
||||
{
|
||||
if( S_ISDIR(mode) ){
|
||||
if( mkdir(zFile, mode) ){
|
||||
/* The mkdir() call to create the directory failed. This might not
|
||||
** be an error though - if there is already a directory at the same
|
||||
** path and either the permissions already match or can be changed
|
||||
** to do so using chmod(), it is not an error. */
|
||||
struct stat sStat;
|
||||
if( errno!=EEXIST
|
||||
|| 0!=fileStat(zFile, &sStat)
|
||||
|| !S_ISDIR(sStat.st_mode)
|
||||
|| ((sStat.st_mode&0777)!=(mode&0777) && 0!=chmod(zFile, mode&0777))
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
sqlite3_int64 nWrite = 0;
|
||||
const char *z;
|
||||
int rc = 0;
|
||||
FILE *out = fopen(zFile, "wb");
|
||||
if( out==0 ) return 1;
|
||||
z = (const char*)sqlite3_value_blob(pData);
|
||||
if( z ){
|
||||
sqlite3_int64 n = fwrite(z, 1, sqlite3_value_bytes(pData), out);
|
||||
nWrite = sqlite3_value_bytes(pData);
|
||||
if( nWrite!=n ){
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
fclose(out);
|
||||
if( rc==0 && mode && chmod(zFile, mode & 0777) ){
|
||||
rc = 1;
|
||||
}
|
||||
if( rc ) return 2;
|
||||
sqlite3_result_int64(pCtx, nWrite);
|
||||
}
|
||||
}
|
||||
|
||||
if( mtime>=0 ){
|
||||
/* Recent unix */
|
||||
struct timespec times[2];
|
||||
times[0].tv_nsec = times[1].tv_nsec = 0;
|
||||
times[0].tv_sec = time(0);
|
||||
times[1].tv_sec = mtime;
|
||||
if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the "writefile(W,X[,Y[,Z]]])" SQL function.
|
||||
** Refer to header comments at the top of this file for details.
|
||||
*/
|
||||
static void writefileFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zFile;
|
||||
mode_t mode = 0;
|
||||
int res;
|
||||
sqlite3_int64 mtime = -1;
|
||||
|
||||
if( argc<2 || argc>4 ){
|
||||
sqlite3_result_error(context,
|
||||
"wrong number of arguments to function writefile()", -1
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
zFile = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zFile==0 ) return;
|
||||
if( argc>=3 ){
|
||||
mode = (mode_t)sqlite3_value_int(argv[2]);
|
||||
}
|
||||
if( argc==4 ){
|
||||
mtime = sqlite3_value_int64(argv[3]);
|
||||
}
|
||||
|
||||
res = writeFile(context, zFile, argv[1], mode, mtime);
|
||||
if( res==1 && errno==ENOENT ){
|
||||
if( makeDirectory(zFile)==SQLITE_OK ){
|
||||
res = writeFile(context, zFile, argv[1], mode, mtime);
|
||||
}
|
||||
}
|
||||
|
||||
if( argc>2 && res!=0 ){
|
||||
if( S_ISLNK(mode) ){
|
||||
ctxErrorMsg(context, "failed to create symlink: %s", zFile);
|
||||
}else if( S_ISDIR(mode) ){
|
||||
ctxErrorMsg(context, "failed to create directory: %s", zFile);
|
||||
}else{
|
||||
ctxErrorMsg(context, "failed to write file: %s", zFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** SQL function: lsmode(MODE)
|
||||
**
|
||||
** Given a numberic st_mode from stat(), convert it into a human-readable
|
||||
** text string in the style of "ls -l".
|
||||
*/
|
||||
static void lsModeFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int i;
|
||||
int iMode = sqlite3_value_int(argv[0]);
|
||||
char z[16];
|
||||
(void)argc;
|
||||
if( S_ISLNK(iMode) ){
|
||||
z[0] = 'l';
|
||||
}else if( S_ISREG(iMode) ){
|
||||
z[0] = '-';
|
||||
}else if( S_ISDIR(iMode) ){
|
||||
z[0] = 'd';
|
||||
}else{
|
||||
z[0] = '?';
|
||||
}
|
||||
for(i=0; i<3; i++){
|
||||
int m = (iMode >> ((2-i)*3));
|
||||
char *a = &z[1 + i*3];
|
||||
a[0] = (m & 0x4) ? 'r' : '-';
|
||||
a[1] = (m & 0x2) ? 'w' : '-';
|
||||
a[2] = (m & 0x1) ? 'x' : '-';
|
||||
}
|
||||
z[10] = '\0';
|
||||
sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/*
|
||||
** Cursor type for recursively iterating through a directory structure.
|
||||
*/
|
||||
typedef struct fsdir_cursor fsdir_cursor;
|
||||
typedef struct FsdirLevel FsdirLevel;
|
||||
|
||||
struct FsdirLevel {
|
||||
DIR *pDir; /* From opendir() */
|
||||
char *zDir; /* Name of directory (nul-terminated) */
|
||||
};
|
||||
|
||||
struct fsdir_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
|
||||
int nLvl; /* Number of entries in aLvl[] array */
|
||||
int iLvl; /* Index of current entry */
|
||||
FsdirLevel *aLvl; /* Hierarchy of directories being traversed */
|
||||
|
||||
const char *zBase;
|
||||
int nBase;
|
||||
|
||||
struct stat sStat; /* Current lstat() results */
|
||||
char *zPath; /* Path to current entry */
|
||||
sqlite3_int64 iRowid; /* Current rowid */
|
||||
};
|
||||
|
||||
typedef struct fsdir_tab fsdir_tab;
|
||||
struct fsdir_tab {
|
||||
sqlite3_vtab base; /* Base class - must be first */
|
||||
};
|
||||
|
||||
/*
|
||||
** Construct a new fsdir virtual table object.
|
||||
*/
|
||||
static int fsdirConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
int argc, const char *const*argv,
|
||||
sqlite3_vtab **ppVtab,
|
||||
char **pzErr
|
||||
){
|
||||
fsdir_tab *pNew = 0;
|
||||
int rc;
|
||||
(void)pAux;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
(void)pzErr;
|
||||
rc = sqlite3_declare_vtab(db, "CREATE TABLE x" FSDIR_SCHEMA);
|
||||
if( rc==SQLITE_OK ){
|
||||
pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
|
||||
}
|
||||
*ppVtab = (sqlite3_vtab*)pNew;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is the destructor for fsdir vtab objects.
|
||||
*/
|
||||
static int fsdirDisconnect(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Constructor for a new fsdir_cursor object.
|
||||
*/
|
||||
static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
|
||||
fsdir_cursor *pCur;
|
||||
(void)p;
|
||||
pCur = sqlite3_malloc( sizeof(*pCur) );
|
||||
if( pCur==0 ) return SQLITE_NOMEM;
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->iLvl = -1;
|
||||
*ppCursor = &pCur->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset a cursor back to the state it was in when first returned
|
||||
** by fsdirOpen().
|
||||
*/
|
||||
static void fsdirResetCursor(fsdir_cursor *pCur){
|
||||
int i;
|
||||
for(i=0; i<=pCur->iLvl; i++){
|
||||
FsdirLevel *pLvl = &pCur->aLvl[i];
|
||||
if( pLvl->pDir ) closedir(pLvl->pDir);
|
||||
sqlite3_free(pLvl->zDir);
|
||||
}
|
||||
sqlite3_free(pCur->zPath);
|
||||
sqlite3_free(pCur->aLvl);
|
||||
pCur->aLvl = 0;
|
||||
pCur->zPath = 0;
|
||||
pCur->zBase = 0;
|
||||
pCur->nBase = 0;
|
||||
pCur->nLvl = 0;
|
||||
pCur->iLvl = -1;
|
||||
pCur->iRowid = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Destructor for an fsdir_cursor.
|
||||
*/
|
||||
static int fsdirClose(sqlite3_vtab_cursor *cur){
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
|
||||
fsdirResetCursor(pCur);
|
||||
sqlite3_free(pCur);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the error message for the virtual table associated with cursor
|
||||
** pCur to the results of vprintf(zFmt, ...).
|
||||
*/
|
||||
static void fsdirSetErrmsg(fsdir_cursor *pCur, const char *zFmt, ...){
|
||||
va_list ap;
|
||||
va_start(ap, zFmt);
|
||||
pCur->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance an fsdir_cursor to its next row of output.
|
||||
*/
|
||||
static int fsdirNext(sqlite3_vtab_cursor *cur){
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
mode_t m = pCur->sStat.st_mode;
|
||||
|
||||
pCur->iRowid++;
|
||||
if( S_ISDIR(m) ){
|
||||
/* Descend into this directory */
|
||||
int iNew = pCur->iLvl + 1;
|
||||
FsdirLevel *pLvl;
|
||||
if( iNew>=pCur->nLvl ){
|
||||
int nNew = iNew+1;
|
||||
sqlite3_int64 nByte = nNew*sizeof(FsdirLevel);
|
||||
FsdirLevel *aNew = (FsdirLevel*)sqlite3_realloc64(pCur->aLvl, nByte);
|
||||
if( aNew==0 ) return SQLITE_NOMEM;
|
||||
memset(&aNew[pCur->nLvl], 0, sizeof(FsdirLevel)*(nNew-pCur->nLvl));
|
||||
pCur->aLvl = aNew;
|
||||
pCur->nLvl = nNew;
|
||||
}
|
||||
pCur->iLvl = iNew;
|
||||
pLvl = &pCur->aLvl[iNew];
|
||||
|
||||
pLvl->zDir = pCur->zPath;
|
||||
pCur->zPath = 0;
|
||||
pLvl->pDir = opendir(pLvl->zDir);
|
||||
if( pLvl->pDir==0 ){
|
||||
fsdirSetErrmsg(pCur, "cannot read directory: %s", pCur->zPath);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
while( pCur->iLvl>=0 ){
|
||||
FsdirLevel *pLvl = &pCur->aLvl[pCur->iLvl];
|
||||
struct dirent *pEntry = readdir(pLvl->pDir);
|
||||
if( pEntry ){
|
||||
if( pEntry->d_name[0]=='.' ){
|
||||
if( pEntry->d_name[1]=='.' && pEntry->d_name[2]=='\0' ) continue;
|
||||
if( pEntry->d_name[1]=='\0' ) continue;
|
||||
}
|
||||
sqlite3_free(pCur->zPath);
|
||||
pCur->zPath = sqlite3_mprintf("%s/%s", pLvl->zDir, pEntry->d_name);
|
||||
if( pCur->zPath==0 ) return SQLITE_NOMEM;
|
||||
if( fileLinkStat(pCur->zPath, &pCur->sStat) ){
|
||||
fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
closedir(pLvl->pDir);
|
||||
sqlite3_free(pLvl->zDir);
|
||||
pLvl->pDir = 0;
|
||||
pLvl->zDir = 0;
|
||||
pCur->iLvl--;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
sqlite3_free(pCur->zPath);
|
||||
pCur->zPath = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return values of columns for the row at which the series_cursor
|
||||
** is currently pointing.
|
||||
*/
|
||||
static int fsdirColumn(
|
||||
sqlite3_vtab_cursor *cur, /* The cursor */
|
||||
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
|
||||
int i /* Which column to return */
|
||||
){
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
switch( i ){
|
||||
case FSDIR_COLUMN_NAME: {
|
||||
sqlite3_result_text(ctx, &pCur->zPath[pCur->nBase], -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
|
||||
case FSDIR_COLUMN_MODE:
|
||||
sqlite3_result_int64(ctx, pCur->sStat.st_mode);
|
||||
break;
|
||||
|
||||
case FSDIR_COLUMN_MTIME:
|
||||
sqlite3_result_int64(ctx, pCur->sStat.st_mtime);
|
||||
break;
|
||||
|
||||
case FSDIR_COLUMN_DATA: {
|
||||
mode_t m = pCur->sStat.st_mode;
|
||||
if( S_ISDIR(m) ){
|
||||
sqlite3_result_null(ctx);
|
||||
}else if( S_ISLNK(m) ){
|
||||
char aStatic[64];
|
||||
char *aBuf = aStatic;
|
||||
sqlite3_int64 nBuf = 64;
|
||||
int n;
|
||||
|
||||
while( 1 ){
|
||||
n = readlink(pCur->zPath, aBuf, nBuf);
|
||||
if( n<nBuf ) break;
|
||||
if( aBuf!=aStatic ) sqlite3_free(aBuf);
|
||||
nBuf = nBuf*2;
|
||||
aBuf = sqlite3_malloc64(nBuf);
|
||||
if( aBuf==0 ){
|
||||
sqlite3_result_error_nomem(ctx);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_result_text(ctx, aBuf, n, SQLITE_TRANSIENT);
|
||||
if( aBuf!=aStatic ) sqlite3_free(aBuf);
|
||||
}else{
|
||||
readFileContents(ctx, pCur->zPath);
|
||||
}
|
||||
}
|
||||
case FSDIR_COLUMN_PATH:
|
||||
default: {
|
||||
/* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters.
|
||||
** always return their values as NULL */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the rowid for the current row. In this implementation, the
|
||||
** first row returned is assigned rowid value 1, and each subsequent
|
||||
** row a value 1 more than that of the previous.
|
||||
*/
|
||||
static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
*pRowid = pCur->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if the cursor has been moved off of the last
|
||||
** row of output.
|
||||
*/
|
||||
static int fsdirEof(sqlite3_vtab_cursor *cur){
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
return (pCur->zPath==0);
|
||||
}
|
||||
|
||||
/*
|
||||
** xFilter callback.
|
||||
**
|
||||
** idxNum==1 PATH parameter only
|
||||
** idxNum==2 Both PATH and DIR supplied
|
||||
*/
|
||||
static int fsdirFilter(
|
||||
sqlite3_vtab_cursor *cur,
|
||||
int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
const char *zDir = 0;
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
(void)idxStr;
|
||||
fsdirResetCursor(pCur);
|
||||
|
||||
if( idxNum==0 ){
|
||||
fsdirSetErrmsg(pCur, "table function fsdir requires an argument");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
assert( argc==idxNum && (argc==1 || argc==2) );
|
||||
zDir = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zDir==0 ){
|
||||
fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if( argc==2 ){
|
||||
pCur->zBase = (const char*)sqlite3_value_text(argv[1]);
|
||||
}
|
||||
if( pCur->zBase ){
|
||||
pCur->nBase = (int)strlen(pCur->zBase)+1;
|
||||
pCur->zPath = sqlite3_mprintf("%s/%s", pCur->zBase, zDir);
|
||||
}else{
|
||||
pCur->zPath = sqlite3_mprintf("%s", zDir);
|
||||
}
|
||||
|
||||
if( pCur->zPath==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
if( fileLinkStat(pCur->zPath, &pCur->sStat) ){
|
||||
fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the generate_series virtual table. This routine needs to create
|
||||
** a query plan for each invocation and compute an estimated cost for that
|
||||
** plan.
|
||||
**
|
||||
** In this implementation idxNum is used to represent the
|
||||
** query plan. idxStr is unused.
|
||||
**
|
||||
** The query plan is represented by values of idxNum:
|
||||
**
|
||||
** (1) The path value is supplied by argv[0]
|
||||
** (2) Path is in argv[0] and dir is in argv[1]
|
||||
*/
|
||||
static int fsdirBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
int i; /* Loop over constraints */
|
||||
int idxPath = -1; /* Index in pIdxInfo->aConstraint of PATH= */
|
||||
int idxDir = -1; /* Index in pIdxInfo->aConstraint of DIR= */
|
||||
int seenPath = 0; /* True if an unusable PATH= constraint is seen */
|
||||
int seenDir = 0; /* True if an unusable DIR= constraint is seen */
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
|
||||
(void)tab;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
||||
switch( pConstraint->iColumn ){
|
||||
case FSDIR_COLUMN_PATH: {
|
||||
if( pConstraint->usable ){
|
||||
idxPath = i;
|
||||
seenPath = 0;
|
||||
}else if( idxPath<0 ){
|
||||
seenPath = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FSDIR_COLUMN_DIR: {
|
||||
if( pConstraint->usable ){
|
||||
idxDir = i;
|
||||
seenDir = 0;
|
||||
}else if( idxDir<0 ){
|
||||
seenDir = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( seenPath || seenDir ){
|
||||
/* If input parameters are unusable, disallow this plan */
|
||||
return SQLITE_CONSTRAINT;
|
||||
}
|
||||
|
||||
if( idxPath<0 ){
|
||||
pIdxInfo->idxNum = 0;
|
||||
/* The pIdxInfo->estimatedCost should have been initialized to a huge
|
||||
** number. Leave it unchanged. */
|
||||
pIdxInfo->estimatedRows = 0x7fffffff;
|
||||
}else{
|
||||
pIdxInfo->aConstraintUsage[idxPath].omit = 1;
|
||||
pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1;
|
||||
if( idxDir>=0 ){
|
||||
pIdxInfo->aConstraintUsage[idxDir].omit = 1;
|
||||
pIdxInfo->aConstraintUsage[idxDir].argvIndex = 2;
|
||||
pIdxInfo->idxNum = 2;
|
||||
pIdxInfo->estimatedCost = 10.0;
|
||||
}else{
|
||||
pIdxInfo->idxNum = 1;
|
||||
pIdxInfo->estimatedCost = 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the "fsdir" virtual table.
|
||||
*/
|
||||
static int fsdirRegister(sqlite3 *db){
|
||||
static sqlite3_module fsdirModule = {
|
||||
0, /* iVersion */
|
||||
0, /* xCreate */
|
||||
fsdirConnect, /* xConnect */
|
||||
fsdirBestIndex, /* xBestIndex */
|
||||
fsdirDisconnect, /* xDisconnect */
|
||||
0, /* xDestroy */
|
||||
fsdirOpen, /* xOpen - open a cursor */
|
||||
fsdirClose, /* xClose - close a cursor */
|
||||
fsdirFilter, /* xFilter - configure scan constraints */
|
||||
fsdirNext, /* xNext - advance a cursor */
|
||||
fsdirEof, /* xEof - check for end of scan */
|
||||
fsdirColumn, /* xColumn - read data */
|
||||
fsdirRowid, /* xRowid - read data */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindMethod */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0, /* xRollbackTo */
|
||||
0, /* xShadowName */
|
||||
};
|
||||
|
||||
int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0);
|
||||
return rc;
|
||||
}
|
||||
#else /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
# define fsdirRegister(x) SQLITE_OK
|
||||
#endif
|
||||
|
||||
int sqlite3_fileio_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "readfile", 1,
|
||||
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
|
||||
readfileFunc, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "writefile", -1,
|
||||
SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
|
||||
writefileFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "lsmode", 1, SQLITE_UTF8, 0,
|
||||
lsModeFunc, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fsdirRegister(db);
|
||||
}
|
||||
return rc;
|
||||
}
|
279
third_party/sqlite3/ieee754.c
vendored
Normal file
279
third_party/sqlite3/ieee754.c
vendored
Normal file
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
** 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 "libc/assert.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/sqlite3/sqlite3ext.h"
|
||||
// clang-format off
|
||||
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
/*
|
||||
** 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 */
|
||||
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
|
||||
){
|
||||
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
|
||||
){
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 },
|
||||
|
||||
};
|
||||
unsigned int i;
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
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;
|
||||
}
|
110
third_party/sqlite3/memtrace.c
vendored
Normal file
110
third_party/sqlite3/memtrace.c
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
** 2019-01-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file implements an extension that uses the SQLITE_CONFIG_MALLOC
|
||||
** mechanism to add a tracing layer on top of SQLite. If this extension
|
||||
** is registered prior to sqlite3_initialize(), it will cause all memory
|
||||
** allocation activities to be logged on standard output, or to some other
|
||||
** FILE specified by the initializer.
|
||||
**
|
||||
** This file needs to be compiled into the application that uses it.
|
||||
**
|
||||
** This extension is used to implement the --memtrace option of the
|
||||
** command-line shell.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/sqlite3/sqlite3.h"
|
||||
// clang-format off
|
||||
|
||||
/* The original memory allocation routines */
|
||||
static sqlite3_mem_methods memtraceBase;
|
||||
static FILE *memtraceOut;
|
||||
|
||||
/* Methods that trace memory allocations */
|
||||
static void *memtraceMalloc(int n){
|
||||
if( memtraceOut ){
|
||||
fprintf(memtraceOut, "MEMTRACE: allocate %d bytes\n",
|
||||
memtraceBase.xRoundup(n));
|
||||
}
|
||||
return memtraceBase.xMalloc(n);
|
||||
}
|
||||
static void memtraceFree(void *p){
|
||||
if( p==0 ) return;
|
||||
if( memtraceOut ){
|
||||
fprintf(memtraceOut, "MEMTRACE: free %d bytes\n", memtraceBase.xSize(p));
|
||||
}
|
||||
memtraceBase.xFree(p);
|
||||
}
|
||||
static void *memtraceRealloc(void *p, int n){
|
||||
if( p==0 ) return memtraceMalloc(n);
|
||||
if( n==0 ){
|
||||
memtraceFree(p);
|
||||
return 0;
|
||||
}
|
||||
if( memtraceOut ){
|
||||
fprintf(memtraceOut, "MEMTRACE: resize %d -> %d bytes\n",
|
||||
memtraceBase.xSize(p), memtraceBase.xRoundup(n));
|
||||
}
|
||||
return memtraceBase.xRealloc(p, n);
|
||||
}
|
||||
static int memtraceSize(void *p){
|
||||
return memtraceBase.xSize(p);
|
||||
}
|
||||
static int memtraceRoundup(int n){
|
||||
return memtraceBase.xRoundup(n);
|
||||
}
|
||||
static int memtraceInit(void *p){
|
||||
return memtraceBase.xInit(p);
|
||||
}
|
||||
static void memtraceShutdown(void *p){
|
||||
memtraceBase.xShutdown(p);
|
||||
}
|
||||
|
||||
/* The substitute memory allocator */
|
||||
static sqlite3_mem_methods ersaztMethods = {
|
||||
memtraceMalloc,
|
||||
memtraceFree,
|
||||
memtraceRealloc,
|
||||
memtraceSize,
|
||||
memtraceRoundup,
|
||||
memtraceInit,
|
||||
memtraceShutdown,
|
||||
0
|
||||
};
|
||||
|
||||
/* Begin tracing memory allocations to out. */
|
||||
int sqlite3MemTraceActivate(FILE *out){
|
||||
int rc = SQLITE_OK;
|
||||
if( memtraceBase.xMalloc==0 ){
|
||||
rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memtraceBase);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &ersaztMethods);
|
||||
}
|
||||
}
|
||||
memtraceOut = out;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Deactivate memory tracing */
|
||||
int sqlite3MemTraceDeactivate(void){
|
||||
int rc = SQLITE_OK;
|
||||
if( memtraceBase.xMalloc!=0 ){
|
||||
rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memtraceBase);
|
||||
if( rc==SQLITE_OK ){
|
||||
memset(&memtraceBase, 0, sizeof(memtraceBase));
|
||||
}
|
||||
}
|
||||
memtraceOut = 0;
|
||||
return rc;
|
||||
}
|
443
third_party/sqlite3/series.c
vendored
Normal file
443
third_party/sqlite3/series.c
vendored
Normal file
|
@ -0,0 +1,443 @@
|
|||
/*
|
||||
** 2015-08-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 file demonstrates how to create a table-valued-function using
|
||||
** a virtual table. This demo implements the generate_series() function
|
||||
** which gives similar results to the eponymous function in PostgreSQL.
|
||||
** 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.
|
||||
**
|
||||
** 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
|
||||
** );
|
||||
**
|
||||
** 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 "libc/assert.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/sqlite3/sqlite3ext.h"
|
||||
// clang-format off
|
||||
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
/* series_cursor is a subclass of sqlite3_vtab_cursor which will
|
||||
** serve as the underlying representation of a cursor that scans
|
||||
** over rows of the result
|
||||
*/
|
||||
typedef struct series_cursor series_cursor;
|
||||
struct series_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
int isDesc; /* True to count down rather than up */
|
||||
sqlite3_int64 iRowid; /* The rowid */
|
||||
sqlite3_int64 iValue; /* Current value ("value") */
|
||||
sqlite3_int64 mnValue; /* Mimimum value ("start") */
|
||||
sqlite3_int64 mxValue; /* Maximum value ("stop") */
|
||||
sqlite3_int64 iStep; /* Increment ("step") */
|
||||
};
|
||||
|
||||
/*
|
||||
** 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;
|
||||
if( pCur->isDesc ){
|
||||
pCur->iValue -= pCur->iStep;
|
||||
}else{
|
||||
pCur->iValue += pCur->iStep;
|
||||
}
|
||||
pCur->iRowid++;
|
||||
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->mnValue; break;
|
||||
case SERIES_COLUMN_STOP: x = pCur->mxValue; break;
|
||||
case SERIES_COLUMN_STEP: x = pCur->iStep; break;
|
||||
default: x = pCur->iValue; break;
|
||||
}
|
||||
sqlite3_result_int64(ctx, x);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the rowid for the current row. In this implementation, the
|
||||
** first row returned is assigned rowid value 1, and each subsequent
|
||||
** row a value 1 more than that of the previous.
|
||||
*/
|
||||
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
series_cursor *pCur = (series_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 seriesEof(sqlite3_vtab_cursor *cur){
|
||||
series_cursor *pCur = (series_cursor*)cur;
|
||||
if( pCur->isDesc ){
|
||||
return pCur->iValue < pCur->mnValue;
|
||||
}else{
|
||||
return pCur->iValue > pCur->mxValue;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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:
|
||||
**
|
||||
** 1: start=VALUE
|
||||
** 2: stop=VALUE
|
||||
** 4: step=VALUE
|
||||
**
|
||||
** Also, if bit 8 is set, that means that the series should be output
|
||||
** in descending order rather than in ascending order. If bit 16 is
|
||||
** set, then output must appear in ascending order.
|
||||
**
|
||||
** 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 & 1 ){
|
||||
pCur->mnValue = sqlite3_value_int64(argv[i++]);
|
||||
}else{
|
||||
pCur->mnValue = 0;
|
||||
}
|
||||
if( idxNum & 2 ){
|
||||
pCur->mxValue = sqlite3_value_int64(argv[i++]);
|
||||
}else{
|
||||
pCur->mxValue = 0xffffffff;
|
||||
}
|
||||
if( idxNum & 4 ){
|
||||
pCur->iStep = sqlite3_value_int64(argv[i++]);
|
||||
if( pCur->iStep==0 ){
|
||||
pCur->iStep = 1;
|
||||
}else if( pCur->iStep<0 ){
|
||||
pCur->iStep = -pCur->iStep;
|
||||
if( (idxNum & 16)==0 ) idxNum |= 8;
|
||||
}
|
||||
}else{
|
||||
pCur->iStep = 1;
|
||||
}
|
||||
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->mnValue = 1;
|
||||
pCur->mxValue = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( idxNum & 8 ){
|
||||
pCur->isDesc = 1;
|
||||
pCur->iValue = pCur->mxValue;
|
||||
if( pCur->iStep>0 ){
|
||||
pCur->iValue -= (pCur->mxValue - pCur->mnValue)%pCur->iStep;
|
||||
}
|
||||
}else{
|
||||
pCur->isDesc = 0;
|
||||
pCur->iValue = pCur->mnValue;
|
||||
}
|
||||
pCur->iRowid = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the generate_series virtual table. This routine needs to create
|
||||
** a query plan for each invocation and compute an estimated cost for that
|
||||
** plan.
|
||||
**
|
||||
** In this implementation idxNum is used to represent the
|
||||
** query plan. idxStr is unused.
|
||||
**
|
||||
** The query plan is represented by bits in idxNum:
|
||||
**
|
||||
** (1) start = $value -- constraint exists
|
||||
** (2) stop = $value -- constraint exists
|
||||
** (4) step = $value -- constraint exists
|
||||
** (8) output in descending order
|
||||
*/
|
||||
static int seriesBestIndex(
|
||||
sqlite3_vtab *tabUnused,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
int i, j; /* Loop over constraints */
|
||||
int idxNum = 0; /* The query plan bitmask */
|
||||
int unusableMask = 0; /* Mask of unusable constraints */
|
||||
int nArg = 0; /* Number of arguments that seriesFilter() expects */
|
||||
int aIdx[3]; /* Constraints on start, stop, and step */
|
||||
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 );
|
||||
(void)tabUnused;
|
||||
aIdx[0] = aIdx[1] = aIdx[2] = -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 */
|
||||
if( pConstraint->iColumn<SERIES_COLUMN_START ) continue;
|
||||
iCol = pConstraint->iColumn - SERIES_COLUMN_START;
|
||||
assert( iCol>=0 && iCol<=2 );
|
||||
iMask = 1 << iCol;
|
||||
if( pConstraint->usable==0 ){
|
||||
unusableMask |= iMask;
|
||||
continue;
|
||||
}else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
idxNum |= iMask;
|
||||
aIdx[iCol] = i;
|
||||
}
|
||||
}
|
||||
for(i=0; i<3; i++){
|
||||
if( (j = aIdx[i])>=0 ){
|
||||
pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg;
|
||||
pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY;
|
||||
}
|
||||
}
|
||||
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 & 3)==3 ){
|
||||
/* 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 ){
|
||||
if( pIdxInfo->aOrderBy[0].desc ){
|
||||
idxNum |= 8;
|
||||
}else{
|
||||
idxNum |= 16;
|
||||
}
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
}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 */
|
||||
};
|
||||
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
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 = 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;
|
||||
}
|
717
third_party/sqlite3/shathree.c
vendored
Normal file
717
third_party/sqlite3/shathree.c
vendored
Normal file
|
@ -0,0 +1,717 @@
|
|||
/*
|
||||
** 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.
|
||||
** Two SQL functions are implemented:
|
||||
**
|
||||
** sha3(X,SIZE)
|
||||
** sha3_query(Y,SIZE)
|
||||
**
|
||||
** The sha3(X) function computes the SHA3 hash of the input X, or NULL if
|
||||
** X is NULL.
|
||||
**
|
||||
** The sha3_query(Y) function evalutes 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.
|
||||
*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/sqlite3/sqlite3ext.h"
|
||||
// clang-format off
|
||||
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
typedef sqlite3_uint64 u64;
|
||||
|
||||
/******************************************************************************
|
||||
** 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]. */
|
||||
};
|
||||
|
||||
/*
|
||||
** 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));
|
||||
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 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 hash_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);
|
||||
}
|
||||
|
||||
/*
|
||||
** 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);
|
||||
hash_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++){
|
||||
switch( sqlite3_column_type(pStmt,i) ){
|
||||
case SQLITE_NULL: {
|
||||
SHA3Update(&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';
|
||||
SHA3Update(&cx, x, 9);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_uint64 u;
|
||||
int j;
|
||||
unsigned char x[9];
|
||||
double r = sqlite3_column_double(pStmt,i);
|
||||
memcpy(&u, &r, 8);
|
||||
for(j=8; j>=1; j--){
|
||||
x[j] = u & 0xff;
|
||||
u >>= 8;
|
||||
}
|
||||
x[0] = 'F';
|
||||
SHA3Update(&cx,x,9);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
int n2 = sqlite3_column_bytes(pStmt, i);
|
||||
const unsigned char *z2 = sqlite3_column_text(pStmt, i);
|
||||
hash_step_vformat(&cx,"T%d:",n2);
|
||||
SHA3Update(&cx, z2, n2);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n2 = sqlite3_column_bytes(pStmt, i);
|
||||
const unsigned char *z2 = sqlite3_column_blob(pStmt, i);
|
||||
hash_step_vformat(&cx,"B%d:",n2);
|
||||
SHA3Update(&cx, z2, n2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
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_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;
|
||||
}
|
9791
third_party/sqlite3/shell.c
vendored
9791
third_party/sqlite3/shell.c
vendored
File diff suppressed because it is too large
Load diff
122
third_party/sqlite3/sqlar.c
vendored
Normal file
122
third_party/sqlite3/sqlar.c
vendored
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
** 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 "libc/assert.h"
|
||||
#include "third_party/sqlite3/sqlite3ext.h"
|
||||
#include "third_party/zlib/zlib.h"
|
||||
// clang-format off
|
||||
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
/*
|
||||
** 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;
|
||||
uLongf 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{
|
||||
const Bytef *pData= sqlite3_value_blob(argv[0]);
|
||||
Bytef *pOut = sqlite3_malloc(sz);
|
||||
if( Z_OK!=uncompress(pOut, &sz, pData, nData) ){
|
||||
sqlite3_result_error(context, "error in uncompress()", -1);
|
||||
}else{
|
||||
sqlite3_result_blob(context, pOut, sz, SQLITE_TRANSIENT);
|
||||
}
|
||||
sqlite3_free(pOut);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
12
third_party/sqlite3/sqlite3.mk
vendored
12
third_party/sqlite3/sqlite3.mk
vendored
|
@ -127,7 +127,8 @@ THIRD_PARTY_SQLITE3_FLAGS = \
|
|||
-DSQLITE_OMIT_AUTOINIT \
|
||||
-DSQLITE_OMIT_GET_TABLE \
|
||||
-DSQLITE_HAVE_C99_MATH_FUNCS \
|
||||
-DSQLITE_ENABLE_MATH_FUNCTIONS
|
||||
-DSQLITE_ENABLE_MATH_FUNCTIONS \
|
||||
-DSQLITE_ENABLE_JSON1 \
|
||||
|
||||
$(THIRD_PARTY_SQLITE3_A_OBJS): \
|
||||
OVERRIDE_CFLAGS += \
|
||||
|
@ -157,13 +158,8 @@ $(THIRD_PARTY_SQLITE3_SHELL_OBJS): \
|
|||
-DSQLITE_ENABLE_FTS5 \
|
||||
-DSQLITE_ENABLE_RTREE \
|
||||
-DSQLITE_ENABLE_GEOPOLY \
|
||||
-DSQLITE_ENABLE_JSON1 \
|
||||
-DHAVE_LINENOISE
|
||||
|
||||
o/$(MODE)/third_party/sqlite3/shell.shell.o: \
|
||||
OVERRIDE_CFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED
|
||||
|
||||
o//third_party/sqlite3/parse.o \
|
||||
o//third_party/sqlite3/select.o \
|
||||
o//third_party/sqlite3/pragma.o \
|
||||
|
@ -171,6 +167,10 @@ o//third_party/sqlite3/vdbe.o: \
|
|||
OVERRIDE_CFLAGS += \
|
||||
-Os
|
||||
|
||||
o/$(MODE)/third_party/sqlite3/shell.shell.o: \
|
||||
OVERRIDE_CFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED
|
||||
|
||||
$(THIRD_PARTY_SQLITE3_A_OBJS) \
|
||||
$(THIRD_PARTY_SQLITE3_SHELL_OBJS): \
|
||||
OVERRIDE_CFLAGS += \
|
||||
|
|
1964
third_party/sqlite3/sqlite3expert.c
vendored
Normal file
1964
third_party/sqlite3/sqlite3expert.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
159
third_party/sqlite3/sqlite3expert.h
vendored
Normal file
159
third_party/sqlite3/sqlite3expert.h
vendored
Normal file
|
@ -0,0 +1,159 @@
|
|||
#ifndef COSMOPOLITAN_THIRD_PARTY_SQLITE3_SQLITE3EXPERT_H_
|
||||
#define COSMOPOLITAN_THIRD_PARTY_SQLITE3_SQLITE3EXPERT_H_
|
||||
#include "third_party/sqlite3/sqlite3.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
typedef struct sqlite3expert sqlite3expert;
|
||||
|
||||
/*
|
||||
** Create a new sqlite3expert object.
|
||||
**
|
||||
** If successful, a pointer to the new object is returned and (*pzErr) set
|
||||
** to NULL. Or, if an error occurs, NULL is returned and (*pzErr) set to
|
||||
** an English-language error message. In this case it is the responsibility
|
||||
** of the caller to eventually free the error message buffer using
|
||||
** sqlite3_free().
|
||||
*/
|
||||
sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErr);
|
||||
|
||||
/*
|
||||
** Configure an sqlite3expert object.
|
||||
**
|
||||
** EXPERT_CONFIG_SAMPLE:
|
||||
** By default, sqlite3_expert_analyze() generates sqlite_stat1 data for
|
||||
** each candidate index. This involves scanning and sorting the entire
|
||||
** contents of each user database table once for each candidate index
|
||||
** associated with the table. For large databases, this can be
|
||||
** prohibitively slow. This option allows the sqlite3expert object to
|
||||
** be configured so that sqlite_stat1 data is instead generated based on a
|
||||
** subset of each table, or so that no sqlite_stat1 data is used at all.
|
||||
**
|
||||
** A single integer argument is passed to this option. If the value is less
|
||||
** than or equal to zero, then no sqlite_stat1 data is generated or used by
|
||||
** the analysis - indexes are recommended based on the database schema only.
|
||||
** Or, if the value is 100 or greater, complete sqlite_stat1 data is
|
||||
** generated for each candidate index (this is the default). Finally, if the
|
||||
** value falls between 0 and 100, then it represents the percentage of user
|
||||
** table rows that should be considered when generating sqlite_stat1 data.
|
||||
**
|
||||
** Examples:
|
||||
**
|
||||
** // Do not generate any sqlite_stat1 data
|
||||
** sqlite3_expert_config(pExpert, EXPERT_CONFIG_SAMPLE, 0);
|
||||
**
|
||||
** // Generate sqlite_stat1 data based on 10% of the rows in each table.
|
||||
** sqlite3_expert_config(pExpert, EXPERT_CONFIG_SAMPLE, 10);
|
||||
*/
|
||||
int sqlite3_expert_config(sqlite3expert *p, int op, ...);
|
||||
|
||||
#define EXPERT_CONFIG_SAMPLE 1 /* int */
|
||||
|
||||
/*
|
||||
** Specify zero or more SQL statements to be included in the analysis.
|
||||
**
|
||||
** Buffer zSql must contain zero or more complete SQL statements. This
|
||||
** function parses all statements contained in the buffer and adds them
|
||||
** to the internal list of statements to analyze. If successful, SQLITE_OK
|
||||
** is returned and (*pzErr) set to NULL. Or, if an error occurs - for example
|
||||
** due to a error in the SQL - an SQLite error code is returned and (*pzErr)
|
||||
** may be set to point to an English language error message. In this case
|
||||
** the caller is responsible for eventually freeing the error message buffer
|
||||
** using sqlite3_free().
|
||||
**
|
||||
** If an error does occur while processing one of the statements in the
|
||||
** buffer passed as the second argument, none of the statements in the
|
||||
** buffer are added to the analysis.
|
||||
**
|
||||
** This function must be called before sqlite3_expert_analyze(). If a call
|
||||
** to this function is made on an sqlite3expert object that has already
|
||||
** been passed to sqlite3_expert_analyze() SQLITE_MISUSE is returned
|
||||
** immediately and no statements are added to the analysis.
|
||||
*/
|
||||
int sqlite3_expert_sql(
|
||||
sqlite3expert *p, /* From a successful sqlite3_expert_new() */
|
||||
const char *zSql, /* SQL statement(s) to add */
|
||||
char **pzErr /* OUT: Error message (if any) */
|
||||
);
|
||||
|
||||
/*
|
||||
** This function is called after the sqlite3expert object has been configured
|
||||
** with all SQL statements using sqlite3_expert_sql() to actually perform
|
||||
** the analysis. Once this function has been called, it is not possible to
|
||||
** add further SQL statements to the analysis.
|
||||
**
|
||||
** If successful, SQLITE_OK is returned and (*pzErr) is set to NULL. Or, if
|
||||
** an error occurs, an SQLite error code is returned and (*pzErr) set to
|
||||
** point to a buffer containing an English language error message. In this
|
||||
** case it is the responsibility of the caller to eventually free the buffer
|
||||
** using sqlite3_free().
|
||||
**
|
||||
** If an error does occur within this function, the sqlite3expert object
|
||||
** is no longer useful for any purpose. At that point it is no longer
|
||||
** possible to add further SQL statements to the object or to re-attempt
|
||||
** the analysis. The sqlite3expert object must still be freed using a call
|
||||
** sqlite3_expert_destroy().
|
||||
*/
|
||||
int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr);
|
||||
|
||||
/*
|
||||
** Return the total number of statements loaded using sqlite3_expert_sql().
|
||||
** The total number of SQL statements may be different from the total number
|
||||
** to calls to sqlite3_expert_sql().
|
||||
*/
|
||||
int sqlite3_expert_count(sqlite3expert *);
|
||||
|
||||
/*
|
||||
** Return a component of the report.
|
||||
**
|
||||
** This function is called after sqlite3_expert_analyze() to extract the
|
||||
** results of the analysis. Each call to this function returns either a
|
||||
** NULL pointer or a pointer to a buffer containing a nul-terminated string.
|
||||
** The value passed as the third argument must be one of the EXPERT_REPORT_*
|
||||
** #define constants defined below.
|
||||
**
|
||||
** For some EXPERT_REPORT_* parameters, the buffer returned contains
|
||||
** information relating to a specific SQL statement. In these cases that
|
||||
** SQL statement is identified by the value passed as the second argument.
|
||||
** SQL statements are numbered from 0 in the order in which they are parsed.
|
||||
** If an out-of-range value (less than zero or equal to or greater than the
|
||||
** value returned by sqlite3_expert_count()) is passed as the second argument
|
||||
** along with such an EXPERT_REPORT_* parameter, NULL is always returned.
|
||||
**
|
||||
** EXPERT_REPORT_SQL:
|
||||
** Return the text of SQL statement iStmt.
|
||||
**
|
||||
** EXPERT_REPORT_INDEXES:
|
||||
** Return a buffer containing the CREATE INDEX statements for all recommended
|
||||
** indexes for statement iStmt. If there are no new recommeded indexes, NULL
|
||||
** is returned.
|
||||
**
|
||||
** EXPERT_REPORT_PLAN:
|
||||
** Return a buffer containing the EXPLAIN QUERY PLAN output for SQL query
|
||||
** iStmt after the proposed indexes have been added to the database schema.
|
||||
**
|
||||
** EXPERT_REPORT_CANDIDATES:
|
||||
** Return a pointer to a buffer containing the CREATE INDEX statements
|
||||
** for all indexes that were tested (for all SQL statements). The iStmt
|
||||
** parameter is ignored for EXPERT_REPORT_CANDIDATES calls.
|
||||
*/
|
||||
const char *sqlite3_expert_report(sqlite3expert *, int iStmt, int eReport);
|
||||
|
||||
/*
|
||||
** Values for the third argument passed to sqlite3_expert_report().
|
||||
*/
|
||||
#define EXPERT_REPORT_SQL 1
|
||||
#define EXPERT_REPORT_INDEXES 2
|
||||
#define EXPERT_REPORT_PLAN 3
|
||||
#define EXPERT_REPORT_CANDIDATES 4
|
||||
|
||||
/*
|
||||
** Free an (sqlite3expert*) handle and all associated resources. There
|
||||
** should be one call to this function for each successful call to
|
||||
** sqlite3-expert_new().
|
||||
*/
|
||||
void sqlite3_expert_destroy(sqlite3expert *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_SQLITE3_SQLITE3EXPERT_H_ */
|
90
third_party/sqlite3/uint.c
vendored
Normal file
90
third_party/sqlite3/uint.c
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
** 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 "libc/assert.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/sqlite3/sqlite3ext.h"
|
||||
// clang-format off
|
||||
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
/*
|
||||
** 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
2172
third_party/sqlite3/zipfile.c
vendored
Normal file
2172
third_party/sqlite3/zipfile.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,37 +1,40 @@
|
|||
/* clang-format off */
|
||||
/************************************************************************
|
||||
* lsqlite3 *
|
||||
* Copyright (C) 2002-2016 Tiago Dionizio, Doug Currie *
|
||||
* All rights reserved. *
|
||||
* Author : Tiago Dionizio <tiago.dionizio@ist.utl.pt> *
|
||||
* Author : Doug Currie <doug.currie@alum.mit.edu> *
|
||||
* Library : lsqlite3 - an SQLite 3 database binding for Lua 5 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining *
|
||||
* a copy of this software and associated documentation files (the *
|
||||
* "Software"), to deal in the Software without restriction, including *
|
||||
* without limitation the rights to use, copy, modify, merge, publish, *
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to *
|
||||
* the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be *
|
||||
* included in all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY *
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, *
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE *
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
************************************************************************/
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ lsqlite3 │
|
||||
│ Copyright (C) 2002-2016 Tiago Dionizio, Doug Currie │
|
||||
│ All rights reserved. │
|
||||
│ Author : Tiago Dionizio <tiago.dionizio@ist.utl.pt> │
|
||||
│ Author : Doug Currie <doug.currie@alum.mit.edu> │
|
||||
│ Library : lsqlite3 - an SQLite 3 database binding for Lua 5 │
|
||||
│ │
|
||||
│ Permission is hereby granted, free of charge, to any person obtaining │
|
||||
│ a copy of this software and associated documentation files (the │
|
||||
│ "Software"), to deal in the Software without restriction, including │
|
||||
│ without limitation the rights to use, copy, modify, merge, publish, │
|
||||
│ distribute, sublicense, and/or sell copies of the Software, and to │
|
||||
│ permit persons to whom the Software is furnished to do so, subject to │
|
||||
│ the following conditions: │
|
||||
│ │
|
||||
│ The above copyright notice and this permission notice shall be │
|
||||
│ included in all copies or substantial portions of the Software. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │
|
||||
│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │
|
||||
│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │
|
||||
│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
#include "third_party/lua/lua.h"
|
||||
#include "third_party/lua/luaconf.h"
|
||||
#include "third_party/sqlite3/extensions.h"
|
||||
#include "third_party/sqlite3/sqlite3.h"
|
||||
// clang-format off
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
lsqlite3 (MIT License)\\n\
|
||||
|
@ -1696,6 +1699,7 @@ static int lsqlite_do_open(lua_State *L, const char *filename, int flags) {
|
|||
|
||||
if (sqlite3_open_v2(filename, &db->db, flags, 0) == SQLITE_OK) {
|
||||
/* database handle already in the stack - return it */
|
||||
sqlite3_zipfile_init(db->db, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue