Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e307e11d0a | |||
| f259df5fc8 | |||
| 9b2e0435d2 | |||
| 908cc6207f | |||
| 2c804becea | |||
| cf8e0e9cb7 | |||
| 7038a991b0 | |||
| 7e7b753158 | |||
| 4396c61959 | |||
| 22716cbb0b | |||
| 4fb5c2dcc1 | |||
| 0d9de99c5c | |||
| 02a43f6def | |||
| c8f44d245c | |||
| f6c37dbc8c | |||
| 5060a67c4d | |||
| 3a9d6c7156 | |||
| ef53a9f0af | |||
| 5c8241b875 | |||
| 512795dfea | |||
| e58f74f680 | |||
| d3f5d6110b | |||
| ec64c6cfd2 | |||
| 1be266ba08 |
+1
-8
@@ -558,8 +558,7 @@ TESTPROGS = \
|
||||
sqlite3$(TEXE) \
|
||||
sqlite3_analyzer$(TEXE) \
|
||||
sqldiff$(TEXE) \
|
||||
dbhash$(TEXE) \
|
||||
sqltclsh$(TEXE)
|
||||
dbhash$(TEXE)
|
||||
|
||||
# Databases containing fuzzer test cases
|
||||
#
|
||||
@@ -1193,12 +1192,6 @@ sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $
|
||||
sqlite3_analyzer$(TEXE): sqlite3_analyzer.c
|
||||
$(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS)
|
||||
|
||||
sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in
|
||||
$(TCLSH_CMD) $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in >sqltclsh.c
|
||||
|
||||
sqltclsh$(TEXE): sqltclsh.c
|
||||
$(LTLINK) sqltclsh.c -o $@ $(LIBTCL) $(TLIBS)
|
||||
|
||||
sqlite3_expert$(TEXE): $(TOP)/ext/expert/sqlite3expert.h $(TOP)/ext/expert/sqlite3expert.c $(TOP)/ext/expert/expert.c sqlite3.c
|
||||
$(LTLINK) $(TOP)/ext/expert/sqlite3expert.h $(TOP)/ext/expert/sqlite3expert.c $(TOP)/ext/expert/expert.c sqlite3.c -o sqlite3_expert $(TLIBS)
|
||||
|
||||
|
||||
+1
-10
@@ -1495,8 +1495,7 @@ TESTPROGS = \
|
||||
sqlite3_analyzer.exe \
|
||||
sqlite3_checker.exe \
|
||||
sqldiff.exe \
|
||||
dbhash.exe \
|
||||
sqltclsh.exe
|
||||
dbhash.exe
|
||||
|
||||
# Databases containing fuzzer test cases
|
||||
#
|
||||
@@ -2223,14 +2222,6 @@ sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqlite3_analyzer.c \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
sqltclsh.c: sqlite3.c $(TOP)\src\tclsqlite.c $(TOP)\tool\sqltclsh.tcl $(TOP)\ext\misc\appendvfs.c $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in
|
||||
$(TCLSH_CMD) $(TOP)\tool\mkccode.tcl $(TOP)\tool\sqltclsh.c.in >sqltclsh.c
|
||||
|
||||
sqltclsh.exe: sqltclsh.c
|
||||
$(LTLINK) $(NO_WARN) -DBUILD_sqlite -I$(TCLINCDIR) sqltclsh.c \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
|
||||
sqlite3_expert.exe: $(SQLITE3C) $(TOP)\ext\expert\sqlite3expert.h $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\ext\expert\sqlite3expert.c $(TOP)\ext\expert\expert.c $(SQLITE3C) $(TLIBS)
|
||||
|
||||
|
||||
+13
-3
@@ -358,6 +358,16 @@ static int fts5SnippetScore(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the value in pVal interpreted as utf-8 text. Except, if pVal
|
||||
** contains a NULL value, return a pointer to a static string zero
|
||||
** bytes in length instead of a NULL pointer.
|
||||
*/
|
||||
static const char *fts5ValueToText(sqlite3_value *pVal){
|
||||
const char *zRet = (const char*)sqlite3_value_text(pVal);
|
||||
return zRet ? zRet : "";
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of snippet() function.
|
||||
*/
|
||||
@@ -393,9 +403,9 @@ static void fts5SnippetFunction(
|
||||
nCol = pApi->xColumnCount(pFts);
|
||||
memset(&ctx, 0, sizeof(HighlightContext));
|
||||
iCol = sqlite3_value_int(apVal[0]);
|
||||
ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
|
||||
ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
|
||||
zEllips = (const char*)sqlite3_value_text(apVal[3]);
|
||||
ctx.zOpen = fts5ValueToText(apVal[1]);
|
||||
ctx.zClose = fts5ValueToText(apVal[2]);
|
||||
zEllips = fts5ValueToText(apVal[3]);
|
||||
nToken = sqlite3_value_int(apVal[4]);
|
||||
|
||||
iBestCol = (iCol>=0 ? iCol : 0);
|
||||
|
||||
@@ -175,6 +175,16 @@ do_execsql_test 5.1 {
|
||||
SELECT snippet(p1, 0, '[', ']', '...', 6) FROM p1('x');
|
||||
} {{[x] a a a a a...}}
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
SELECT snippet(p1, 0, '[', ']', NULL, 6) FROM p1('x');
|
||||
} {{[x] a a a a a}}
|
||||
do_execsql_test 5.3 {
|
||||
SELECT snippet(p1, 0, NULL, ']', '...', 6) FROM p1('x');
|
||||
} {{x] a a a a a...}}
|
||||
do_execsql_test 5.4 {
|
||||
SELECT snippet(p1, 0, '[', NULL, '...', 6) FROM p1('x');
|
||||
} {{[x a a a a a...}}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1,565 +0,0 @@
|
||||
/*
|
||||
** 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 an offset to page 1. 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 is as a
|
||||
** database appended onto some other file. Here are the rules:
|
||||
**
|
||||
** (1) When opening a new empty file, that file is treated as an ordinary
|
||||
** database.
|
||||
**
|
||||
** (2) When opening a file that begins with the standard SQLite prefix
|
||||
** string "SQLite format 3", that file is treated as an ordinary
|
||||
** database.
|
||||
**
|
||||
** (3) When opening a file that ends with the appendvfs trailer string
|
||||
** "Start-Of-SQLite3-NNNNNNNN" that file is treated as an appended
|
||||
** 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 1GB. This VFS will refuse
|
||||
** to read or write past the 1GB mark. This restriction might be lifted in
|
||||
** future versions. For now, if you need a large database, then keep the
|
||||
** database in a separate file.
|
||||
**
|
||||
** If the file being opened is not an appended database, then this shim is
|
||||
** a pass-through into the default underlying VFS.
|
||||
**/
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include <sqlite3ext.h>
|
||||
#endif
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* The append mark at the end of the database is:
|
||||
**
|
||||
** Start-Of-SQLite3-NNNNNNNN
|
||||
** 123456789 123456789 12345
|
||||
**
|
||||
** The NNNNNNNN represents a 64-bit big-endian unsigned integer which is
|
||||
** the offset to page 1.
|
||||
*/
|
||||
#define APND_MARK_PREFIX "Start-Of-SQLite3-"
|
||||
#define APND_MARK_PREFIX_SZ 17
|
||||
#define APND_MARK_SIZE 25
|
||||
|
||||
/*
|
||||
** 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 (65536*15259)
|
||||
|
||||
/*
|
||||
** 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 file */
|
||||
struct ApndFile {
|
||||
sqlite3_file base; /* IO methods */
|
||||
sqlite3_int64 iPgOne; /* File offset to page 1 */
|
||||
sqlite3_int64 iMark; /* Start of the append-mark */
|
||||
};
|
||||
|
||||
/*
|
||||
** 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 *p = (ApndFile *)pFile;
|
||||
pFile = ORIGFILE(pFile);
|
||||
return pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst+p->iPgOne);
|
||||
}
|
||||
|
||||
/*
|
||||
** Add the append-mark onto the end of the file.
|
||||
*/
|
||||
static int apndWriteMark(ApndFile *p, sqlite3_file *pFile){
|
||||
int i;
|
||||
unsigned char a[APND_MARK_SIZE];
|
||||
memcpy(a, APND_MARK_PREFIX, APND_MARK_PREFIX_SZ);
|
||||
for(i=0; i<8; i++){
|
||||
a[APND_MARK_PREFIX_SZ+i] = (p->iPgOne >> (56 - i*8)) & 0xff;
|
||||
}
|
||||
return pFile->pMethods->xWrite(pFile, a, APND_MARK_SIZE, p->iMark);
|
||||
}
|
||||
|
||||
/*
|
||||
** Write data to an apnd-file.
|
||||
*/
|
||||
static int apndWrite(
|
||||
sqlite3_file *pFile,
|
||||
const void *zBuf,
|
||||
int iAmt,
|
||||
sqlite_int64 iOfst
|
||||
){
|
||||
int rc;
|
||||
ApndFile *p = (ApndFile *)pFile;
|
||||
pFile = ORIGFILE(pFile);
|
||||
if( iOfst+iAmt>=APND_MAX_SIZE ) return SQLITE_FULL;
|
||||
rc = pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst+p->iPgOne);
|
||||
if( rc==SQLITE_OK && iOfst + iAmt + p->iPgOne > p->iMark ){
|
||||
sqlite3_int64 sz = 0;
|
||||
rc = pFile->pMethods->xFileSize(pFile, &sz);
|
||||
if( rc==SQLITE_OK ){
|
||||
p->iMark = sz - APND_MARK_SIZE;
|
||||
if( iOfst + iAmt + p->iPgOne > p->iMark ){
|
||||
p->iMark = p->iPgOne + iOfst + iAmt;
|
||||
rc = apndWriteMark(p, pFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Truncate an apnd-file.
|
||||
*/
|
||||
static int apndTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
||||
int rc;
|
||||
ApndFile *p = (ApndFile *)pFile;
|
||||
pFile = ORIGFILE(pFile);
|
||||
rc = pFile->pMethods->xTruncate(pFile, size+p->iPgOne+APND_MARK_SIZE);
|
||||
if( rc==SQLITE_OK ){
|
||||
p->iMark = p->iPgOne+size;
|
||||
rc = apndWriteMark(p, pFile);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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.
|
||||
*/
|
||||
static int apndFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
||||
ApndFile *p = (ApndFile *)pFile;
|
||||
int rc;
|
||||
pFile = ORIGFILE(p);
|
||||
rc = pFile->pMethods->xFileSize(pFile, pSize);
|
||||
if( rc==SQLITE_OK && p->iPgOne ){
|
||||
*pSize -= p->iPgOne + APND_MARK_SIZE;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 *p = (ApndFile *)pFile;
|
||||
int rc;
|
||||
pFile = ORIGFILE(pFile);
|
||||
rc = pFile->pMethods->xFileControl(pFile, op, pArg);
|
||||
if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
|
||||
*(char**)pArg = sqlite3_mprintf("apnd(%lld)/%z", p->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;
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if the file is an ordinary SQLite database file.
|
||||
*/
|
||||
static int apndIsOrdinaryDatabaseFile(sqlite3_int64 sz, sqlite3_file *pFile){
|
||||
int rc;
|
||||
char zHdr[16];
|
||||
static const char aSqliteHdr[] = "SQLite format 3";
|
||||
if( sz<512 ) return 0;
|
||||
rc = pFile->pMethods->xRead(pFile, zHdr, sizeof(zHdr), 0);
|
||||
if( rc ) return 0;
|
||||
return memcmp(zHdr, aSqliteHdr, sizeof(zHdr))==0;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 append-mark, return -1;
|
||||
*/
|
||||
static sqlite3_int64 apndReadMark(sqlite3_int64 sz, sqlite3_file *pFile){
|
||||
int rc, i;
|
||||
sqlite3_int64 iMark;
|
||||
unsigned char a[APND_MARK_SIZE];
|
||||
|
||||
if( sz<=APND_MARK_SIZE ) 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))<<56;
|
||||
for(i=1; i<8; i++){
|
||||
iMark += (sqlite3_int64)a[APND_MARK_PREFIX_SZ+i]<<(56-8*i);
|
||||
}
|
||||
return iMark;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open an apnd file handle.
|
||||
*/
|
||||
static int apndOpen(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName,
|
||||
sqlite3_file *pFile,
|
||||
int flags,
|
||||
int *pOutFlags
|
||||
){
|
||||
ApndFile *p;
|
||||
sqlite3_file *pSubFile;
|
||||
sqlite3_vfs *pSubVfs;
|
||||
int rc;
|
||||
sqlite3_int64 sz;
|
||||
pSubVfs = ORIGVFS(pVfs);
|
||||
if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
|
||||
return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
|
||||
}
|
||||
p = (ApndFile*)pFile;
|
||||
memset(p, 0, sizeof(*p));
|
||||
pSubFile = ORIGFILE(pFile);
|
||||
p->base.pMethods = &apnd_io_methods;
|
||||
rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
|
||||
if( rc ) goto apnd_open_done;
|
||||
rc = pSubFile->pMethods->xFileSize(pSubFile, &sz);
|
||||
if( rc ){
|
||||
pSubFile->pMethods->xClose(pSubFile);
|
||||
goto apnd_open_done;
|
||||
}
|
||||
if( apndIsOrdinaryDatabaseFile(sz, pSubFile) ){
|
||||
memmove(pFile, pSubFile, pSubVfs->szOsFile);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
p->iMark = 0;
|
||||
p->iPgOne = apndReadMark(sz, pFile);
|
||||
if( p->iPgOne>0 ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( (flags & SQLITE_OPEN_CREATE)==0 ){
|
||||
pSubFile->pMethods->xClose(pSubFile);
|
||||
rc = SQLITE_CANTOPEN;
|
||||
}
|
||||
p->iPgOne = (sz+0xfff) & ~(sqlite3_int64)0xfff;
|
||||
apnd_open_done:
|
||||
if( rc ) pFile->pMethods = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** All other VFS methods are pass-thrus.
|
||||
*/
|
||||
static int apndDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
||||
return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
|
||||
}
|
||||
static int apndAccess(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int flags,
|
||||
int *pResOut
|
||||
){
|
||||
return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
|
||||
}
|
||||
static int apndFullPathname(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zPath,
|
||||
int nOut,
|
||||
char *zOut
|
||||
){
|
||||
return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut);
|
||||
}
|
||||
static void *apndDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
||||
return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
|
||||
}
|
||||
static void apndDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
||||
ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
|
||||
}
|
||||
static void (*apndDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
||||
return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
|
||||
}
|
||||
static void apndDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
||||
ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
|
||||
}
|
||||
static int apndRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
||||
return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
|
||||
}
|
||||
static int apndSleep(sqlite3_vfs *pVfs, int nMicro){
|
||||
return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
|
||||
}
|
||||
static int apndCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
||||
return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
|
||||
}
|
||||
static int apndGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
||||
return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
|
||||
}
|
||||
static int apndCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
||||
return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
|
||||
}
|
||||
static int apndSetSystemCall(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName,
|
||||
sqlite3_syscall_ptr pCall
|
||||
){
|
||||
return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall);
|
||||
}
|
||||
static sqlite3_syscall_ptr apndGetSystemCall(
|
||||
sqlite3_vfs *pVfs,
|
||||
const char *zName
|
||||
){
|
||||
return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName);
|
||||
}
|
||||
static const char *apndNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
|
||||
return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
/*
|
||||
** This routine is called when the extension is loaded.
|
||||
** Register the new VFS.
|
||||
*/
|
||||
int sqlite3_appendvfs_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_vfs *pOrig;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
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;
|
||||
}
|
||||
@@ -486,8 +486,7 @@ TESTPROGS = \
|
||||
sqlite3_analyzer$(EXE) \
|
||||
sqlite3_checker$(EXE) \
|
||||
sqldiff$(EXE) \
|
||||
dbhash$(EXE) \
|
||||
sqltclsh$(EXE)
|
||||
dbhash$(EXE)
|
||||
|
||||
# Databases containing fuzzer test cases
|
||||
#
|
||||
@@ -814,12 +813,6 @@ sqlite3_analyzer.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl $
|
||||
sqlite3_analyzer$(EXE): sqlite3_analyzer.c
|
||||
$(TCCX) $(TCL_FLAGS) sqlite3_analyzer.c -o $@ $(LIBTCL) $(THREADLIB)
|
||||
|
||||
sqltclsh.c: sqlite3.c $(TOP)/src/tclsqlite.c $(TOP)/tool/sqltclsh.tcl $(TOP)/ext/misc/appendvfs.c $(TOP)/tool/mkccode.tcl
|
||||
tclsh $(TOP)/tool/mkccode.tcl $(TOP)/tool/sqltclsh.c.in >sqltclsh.c
|
||||
|
||||
sqltclsh$(EXE): sqltclsh.c
|
||||
$(TCCX) $(TCL_FLAGS) sqltclsh.c -o $@ $(LIBTCL) $(THREADLIB)
|
||||
|
||||
sqlite3_expert$(EXE): $(TOP)/ext/expert/sqlite3expert.h $(TOP)/ext/expert/sqlite3expert.c $(TOP)/ext/expert/expert.c sqlite3.c
|
||||
$(TCCX) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION $(TOP)/ext/expert/sqlite3expert.c $(TOP)/ext/expert/expert.c sqlite3.c -o sqlite3_expert$(EXE) $(THREADLIB)
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
C Fix\sthe\sappendvfs\sso\sthat\sit\sleaves\ssqlite3_file->pMethods\sas\sNULL\sif\sit\nfails\sto\sopen.
|
||||
D 2018-01-06T13:33:21.118
|
||||
F Makefile.in f2dc8c140e1d728157834da295eaaa8a0cb29620595c2a9f0efc7258797e6f24
|
||||
C Omit\sall\ssqlite3_trace()\soutput\sfrom\sthe\striggers\sassociated\swith\nforeign\skey\sconstraints.
|
||||
D 2017-12-27T21:30:34.358
|
||||
F Makefile.in ceb40bfcb30ebba8e1202b34c56ff7e13e112f9809e2381d99be32c2726058f5
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc 80cca5fd5760cb3e26aaedaf6ef0df155cab3761a98efd0aa819973713839ca9
|
||||
F Makefile.msc 6480671f7c129e61208d69492b3c71ce4310d49fceac83cfb17f1c081e242b69
|
||||
F README.md eeae1e552f93ef72ef7c5b8f6647b368a001c28820ad1df179d3dae602bef681
|
||||
F VERSION 0c10cdfed866fdd2d80434f64f042c3330f1daaed12e54287beb104f04b3faaf
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
@@ -106,7 +106,7 @@ F ext/fts3/unicode/parseunicode.tcl da577d1384810fb4e2b209bf3313074353193e95
|
||||
F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0
|
||||
F ext/fts5/fts5.h 62f3e33ceeb9a428db139f9c012186b371da1cc7
|
||||
F ext/fts5/fts5Int.h eda28e3a0a5d87c412e8355fe35da875b04cb389908c8eb0d867ad662adbc491
|
||||
F ext/fts5/fts5_aux.c 67acf8d51723cf28ffc3828210ba662df4b8d267
|
||||
F ext/fts5/fts5_aux.c ca666a3bbe07c5a3bbe9fffaea19c935a1efaf337333e28bad7bdd1971ffd093
|
||||
F ext/fts5/fts5_buffer.c 1dd1ec0446b3acfc2d7d407eb894762a461613e2695273f48e449bfd13e973ff
|
||||
F ext/fts5/fts5_config.c 5af9c360e99669d29f06492c370892394aba0857
|
||||
F ext/fts5/fts5_expr.c 01048018d21524e2c302b063ff5c3cdcf546e03297215e577205d85b47499deb
|
||||
@@ -129,7 +129,7 @@ F ext/fts5/test/fts5ab.test 9205c839332c908aaad2b01ab8670ece8b161e8f2ec8a9fabf18
|
||||
F ext/fts5/test/fts5ac.test a7aa7e1fefc6e1918aa4d3111d5c44a09177168e962c5fd2cca9620de8a7ed6d
|
||||
F ext/fts5/test/fts5ad.test e8cf959dfcd57c8e46d6f5f25665686f3b6627130a9a981371dafdf6482790de
|
||||
F ext/fts5/test/fts5ae.test 1142d16d9cc193894dc13cc8f9c7a8a21411ac61b5567a878514df6f9f0d7bb7
|
||||
F ext/fts5/test/fts5af.test aa635947bda31ac87fbe99483eef4d9a8571f58ad89c75dfb63312a35688eceb
|
||||
F ext/fts5/test/fts5af.test 724247405b13f8f06cc6ce464dc4f152dc5dd4e86b12c2099685d8f19747bf7b
|
||||
F ext/fts5/test/fts5ag.test 7816f25a0707578f08145ab539fc0ca025f8951e788b28a6a18a06b2099469dd
|
||||
F ext/fts5/test/fts5ah.test 27b5a33bfd0363ca8a4dc659e6e2a5df3dea1c3c5b04bc51ca6aeb1277bd9b21
|
||||
F ext/fts5/test/fts5ai.test d837c42249c0d8ad1a2912270e22cf2f303790a611f85c0be3a58e42a3696e3d
|
||||
@@ -266,7 +266,6 @@ F ext/lsm1/tool/mklsm1c.tcl f31561bbee5349f0a554d1ad7236ac1991fc09176626f529f607
|
||||
F ext/misc/README.md 8e008c8d2b02e09096b31dfba033253ac27c6c06a18aa5826e299fa7601d90b2
|
||||
F ext/misc/amatch.c 6db4607cb17c54b853a2d7c7c36046d004853f65b9b733e6f019d543d5dfae87
|
||||
F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb
|
||||
F ext/misc/appendvfs.c 24b72d0556e81939c19316ca48c628938b4bd0ec81dac076406260c3ccd51def
|
||||
F ext/misc/btreeinfo.c d7fd9a2fe2fa33ba28488e2fce703ebecc759219ea9e0bb3b254784866c0a676
|
||||
F ext/misc/carray.c ed96c218ea940b85c9a274c4d9c59fe9491c299147a38a8bba537687bd6c6005
|
||||
F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704
|
||||
@@ -402,7 +401,7 @@ F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 992bddc5dc2f37faac929cabc367a89304436566eb385ab89e83d94f4107ad69
|
||||
F main.mk 50bac9920024b5485f06398b3980f09e97ab28cd4b5b6dcd829d2a5e3ce22e7a
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
@@ -424,7 +423,7 @@ F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
|
||||
F src/btree.c b83a6b03f160528020bb965f0c3a40af5286cd4923c3870fd218177f03a120a7
|
||||
F src/btree.h 32ef5d3f25dc70ef1ee9cecf84a023c21378f06a57cd701d2e866e141b150f09
|
||||
F src/btreeInt.h 55b702efce17e5d1941865464227d3802cfc9c7c832fac81d4c94dced47a71fc
|
||||
F src/build.c ed567f088edbc305dad33a6b14e08f8216a3860f6bad1d180450d5a5414bf346
|
||||
F src/build.c a03eb5a1cfff74784c24a4478ba5455711571936f1ac9d43f94fa7df57509977
|
||||
F src/callback.c fe677cb5f5abb02f7a772a62a98c2f516426081df68856e8f2d5f950929b966a
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c ff1be3eed7bdd75aaca61ca8dc848f7c9f850ef2fb9cb56f2734e922a098f9c0
|
||||
@@ -432,7 +431,7 @@ F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
|
||||
F src/dbpage.c 8db4c97f630e7d83f884ea75caf1ffd0988c160e9d530194d93721c80821e0f6
|
||||
F src/dbstat.c 7a4ba8518b6369ef3600c49cf9c918ad979acba610b2aebef1b656d649b96720
|
||||
F src/delete.c 74667ad914ac143731a444a1bacf29ceb18f6eded8a0dd17aafae80baa07f8bb
|
||||
F src/expr.c fe11b91bb65b869143bd42023427c4429778ae42c0a0db7762f68f75b347a958
|
||||
F src/expr.c f03666d7f50caf4139eb777085833808028e8af6b6dfafa722b3e843a369a126
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c d617daf66b5515e2b42c1405b2b4984c30ca50fb705ab164271a9bf66c69e331
|
||||
F src/func.c 0fb9a2d678d3c8aba89b46468b309cd7e8fa9806a369a30aa89024660845bb13
|
||||
@@ -444,8 +443,8 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c cb67cc56ef2ddd13e6944b2c0dd08a920bcd9503230adef8b9928d338097c722
|
||||
F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
|
||||
F src/loadext.c 55bcc3c741059a1056859e8adaf133aa179e22be12215c0936b2f354ef71209b
|
||||
F src/main.c 7ce55fa3c0bf669944de309ebab1655ed06ec67869adb0372c7a1062e461c448
|
||||
F src/malloc.c a02c9e69bc76bee0f639416b947a946412890b606301454727feadcb313536d6
|
||||
F src/main.c 690c4134f944cbd5b71d59dd6e61ce4131f6a50ab774f38108e57d07d79cf876
|
||||
F src/malloc.c 6f684fd039f53bf9195193eb0cde731a2954970fabc6ef054ba379b6052dc296
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
|
||||
F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3
|
||||
@@ -468,7 +467,7 @@ F src/os_win.c 0a4afa35cc8e812000df3ea2f64b476131b39e29e75d8007d0504726e4761de4
|
||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||
F src/pager.c 07cf850241667874fcce9d7d924c814305e499b26c804322e2261247b5921903
|
||||
F src/pager.h 581698f2177e8bd4008fe4760898ce20b6133d1df22139b9101b5155f900df7a
|
||||
F src/parse.y d79001da275bfe344b409006b85e81e486a0f6afc3762fdf0944f000f4aa0111
|
||||
F src/parse.y 44cbbc3e132ea128258eff7be7f6d5c5dfa25522f89ec8b5501276966511bd50
|
||||
F src/pcache.c 7ae91a4557a43d77d449accbfdc68846e6516f8e2eda46e8bbe4536fb669b201
|
||||
F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170
|
||||
F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880
|
||||
@@ -480,11 +479,11 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c bbee7e31d369a18a2f4836644769882e9c5d40ef4a3af911db06410b65cb3730
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c 8b22abe193e4d8243befa2038e4ae2405802fed1c446e5e502d11f652e09ba74
|
||||
F src/shell.c.in 339169a3d1307b5566ebe9ce15832d03439206106724c78cc3d9125a7b851795
|
||||
F src/sqlite.h.in 2126192945019d4cdce335cb236b440a05ec75c93e4cd94c9c6d6e7fcc654cc4
|
||||
F src/shell.c.in f3ec8f90dd698ea98781a90642c91eacbc24f4e55bb551c7b2762000d3ef55dc
|
||||
F src/sqlite.h.in b4dc75265ed04b98e2184011a7dd0054ce2137ff84867a6be8b4f3bdfbc03d30
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h c02d628cca67f3889c689d82d25c3eb45e2c155db08e4c6089b5840d64687d34
|
||||
F src/sqliteInt.h 003b78433baae4e5c997f99f2f9cf98d90754f256baeacb32f8189569a48251f
|
||||
F src/sqliteInt.h 394655443ab2ee9bde2c2f629bd1e613d5ee211dd6c16995f68e6014bbe60b5d
|
||||
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
||||
F src/status.c 9737ed017279a9e0c5da748701c3c7bf1e8ae0dae459aad20dd64fcff97a7e35
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
@@ -543,12 +542,12 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c 1003d6d90c6783206c711f0a9397656fa5b055209f4d092caa43bb3bf5215db5
|
||||
F src/treeview.c eae35972ff44f67064de2eaf35f04afe94e7aea3271a8b3bcebb3f954880fec3
|
||||
F src/trigger.c 775053eecf6b73062e243404b56f5064446254d5cce17d8704d5cdffd72a546a
|
||||
F src/trigger.c a34539c69433276d37b0da9a89c117726ff2d292c0902895af1f393a983cd3a1
|
||||
F src/update.c 961bd1265d4d1e5cd65c9a54fa5122fb7aefcb003fcf2de0c092fceb7e58972c
|
||||
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
|
||||
F src/util.c d01fa6f45bfad3b65fb2490513aa2e0676412c61b4b094340b513cf72c3704a4
|
||||
F src/util.c 7315e97a8dc2c8e19ca64196c652cf0a65d13fd0a211b2cec082062372dc6261
|
||||
F src/vacuum.c 90839322fd5f00df9617eb21b68beda9b6e2a2937576b0d65985e4aeb1c53739
|
||||
F src/vdbe.c 3393b508d9ad084ffce232a7c53e375ef5ac99b50b685c5131fcdfce97a9d534
|
||||
F src/vdbe.c e0a1ca7539d7071d5fa582bdf27195a51197b5ae1ab0f143888b1561df592fd0
|
||||
F src/vdbe.h d50cadf12bcf9fb99117ef392ce1ea283aa429270481426b6e8b0280c101fd97
|
||||
F src/vdbeInt.h 1fe00770144c12c4913128f35262d11527ef3284561baaab59b947a41c08d0d9
|
||||
F src/vdbeapi.c 9c670ca0dcc1cd86373aa353b747b26fe531ca5cd4331690c611d1f03842e2a1
|
||||
@@ -659,7 +658,7 @@ F test/busy.test 510dc6daaad18bcbbc085bcc6217d6dc418def5e73f72ce1475eea0cb783472
|
||||
F test/cache.test 13bc046b26210471ca6f2889aceb1ea52dc717de
|
||||
F test/cacheflush.test af25bb1509df04c1da10e38d8f322d66eceedf61
|
||||
F test/cachespill.test 895997f84a25b323b166aecb69baab2d6380ea98f9e0bcc688c4493c535cfab9
|
||||
F test/capi2.test 011c16da245fdc0106a2785035de6b242c05e738
|
||||
F test/capi2.test d3267a146df2251e7ad09a672d59e33e836d74c6fbeb843c18e87835ef646c12
|
||||
F test/capi3.test 986e57cea8ab423b3fc8c2e3b69330394252d3d2a4496122ff3749e258305695
|
||||
F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
|
||||
F test/capi3c.test 7ebed1d8fa2f3190149d556fe8cff5a006be62af437c5c4640db614470126098
|
||||
@@ -683,7 +682,7 @@ F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a
|
||||
F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
|
||||
F test/collateB.test 1e68906951b846570f29f20102ed91d29e634854ee47454d725f2151ecac0b95
|
||||
F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1
|
||||
F test/colname.test a7ecb8f1d6d8b30a6cf8fa84a2cd6f6e91cad8296376fabe485cf93cd5eb6229
|
||||
F test/colname.test 101aa39392a1f6883278f588836a3ab99178f8103f78032433400475cc05109f
|
||||
F test/conflict.test 029faa2d81a0d1cafb5f88614beb663d972c01db
|
||||
F test/conflict2.test bb0b94cf7196c64a3cbd815c66d3ee98c2fecd9c
|
||||
F test/conflict3.test a83db76a6c3503b2fa057c7bfb08c318d8a422202d8bc5b86226e078e5b49ff9
|
||||
@@ -789,7 +788,7 @@ F test/extraquick.test cb254400bd42bfb777ff675356aabf3287978f79
|
||||
F test/fallocate.test 87b5e43c872b7e69cd80b7b8813eb102b571a75d45dda24e38b65537bcc85733
|
||||
F test/filectrl.test 6e871c2d35dead1d9a88e176e8d2ca094fec6bb3
|
||||
F test/filefmt.test f393e80c4b8d493b7a7f8f3809a8425bbf4292af1f5140f01cb1427798a2bbd4
|
||||
F test/fkey1.test ba64806ff9a04eecab2679caad377ae99a5e94e4
|
||||
F test/fkey1.test 9d7e3a0d409e7f64ab077af3b4fc5e5ce1a4e8d8f1272b65b9d93480aeb1fa2b
|
||||
F test/fkey2.test 155809016fad6b2a1491facf2ac53a551bc57c2c
|
||||
F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
|
||||
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
|
||||
@@ -1249,7 +1248,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef
|
||||
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
|
||||
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
|
||||
F test/speedtest1.c e44c5fccddcfe916c3bf7fe2f87dcc4b4fd66a0d923eb83515f311212670f267
|
||||
F test/speedtest1.c a5faf4cbe5769eee4b721b3875cb3f12520a9b99d9026b1063b47c39603375b8
|
||||
F test/spellfix.test f9c1f431e2c096c8775fec032952320c0e4700db
|
||||
F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3
|
||||
F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33
|
||||
@@ -1444,7 +1443,7 @@ F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
|
||||
F test/tkt4018.test 18dbc6617f7a4b90e938d1bd6d26ad18daafaf08
|
||||
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
|
||||
F test/tpch01.test 04adbf8d8300fa60a222f28d901abd76e7be6dd4
|
||||
F test/trace.test 6f676313e3ebd2a50585036d2f212a3319dd5836
|
||||
F test/trace.test a659a9862957f4789e37a92b3bf6d2caf5c86b02cdeefc41e850ae53acf6992a
|
||||
F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983
|
||||
F test/trace3.test 56ab944fddacf628b118cc298503fc45c2e50ab0
|
||||
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
|
||||
@@ -1609,8 +1608,8 @@ F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
|
||||
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
|
||||
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
|
||||
F tool/kvtest-speed.sh 4761a9c4b3530907562314d7757995787f7aef8f
|
||||
F tool/lemon.c e6056373044d55296d21f81467dba7632bbb81dc49af072b3f0e76338771497e
|
||||
F tool/lempar.c 967ebf585cd09b11b89d255d213865109a9c4ff075680d22580a2826de288c89
|
||||
F tool/lemon.c 7f7735326ca9c3b48327b241063cee52d35d44e20ebe1b3624a81658052a4d39
|
||||
F tool/lempar.c dddd4f592b8bad36aec4500d456c5db5fe42fefc4ee384913880439d8917f87a
|
||||
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
|
||||
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
|
||||
F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca
|
||||
@@ -1648,7 +1647,7 @@ F tool/showstat4.c 0682ebea7abf4d3657f53c4a243f2e7eab48eab344ed36a94bb75dcd19a5c
|
||||
F tool/showwal.c ad9d768f96ca6199ad3a8c9562d679680bd032dd01204ea3e5ea6fb931d81847
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
F tool/spaceanal.tcl 4bfd19aad7eb3ce0372ef0255f58035e0bba4ff5e9acfd763a10c6fb365c8dec
|
||||
F tool/speed-check.sh a97ae367e9172a706101901e7caef48f1a14fc8a49053b25e79f6a67296b3412
|
||||
F tool/speed-check.sh 9ae425da8819e54e780cf494fc6d8175dfb16e109ae3214a45a5c9bb2b74e2c4
|
||||
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
|
||||
F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
@@ -1657,8 +1656,6 @@ F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd
|
||||
F tool/split-sqlite3c.tcl 3efcd4240b738f6bb2b5af0aea7e1e0ef9bc1c61654f645076cec883030b710c
|
||||
F tool/sqldiff.c 30879bbc8de686df4624e86adce2d8981f500904c1cfb55b5d1eea2ffd9341eb
|
||||
F tool/sqlite3_analyzer.c.in 7eeaae8b0d7577662acaabbb11107af0659d1b41bc1dfdd4d91422de27127968
|
||||
F tool/sqltclsh.c.in e1f48150f755bfbe0194478cba50aa9f2f5183bb1efbdd6456532cce3cd2e18d
|
||||
F tool/sqltclsh.tcl 18adb7d4a24374a3c79a2acc011b5f54a22186beec13056b0fa3ca011711c4f3
|
||||
F tool/srcck1.c 371de5363b70154012955544f86fdee8f6e5326f
|
||||
F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
|
||||
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
|
||||
@@ -1690,7 +1687,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P a6d5c7c2aabd70b4fb2bd8f6278f70272a5bd166adf2f7225ea582e10a46fafa
|
||||
R 11d42f816c24c039c3f505e6266a4bae
|
||||
P fe3d2b97d8945e6a9636472f77bcdc92cc5dda8b3e6e3a4fcbdd0a212ea5b9aa
|
||||
R 22de62f1b2836f49ea0af57f6725c723
|
||||
U drh
|
||||
Z 83fb91d11f3679f988cee9a994d344b9
|
||||
Z 3aa637606646f6f00ff3a9a21b2a3987
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
46b341e3ad11b807ae50f001b970299de7ea0d523dbb639ee10f1d5aca5d958b
|
||||
fda08e3d10cc850664a356efdafcfc68187053849e1b00991b0b35d892a6776b
|
||||
+13
-8
@@ -1221,34 +1221,37 @@ char sqlite3AffinityType(const char *zIn, u8 *pszEst){
|
||||
** This routine is called by the parser while in the middle of
|
||||
** parsing a CREATE TABLE statement.
|
||||
*/
|
||||
void sqlite3AddDefaultValue(Parse *pParse, ExprSpan *pSpan){
|
||||
void sqlite3AddDefaultValue(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Expr *pExpr, /* The parsed expression of the default value */
|
||||
const char *zStart, /* Start of the default value text */
|
||||
const char *zEnd /* First character past end of defaut value text */
|
||||
){
|
||||
Table *p;
|
||||
Column *pCol;
|
||||
sqlite3 *db = pParse->db;
|
||||
p = pParse->pNewTable;
|
||||
if( p!=0 ){
|
||||
pCol = &(p->aCol[p->nCol-1]);
|
||||
if( !sqlite3ExprIsConstantOrFunction(pSpan->pExpr, db->init.busy) ){
|
||||
if( !sqlite3ExprIsConstantOrFunction(pExpr, db->init.busy) ){
|
||||
sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant",
|
||||
pCol->zName);
|
||||
}else{
|
||||
/* A copy of pExpr is used instead of the original, as pExpr contains
|
||||
** tokens that point to volatile memory. The 'span' of the expression
|
||||
** is required by pragma table_info.
|
||||
** tokens that point to volatile memory.
|
||||
*/
|
||||
Expr x;
|
||||
sqlite3ExprDelete(db, pCol->pDflt);
|
||||
memset(&x, 0, sizeof(x));
|
||||
x.op = TK_SPAN;
|
||||
x.u.zToken = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
|
||||
(int)(pSpan->zEnd - pSpan->zStart));
|
||||
x.pLeft = pSpan->pExpr;
|
||||
x.u.zToken = sqlite3DbSpanDup(db, zStart, zEnd);
|
||||
x.pLeft = pExpr;
|
||||
x.flags = EP_Skip;
|
||||
pCol->pDflt = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE);
|
||||
sqlite3DbFree(db, x.u.zToken);
|
||||
}
|
||||
}
|
||||
sqlite3ExprDelete(db, pSpan->pExpr);
|
||||
sqlite3ExprDelete(db, pExpr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1965,6 +1968,7 @@ void sqlite3EndTable(
|
||||
pParse->nTab = 2;
|
||||
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
|
||||
if( pParse->nErr ) return;
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
|
||||
if( pSelTab==0 ) return;
|
||||
assert( p->aCol==0 );
|
||||
@@ -1975,6 +1979,7 @@ void sqlite3EndTable(
|
||||
sqlite3DeleteTable(db, pSelTab);
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
|
||||
sqlite3Select(pParse, pSelect, &dest);
|
||||
if( pParse->nErr ) return;
|
||||
sqlite3VdbeEndCoroutine(v, regYield);
|
||||
sqlite3VdbeJumpHere(v, addrTop - 1);
|
||||
addrInsLoop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
|
||||
|
||||
+3
-4
@@ -1654,17 +1654,16 @@ void sqlite3ExprListSetName(
|
||||
void sqlite3ExprListSetSpan(
|
||||
Parse *pParse, /* Parsing context */
|
||||
ExprList *pList, /* List to which to add the span. */
|
||||
ExprSpan *pSpan /* The span to be added */
|
||||
const char *zStart, /* Start of the span */
|
||||
const char *zEnd /* End of the span */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
assert( pList!=0 || db->mallocFailed!=0 );
|
||||
if( pList ){
|
||||
struct ExprList_item *pItem = &pList->a[pList->nExpr-1];
|
||||
assert( pList->nExpr>0 );
|
||||
assert( db->mallocFailed || pItem->pExpr==pSpan->pExpr );
|
||||
sqlite3DbFree(db, pItem->zSpan);
|
||||
pItem->zSpan = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
|
||||
(int)(pSpan->zEnd - pSpan->zStart));
|
||||
pItem->zSpan = sqlite3DbSpanDup(db, zStart, zEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
@@ -3911,6 +3911,22 @@ int sqlite3_test_control(int op, ...){
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(YYCOVERAGE)
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_PARSER_COVERAGE, FILE *out)
|
||||
**
|
||||
** This test control (only available when SQLite is compiled with
|
||||
** -DYYCOVERAGE) writes a report onto "out" that shows all
|
||||
** state/lookahead combinations in the parser state machine
|
||||
** which are never exercised. If any state is missed, make the
|
||||
** return code SQLITE_ERROR.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_PARSER_COVERAGE: {
|
||||
FILE *out = va_arg(ap, FILE*);
|
||||
if( sqlite3ParserCoverage(out) ) rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
#endif /* defined(YYCOVERAGE) */
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_UNTESTABLE */
|
||||
|
||||
@@ -627,6 +627,19 @@ char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
|
||||
return zNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** The text between zStart and zEnd represents a phrase within a larger
|
||||
** SQL statement. Make a copy of this phrase in space obtained form
|
||||
** sqlite3DbMalloc(). Omit leading and trailing whitespace.
|
||||
*/
|
||||
char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){
|
||||
int n;
|
||||
while( sqlite3Isspace(zStart[0]) ) zStart++;
|
||||
n = (int)(zEnd - zStart);
|
||||
while( n>0 && sqlite3Isspace(zStart[n-1]) ) n--;
|
||||
return sqlite3DbStrNDup(db, zStart, n);
|
||||
}
|
||||
|
||||
/*
|
||||
** Free any prior content in *pz and replace it with a copy of zNew.
|
||||
*/
|
||||
|
||||
+177
-247
@@ -273,26 +273,44 @@ typename(A) ::= typename(A) ids(Y). {A.n=Y.n+(int)(Y.z-A.z);}
|
||||
signed ::= plus_num.
|
||||
signed ::= minus_num.
|
||||
|
||||
// The scanpt non-terminal takes a value which is a pointer to the
|
||||
// input text just past the last token that has been shifted into
|
||||
// the parser. By surrounding some phrase in the grammar with two
|
||||
// scanpt non-terminals, we can capture the input text for that phrase.
|
||||
// For example:
|
||||
//
|
||||
// something ::= .... scanpt(A) phrase scanpt(Z).
|
||||
//
|
||||
// The text that is parsed as "phrase" is a string starting at A
|
||||
// and containing (int)(Z-A) characters. There might be some extra
|
||||
// whitespace on either end of the text, but that can be removed in
|
||||
// post-processing, if needed.
|
||||
//
|
||||
%type scanpt {const char*}
|
||||
scanpt(A) ::= . {
|
||||
assert( yyLookahead!=YYNOCODE );
|
||||
A = yyLookaheadToken.z;
|
||||
}
|
||||
|
||||
// "carglist" is a list of additional constraints that come after the
|
||||
// column name and column type in a CREATE TABLE statement.
|
||||
//
|
||||
carglist ::= carglist ccons.
|
||||
carglist ::= .
|
||||
ccons ::= CONSTRAINT nm(X). {pParse->constraintName = X;}
|
||||
ccons ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,&X);}
|
||||
ccons ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,&X);}
|
||||
ccons ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,&X);}
|
||||
ccons ::= DEFAULT MINUS(A) term(X). {
|
||||
ExprSpan v;
|
||||
v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, X.pExpr, 0);
|
||||
v.zStart = A.z;
|
||||
v.zEnd = X.zEnd;
|
||||
sqlite3AddDefaultValue(pParse,&v);
|
||||
ccons ::= DEFAULT scanpt(A) term(X) scanpt(Z).
|
||||
{sqlite3AddDefaultValue(pParse,X,A,Z);}
|
||||
ccons ::= DEFAULT LP(A) expr(X) RP(Z).
|
||||
{sqlite3AddDefaultValue(pParse,X,A.z+1,Z.z);}
|
||||
ccons ::= DEFAULT PLUS(A) term(X) scanpt(Z).
|
||||
{sqlite3AddDefaultValue(pParse,X,A.z,Z);}
|
||||
ccons ::= DEFAULT MINUS(A) term(X) scanpt(Z). {
|
||||
Expr *p = sqlite3PExpr(pParse, TK_UMINUS, X, 0);
|
||||
sqlite3AddDefaultValue(pParse,p,A.z,Z);
|
||||
}
|
||||
ccons ::= DEFAULT id(X). {
|
||||
ExprSpan v;
|
||||
spanExpr(&v, pParse, TK_STRING, X);
|
||||
sqlite3AddDefaultValue(pParse,&v);
|
||||
ccons ::= DEFAULT scanpt id(X). {
|
||||
Expr *p = tokenExpr(pParse, TK_STRING, X);
|
||||
sqlite3AddDefaultValue(pParse,p,X.z,X.z+X.n);
|
||||
}
|
||||
|
||||
// In addition to the type name, we also care about the primary key and
|
||||
@@ -304,7 +322,7 @@ ccons ::= PRIMARY KEY sortorder(Z) onconf(R) autoinc(I).
|
||||
{sqlite3AddPrimaryKey(pParse,0,R,I,Z);}
|
||||
ccons ::= UNIQUE onconf(R). {sqlite3CreateIndex(pParse,0,0,0,0,R,0,0,0,0,
|
||||
SQLITE_IDXTYPE_UNIQUE);}
|
||||
ccons ::= CHECK LP expr(X) RP. {sqlite3AddCheckConstraint(pParse,X.pExpr);}
|
||||
ccons ::= CHECK LP expr(X) RP. {sqlite3AddCheckConstraint(pParse,X);}
|
||||
ccons ::= REFERENCES nm(T) eidlist_opt(TA) refargs(R).
|
||||
{sqlite3CreateForeignKey(pParse,0,&T,TA,R);}
|
||||
ccons ::= defer_subclause(D). {sqlite3DeferForeignKey(pParse,D);}
|
||||
@@ -355,7 +373,7 @@ tcons ::= UNIQUE LP sortlist(X) RP onconf(R).
|
||||
{sqlite3CreateIndex(pParse,0,0,0,X,R,0,0,0,0,
|
||||
SQLITE_IDXTYPE_UNIQUE);}
|
||||
tcons ::= CHECK LP expr(E) RP onconf.
|
||||
{sqlite3AddCheckConstraint(pParse,E.pExpr);}
|
||||
{sqlite3AddCheckConstraint(pParse,E);}
|
||||
tcons ::= FOREIGN KEY LP eidlist(FA) RP
|
||||
REFERENCES nm(T) eidlist_opt(TA) refargs(R) defer_subclause_opt(D). {
|
||||
sqlite3CreateForeignKey(pParse, FA, &T, TA, R);
|
||||
@@ -549,16 +567,16 @@ distinct(A) ::= . {A = 0;}
|
||||
%destructor sclp {sqlite3ExprListDelete(pParse->db, $$);}
|
||||
sclp(A) ::= selcollist(A) COMMA.
|
||||
sclp(A) ::= . {A = 0;}
|
||||
selcollist(A) ::= sclp(A) expr(X) as(Y). {
|
||||
A = sqlite3ExprListAppend(pParse, A, X.pExpr);
|
||||
selcollist(A) ::= sclp(A) scanpt(B) expr(X) scanpt(Z) as(Y). {
|
||||
A = sqlite3ExprListAppend(pParse, A, X);
|
||||
if( Y.n>0 ) sqlite3ExprListSetName(pParse, A, &Y, 1);
|
||||
sqlite3ExprListSetSpan(pParse,A,&X);
|
||||
sqlite3ExprListSetSpan(pParse,A,B,Z);
|
||||
}
|
||||
selcollist(A) ::= sclp(A) STAR. {
|
||||
selcollist(A) ::= sclp(A) scanpt STAR. {
|
||||
Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
|
||||
A = sqlite3ExprListAppend(pParse, A, p);
|
||||
}
|
||||
selcollist(A) ::= sclp(A) nm(X) DOT STAR. {
|
||||
selcollist(A) ::= sclp(A) scanpt nm(X) DOT STAR. {
|
||||
Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0);
|
||||
Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1);
|
||||
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
|
||||
@@ -656,7 +674,7 @@ joinop(X) ::= JOIN_KW(A) nm(B) nm(C) JOIN.
|
||||
|
||||
%type on_opt {Expr*}
|
||||
%destructor on_opt {sqlite3ExprDelete(pParse->db, $$);}
|
||||
on_opt(N) ::= ON expr(E). {N = E.pExpr;}
|
||||
on_opt(N) ::= ON expr(E). {N = E;}
|
||||
on_opt(N) ::= . {N = 0;}
|
||||
|
||||
// Note that this block abuses the Token type just a little. If there is
|
||||
@@ -693,11 +711,11 @@ using_opt(U) ::= . {U = 0;}
|
||||
orderby_opt(A) ::= . {A = 0;}
|
||||
orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;}
|
||||
sortlist(A) ::= sortlist(A) COMMA expr(Y) sortorder(Z). {
|
||||
A = sqlite3ExprListAppend(pParse,A,Y.pExpr);
|
||||
A = sqlite3ExprListAppend(pParse,A,Y);
|
||||
sqlite3ExprListSetSortOrder(A,Z);
|
||||
}
|
||||
sortlist(A) ::= expr(Y) sortorder(Z). {
|
||||
A = sqlite3ExprListAppend(pParse,0,Y.pExpr); /*A-overwrites-Y*/
|
||||
A = sqlite3ExprListAppend(pParse,0,Y); /*A-overwrites-Y*/
|
||||
sqlite3ExprListSetSortOrder(A,Z);
|
||||
}
|
||||
|
||||
@@ -715,7 +733,7 @@ groupby_opt(A) ::= GROUP BY nexprlist(X). {A = X;}
|
||||
%type having_opt {Expr*}
|
||||
%destructor having_opt {sqlite3ExprDelete(pParse->db, $$);}
|
||||
having_opt(A) ::= . {A = 0;}
|
||||
having_opt(A) ::= HAVING expr(X). {A = X.pExpr;}
|
||||
having_opt(A) ::= HAVING expr(X). {A = X;}
|
||||
|
||||
%type limit_opt {Expr*}
|
||||
|
||||
@@ -729,11 +747,11 @@ having_opt(A) ::= HAVING expr(X). {A = X.pExpr;}
|
||||
//%destructor limit_opt {sqlite3ExprDelete(pParse->db, $$);}
|
||||
limit_opt(A) ::= . {A = 0;}
|
||||
limit_opt(A) ::= LIMIT expr(X).
|
||||
{A = sqlite3PExpr(pParse,TK_LIMIT,X.pExpr,0);}
|
||||
{A = sqlite3PExpr(pParse,TK_LIMIT,X,0);}
|
||||
limit_opt(A) ::= LIMIT expr(X) OFFSET expr(Y).
|
||||
{A = sqlite3PExpr(pParse,TK_LIMIT,X.pExpr,Y.pExpr);}
|
||||
{A = sqlite3PExpr(pParse,TK_LIMIT,X,Y);}
|
||||
limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
|
||||
{A = sqlite3PExpr(pParse,TK_LIMIT,Y.pExpr,X.pExpr);}
|
||||
{A = sqlite3PExpr(pParse,TK_LIMIT,Y,X);}
|
||||
|
||||
/////////////////////////// The DELETE statement /////////////////////////////
|
||||
//
|
||||
@@ -757,7 +775,7 @@ cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W). {
|
||||
%destructor where_opt {sqlite3ExprDelete(pParse->db, $$);}
|
||||
|
||||
where_opt(A) ::= . {A = 0;}
|
||||
where_opt(A) ::= WHERE expr(X). {A = X.pExpr;}
|
||||
where_opt(A) ::= WHERE expr(X). {A = X;}
|
||||
|
||||
////////////////////////// The UPDATE command ////////////////////////////////
|
||||
//
|
||||
@@ -784,18 +802,18 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
|
||||
%destructor setlist {sqlite3ExprListDelete(pParse->db, $$);}
|
||||
|
||||
setlist(A) ::= setlist(A) COMMA nm(X) EQ expr(Y). {
|
||||
A = sqlite3ExprListAppend(pParse, A, Y.pExpr);
|
||||
A = sqlite3ExprListAppend(pParse, A, Y);
|
||||
sqlite3ExprListSetName(pParse, A, &X, 1);
|
||||
}
|
||||
setlist(A) ::= setlist(A) COMMA LP idlist(X) RP EQ expr(Y). {
|
||||
A = sqlite3ExprListAppendVector(pParse, A, X, Y.pExpr);
|
||||
A = sqlite3ExprListAppendVector(pParse, A, X, Y);
|
||||
}
|
||||
setlist(A) ::= nm(X) EQ expr(Y). {
|
||||
A = sqlite3ExprListAppend(pParse, 0, Y.pExpr);
|
||||
A = sqlite3ExprListAppend(pParse, 0, Y);
|
||||
sqlite3ExprListSetName(pParse, A, &X, 1);
|
||||
}
|
||||
setlist(A) ::= LP idlist(X) RP EQ expr(Y). {
|
||||
A = sqlite3ExprListAppendVector(pParse, 0, X, Y.pExpr);
|
||||
A = sqlite3ExprListAppendVector(pParse, 0, X, Y);
|
||||
}
|
||||
|
||||
////////////////////////// The INSERT command /////////////////////////////////
|
||||
@@ -829,26 +847,18 @@ idlist(A) ::= nm(Y).
|
||||
/////////////////////////// Expression Processing /////////////////////////////
|
||||
//
|
||||
|
||||
%type expr {ExprSpan}
|
||||
%destructor expr {sqlite3ExprDelete(pParse->db, $$.pExpr);}
|
||||
%type term {ExprSpan}
|
||||
%destructor term {sqlite3ExprDelete(pParse->db, $$.pExpr);}
|
||||
%type expr {Expr*}
|
||||
%destructor expr {sqlite3ExprDelete(pParse->db, $$);}
|
||||
%type term {Expr*}
|
||||
%destructor term {sqlite3ExprDelete(pParse->db, $$);}
|
||||
|
||||
%include {
|
||||
/* This is a utility routine used to set the ExprSpan.zStart and
|
||||
** ExprSpan.zEnd values of pOut so that the span covers the complete
|
||||
** range of text beginning with pStart and going to the end of pEnd.
|
||||
*/
|
||||
static void spanSet(ExprSpan *pOut, Token *pStart, Token *pEnd){
|
||||
pOut->zStart = pStart->z;
|
||||
pOut->zEnd = &pEnd->z[pEnd->n];
|
||||
}
|
||||
|
||||
/* Construct a new Expr object from a single identifier. Use the
|
||||
** new Expr to populate pOut. Set the span of pOut to be the identifier
|
||||
** that created the expression.
|
||||
*/
|
||||
static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token t){
|
||||
static Expr *tokenExpr(Parse *pParse, int op, Token t){
|
||||
Expr *p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1);
|
||||
if( p ){
|
||||
memset(p, 0, sizeof(Expr));
|
||||
@@ -866,136 +876,98 @@ idlist(A) ::= nm(Y).
|
||||
p->nHeight = 1;
|
||||
#endif
|
||||
}
|
||||
pOut->pExpr = p;
|
||||
pOut->zStart = t.z;
|
||||
pOut->zEnd = &t.z[t.n];
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
expr(A) ::= term(A).
|
||||
expr(A) ::= LP(B) expr(X) RP(E).
|
||||
{spanSet(&A,&B,&E); /*A-overwrites-B*/ A.pExpr = X.pExpr;}
|
||||
expr(A) ::= id(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/}
|
||||
expr(A) ::= JOIN_KW(X). {spanExpr(&A,pParse,TK_ID,X); /*A-overwrites-X*/}
|
||||
expr(A) ::= LP expr(X) RP. {A = X;}
|
||||
expr(A) ::= id(X). {A=tokenExpr(pParse,TK_ID,X); /*A-overwrites-X*/}
|
||||
expr(A) ::= JOIN_KW(X). {A=tokenExpr(pParse,TK_ID,X); /*A-overwrites-X*/}
|
||||
expr(A) ::= nm(X) DOT nm(Y). {
|
||||
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1);
|
||||
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1);
|
||||
spanSet(&A,&X,&Y); /*A-overwrites-X*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
|
||||
A = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
|
||||
}
|
||||
expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). {
|
||||
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1);
|
||||
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1);
|
||||
Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &Z, 1);
|
||||
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3);
|
||||
spanSet(&A,&X,&Z); /*A-overwrites-X*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
|
||||
A = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
|
||||
}
|
||||
term(A) ::= NULL|FLOAT|BLOB(X). {spanExpr(&A,pParse,@X,X); /*A-overwrites-X*/}
|
||||
term(A) ::= STRING(X). {spanExpr(&A,pParse,@X,X); /*A-overwrites-X*/}
|
||||
term(A) ::= NULL|FLOAT|BLOB(X). {A=tokenExpr(pParse,@X,X); /*A-overwrites-X*/}
|
||||
term(A) ::= STRING(X). {A=tokenExpr(pParse,@X,X); /*A-overwrites-X*/}
|
||||
term(A) ::= INTEGER(X). {
|
||||
A.pExpr = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &X, 1);
|
||||
A.zStart = X.z;
|
||||
A.zEnd = X.z + X.n;
|
||||
A = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &X, 1);
|
||||
}
|
||||
expr(A) ::= VARIABLE(X). {
|
||||
if( !(X.z[0]=='#' && sqlite3Isdigit(X.z[1])) ){
|
||||
u32 n = X.n;
|
||||
spanExpr(&A, pParse, TK_VARIABLE, X);
|
||||
sqlite3ExprAssignVarNumber(pParse, A.pExpr, n);
|
||||
A = tokenExpr(pParse, TK_VARIABLE, X);
|
||||
sqlite3ExprAssignVarNumber(pParse, A, n);
|
||||
}else{
|
||||
/* When doing a nested parse, one can include terms in an expression
|
||||
** that look like this: #1 #2 ... These terms refer to registers
|
||||
** in the virtual machine. #N is the N-th register. */
|
||||
Token t = X; /*A-overwrites-X*/
|
||||
assert( t.n>=2 );
|
||||
spanSet(&A, &t, &t);
|
||||
if( pParse->nested==0 ){
|
||||
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
|
||||
A.pExpr = 0;
|
||||
A = 0;
|
||||
}else{
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
|
||||
if( A.pExpr ) sqlite3GetInt32(&t.z[1], &A.pExpr->iTable);
|
||||
A = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
|
||||
if( A ) sqlite3GetInt32(&t.z[1], &A->iTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
expr(A) ::= expr(A) COLLATE ids(C). {
|
||||
A.pExpr = sqlite3ExprAddCollateToken(pParse, A.pExpr, &C, 1);
|
||||
A.zEnd = &C.z[C.n];
|
||||
A = sqlite3ExprAddCollateToken(pParse, A, &C, 1);
|
||||
}
|
||||
%ifndef SQLITE_OMIT_CAST
|
||||
expr(A) ::= CAST(X) LP expr(E) AS typetoken(T) RP(Y). {
|
||||
spanSet(&A,&X,&Y); /*A-overwrites-X*/
|
||||
A.pExpr = sqlite3ExprAlloc(pParse->db, TK_CAST, &T, 1);
|
||||
sqlite3ExprAttachSubtrees(pParse->db, A.pExpr, E.pExpr, 0);
|
||||
expr(A) ::= CAST LP expr(E) AS typetoken(T) RP. {
|
||||
A = sqlite3ExprAlloc(pParse->db, TK_CAST, &T, 1);
|
||||
sqlite3ExprAttachSubtrees(pParse->db, A, E, 0);
|
||||
}
|
||||
%endif SQLITE_OMIT_CAST
|
||||
expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
|
||||
expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP. {
|
||||
if( Y && Y->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
|
||||
sqlite3ErrorMsg(pParse, "too many arguments on function %T", &X);
|
||||
}
|
||||
A.pExpr = sqlite3ExprFunction(pParse, Y, &X);
|
||||
spanSet(&A,&X,&E);
|
||||
if( D==SF_Distinct && A.pExpr ){
|
||||
A.pExpr->flags |= EP_Distinct;
|
||||
A = sqlite3ExprFunction(pParse, Y, &X);
|
||||
if( D==SF_Distinct && A ){
|
||||
A->flags |= EP_Distinct;
|
||||
}
|
||||
}
|
||||
expr(A) ::= id(X) LP STAR RP(E). {
|
||||
A.pExpr = sqlite3ExprFunction(pParse, 0, &X);
|
||||
spanSet(&A,&X,&E);
|
||||
expr(A) ::= id(X) LP STAR RP. {
|
||||
A = sqlite3ExprFunction(pParse, 0, &X);
|
||||
}
|
||||
term(A) ::= CTIME_KW(OP). {
|
||||
A.pExpr = sqlite3ExprFunction(pParse, 0, &OP);
|
||||
spanSet(&A, &OP, &OP);
|
||||
A = sqlite3ExprFunction(pParse, 0, &OP);
|
||||
}
|
||||
|
||||
%include {
|
||||
/* This routine constructs a binary expression node out of two ExprSpan
|
||||
** objects and uses the result to populate a new ExprSpan object.
|
||||
*/
|
||||
static void spanBinaryExpr(
|
||||
Parse *pParse, /* The parsing context. Errors accumulate here */
|
||||
int op, /* The binary operation */
|
||||
ExprSpan *pLeft, /* The left operand, and output */
|
||||
ExprSpan *pRight /* The right operand */
|
||||
){
|
||||
pLeft->pExpr = sqlite3PExpr(pParse, op, pLeft->pExpr, pRight->pExpr);
|
||||
pLeft->zEnd = pRight->zEnd;
|
||||
}
|
||||
|
||||
/* If doNot is true, then add a TK_NOT Expr-node wrapper around the
|
||||
** outside of *ppExpr.
|
||||
*/
|
||||
static void exprNot(Parse *pParse, int doNot, ExprSpan *pSpan){
|
||||
if( doNot ){
|
||||
pSpan->pExpr = sqlite3PExpr(pParse, TK_NOT, pSpan->pExpr, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expr(A) ::= LP(L) nexprlist(X) COMMA expr(Y) RP(R). {
|
||||
ExprList *pList = sqlite3ExprListAppend(pParse, X, Y.pExpr);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->x.pList = pList;
|
||||
spanSet(&A, &L, &R);
|
||||
expr(A) ::= LP nexprlist(X) COMMA expr(Y) RP. {
|
||||
ExprList *pList = sqlite3ExprListAppend(pParse, X, Y);
|
||||
A = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
|
||||
if( A ){
|
||||
A->x.pList = pList;
|
||||
}else{
|
||||
sqlite3ExprListDelete(pParse->db, pList);
|
||||
}
|
||||
}
|
||||
|
||||
expr(A) ::= expr(A) AND(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
expr(A) ::= expr(A) OR(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
expr(A) ::= expr(A) AND(OP) expr(Y). {A=sqlite3PExpr(pParse,@OP,A,Y);}
|
||||
expr(A) ::= expr(A) OR(OP) expr(Y). {A=sqlite3PExpr(pParse,@OP,A,Y);}
|
||||
expr(A) ::= expr(A) LT|GT|GE|LE(OP) expr(Y).
|
||||
{spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
expr(A) ::= expr(A) EQ|NE(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
{A=sqlite3PExpr(pParse,@OP,A,Y);}
|
||||
expr(A) ::= expr(A) EQ|NE(OP) expr(Y). {A=sqlite3PExpr(pParse,@OP,A,Y);}
|
||||
expr(A) ::= expr(A) BITAND|BITOR|LSHIFT|RSHIFT(OP) expr(Y).
|
||||
{spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
{A=sqlite3PExpr(pParse,@OP,A,Y);}
|
||||
expr(A) ::= expr(A) PLUS|MINUS(OP) expr(Y).
|
||||
{spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
{A=sqlite3PExpr(pParse,@OP,A,Y);}
|
||||
expr(A) ::= expr(A) STAR|SLASH|REM(OP) expr(Y).
|
||||
{spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
expr(A) ::= expr(A) CONCAT(OP) expr(Y). {spanBinaryExpr(pParse,@OP,&A,&Y);}
|
||||
{A=sqlite3PExpr(pParse,@OP,A,Y);}
|
||||
expr(A) ::= expr(A) CONCAT(OP) expr(Y). {A=sqlite3PExpr(pParse,@OP,A,Y);}
|
||||
%type likeop {Token}
|
||||
likeop(A) ::= LIKE_KW|MATCH(A).
|
||||
likeop(A) ::= NOT LIKE_KW|MATCH(X). {A=X; A.n|=0x80000000; /*A-overwrite-X*/}
|
||||
@@ -1003,42 +975,26 @@ expr(A) ::= expr(A) likeop(OP) expr(Y). [LIKE_KW] {
|
||||
ExprList *pList;
|
||||
int bNot = OP.n & 0x80000000;
|
||||
OP.n &= 0x7fffffff;
|
||||
pList = sqlite3ExprListAppend(pParse,0, Y.pExpr);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, A.pExpr);
|
||||
A.pExpr = sqlite3ExprFunction(pParse, pList, &OP);
|
||||
exprNot(pParse, bNot, &A);
|
||||
A.zEnd = Y.zEnd;
|
||||
if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc;
|
||||
pList = sqlite3ExprListAppend(pParse,0, Y);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, A);
|
||||
A = sqlite3ExprFunction(pParse, pList, &OP);
|
||||
if( bNot ) A = sqlite3PExpr(pParse, TK_NOT, A, 0);
|
||||
if( A ) A->flags |= EP_InfixFunc;
|
||||
}
|
||||
expr(A) ::= expr(A) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] {
|
||||
ExprList *pList;
|
||||
int bNot = OP.n & 0x80000000;
|
||||
OP.n &= 0x7fffffff;
|
||||
pList = sqlite3ExprListAppend(pParse,0, Y.pExpr);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, A.pExpr);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, E.pExpr);
|
||||
A.pExpr = sqlite3ExprFunction(pParse, pList, &OP);
|
||||
exprNot(pParse, bNot, &A);
|
||||
A.zEnd = E.zEnd;
|
||||
if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc;
|
||||
pList = sqlite3ExprListAppend(pParse,0, Y);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, A);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, E);
|
||||
A = sqlite3ExprFunction(pParse, pList, &OP);
|
||||
if( bNot ) A = sqlite3PExpr(pParse, TK_NOT, A, 0);
|
||||
if( A ) A->flags |= EP_InfixFunc;
|
||||
}
|
||||
|
||||
%include {
|
||||
/* Construct an expression node for a unary postfix operator
|
||||
*/
|
||||
static void spanUnaryPostfix(
|
||||
Parse *pParse, /* Parsing context to record errors */
|
||||
int op, /* The operator */
|
||||
ExprSpan *pOperand, /* The operand, and output */
|
||||
Token *pPostOp /* The operand token for setting the span */
|
||||
){
|
||||
pOperand->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0);
|
||||
pOperand->zEnd = &pPostOp->z[pPostOp->n];
|
||||
}
|
||||
}
|
||||
|
||||
expr(A) ::= expr(A) ISNULL|NOTNULL(E). {spanUnaryPostfix(pParse,@E,&A,&E);}
|
||||
expr(A) ::= expr(A) NOT NULL(E). {spanUnaryPostfix(pParse,TK_NOTNULL,&A,&E);}
|
||||
expr(A) ::= expr(A) ISNULL|NOTNULL(E). {A = sqlite3PExpr(pParse,@E,A,0);}
|
||||
expr(A) ::= expr(A) NOT NULL. {A = sqlite3PExpr(pParse,TK_NOTNULL,A,0);}
|
||||
|
||||
%include {
|
||||
/* A routine to convert a binary TK_IS or TK_ISNOT expression into a
|
||||
@@ -1060,61 +1016,42 @@ expr(A) ::= expr(A) NOT NULL(E). {spanUnaryPostfix(pParse,TK_NOTNULL,&A,&E);}
|
||||
// is any other expression, code as TK_IS or TK_ISNOT.
|
||||
//
|
||||
expr(A) ::= expr(A) IS expr(Y). {
|
||||
spanBinaryExpr(pParse,TK_IS,&A,&Y);
|
||||
binaryToUnaryIfNull(pParse, Y.pExpr, A.pExpr, TK_ISNULL);
|
||||
A = sqlite3PExpr(pParse,TK_IS,A,Y);
|
||||
binaryToUnaryIfNull(pParse, Y, A, TK_ISNULL);
|
||||
}
|
||||
expr(A) ::= expr(A) IS NOT expr(Y). {
|
||||
spanBinaryExpr(pParse,TK_ISNOT,&A,&Y);
|
||||
binaryToUnaryIfNull(pParse, Y.pExpr, A.pExpr, TK_NOTNULL);
|
||||
A = sqlite3PExpr(pParse,TK_ISNOT,A,Y);
|
||||
binaryToUnaryIfNull(pParse, Y, A, TK_NOTNULL);
|
||||
}
|
||||
|
||||
%include {
|
||||
/* Construct an expression node for a unary prefix operator
|
||||
*/
|
||||
static void spanUnaryPrefix(
|
||||
ExprSpan *pOut, /* Write the new expression node here */
|
||||
Parse *pParse, /* Parsing context to record errors */
|
||||
int op, /* The operator */
|
||||
ExprSpan *pOperand, /* The operand */
|
||||
Token *pPreOp /* The operand token for setting the span */
|
||||
){
|
||||
pOut->zStart = pPreOp->z;
|
||||
pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0);
|
||||
pOut->zEnd = pOperand->zEnd;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
expr(A) ::= NOT(B) expr(X).
|
||||
{spanUnaryPrefix(&A,pParse,@B,&X,&B);/*A-overwrites-B*/}
|
||||
{A = sqlite3PExpr(pParse, @B, X, 0);/*A-overwrites-B*/}
|
||||
expr(A) ::= BITNOT(B) expr(X).
|
||||
{spanUnaryPrefix(&A,pParse,@B,&X,&B);/*A-overwrites-B*/}
|
||||
expr(A) ::= MINUS(B) expr(X). [BITNOT]
|
||||
{spanUnaryPrefix(&A,pParse,TK_UMINUS,&X,&B);/*A-overwrites-B*/}
|
||||
expr(A) ::= PLUS(B) expr(X). [BITNOT]
|
||||
{spanUnaryPrefix(&A,pParse,TK_UPLUS,&X,&B);/*A-overwrites-B*/}
|
||||
{A = sqlite3PExpr(pParse, @B, X, 0);/*A-overwrites-B*/}
|
||||
expr(A) ::= MINUS expr(X). [BITNOT]
|
||||
{A = sqlite3PExpr(pParse, TK_UMINUS, X, 0);}
|
||||
expr(A) ::= PLUS expr(X). [BITNOT]
|
||||
{A = sqlite3PExpr(pParse, TK_UPLUS, X, 0);}
|
||||
|
||||
%type between_op {int}
|
||||
between_op(A) ::= BETWEEN. {A = 0;}
|
||||
between_op(A) ::= NOT BETWEEN. {A = 1;}
|
||||
expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
ExprList *pList = sqlite3ExprListAppend(pParse,0, X.pExpr);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, Y.pExpr);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, A.pExpr, 0);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->x.pList = pList;
|
||||
ExprList *pList = sqlite3ExprListAppend(pParse,0, X);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, Y);
|
||||
A = sqlite3PExpr(pParse, TK_BETWEEN, A, 0);
|
||||
if( A ){
|
||||
A->x.pList = pList;
|
||||
}else{
|
||||
sqlite3ExprListDelete(pParse->db, pList);
|
||||
}
|
||||
exprNot(pParse, N, &A);
|
||||
A.zEnd = Y.zEnd;
|
||||
if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0);
|
||||
}
|
||||
%ifndef SQLITE_OMIT_SUBQUERY
|
||||
%type in_op {int}
|
||||
in_op(A) ::= IN. {A = 0;}
|
||||
in_op(A) ::= NOT IN. {A = 1;}
|
||||
expr(A) ::= expr(A) in_op(N) LP exprlist(Y) RP(E). [IN] {
|
||||
expr(A) ::= expr(A) in_op(N) LP exprlist(Y) RP. [IN] {
|
||||
if( Y==0 ){
|
||||
/* Expressions of the form
|
||||
**
|
||||
@@ -1124,8 +1061,8 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
** simplify to constants 0 (false) and 1 (true), respectively,
|
||||
** regardless of the value of expr1.
|
||||
*/
|
||||
sqlite3ExprDelete(pParse->db, A.pExpr);
|
||||
A.pExpr = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[N],1);
|
||||
sqlite3ExprDelete(pParse->db, A);
|
||||
A = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[N],1);
|
||||
}else if( Y->nExpr==1 ){
|
||||
/* Expressions of the form:
|
||||
**
|
||||
@@ -1152,54 +1089,48 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
pRHS->flags &= ~EP_Collate;
|
||||
pRHS->flags |= EP_Generic;
|
||||
}
|
||||
A.pExpr = sqlite3PExpr(pParse, N ? TK_NE : TK_EQ, A.pExpr, pRHS);
|
||||
A = sqlite3PExpr(pParse, N ? TK_NE : TK_EQ, A, pRHS);
|
||||
}else{
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->x.pList = Y;
|
||||
sqlite3ExprSetHeightAndFlags(pParse, A.pExpr);
|
||||
A = sqlite3PExpr(pParse, TK_IN, A, 0);
|
||||
if( A ){
|
||||
A->x.pList = Y;
|
||||
sqlite3ExprSetHeightAndFlags(pParse, A);
|
||||
}else{
|
||||
sqlite3ExprListDelete(pParse->db, Y);
|
||||
}
|
||||
exprNot(pParse, N, &A);
|
||||
if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0);
|
||||
}
|
||||
A.zEnd = &E.z[E.n];
|
||||
}
|
||||
expr(A) ::= LP(B) select(X) RP(E). {
|
||||
spanSet(&A,&B,&E); /*A-overwrites-B*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
|
||||
sqlite3PExprAddSelect(pParse, A.pExpr, X);
|
||||
expr(A) ::= LP select(X) RP. {
|
||||
A = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
|
||||
sqlite3PExprAddSelect(pParse, A, X);
|
||||
}
|
||||
expr(A) ::= expr(A) in_op(N) LP select(Y) RP(E). [IN] {
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0);
|
||||
sqlite3PExprAddSelect(pParse, A.pExpr, Y);
|
||||
exprNot(pParse, N, &A);
|
||||
A.zEnd = &E.z[E.n];
|
||||
expr(A) ::= expr(A) in_op(N) LP select(Y) RP. [IN] {
|
||||
A = sqlite3PExpr(pParse, TK_IN, A, 0);
|
||||
sqlite3PExprAddSelect(pParse, A, Y);
|
||||
if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0);
|
||||
}
|
||||
expr(A) ::= expr(A) in_op(N) nm(Y) dbnm(Z) paren_exprlist(E). [IN] {
|
||||
SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&Y,&Z);
|
||||
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
|
||||
if( E ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, E);
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_IN, A.pExpr, 0);
|
||||
sqlite3PExprAddSelect(pParse, A.pExpr, pSelect);
|
||||
exprNot(pParse, N, &A);
|
||||
A.zEnd = Z.z ? &Z.z[Z.n] : &Y.z[Y.n];
|
||||
A = sqlite3PExpr(pParse, TK_IN, A, 0);
|
||||
sqlite3PExprAddSelect(pParse, A, pSelect);
|
||||
if( N ) A = sqlite3PExpr(pParse, TK_NOT, A, 0);
|
||||
}
|
||||
expr(A) ::= EXISTS(B) LP select(Y) RP(E). {
|
||||
expr(A) ::= EXISTS LP select(Y) RP. {
|
||||
Expr *p;
|
||||
spanSet(&A,&B,&E); /*A-overwrites-B*/
|
||||
p = A.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
|
||||
p = A = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
|
||||
sqlite3PExprAddSelect(pParse, p, Y);
|
||||
}
|
||||
%endif SQLITE_OMIT_SUBQUERY
|
||||
|
||||
/* CASE expressions */
|
||||
expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). {
|
||||
spanSet(&A,&C,&E); /*A-overwrites-C*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_CASE, X, 0);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->x.pList = Z ? sqlite3ExprListAppend(pParse,Y,Z) : Y;
|
||||
sqlite3ExprSetHeightAndFlags(pParse, A.pExpr);
|
||||
expr(A) ::= CASE case_operand(X) case_exprlist(Y) case_else(Z) END. {
|
||||
A = sqlite3PExpr(pParse, TK_CASE, X, 0);
|
||||
if( A ){
|
||||
A->x.pList = Z ? sqlite3ExprListAppend(pParse,Y,Z) : Y;
|
||||
sqlite3ExprSetHeightAndFlags(pParse, A);
|
||||
}else{
|
||||
sqlite3ExprListDelete(pParse->db, Y);
|
||||
sqlite3ExprDelete(pParse->db, Z);
|
||||
@@ -1208,20 +1139,20 @@ expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). {
|
||||
%type case_exprlist {ExprList*}
|
||||
%destructor case_exprlist {sqlite3ExprListDelete(pParse->db, $$);}
|
||||
case_exprlist(A) ::= case_exprlist(A) WHEN expr(Y) THEN expr(Z). {
|
||||
A = sqlite3ExprListAppend(pParse,A, Y.pExpr);
|
||||
A = sqlite3ExprListAppend(pParse,A, Z.pExpr);
|
||||
A = sqlite3ExprListAppend(pParse,A, Y);
|
||||
A = sqlite3ExprListAppend(pParse,A, Z);
|
||||
}
|
||||
case_exprlist(A) ::= WHEN expr(Y) THEN expr(Z). {
|
||||
A = sqlite3ExprListAppend(pParse,0, Y.pExpr);
|
||||
A = sqlite3ExprListAppend(pParse,A, Z.pExpr);
|
||||
A = sqlite3ExprListAppend(pParse,0, Y);
|
||||
A = sqlite3ExprListAppend(pParse,A, Z);
|
||||
}
|
||||
%type case_else {Expr*}
|
||||
%destructor case_else {sqlite3ExprDelete(pParse->db, $$);}
|
||||
case_else(A) ::= ELSE expr(X). {A = X.pExpr;}
|
||||
case_else(A) ::= ELSE expr(X). {A = X;}
|
||||
case_else(A) ::= . {A = 0;}
|
||||
%type case_operand {Expr*}
|
||||
%destructor case_operand {sqlite3ExprDelete(pParse->db, $$);}
|
||||
case_operand(A) ::= expr(X). {A = X.pExpr; /*A-overwrites-X*/}
|
||||
case_operand(A) ::= expr(X). {A = X; /*A-overwrites-X*/}
|
||||
case_operand(A) ::= . {A = 0;}
|
||||
|
||||
%type exprlist {ExprList*}
|
||||
@@ -1232,9 +1163,9 @@ case_operand(A) ::= . {A = 0;}
|
||||
exprlist(A) ::= nexprlist(A).
|
||||
exprlist(A) ::= . {A = 0;}
|
||||
nexprlist(A) ::= nexprlist(A) COMMA expr(Y).
|
||||
{A = sqlite3ExprListAppend(pParse,A,Y.pExpr);}
|
||||
{A = sqlite3ExprListAppend(pParse,A,Y);}
|
||||
nexprlist(A) ::= expr(Y).
|
||||
{A = sqlite3ExprListAppend(pParse,0,Y.pExpr); /*A-overwrites-Y*/}
|
||||
{A = sqlite3ExprListAppend(pParse,0,Y); /*A-overwrites-Y*/}
|
||||
|
||||
%ifndef SQLITE_OMIT_SUBQUERY
|
||||
/* A paren_exprlist is an optional expression list contained inside
|
||||
@@ -1388,7 +1319,7 @@ foreach_clause ::= FOR EACH ROW.
|
||||
%type when_clause {Expr*}
|
||||
%destructor when_clause {sqlite3ExprDelete(pParse->db, $$);}
|
||||
when_clause(A) ::= . { A = 0; }
|
||||
when_clause(A) ::= WHEN expr(X). { A = X.pExpr; }
|
||||
when_clause(A) ::= WHEN expr(X). { A = X; }
|
||||
|
||||
%type trigger_cmd_list {TriggerStep*}
|
||||
%destructor trigger_cmd_list {sqlite3DeleteTriggerStep(pParse->db, $$);}
|
||||
@@ -1437,34 +1368,33 @@ tridxby ::= NOT INDEXED. {
|
||||
%destructor trigger_cmd {sqlite3DeleteTriggerStep(pParse->db, $$);}
|
||||
// UPDATE
|
||||
trigger_cmd(A) ::=
|
||||
UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z).
|
||||
{A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R);}
|
||||
UPDATE(B) orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z) scanpt(E).
|
||||
{A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R, B.z, E);}
|
||||
|
||||
// INSERT
|
||||
trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) idlist_opt(F) select(S).
|
||||
{A = sqlite3TriggerInsertStep(pParse->db, &X, F, S, R);/*A-overwrites-R*/}
|
||||
trigger_cmd(A) ::= scanpt(B) insert_cmd(R) INTO
|
||||
trnm(X) idlist_opt(F) select(S) scanpt(Z).
|
||||
{A = sqlite3TriggerInsertStep(pParse->db,&X,F,S,R,B,Z);/*A-overwrites-R*/}
|
||||
|
||||
// DELETE
|
||||
trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y).
|
||||
{A = sqlite3TriggerDeleteStep(pParse->db, &X, Y);}
|
||||
trigger_cmd(A) ::= DELETE(B) FROM trnm(X) tridxby where_opt(Y) scanpt(E).
|
||||
{A = sqlite3TriggerDeleteStep(pParse->db, &X, Y, B.z, E);}
|
||||
|
||||
// SELECT
|
||||
trigger_cmd(A) ::= select(X).
|
||||
{A = sqlite3TriggerSelectStep(pParse->db, X); /*A-overwrites-X*/}
|
||||
trigger_cmd(A) ::= scanpt(B) select(X) scanpt(E).
|
||||
{A = sqlite3TriggerSelectStep(pParse->db, X, B, E); /*A-overwrites-X*/}
|
||||
|
||||
// The special RAISE expression that may occur in trigger programs
|
||||
expr(A) ::= RAISE(X) LP IGNORE RP(Y). {
|
||||
spanSet(&A,&X,&Y); /*A-overwrites-X*/
|
||||
A.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
|
||||
if( A.pExpr ){
|
||||
A.pExpr->affinity = OE_Ignore;
|
||||
expr(A) ::= RAISE LP IGNORE RP. {
|
||||
A = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
|
||||
if( A ){
|
||||
A->affinity = OE_Ignore;
|
||||
}
|
||||
}
|
||||
expr(A) ::= RAISE(X) LP raisetype(T) COMMA nm(Z) RP(Y). {
|
||||
spanSet(&A,&X,&Y); /*A-overwrites-X*/
|
||||
A.pExpr = sqlite3ExprAlloc(pParse->db, TK_RAISE, &Z, 1);
|
||||
if( A.pExpr ) {
|
||||
A.pExpr->affinity = (char)T;
|
||||
expr(A) ::= RAISE LP raisetype(T) COMMA nm(Z) RP. {
|
||||
A = sqlite3ExprAlloc(pParse->db, TK_RAISE, &Z, 1);
|
||||
if( A ) {
|
||||
A->affinity = (char)T;
|
||||
}
|
||||
}
|
||||
%endif !SQLITE_OMIT_TRIGGER
|
||||
@@ -1485,16 +1415,16 @@ cmd ::= DROP TRIGGER ifexists(NOERR) fullname(X). {
|
||||
//////////////////////// ATTACH DATABASE file AS name /////////////////////////
|
||||
%ifndef SQLITE_OMIT_ATTACH
|
||||
cmd ::= ATTACH database_kw_opt expr(F) AS expr(D) key_opt(K). {
|
||||
sqlite3Attach(pParse, F.pExpr, D.pExpr, K);
|
||||
sqlite3Attach(pParse, F, D, K);
|
||||
}
|
||||
cmd ::= DETACH database_kw_opt expr(D). {
|
||||
sqlite3Detach(pParse, D.pExpr);
|
||||
sqlite3Detach(pParse, D);
|
||||
}
|
||||
|
||||
%type key_opt {Expr*}
|
||||
%destructor key_opt {sqlite3ExprDelete(pParse->db, $$);}
|
||||
key_opt(A) ::= . { A = 0; }
|
||||
key_opt(A) ::= KEY expr(X). { A = X.pExpr; }
|
||||
key_opt(A) ::= KEY expr(X). { A = X; }
|
||||
|
||||
database_kw_opt ::= DATABASE.
|
||||
database_kw_opt ::= .
|
||||
|
||||
@@ -6108,6 +6108,9 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
{ "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,"BOOLEAN" },
|
||||
{ "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT, "BOOLEAN" },
|
||||
{ "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS, "DISABLE-MASK" },
|
||||
#ifdef YYCOVERAGE
|
||||
{ "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE, "" },
|
||||
#endif
|
||||
{ "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " },
|
||||
{ "prng_reset", SQLITE_TESTCTRL_PRNG_RESET, "" },
|
||||
{ "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" },
|
||||
@@ -6233,6 +6236,14 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
isOk = 3;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef YYCOVERAGE
|
||||
case SQLITE_TESTCTRL_PARSER_COVERAGE:
|
||||
if( nArg==2 ){
|
||||
sqlite3_test_control(testctrl, p->out);
|
||||
isOk = 3;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if( isOk==0 && iCtrl>=0 ){
|
||||
|
||||
+2
-1
@@ -7042,7 +7042,8 @@ int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_ISINIT 23
|
||||
#define SQLITE_TESTCTRL_SORTER_MMAP 24
|
||||
#define SQLITE_TESTCTRL_IMPOSTER 25
|
||||
#define SQLITE_TESTCTRL_LAST 25 /* Largest TESTCTRL */
|
||||
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
|
||||
#define SQLITE_TESTCTRL_LAST 26 /* Largest TESTCTRL */
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQLite Runtime Status
|
||||
|
||||
+14
-18
@@ -1063,7 +1063,6 @@ typedef struct Db Db;
|
||||
typedef struct Schema Schema;
|
||||
typedef struct Expr Expr;
|
||||
typedef struct ExprList ExprList;
|
||||
typedef struct ExprSpan ExprSpan;
|
||||
typedef struct FKey FKey;
|
||||
typedef struct FuncDestructor FuncDestructor;
|
||||
typedef struct FuncDef FuncDef;
|
||||
@@ -2504,17 +2503,6 @@ struct ExprList {
|
||||
} a[1]; /* One slot for each expression in the list */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of this structure is used by the parser to record both
|
||||
** the parse tree for an expression and the span of input text for an
|
||||
** expression.
|
||||
*/
|
||||
struct ExprSpan {
|
||||
Expr *pExpr; /* The expression parse tree */
|
||||
const char *zStart; /* First character of input text */
|
||||
const char *zEnd; /* One character past the end of input text */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of this structure can hold a simple list of identifiers,
|
||||
** such as the list "a,b,c" in the following statements:
|
||||
@@ -3215,6 +3203,7 @@ struct TriggerStep {
|
||||
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
|
||||
ExprList *pExprList; /* SET clause for UPDATE. */
|
||||
IdList *pIdList; /* Column names for INSERT */
|
||||
char *zSpan; /* Original SQL text of this command */
|
||||
TriggerStep *pNext; /* Next in the link-list */
|
||||
TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */
|
||||
};
|
||||
@@ -3525,6 +3514,7 @@ void *sqlite3DbMallocRaw(sqlite3*, u64);
|
||||
void *sqlite3DbMallocRawNN(sqlite3*, u64);
|
||||
char *sqlite3DbStrDup(sqlite3*,const char*);
|
||||
char *sqlite3DbStrNDup(sqlite3*,const char*, u64);
|
||||
char *sqlite3DbSpanDup(sqlite3*,const char*,const char*);
|
||||
void *sqlite3Realloc(void*, u64);
|
||||
void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64);
|
||||
void *sqlite3DbRealloc(sqlite3 *, void *, u64);
|
||||
@@ -3663,7 +3653,7 @@ ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
|
||||
ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
|
||||
void sqlite3ExprListSetSortOrder(ExprList*,int);
|
||||
void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int);
|
||||
void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*);
|
||||
void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*);
|
||||
void sqlite3ExprListDelete(sqlite3*, ExprList*);
|
||||
u32 sqlite3ExprListFlags(const ExprList*);
|
||||
int sqlite3Init(sqlite3*, char**);
|
||||
@@ -3693,7 +3683,7 @@ void sqlite3AddColumn(Parse*,Token*,Token*);
|
||||
void sqlite3AddNotNull(Parse*, int);
|
||||
void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int);
|
||||
void sqlite3AddCheckConstraint(Parse*, Expr*);
|
||||
void sqlite3AddDefaultValue(Parse*,ExprSpan*);
|
||||
void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*);
|
||||
void sqlite3AddCollateType(Parse*, Token*);
|
||||
void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
|
||||
int sqlite3ParseUri(const char*,const char*,unsigned int*,
|
||||
@@ -3914,11 +3904,14 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
|
||||
void sqlite3CodeRowTriggerDirect(Parse *, Trigger *, Table *, int, int, int);
|
||||
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
|
||||
void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
|
||||
TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*);
|
||||
TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*,
|
||||
const char*,const char*);
|
||||
TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*,
|
||||
Select*,u8);
|
||||
TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8);
|
||||
TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*);
|
||||
Select*,u8,const char*,const char*);
|
||||
TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8,
|
||||
const char*,const char*);
|
||||
TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*,
|
||||
const char*,const char*);
|
||||
void sqlite3DeleteTrigger(sqlite3*, Trigger*);
|
||||
void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
|
||||
u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int);
|
||||
@@ -4348,6 +4341,9 @@ void sqlite3Put4byte(u8*, u32);
|
||||
#ifdef SQLITE_DEBUG
|
||||
void sqlite3ParserTrace(FILE*, char *);
|
||||
#endif
|
||||
#if defined(YYCOVERAGE)
|
||||
int sqlite3ParserCoverage(FILE*);
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If the SQLITE_ENABLE IOTRACE exists then the global variable
|
||||
|
||||
+48
-11
@@ -25,6 +25,7 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){
|
||||
sqlite3ExprListDelete(db, pTmp->pExprList);
|
||||
sqlite3SelectDelete(db, pTmp->pSelect);
|
||||
sqlite3IdListDelete(db, pTmp->pIdList);
|
||||
sqlite3DbFree(db, pTmp->zSpan);
|
||||
|
||||
sqlite3DbFree(db, pTmp);
|
||||
}
|
||||
@@ -339,6 +340,17 @@ triggerfinish_cleanup:
|
||||
sqlite3DeleteTriggerStep(db, pStepList);
|
||||
}
|
||||
|
||||
/*
|
||||
** Duplicate a range of text from an SQL statement, then convert all
|
||||
** whitespace characters into ordinary space characters.
|
||||
*/
|
||||
static char *triggerSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){
|
||||
char *z = sqlite3DbSpanDup(db, zStart, zEnd);
|
||||
int i;
|
||||
if( z ) for(i=0; z[i]; i++) if( sqlite3Isspace(z[i]) ) z[i] = ' ';
|
||||
return z;
|
||||
}
|
||||
|
||||
/*
|
||||
** Turn a SELECT statement (that the pSelect parameter points to) into
|
||||
** a trigger step. Return a pointer to a TriggerStep structure.
|
||||
@@ -346,7 +358,12 @@ triggerfinish_cleanup:
|
||||
** The parser calls this routine when it finds a SELECT statement in
|
||||
** body of a TRIGGER.
|
||||
*/
|
||||
TriggerStep *sqlite3TriggerSelectStep(sqlite3 *db, Select *pSelect){
|
||||
TriggerStep *sqlite3TriggerSelectStep(
|
||||
sqlite3 *db, /* Database connection */
|
||||
Select *pSelect, /* The SELECT statement */
|
||||
const char *zStart, /* Start of SQL text */
|
||||
const char *zEnd /* End of SQL text */
|
||||
){
|
||||
TriggerStep *pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep));
|
||||
if( pTriggerStep==0 ) {
|
||||
sqlite3SelectDelete(db, pSelect);
|
||||
@@ -355,6 +372,7 @@ TriggerStep *sqlite3TriggerSelectStep(sqlite3 *db, Select *pSelect){
|
||||
pTriggerStep->op = TK_SELECT;
|
||||
pTriggerStep->pSelect = pSelect;
|
||||
pTriggerStep->orconf = OE_Default;
|
||||
pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
|
||||
return pTriggerStep;
|
||||
}
|
||||
|
||||
@@ -367,7 +385,9 @@ TriggerStep *sqlite3TriggerSelectStep(sqlite3 *db, Select *pSelect){
|
||||
static TriggerStep *triggerStepAllocate(
|
||||
sqlite3 *db, /* Database connection */
|
||||
u8 op, /* Trigger opcode */
|
||||
Token *pName /* The target name */
|
||||
Token *pName, /* The target name */
|
||||
const char *zStart, /* Start of SQL text */
|
||||
const char *zEnd /* End of SQL text */
|
||||
){
|
||||
TriggerStep *pTriggerStep;
|
||||
|
||||
@@ -378,6 +398,7 @@ static TriggerStep *triggerStepAllocate(
|
||||
sqlite3Dequote(z);
|
||||
pTriggerStep->zTarget = z;
|
||||
pTriggerStep->op = op;
|
||||
pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
|
||||
}
|
||||
return pTriggerStep;
|
||||
}
|
||||
@@ -394,13 +415,15 @@ TriggerStep *sqlite3TriggerInsertStep(
|
||||
Token *pTableName, /* Name of the table into which we insert */
|
||||
IdList *pColumn, /* List of columns in pTableName to insert into */
|
||||
Select *pSelect, /* A SELECT statement that supplies values */
|
||||
u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
|
||||
u8 orconf, /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
|
||||
const char *zStart, /* Start of SQL text */
|
||||
const char *zEnd /* End of SQL text */
|
||||
){
|
||||
TriggerStep *pTriggerStep;
|
||||
|
||||
assert(pSelect != 0 || db->mallocFailed);
|
||||
|
||||
pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName);
|
||||
pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName, zStart, zEnd);
|
||||
if( pTriggerStep ){
|
||||
pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
|
||||
pTriggerStep->pIdList = pColumn;
|
||||
@@ -423,11 +446,13 @@ TriggerStep *sqlite3TriggerUpdateStep(
|
||||
Token *pTableName, /* Name of the table to be updated */
|
||||
ExprList *pEList, /* The SET clause: list of column and new values */
|
||||
Expr *pWhere, /* The WHERE clause */
|
||||
u8 orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
|
||||
u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
|
||||
const char *zStart, /* Start of SQL text */
|
||||
const char *zEnd /* End of SQL text */
|
||||
){
|
||||
TriggerStep *pTriggerStep;
|
||||
|
||||
pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName);
|
||||
pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName, zStart, zEnd);
|
||||
if( pTriggerStep ){
|
||||
pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
|
||||
pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
||||
@@ -446,11 +471,13 @@ TriggerStep *sqlite3TriggerUpdateStep(
|
||||
TriggerStep *sqlite3TriggerDeleteStep(
|
||||
sqlite3 *db, /* Database connection */
|
||||
Token *pTableName, /* The table from which rows are deleted */
|
||||
Expr *pWhere /* The WHERE clause */
|
||||
Expr *pWhere, /* The WHERE clause */
|
||||
const char *zStart, /* Start of SQL text */
|
||||
const char *zEnd /* End of SQL text */
|
||||
){
|
||||
TriggerStep *pTriggerStep;
|
||||
|
||||
pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName);
|
||||
pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName, zStart, zEnd);
|
||||
if( pTriggerStep ){
|
||||
pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
||||
pTriggerStep->orconf = OE_Default;
|
||||
@@ -705,6 +732,14 @@ static int codeTriggerProgram(
|
||||
pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:(u8)orconf;
|
||||
assert( pParse->okConstFactor==0 );
|
||||
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
if( pStep->zSpan ){
|
||||
sqlite3VdbeAddOp4(v, OP_Trace, 0x7fffffff, 1, 0,
|
||||
sqlite3MPrintf(db, "-- %s", pStep->zSpan),
|
||||
P4_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
switch( pStep->op ){
|
||||
case TK_UPDATE: {
|
||||
sqlite3Update(pParse,
|
||||
@@ -845,9 +880,11 @@ static TriggerPrg *codeRowTrigger(
|
||||
pTab->zName
|
||||
));
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
sqlite3VdbeChangeP4(v, -1,
|
||||
sqlite3MPrintf(db, "-- TRIGGER %s", pTrigger->zName), P4_DYNAMIC
|
||||
);
|
||||
if( pTrigger->zName ){
|
||||
sqlite3VdbeChangeP4(v, -1,
|
||||
sqlite3MPrintf(db, "-- TRIGGER %s", pTrigger->zName), P4_DYNAMIC
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If one was specified, code the WHEN clause. If it evaluates to false
|
||||
|
||||
+20
-6
@@ -320,6 +320,24 @@ int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
|
||||
return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b];
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute 10 to the E-th power. Examples: E==1 results in 10.
|
||||
** E==2 results in 100. E==50 results in 1.0e50.
|
||||
**
|
||||
** This routine only works for values of E between 1 and 341.
|
||||
*/
|
||||
static LONGDOUBLE_TYPE sqlite3Pow10(int E){
|
||||
LONGDOUBLE_TYPE x = 10.0;
|
||||
LONGDOUBLE_TYPE r = 1.0;
|
||||
while(1){
|
||||
if( E & 1 ) r *= x;
|
||||
E >>= 1;
|
||||
if( E==0 ) break;
|
||||
x *= x;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
** The string z[] is an text representation of a real number.
|
||||
** Convert this string to a double and write it into *pResult.
|
||||
@@ -475,11 +493,10 @@ do_atof_calc:
|
||||
if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
result = (double)s;
|
||||
}else{
|
||||
LONGDOUBLE_TYPE scale = 1.0;
|
||||
/* attempt to handle extremely small/large numbers better */
|
||||
if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
while( e%308 ) { scale *= 1.0e+1; e -= 1; }
|
||||
LONGDOUBLE_TYPE scale = sqlite3Pow10(e-308);
|
||||
if( esign<0 ){
|
||||
result = s / scale;
|
||||
result /= 1.0e+308;
|
||||
@@ -499,10 +516,7 @@ do_atof_calc:
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* 1.0e+22 is the largest power of 10 than can be
|
||||
** represented exactly. */
|
||||
while( e%22 ) { scale *= 1.0e+1; e -= 1; }
|
||||
while( e>0 ) { scale *= 1.0e+22; e -= 22; }
|
||||
LONGDOUBLE_TYPE scale = sqlite3Pow10(e);
|
||||
if( esign<0 ){
|
||||
result = s / scale;
|
||||
}else{
|
||||
|
||||
+12
-2
@@ -7016,7 +7016,13 @@ case OP_Function: {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Opcode: Trace P1 P2 * P4 *
|
||||
**
|
||||
** Write P4 on the statement trace output if statement tracing is
|
||||
** enabled.
|
||||
**
|
||||
** Operand P1 must be 0x7fffffff and P2 must positive.
|
||||
*/
|
||||
/* Opcode: Init P1 P2 P3 P4 *
|
||||
** Synopsis: Start at P2
|
||||
**
|
||||
@@ -7035,6 +7041,7 @@ case OP_Function: {
|
||||
** If P3 is not zero, then it is an address to jump to if an SQLITE_CORRUPT
|
||||
** error is encountered.
|
||||
*/
|
||||
case OP_Trace:
|
||||
case OP_Init: { /* jump */
|
||||
char *zTrace;
|
||||
int i;
|
||||
@@ -7049,7 +7056,9 @@ case OP_Init: { /* jump */
|
||||
** sqlite3_expanded_sql(P) otherwise.
|
||||
*/
|
||||
assert( pOp->p4.z==0 || strncmp(pOp->p4.z, "-" "- ", 3)==0 );
|
||||
assert( pOp==p->aOp ); /* Always instruction 0 */
|
||||
|
||||
/* OP_Init is always instruction 0 */
|
||||
assert( pOp==p->aOp || pOp->opcode==OP_Trace );
|
||||
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0
|
||||
@@ -7092,6 +7101,7 @@ case OP_Init: { /* jump */
|
||||
#endif /* SQLITE_OMIT_TRACE */
|
||||
assert( pOp->p2>0 );
|
||||
if( pOp->p1>=sqlite3GlobalConfig.iOnceResetThreshold ){
|
||||
if( pOp->opcode==OP_Trace ) break;
|
||||
for(i=1; i<p->nOp; i++){
|
||||
if( p->aOp[i].opcode==OP_Once ) p->aOp[i].p1 = 0;
|
||||
}
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ do_test capi2-3.5 {
|
||||
} {1 {(1) no such column: bogus} {;;x;}}
|
||||
do_test capi2-3.6 {
|
||||
set rc [catch {
|
||||
sqlite3_prepare $DB {select 5/0} -1 TAIL
|
||||
sqlite3_prepare $DB {select 5/0;} -1 TAIL
|
||||
} VM]
|
||||
lappend rc $TAIL
|
||||
} {0 {}}
|
||||
|
||||
@@ -398,6 +398,25 @@ do_execsql_test colname-9.320 {
|
||||
SELECT name FROM pragma_table_info('t2');
|
||||
} {Bbb}
|
||||
|
||||
# Issue detected by OSSFuzz on 2017-12-24 (Christmas Eve)
|
||||
# caused by check-in https://sqlite.org/src/info/6b2ff26c25
|
||||
#
|
||||
# Prior to being fixed, the following CREATE TABLE was dereferencing
|
||||
# a NULL pointer and segfaulting.
|
||||
#
|
||||
do_catchsql_test colname-9.400 {
|
||||
CREATE TABLE t4 AS SELECT #0;
|
||||
} {1 {near "#0": syntax error}}
|
||||
|
||||
# Issue detected by OSSFuzz on 2017-12-25 (Christmas Day)
|
||||
# also caused by check-in https://sqlite.org/src/info/6b2ff26c25
|
||||
#
|
||||
# Prior to being fixed, the following CREATE TABLE caused an
|
||||
# assertion fault.
|
||||
#
|
||||
do_catchsql_test colname-9.410 {
|
||||
CREATE TABLE t5 AS SELECT RAISE(abort,a);
|
||||
} {1 {RAISE() may only be used within a trigger-program}}
|
||||
|
||||
# Make sure the quotation marks get removed from the column names
|
||||
# when constructing a new table from an aggregate SELECT.
|
||||
|
||||
@@ -171,6 +171,20 @@ do_catchsql_test fkey1-5.2 {
|
||||
INSERT OR REPLACE INTO t11 VALUES (2, 3);
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
# Make sure sqlite3_trace() output works with triggers used to implement
|
||||
# FK constraints
|
||||
#
|
||||
proc sqltrace {txt} {
|
||||
global traceoutput
|
||||
lappend traceoutput $txt
|
||||
}
|
||||
do_test fkey1-5.2.1 {
|
||||
unset -nocomplain traceoutput
|
||||
db trace sqltrace
|
||||
catch {db eval {INSERT OR REPLACE INTO t11 VALUES(2,3);}}
|
||||
set traceoutput
|
||||
} {{INSERT OR REPLACE INTO t11 VALUES(2,3);} {INSERT OR REPLACE INTO t11 VALUES(2,3);} {INSERT OR REPLACE INTO t11 VALUES(2,3);}}
|
||||
|
||||
# A similar test to the above.
|
||||
do_execsql_test fkey1-5.3 {
|
||||
CREATE TABLE Foo (
|
||||
|
||||
+74
-2
@@ -32,7 +32,7 @@ static const char zHelp[] =
|
||||
" --size N Relative test size. Default=100\n"
|
||||
" --stats Show statistics at the end\n"
|
||||
" --temp N N from 0 to 9. 0: no temp table. 9: all temp tables\n"
|
||||
" --testset T Run test-set T (main, cte, rtree, orm, debug)\n"
|
||||
" --testset T Run test-set T (main, cte, rtree, orm, fp, debug)\n"
|
||||
" --trace Turn on SQL tracing\n"
|
||||
" --threads N Use up to N threads for sorting\n"
|
||||
" --utf16be Set text encoding to UTF-16BE\n"
|
||||
@@ -1120,7 +1120,77 @@ void testset_cte(void){
|
||||
);
|
||||
speedtest1_run();
|
||||
speedtest1_end_test();
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute a pseudo-random floating point ascii number.
|
||||
*/
|
||||
void speedtest1_random_ascii_fp(char *zFP){
|
||||
int x = speedtest1_random();
|
||||
int y = speedtest1_random();
|
||||
int z;
|
||||
z = y%10;
|
||||
if( z<0 ) z = -z;
|
||||
y /= 10;
|
||||
sqlite3_snprintf(100,zFP,"%d.%de%d",y,z,x%200);
|
||||
}
|
||||
|
||||
/*
|
||||
** A testset for floating-point numbers.
|
||||
*/
|
||||
void testset_fp(void){
|
||||
int n;
|
||||
int i;
|
||||
char zFP1[100];
|
||||
char zFP2[100];
|
||||
|
||||
n = g.szTest*5000;
|
||||
speedtest1_begin_test(100, "Fill a table with %d FP values", n*2);
|
||||
speedtest1_exec("BEGIN");
|
||||
speedtest1_exec("CREATE%s TABLE t1(a REAL %s, b REAL %s);",
|
||||
isTemp(1), g.zNN, g.zNN);
|
||||
speedtest1_prepare("INSERT INTO t1 VALUES(?1,?2); -- %d times", n);
|
||||
for(i=1; i<=n; i++){
|
||||
speedtest1_random_ascii_fp(zFP1);
|
||||
speedtest1_random_ascii_fp(zFP2);
|
||||
sqlite3_bind_text(g.pStmt, 1, zFP1, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(g.pStmt, 2, zFP2, -1, SQLITE_STATIC);
|
||||
speedtest1_run();
|
||||
}
|
||||
speedtest1_exec("COMMIT");
|
||||
speedtest1_end_test();
|
||||
|
||||
n = g.szTest/25 + 2;
|
||||
speedtest1_begin_test(110, "%d range queries", n);
|
||||
speedtest1_prepare("SELECT sum(b) FROM t1 WHERE a BETWEEN ?1 AND ?2");
|
||||
for(i=1; i<=n; i++){
|
||||
speedtest1_random_ascii_fp(zFP1);
|
||||
speedtest1_random_ascii_fp(zFP2);
|
||||
sqlite3_bind_text(g.pStmt, 1, zFP1, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(g.pStmt, 2, zFP2, -1, SQLITE_STATIC);
|
||||
speedtest1_run();
|
||||
}
|
||||
speedtest1_end_test();
|
||||
|
||||
speedtest1_begin_test(120, "CREATE INDEX three times");
|
||||
speedtest1_exec("BEGIN;");
|
||||
speedtest1_exec("CREATE INDEX t1a ON t1(a);");
|
||||
speedtest1_exec("CREATE INDEX t1b ON t1(b);");
|
||||
speedtest1_exec("CREATE INDEX t1ab ON t1(a,b);");
|
||||
speedtest1_exec("COMMIT;");
|
||||
speedtest1_end_test();
|
||||
|
||||
n = g.szTest/3 + 2;
|
||||
speedtest1_begin_test(130, "%d indexed range queries", n);
|
||||
speedtest1_prepare("SELECT sum(b) FROM t1 WHERE a BETWEEN ?1 AND ?2");
|
||||
for(i=1; i<=n; i++){
|
||||
speedtest1_random_ascii_fp(zFP1);
|
||||
speedtest1_random_ascii_fp(zFP2);
|
||||
sqlite3_bind_text(g.pStmt, 1, zFP1, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(g.pStmt, 2, zFP2, -1, SQLITE_STATIC);
|
||||
speedtest1_run();
|
||||
}
|
||||
speedtest1_end_test();
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
@@ -1873,6 +1943,8 @@ int main(int argc, char **argv){
|
||||
testset_orm();
|
||||
}else if( strcmp(zTSet,"cte")==0 ){
|
||||
testset_cte();
|
||||
}else if( strcmp(zTSet,"fp")==0 ){
|
||||
testset_fp();
|
||||
}else if( strcmp(zTSet,"rtree")==0 ){
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
testset_rtree(6, 147);
|
||||
@@ -1881,7 +1953,7 @@ int main(int argc, char **argv){
|
||||
"the R-Tree tests\n");
|
||||
#endif
|
||||
}else{
|
||||
fatal_error("unknown testset: \"%s\"\nChoices: main debug1 cte rtree\n",
|
||||
fatal_error("unknown testset: \"%s\"\nChoices: main debug1 cte rtree fp\n",
|
||||
zTSet);
|
||||
}
|
||||
speedtest1_final();
|
||||
|
||||
+1
-1
@@ -197,7 +197,7 @@ ifcapable trigger {
|
||||
UPDATE t1 SET a=a+1;
|
||||
}
|
||||
set TRACE_OUT
|
||||
} {{UPDATE t1 SET a=a+1;} {-- TRIGGER r1t1} {-- TRIGGER r1t2} {-- TRIGGER r1t1} {-- TRIGGER r1t2} {-- TRIGGER r1t1} {-- TRIGGER r1t2}}
|
||||
} {{UPDATE t1 SET a=a+1;} {-- TRIGGER r1t1} {-- UPDATE t2 SET a=new.a WHERE rowid=new.rowid} {-- TRIGGER r1t2} {-- SELECT 'hello'} {-- TRIGGER r1t1} {-- UPDATE t2 SET a=new.a WHERE rowid=new.rowid} {-- TRIGGER r1t2} {-- SELECT 'hello'} {-- TRIGGER r1t1} {-- UPDATE t2 SET a=new.a WHERE rowid=new.rowid} {-- TRIGGER r1t2} {-- SELECT 'hello'}}
|
||||
}
|
||||
|
||||
# With 3.6.21, we add the ability to expand host parameters in the trace
|
||||
|
||||
+114
-52
@@ -384,6 +384,12 @@ struct lemon {
|
||||
int nrule; /* Number of rules */
|
||||
int nsymbol; /* Number of terminal and nonterminal symbols */
|
||||
int nterminal; /* Number of terminal symbols */
|
||||
int minShiftReduce; /* Minimum shift-reduce action value */
|
||||
int errAction; /* Error action value */
|
||||
int accAction; /* Accept action value */
|
||||
int noAction; /* No-op action value */
|
||||
int minReduce; /* Minimum reduce action */
|
||||
int maxAction; /* Maximum action value of any kind */
|
||||
struct symbol **symbols; /* Sorted array of pointers to symbols */
|
||||
int errorcnt; /* Number of errors */
|
||||
struct symbol *errsym; /* The error symbol */
|
||||
@@ -407,6 +413,7 @@ struct lemon {
|
||||
char *tokenprefix; /* A prefix added to token names in the .h file */
|
||||
int nconflict; /* Number of parsing conflicts */
|
||||
int nactiontab; /* Number of entries in the yy_action[] table */
|
||||
int nlookaheadtab; /* Number of entries in yy_lookahead[] */
|
||||
int tablesize; /* Total table size of all tables in bytes */
|
||||
int basisflag; /* Print only basis configurations */
|
||||
int has_fallback; /* True if any %fallback is seen in the grammar */
|
||||
@@ -583,10 +590,12 @@ struct acttab {
|
||||
int mxLookahead; /* Maximum aLookahead[].lookahead */
|
||||
int nLookahead; /* Used slots in aLookahead[] */
|
||||
int nLookaheadAlloc; /* Slots allocated in aLookahead[] */
|
||||
int nterminal; /* Number of terminal symbols */
|
||||
int nsymbol; /* total number of symbols */
|
||||
};
|
||||
|
||||
/* Return the number of entries in the yy_action table */
|
||||
#define acttab_size(X) ((X)->nAction)
|
||||
#define acttab_lookahead_size(X) ((X)->nAction)
|
||||
|
||||
/* The value for the N-th entry in yy_action */
|
||||
#define acttab_yyaction(X,N) ((X)->aAction[N].action)
|
||||
@@ -602,13 +611,15 @@ void acttab_free(acttab *p){
|
||||
}
|
||||
|
||||
/* Allocate a new acttab structure */
|
||||
acttab *acttab_alloc(void){
|
||||
acttab *acttab_alloc(int nsymbol, int nterminal){
|
||||
acttab *p = (acttab *) calloc( 1, sizeof(*p) );
|
||||
if( p==0 ){
|
||||
fprintf(stderr,"Unable to allocate memory for a new acttab.");
|
||||
exit(1);
|
||||
}
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->nsymbol = nsymbol;
|
||||
p->nterminal = nterminal;
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -649,16 +660,24 @@ void acttab_action(acttab *p, int lookahead, int action){
|
||||
** to an empty set in preparation for a new round of acttab_action() calls.
|
||||
**
|
||||
** Return the offset into the action table of the new transaction.
|
||||
**
|
||||
** If the makeItSafe parameter is true, then the offset is chosen so that
|
||||
** it is impossible to overread the yy_lookaside[] table regardless of
|
||||
** the lookaside token. This is done for the terminal symbols, as they
|
||||
** come from external inputs and can contain syntax errors. When makeItSafe
|
||||
** is false, there is more flexibility in selecting offsets, resulting in
|
||||
** a smaller table. For non-terminal symbols, which are never syntax errors,
|
||||
** makeItSafe can be false.
|
||||
*/
|
||||
int acttab_insert(acttab *p){
|
||||
int i, j, k, n;
|
||||
int acttab_insert(acttab *p, int makeItSafe){
|
||||
int i, j, k, n, end;
|
||||
assert( p->nLookahead>0 );
|
||||
|
||||
/* Make sure we have enough space to hold the expanded action table
|
||||
** in the worst case. The worst case occurs if the transaction set
|
||||
** must be appended to the current action table
|
||||
*/
|
||||
n = p->mxLookahead + 1;
|
||||
n = p->nsymbol + 1;
|
||||
if( p->nAction + n >= p->nActionAlloc ){
|
||||
int oldAlloc = p->nActionAlloc;
|
||||
p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20;
|
||||
@@ -680,7 +699,8 @@ int acttab_insert(acttab *p){
|
||||
**
|
||||
** i is the index in p->aAction[] where p->mnLookahead is inserted.
|
||||
*/
|
||||
for(i=p->nAction-1; i>=0; i--){
|
||||
end = makeItSafe ? p->mnLookahead : 0;
|
||||
for(i=p->nAction-1; i>=end; i--){
|
||||
if( p->aAction[i].lookahead==p->mnLookahead ){
|
||||
/* All lookaheads and actions in the aLookahead[] transaction
|
||||
** must match against the candidate aAction[i] entry. */
|
||||
@@ -710,12 +730,13 @@ int acttab_insert(acttab *p){
|
||||
** an empty offset in the aAction[] table in which we can add the
|
||||
** aLookahead[] transaction.
|
||||
*/
|
||||
if( i<0 ){
|
||||
if( i<end ){
|
||||
/* Look for holes in the aAction[] table that fit the current
|
||||
** aLookahead[] transaction. Leave i set to the offset of the hole.
|
||||
** If no holes are found, i is left at p->nAction, which means the
|
||||
** transaction will be appended. */
|
||||
for(i=0; i<p->nActionAlloc - p->mxLookahead; i++){
|
||||
i = makeItSafe ? p->mnLookahead : 0;
|
||||
for(; i<p->nActionAlloc - p->mxLookahead; i++){
|
||||
if( p->aAction[i].lookahead<0 ){
|
||||
for(j=0; j<p->nLookahead; j++){
|
||||
k = p->aLookahead[j].lookahead - p->mnLookahead + i;
|
||||
@@ -733,11 +754,19 @@ int acttab_insert(acttab *p){
|
||||
}
|
||||
}
|
||||
/* Insert transaction set at index i. */
|
||||
#if 0
|
||||
printf("Acttab:");
|
||||
for(j=0; j<p->nLookahead; j++){
|
||||
printf(" %d", p->aLookahead[j].lookahead);
|
||||
}
|
||||
printf(" inserted at %d\n", i);
|
||||
#endif
|
||||
for(j=0; j<p->nLookahead; j++){
|
||||
k = p->aLookahead[j].lookahead - p->mnLookahead + i;
|
||||
p->aAction[k] = p->aLookahead[j];
|
||||
if( k>=p->nAction ) p->nAction = k+1;
|
||||
}
|
||||
if( makeItSafe && i+p->nterminal>=p->nAction ) p->nAction = i+p->nterminal+1;
|
||||
p->nLookahead = 0;
|
||||
|
||||
/* Return the offset that is added to the lookahead in order to get the
|
||||
@@ -745,6 +774,16 @@ int acttab_insert(acttab *p){
|
||||
return i - p->mnLookahead;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size of the action table without the trailing syntax error
|
||||
** entries.
|
||||
*/
|
||||
int acttab_action_size(acttab *p){
|
||||
int n = p->nAction;
|
||||
while( n>0 && p->aAction[n-1].lookahead<0 ){ n--; }
|
||||
return n;
|
||||
}
|
||||
|
||||
/********************** From the file "build.c" *****************************/
|
||||
/*
|
||||
** Routines to construction the finite state machine for the LEMON
|
||||
@@ -1718,6 +1757,7 @@ int main(int argc, char **argv)
|
||||
stats_line("states", lem.nxstate);
|
||||
stats_line("conflicts", lem.nconflict);
|
||||
stats_line("action table entries", lem.nactiontab);
|
||||
stats_line("lookahead table entries", lem.nlookaheadtab);
|
||||
stats_line("total table size (bytes)", lem.tablesize);
|
||||
}
|
||||
if( lem.nconflict > 0 ){
|
||||
@@ -3020,6 +3060,27 @@ PRIVATE FILE *file_open(
|
||||
return fp;
|
||||
}
|
||||
|
||||
/* Print the text of a rule
|
||||
*/
|
||||
void rule_print(FILE *out, struct rule *rp){
|
||||
int i, j;
|
||||
fprintf(out, "%s",rp->lhs->name);
|
||||
/* if( rp->lhsalias ) fprintf(out,"(%s)",rp->lhsalias); */
|
||||
fprintf(out," ::=");
|
||||
for(i=0; i<rp->nrhs; i++){
|
||||
struct symbol *sp = rp->rhs[i];
|
||||
if( sp->type==MULTITERMINAL ){
|
||||
fprintf(out," %s", sp->subsym[0]->name);
|
||||
for(j=1; j<sp->nsubsym; j++){
|
||||
fprintf(out,"|%s", sp->subsym[j]->name);
|
||||
}
|
||||
}else{
|
||||
fprintf(out," %s", sp->name);
|
||||
}
|
||||
/* if( rp->rhsalias[i] ) fprintf(out,"(%s)",rp->rhsalias[i]); */
|
||||
}
|
||||
}
|
||||
|
||||
/* Duplicate the input file without comments and without actions
|
||||
** on rules */
|
||||
void Reprint(struct lemon *lemp)
|
||||
@@ -3047,21 +3108,7 @@ void Reprint(struct lemon *lemp)
|
||||
printf("\n");
|
||||
}
|
||||
for(rp=lemp->rule; rp; rp=rp->next){
|
||||
printf("%s",rp->lhs->name);
|
||||
/* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */
|
||||
printf(" ::=");
|
||||
for(i=0; i<rp->nrhs; i++){
|
||||
sp = rp->rhs[i];
|
||||
if( sp->type==MULTITERMINAL ){
|
||||
printf(" %s", sp->subsym[0]->name);
|
||||
for(j=1; j<sp->nsubsym; j++){
|
||||
printf("|%s", sp->subsym[j]->name);
|
||||
}
|
||||
}else{
|
||||
printf(" %s", sp->name);
|
||||
}
|
||||
/* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */
|
||||
}
|
||||
rule_print(stdout, rp);
|
||||
printf(".");
|
||||
if( rp->precsym ) printf(" [%s]",rp->precsym->name);
|
||||
/* if( rp->code ) printf("\n %s",rp->code); */
|
||||
@@ -3321,16 +3368,19 @@ PRIVATE int compute_action(struct lemon *lemp, struct action *ap)
|
||||
switch( ap->type ){
|
||||
case SHIFT: act = ap->x.stp->statenum; break;
|
||||
case SHIFTREDUCE: {
|
||||
act = ap->x.rp->iRule + lemp->nstate;
|
||||
/* Since a SHIFT is inherient after a prior REDUCE, convert any
|
||||
** SHIFTREDUCE action with a nonterminal on the LHS into a simple
|
||||
** REDUCE action: */
|
||||
if( ap->sp->index>=lemp->nterminal ) act += lemp->nrule;
|
||||
if( ap->sp->index>=lemp->nterminal ){
|
||||
act = lemp->minReduce + ap->x.rp->iRule;
|
||||
}else{
|
||||
act = lemp->minShiftReduce + ap->x.rp->iRule;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case REDUCE: act = ap->x.rp->iRule + lemp->nstate+lemp->nrule; break;
|
||||
case ERROR: act = lemp->nstate + lemp->nrule*2; break;
|
||||
case ACCEPT: act = lemp->nstate + lemp->nrule*2 + 1; break;
|
||||
case REDUCE: act = lemp->minReduce + ap->x.rp->iRule; break;
|
||||
case ERROR: act = lemp->errAction; break;
|
||||
case ACCEPT: act = lemp->accAction; break;
|
||||
default: act = -1; break;
|
||||
}
|
||||
return act;
|
||||
@@ -4038,6 +4088,13 @@ void ReportTable(
|
||||
int mnNtOfst, mxNtOfst;
|
||||
struct axset *ax;
|
||||
|
||||
lemp->minShiftReduce = lemp->nstate;
|
||||
lemp->errAction = lemp->minShiftReduce + lemp->nrule;
|
||||
lemp->accAction = lemp->errAction + 1;
|
||||
lemp->noAction = lemp->accAction + 1;
|
||||
lemp->minReduce = lemp->noAction + 1;
|
||||
lemp->maxAction = lemp->minReduce + lemp->nrule;
|
||||
|
||||
in = tplt_open(lemp);
|
||||
if( in==0 ) return;
|
||||
out = file_open(lemp,".c","wb");
|
||||
@@ -4076,7 +4133,7 @@ void ReportTable(
|
||||
minimum_size_type(0, lemp->nsymbol+1, &szCodeType)); lineno++;
|
||||
fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++;
|
||||
fprintf(out,"#define YYACTIONTYPE %s\n",
|
||||
minimum_size_type(0,lemp->nstate+lemp->nrule*2+5,&szActionType)); lineno++;
|
||||
minimum_size_type(0,lemp->maxAction,&szActionType)); lineno++;
|
||||
if( lemp->wildcard ){
|
||||
fprintf(out,"#define YYWILDCARD %d\n",
|
||||
lemp->wildcard->index); lineno++;
|
||||
@@ -4144,7 +4201,7 @@ void ReportTable(
|
||||
** of placing the largest action sets first */
|
||||
for(i=0; i<lemp->nxstate*2; i++) ax[i].iOrder = i;
|
||||
qsort(ax, lemp->nxstate*2, sizeof(ax[0]), axset_compare);
|
||||
pActtab = acttab_alloc();
|
||||
pActtab = acttab_alloc(lemp->nsymbol, lemp->nterminal);
|
||||
for(i=0; i<lemp->nxstate*2 && ax[i].nAction>0; i++){
|
||||
stp = ax[i].stp;
|
||||
if( ax[i].isTkn ){
|
||||
@@ -4155,7 +4212,7 @@ void ReportTable(
|
||||
if( action<0 ) continue;
|
||||
acttab_action(pActtab, ap->sp->index, action);
|
||||
}
|
||||
stp->iTknOfst = acttab_insert(pActtab);
|
||||
stp->iTknOfst = acttab_insert(pActtab, 1);
|
||||
if( stp->iTknOfst<mnTknOfst ) mnTknOfst = stp->iTknOfst;
|
||||
if( stp->iTknOfst>mxTknOfst ) mxTknOfst = stp->iTknOfst;
|
||||
}else{
|
||||
@@ -4167,7 +4224,7 @@ void ReportTable(
|
||||
if( action<0 ) continue;
|
||||
acttab_action(pActtab, ap->sp->index, action);
|
||||
}
|
||||
stp->iNtOfst = acttab_insert(pActtab);
|
||||
stp->iNtOfst = acttab_insert(pActtab, 0);
|
||||
if( stp->iNtOfst<mnNtOfst ) mnNtOfst = stp->iNtOfst;
|
||||
if( stp->iNtOfst>mxNtOfst ) mxNtOfst = stp->iNtOfst;
|
||||
}
|
||||
@@ -4200,16 +4257,18 @@ void ReportTable(
|
||||
** been computed */
|
||||
fprintf(out,"#define YYNSTATE %d\n",lemp->nxstate); lineno++;
|
||||
fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++;
|
||||
fprintf(out,"#define YYNTOKEN %d\n",lemp->nterminal); lineno++;
|
||||
fprintf(out,"#define YY_MAX_SHIFT %d\n",lemp->nxstate-1); lineno++;
|
||||
fprintf(out,"#define YY_MIN_SHIFTREDUCE %d\n",lemp->nstate); lineno++;
|
||||
i = lemp->nstate + lemp->nrule;
|
||||
i = lemp->minShiftReduce;
|
||||
fprintf(out,"#define YY_MIN_SHIFTREDUCE %d\n",i); lineno++;
|
||||
i += lemp->nrule;
|
||||
fprintf(out,"#define YY_MAX_SHIFTREDUCE %d\n", i-1); lineno++;
|
||||
fprintf(out,"#define YY_MIN_REDUCE %d\n", i); lineno++;
|
||||
i = lemp->nstate + lemp->nrule*2;
|
||||
fprintf(out,"#define YY_ERROR_ACTION %d\n", lemp->errAction); lineno++;
|
||||
fprintf(out,"#define YY_ACCEPT_ACTION %d\n", lemp->accAction); lineno++;
|
||||
fprintf(out,"#define YY_NO_ACTION %d\n", lemp->noAction); lineno++;
|
||||
fprintf(out,"#define YY_MIN_REDUCE %d\n", lemp->minReduce); lineno++;
|
||||
i = lemp->minReduce + lemp->nrule;
|
||||
fprintf(out,"#define YY_MAX_REDUCE %d\n", i-1); lineno++;
|
||||
fprintf(out,"#define YY_ERROR_ACTION %d\n", i); lineno++;
|
||||
fprintf(out,"#define YY_ACCEPT_ACTION %d\n", i+1); lineno++;
|
||||
fprintf(out,"#define YY_NO_ACTION %d\n", i+2); lineno++;
|
||||
tplt_xfer(lemp->name,in,out,&lineno);
|
||||
|
||||
/* Now output the action table and its associates:
|
||||
@@ -4225,13 +4284,13 @@ void ReportTable(
|
||||
*/
|
||||
|
||||
/* Output the yy_action table */
|
||||
lemp->nactiontab = n = acttab_size(pActtab);
|
||||
lemp->nactiontab = n = acttab_action_size(pActtab);
|
||||
lemp->tablesize += n*szActionType;
|
||||
fprintf(out,"#define YY_ACTTAB_COUNT (%d)\n", n); lineno++;
|
||||
fprintf(out,"static const YYACTIONTYPE yy_action[] = {\n"); lineno++;
|
||||
for(i=j=0; i<n; i++){
|
||||
int action = acttab_yyaction(pActtab, i);
|
||||
if( action<0 ) action = lemp->nstate + lemp->nrule + 2;
|
||||
if( action<0 ) action = lemp->noAction;
|
||||
if( j==0 ) fprintf(out," /* %5d */ ", i);
|
||||
fprintf(out, " %4d,", action);
|
||||
if( j==9 || i==n-1 ){
|
||||
@@ -4244,6 +4303,7 @@ void ReportTable(
|
||||
fprintf(out, "};\n"); lineno++;
|
||||
|
||||
/* Output the yy_lookahead table */
|
||||
lemp->nlookaheadtab = n = acttab_lookahead_size(pActtab);
|
||||
lemp->tablesize += n*szCodeType;
|
||||
fprintf(out,"static const YYCODETYPE yy_lookahead[] = {\n"); lineno++;
|
||||
for(i=j=0; i<n; i++){
|
||||
@@ -4263,7 +4323,6 @@ void ReportTable(
|
||||
/* Output the yy_shift_ofst[] table */
|
||||
n = lemp->nxstate;
|
||||
while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--;
|
||||
fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", lemp->nactiontab); lineno++;
|
||||
fprintf(out, "#define YY_SHIFT_COUNT (%d)\n", n-1); lineno++;
|
||||
fprintf(out, "#define YY_SHIFT_MIN (%d)\n", mnTknOfst); lineno++;
|
||||
fprintf(out, "#define YY_SHIFT_MAX (%d)\n", mxTknOfst); lineno++;
|
||||
@@ -4288,7 +4347,6 @@ void ReportTable(
|
||||
fprintf(out, "};\n"); lineno++;
|
||||
|
||||
/* Output the yy_reduce_ofst[] table */
|
||||
fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++;
|
||||
n = lemp->nxstate;
|
||||
while( n>0 && lemp->sorted[n-1]->iNtOfst==NO_OFFSET ) n--;
|
||||
fprintf(out, "#define YY_REDUCE_COUNT (%d)\n", n-1); lineno++;
|
||||
@@ -4320,7 +4378,11 @@ void ReportTable(
|
||||
for(i=j=0; i<n; i++){
|
||||
stp = lemp->sorted[i];
|
||||
if( j==0 ) fprintf(out," /* %5d */ ", i);
|
||||
fprintf(out, " %4d,", stp->iDfltReduce+lemp->nstate+lemp->nrule);
|
||||
if( stp->iDfltReduce<0 ){
|
||||
fprintf(out, " %4d,", lemp->errAction);
|
||||
}else{
|
||||
fprintf(out, " %4d,", stp->iDfltReduce + lemp->minReduce);
|
||||
}
|
||||
if( j==9 || i==n-1 ){
|
||||
fprintf(out, "\n"); lineno++;
|
||||
j = 0;
|
||||
@@ -4354,10 +4416,8 @@ void ReportTable(
|
||||
*/
|
||||
for(i=0; i<lemp->nsymbol; i++){
|
||||
lemon_sprintf(line,"\"%s\",",lemp->symbols[i]->name);
|
||||
fprintf(out," %-15s",line);
|
||||
if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; }
|
||||
fprintf(out," /* %4d */ \"%s\",\n",i, lemp->symbols[i]->name); lineno++;
|
||||
}
|
||||
if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; }
|
||||
tplt_xfer(lemp->name,in,out,&lineno);
|
||||
|
||||
/* Generate a table containing a text string that describes every
|
||||
@@ -4401,7 +4461,7 @@ void ReportTable(
|
||||
if( sp==0 || sp->type==TERMINAL ||
|
||||
sp->index<=0 || sp->destructor!=0 ) continue;
|
||||
if( once ){
|
||||
fprintf(out, " /* Default NON-TERMINAL Destructor */\n"); lineno++;
|
||||
fprintf(out, " /* Default NON-TERMINAL Destructor */\n");lineno++;
|
||||
once = 0;
|
||||
}
|
||||
fprintf(out," case %d: /* %s */\n", sp->index, sp->name); lineno++;
|
||||
@@ -4444,8 +4504,10 @@ void ReportTable(
|
||||
** Note: This code depends on the fact that rules are number
|
||||
** sequentually beginning with 0.
|
||||
*/
|
||||
for(rp=lemp->rule; rp; rp=rp->next){
|
||||
fprintf(out," { %d, %d },\n",rp->lhs->index,-rp->nrhs); lineno++;
|
||||
for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){
|
||||
fprintf(out," { %4d, %4d }, /* (%d) ",rp->lhs->index,-rp->nrhs,i);
|
||||
rule_print(out, rp);
|
||||
fprintf(out," */\n"); lineno++;
|
||||
}
|
||||
tplt_xfer(lemp->name,in,out,&lineno);
|
||||
|
||||
@@ -4711,7 +4773,7 @@ void ResortStates(struct lemon *lemp)
|
||||
for(i=0; i<lemp->nstate; i++){
|
||||
stp = lemp->sorted[i];
|
||||
stp->nTknAct = stp->nNtAct = 0;
|
||||
stp->iDfltReduce = lemp->nrule; /* Init dflt action to "syntax error" */
|
||||
stp->iDfltReduce = -1; /* Init dflt action to "syntax error" */
|
||||
stp->iTknOfst = NO_OFFSET;
|
||||
stp->iNtOfst = NO_OFFSET;
|
||||
for(ap=stp->ap; ap; ap=ap->next){
|
||||
@@ -4723,7 +4785,7 @@ void ResortStates(struct lemon *lemp)
|
||||
stp->nNtAct++;
|
||||
}else{
|
||||
assert( stp->autoReduce==0 || stp->pDfltReduce==ap->x.rp );
|
||||
stp->iDfltReduce = iAction - lemp->nstate - lemp->nrule;
|
||||
stp->iDfltReduce = iAction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+91
-42
@@ -72,14 +72,15 @@
|
||||
** defined, then do no error processing.
|
||||
** YYNSTATE the combined number of states.
|
||||
** YYNRULE the number of rules in the grammar
|
||||
** YYNTOKEN Number of terminal symbols
|
||||
** YY_MAX_SHIFT Maximum value for shift actions
|
||||
** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
|
||||
** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
|
||||
** YY_MIN_REDUCE Minimum value for reduce actions
|
||||
** YY_MAX_REDUCE Maximum value for reduce actions
|
||||
** YY_ERROR_ACTION The yy_action[] code for syntax error
|
||||
** YY_ACCEPT_ACTION The yy_action[] code for accept
|
||||
** YY_NO_ACTION The yy_action[] code for no-op
|
||||
** YY_MIN_REDUCE Minimum value for reduce actions
|
||||
** YY_MAX_REDUCE Maximum value for reduce actions
|
||||
*/
|
||||
#ifndef INTERFACE
|
||||
# define INTERFACE 1
|
||||
@@ -115,9 +116,6 @@
|
||||
** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
|
||||
** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE.
|
||||
**
|
||||
** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
|
||||
** and YY_MAX_REDUCE
|
||||
**
|
||||
** N == YY_ERROR_ACTION A syntax error has occurred.
|
||||
**
|
||||
** N == YY_ACCEPT_ACTION The parser accepts its input.
|
||||
@@ -125,25 +123,22 @@
|
||||
** N == YY_NO_ACTION No such action. Denotes unused
|
||||
** slots in the yy_action[] table.
|
||||
**
|
||||
** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
|
||||
** and YY_MAX_REDUCE
|
||||
**
|
||||
** The action table is constructed as a single large table named yy_action[].
|
||||
** Given state S and lookahead X, the action is computed as either:
|
||||
**
|
||||
** (A) N = yy_action[ yy_shift_ofst[S] + X ]
|
||||
** (B) N = yy_default[S]
|
||||
**
|
||||
** The (A) formula is preferred. The B formula is used instead if:
|
||||
** (1) The yy_shift_ofst[S]+X value is out of range, or
|
||||
** (2) yy_lookahead[yy_shift_ofst[S]+X] is not equal to X, or
|
||||
** (3) yy_shift_ofst[S] equal YY_SHIFT_USE_DFLT.
|
||||
** (Implementation note: YY_SHIFT_USE_DFLT is chosen so that
|
||||
** YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X.
|
||||
** Hence only tests (1) and (2) need to be evaluated.)
|
||||
** The (A) formula is preferred. The B formula is used instead if
|
||||
** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X.
|
||||
**
|
||||
** The formulas above are for computing the action when the lookahead is
|
||||
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
|
||||
** a reduce action) then the yy_reduce_ofst[] array is used in place of
|
||||
** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
|
||||
** YY_SHIFT_USE_DFLT.
|
||||
** the yy_shift_ofst[] array.
|
||||
**
|
||||
** The following are the tables generated in this section:
|
||||
**
|
||||
@@ -259,13 +254,13 @@ void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
|
||||
#ifndef NDEBUG
|
||||
#if defined(YYCOVERAGE) || !defined(NDEBUG)
|
||||
/* For tracing shifts, the names of all terminals and nonterminals
|
||||
** are required. The following table supplies these names */
|
||||
static const char *const yyTokenName[] = {
|
||||
%%
|
||||
};
|
||||
#endif /* NDEBUG */
|
||||
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* For tracing reduce actions, the names of all rules are required.
|
||||
@@ -461,6 +456,43 @@ int ParseStackPeak(void *p){
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This array of booleans keeps track of the parser statement
|
||||
** coverage. The element yycoverage[X][Y] is set when the parser
|
||||
** is in state X and has a lookahead token Y. In a well-tested
|
||||
** systems, every element of this matrix should end up being set.
|
||||
*/
|
||||
#if defined(YYCOVERAGE)
|
||||
static unsigned char yycoverage[YYNSTATE][YYNTOKEN];
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Write into out a description of every state/lookahead combination that
|
||||
**
|
||||
** (1) has not been used by the parser, and
|
||||
** (2) is not a syntax error.
|
||||
**
|
||||
** Return the number of missed state/lookahead combinations.
|
||||
*/
|
||||
#if defined(YYCOVERAGE)
|
||||
int ParseCoverage(FILE *out){
|
||||
int stateno, iLookAhead, i;
|
||||
int nMissed = 0;
|
||||
for(stateno=0; stateno<YYNSTATE; stateno++){
|
||||
i = yy_shift_ofst[stateno];
|
||||
for(iLookAhead=0; iLookAhead<YYNTOKEN; iLookAhead++){
|
||||
if( yy_lookahead[i+iLookAhead]!=iLookAhead ) continue;
|
||||
if( yycoverage[stateno][iLookAhead]==0 ) nMissed++;
|
||||
if( out ){
|
||||
fprintf(out,"State %d lookahead %s %s\n", stateno,
|
||||
yyTokenName[iLookAhead],
|
||||
yycoverage[stateno][iLookAhead] ? "ok" : "missed");
|
||||
}
|
||||
}
|
||||
}
|
||||
return nMissed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Find the appropriate action for a parser given the terminal
|
||||
** look-ahead token iLookAhead.
|
||||
@@ -472,13 +504,18 @@ static unsigned int yy_find_shift_action(
|
||||
int i;
|
||||
int stateno = pParser->yytos->stateno;
|
||||
|
||||
if( stateno>=YY_MIN_REDUCE ) return stateno;
|
||||
if( stateno>YY_MAX_SHIFT ) return stateno;
|
||||
assert( stateno <= YY_SHIFT_COUNT );
|
||||
#if defined(YYCOVERAGE)
|
||||
yycoverage[stateno][iLookAhead] = 1;
|
||||
#endif
|
||||
do{
|
||||
i = yy_shift_ofst[stateno];
|
||||
assert( i>=0 && i+YYNTOKEN<=sizeof(yy_lookahead)/sizeof(yy_lookahead[0]) );
|
||||
assert( iLookAhead!=YYNOCODE );
|
||||
assert( iLookAhead < YYNTOKEN );
|
||||
i += iLookAhead;
|
||||
if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
|
||||
if( yy_lookahead[i]!=iLookAhead ){
|
||||
#ifdef YYFALLBACK
|
||||
YYCODETYPE iFallback; /* Fallback token */
|
||||
if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
|
||||
@@ -541,7 +578,6 @@ static int yy_find_reduce_action(
|
||||
assert( stateno<=YY_REDUCE_COUNT );
|
||||
#endif
|
||||
i = yy_reduce_ofst[stateno];
|
||||
assert( i!=YY_REDUCE_USE_DFLT );
|
||||
assert( iLookAhead!=YYNOCODE );
|
||||
i += iLookAhead;
|
||||
#ifdef YYERRORSYMBOL
|
||||
@@ -578,20 +614,21 @@ static void yyStackOverflow(yyParser *yypParser){
|
||||
** Print tracing information for a SHIFT action
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
static void yyTraceShift(yyParser *yypParser, int yyNewState){
|
||||
static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){
|
||||
if( yyTraceFILE ){
|
||||
if( yyNewState<YYNSTATE ){
|
||||
fprintf(yyTraceFILE,"%sShift '%s', go to state %d\n",
|
||||
yyTracePrompt,yyTokenName[yypParser->yytos->major],
|
||||
fprintf(yyTraceFILE,"%s%s '%s', go to state %d\n",
|
||||
yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
|
||||
yyNewState);
|
||||
}else{
|
||||
fprintf(yyTraceFILE,"%sShift '%s'\n",
|
||||
yyTracePrompt,yyTokenName[yypParser->yytos->major]);
|
||||
fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n",
|
||||
yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
|
||||
yyNewState - YY_MIN_REDUCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define yyTraceShift(X,Y)
|
||||
# define yyTraceShift(X,Y,Z)
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -633,7 +670,7 @@ static void yy_shift(
|
||||
yytos->stateno = (YYACTIONTYPE)yyNewState;
|
||||
yytos->major = (YYCODETYPE)yyMajor;
|
||||
yytos->minor.yy0 = yyMinor;
|
||||
yyTraceShift(yypParser, yyNewState);
|
||||
yyTraceShift(yypParser, yyNewState, "Shift");
|
||||
}
|
||||
|
||||
/* The following table contains information about every rule that
|
||||
@@ -673,8 +710,14 @@ static void yy_reduce(
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
|
||||
yysize = yyRuleInfo[yyruleno].nrhs;
|
||||
fprintf(yyTraceFILE, "%sReduce [%s], go to state %d.\n", yyTracePrompt,
|
||||
yyRuleName[yyruleno], yymsp[yysize].stateno);
|
||||
if( yysize ){
|
||||
fprintf(yyTraceFILE, "%sReduce %d [%s], go to state %d.\n",
|
||||
yyTracePrompt,
|
||||
yyruleno, yyRuleName[yyruleno], yymsp[yysize].stateno);
|
||||
}else{
|
||||
fprintf(yyTraceFILE, "%sReduce %d [%s].\n",
|
||||
yyTracePrompt, yyruleno, yyRuleName[yyruleno]);
|
||||
}
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
|
||||
@@ -729,16 +772,11 @@ static void yy_reduce(
|
||||
/* It is not possible for a REDUCE to be followed by an error */
|
||||
assert( yyact!=YY_ERROR_ACTION );
|
||||
|
||||
if( yyact==YY_ACCEPT_ACTION ){
|
||||
yypParser->yytos += yysize;
|
||||
yy_accept(yypParser);
|
||||
}else{
|
||||
yymsp += yysize+1;
|
||||
yypParser->yytos = yymsp;
|
||||
yymsp->stateno = (YYACTIONTYPE)yyact;
|
||||
yymsp->major = (YYCODETYPE)yygoto;
|
||||
yyTraceShift(yypParser, yyact);
|
||||
}
|
||||
yymsp += yysize+1;
|
||||
yypParser->yytos = yymsp;
|
||||
yymsp->stateno = (YYACTIONTYPE)yyact;
|
||||
yymsp->major = (YYCODETYPE)yygoto;
|
||||
yyTraceShift(yypParser, yyact, "... then shift");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -848,20 +886,31 @@ void Parse(
|
||||
|
||||
#ifndef NDEBUG
|
||||
if( yyTraceFILE ){
|
||||
fprintf(yyTraceFILE,"%sInput '%s'\n",yyTracePrompt,yyTokenName[yymajor]);
|
||||
int stateno = yypParser->yytos->stateno;
|
||||
if( stateno < YY_MIN_REDUCE ){
|
||||
fprintf(yyTraceFILE,"%sInput '%s' in state %d\n",
|
||||
yyTracePrompt,yyTokenName[yymajor],stateno);
|
||||
}else{
|
||||
fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n",
|
||||
yyTracePrompt,yyTokenName[yymajor],stateno-YY_MIN_REDUCE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
do{
|
||||
yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
|
||||
if( yyact <= YY_MAX_SHIFTREDUCE ){
|
||||
if( yyact >= YY_MIN_REDUCE ){
|
||||
yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor,yyminor);
|
||||
}else if( yyact <= YY_MAX_SHIFTREDUCE ){
|
||||
yy_shift(yypParser,yyact,yymajor,yyminor);
|
||||
#ifndef YYNOERRORRECOVERY
|
||||
yypParser->yyerrcnt--;
|
||||
#endif
|
||||
yymajor = YYNOCODE;
|
||||
}else if( yyact <= YY_MAX_REDUCE ){
|
||||
yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor,yyminor);
|
||||
}else if( yyact==YY_ACCEPT_ACTION ){
|
||||
yypParser->yytos--;
|
||||
yy_accept(yypParser);
|
||||
return;
|
||||
}else{
|
||||
assert( yyact == YY_ERROR_ACTION );
|
||||
yyminorunion.yy0 = yyminor;
|
||||
|
||||
@@ -117,6 +117,12 @@ while test "$1" != ""; do
|
||||
--orm)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset orm"
|
||||
;;
|
||||
--cte)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset cte"
|
||||
;;
|
||||
--fp)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset fp"
|
||||
;;
|
||||
-*)
|
||||
CC_OPTS="$CC_OPTS $1"
|
||||
;;
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
** This is the source code to a "tclsh" that has SQLite built-in.
|
||||
**
|
||||
** The startup script is located as follows:
|
||||
**
|
||||
** (1) Open the executable as an appended SQLite database and try to
|
||||
** read the startup script out of that database.
|
||||
**
|
||||
** (2) If the first argument is a readable file, try to open that file
|
||||
** as an SQLite database and read the startup script out of that
|
||||
** database.
|
||||
**
|
||||
** (3) If the first argument is a readable file with a ".tcl" extension,
|
||||
** then try to run that script directly.
|
||||
**
|
||||
** If none of the above steps work, then the program runs as an interactive
|
||||
** tclsh.
|
||||
*/
|
||||
#define TCLSH_INIT_PROC sqlite3_tclapp_init_proc
|
||||
#define SQLITE_ENABLE_DBSTAT_VTAB 1
|
||||
#undef SQLITE_THREADSAFE
|
||||
#define SQLITE_THREADSAFE 0
|
||||
#undef SQLITE_ENABLE_COLUMN_METADATA
|
||||
#define SQLITE_OMIT_DECLTYPE 1
|
||||
#define SQLITE_OMIT_DEPRECATED 1
|
||||
#define SQLITE_OMIT_PROGRESS_CALLBACK 1
|
||||
#define SQLITE_OMIT_SHARED_CACHE 1
|
||||
#define SQLITE_DEFAULT_MEMSTATUS 0
|
||||
#define SQLITE_MAX_EXPR_DEPTH 0
|
||||
INCLUDE sqlite3.c
|
||||
INCLUDE $ROOT/ext/misc/appendvfs.c
|
||||
INCLUDE $ROOT/src/tclsqlite.c
|
||||
|
||||
const char *sqlite3_tclapp_init_proc(Tcl_Interp *interp){
|
||||
(void)interp;
|
||||
sqlite3_appendvfs_init(0,0,0);
|
||||
return
|
||||
BEGIN_STRING
|
||||
INCLUDE $ROOT/tool/sqltclsh.tcl
|
||||
END_STRING
|
||||
;
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
# Try to open the executable as a database and read the "scripts.data"
|
||||
# field where "scripts.name" is 'main.tcl'
|
||||
#
|
||||
catch {
|
||||
if {![file exists $argv0] && [file exists $argv0.exe]} {
|
||||
append argv0 .exe
|
||||
}
|
||||
sqlite3 db $argv0 -vfs apndvfs -create 0
|
||||
set mainscript [db one {SELECT data FROM scripts WHERE name='main.tcl'}]
|
||||
}
|
||||
if {[info exists mainscript]} {
|
||||
eval $mainscript
|
||||
return
|
||||
} else {
|
||||
catch {db close}
|
||||
}
|
||||
|
||||
# Try to open file named in the first argument as a database and
|
||||
# read the "scripts.data" field where "scripts.name" is 'main.tcl'
|
||||
#
|
||||
if {[llength $argv]>0 && [file readable [lindex $argv 0]]} {
|
||||
catch {
|
||||
sqlite3 db [lindex $argv 0] -vfs apndvfs -create 0
|
||||
set mainscript [db one {SELECT data FROM scripts WHERE name='main.tcl'}]
|
||||
set argv0 [lindex $argv 0]
|
||||
set argv [lrange $argv 1 end]
|
||||
}
|
||||
if {[info exists mainscript]} {
|
||||
eval $mainscript
|
||||
return
|
||||
} else {
|
||||
catch {db close}
|
||||
}
|
||||
if {[string match *.tcl [lindex $argv 0]]} {
|
||||
set fd [open [lindex $argv 0] rb]
|
||||
set mainscript [read $fd]
|
||||
close $fd
|
||||
unset fd
|
||||
set argv0 [lindex $argv 0]
|
||||
set argv [lrange $argv 1 end]
|
||||
}
|
||||
if {[info exists mainscript]} {
|
||||
eval $mainscript
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
# If all else fails, do an interactive loop
|
||||
#
|
||||
set line {}
|
||||
while {![eof stdin]} {
|
||||
if {$line!=""} {
|
||||
puts -nonewline "> "
|
||||
} else {
|
||||
puts -nonewline "% "
|
||||
}
|
||||
flush stdout
|
||||
append line [gets stdin]
|
||||
if {[info complete $line]} {
|
||||
if {[catch {uplevel #0 $line} result]} {
|
||||
puts stderr "Error: $result"
|
||||
} elseif {$result!=""} {
|
||||
puts $result
|
||||
}
|
||||
set line {}
|
||||
} else {
|
||||
append line \\n"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user