mirror of
https://github.com/php/php-src.git
synced 2025-08-18 15:08:55 +02:00
Upgrade bundled library to 2.8.14 + misc fixes
(http://www.sqlite.org/cvstrac/chngview?cn=1742)
This commit is contained in:
parent
cd732f1a3f
commit
e563b4eafa
43 changed files with 5953 additions and 5559 deletions
|
@ -23,12 +23,15 @@
|
|||
** The pFilename and pDbname arguments are the tokens that define the
|
||||
** filename and dbname in the ATTACH statement.
|
||||
*/
|
||||
void sqliteAttach(Parse *pParse, Token *pFilename, Token *pDbname){
|
||||
void sqliteAttach(Parse *pParse, Token *pFilename, Token *pDbname, Token *pKey){
|
||||
Db *aNew;
|
||||
int rc, i;
|
||||
char *zFile, *zName;
|
||||
sqlite *db;
|
||||
Vdbe *v;
|
||||
|
||||
v = sqliteGetVdbe(pParse);
|
||||
sqliteVdbeAddOp(v, OP_Halt, 0, 0);
|
||||
if( pParse->explain ) return;
|
||||
db = pParse->db;
|
||||
if( db->file_format<4 ){
|
||||
|
@ -88,6 +91,22 @@ void sqliteAttach(Parse *pParse, Token *pFilename, Token *pDbname){
|
|||
if( rc ){
|
||||
sqliteErrorMsg(pParse, "unable to open database: %s", zFile);
|
||||
}
|
||||
#if SQLITE_HAS_CODEC
|
||||
{
|
||||
extern int sqliteCodecAttach(sqlite*, int, void*, int);
|
||||
char *zKey = 0;
|
||||
int nKey;
|
||||
if( pKey && pKey->z && pKey->n ){
|
||||
sqliteSetNString(&zKey, pKey->z, pKey->n, 0);
|
||||
sqliteDequote(zKey);
|
||||
nKey = strlen(zKey);
|
||||
}else{
|
||||
zKey = 0;
|
||||
nKey = 0;
|
||||
}
|
||||
sqliteCodecAttach(db, db->nDb-1, zKey, nKey);
|
||||
}
|
||||
#endif
|
||||
sqliteFree(zFile);
|
||||
db->flags &= ~SQLITE_Initialized;
|
||||
if( pParse->nErr ) return;
|
||||
|
@ -117,13 +136,18 @@ void sqliteAttach(Parse *pParse, Token *pFilename, Token *pDbname){
|
|||
void sqliteDetach(Parse *pParse, Token *pDbname){
|
||||
int i;
|
||||
sqlite *db;
|
||||
Vdbe *v;
|
||||
Db *pDb;
|
||||
|
||||
v = sqliteGetVdbe(pParse);
|
||||
sqliteVdbeAddOp(v, OP_Halt, 0, 0);
|
||||
if( pParse->explain ) return;
|
||||
db = pParse->db;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt==0 || db->aDb[i].zName==0 ) continue;
|
||||
if( strlen(db->aDb[i].zName)!=pDbname->n ) continue;
|
||||
if( sqliteStrNICmp(db->aDb[i].zName, pDbname->z, pDbname->n)==0 ) break;
|
||||
pDb = &db->aDb[i];
|
||||
if( pDb->pBt==0 || pDb->zName==0 ) continue;
|
||||
if( strlen(pDb->zName)!=pDbname->n ) continue;
|
||||
if( sqliteStrNICmp(pDb->zName, pDbname->z, pDbname->n)==0 ) break;
|
||||
}
|
||||
if( i>=db->nDb ){
|
||||
sqliteErrorMsg(pParse, "no such database: %T", pDbname);
|
||||
|
@ -138,10 +162,11 @@ void sqliteDetach(Parse *pParse, Token *pDbname){
|
|||
return;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_AUTHORIZATION */
|
||||
sqliteBtreeClose(db->aDb[i].pBt);
|
||||
db->aDb[i].pBt = 0;
|
||||
sqliteFree(db->aDb[i].zName);
|
||||
sqliteBtreeClose(pDb->pBt);
|
||||
pDb->pBt = 0;
|
||||
sqliteFree(pDb->zName);
|
||||
sqliteResetInternalSchema(db, i);
|
||||
if( pDb->pAux && pDb->xFreeAux ) pDb->xFreeAux(pDb->pAux);
|
||||
db->nDb--;
|
||||
if( i<db->nDb ){
|
||||
db->aDb[i] = db->aDb[db->nDb];
|
||||
|
|
|
@ -85,12 +85,9 @@ int sqlite_set_authorizer(
|
|||
** user-supplied authorization function returned an illegal value.
|
||||
*/
|
||||
static void sqliteAuthBadReturnCode(Parse *pParse, int rc){
|
||||
char zBuf[20];
|
||||
sprintf(zBuf, "(%d)", rc);
|
||||
sqliteSetString(&pParse->zErrMsg, "illegal return value ", zBuf,
|
||||
" from the authorization function - should be SQLITE_OK, "
|
||||
"SQLITE_IGNORE, or SQLITE_DENY", (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "illegal return value (%d) from the "
|
||||
"authorization function - should be SQLITE_OK, SQLITE_IGNORE, "
|
||||
"or SQLITE_DENY", rc);
|
||||
pParse->rc = SQLITE_MISUSE;
|
||||
}
|
||||
|
||||
|
@ -150,13 +147,11 @@ void sqliteAuthRead(
|
|||
pExpr->op = TK_NULL;
|
||||
}else if( rc==SQLITE_DENY ){
|
||||
if( db->nDb>2 || pExpr->iDb!=0 ){
|
||||
sqliteSetString(&pParse->zErrMsg,"access to ", zDBase, ".",
|
||||
pTab->zName, ".", zCol, " is prohibited", (char*)0);
|
||||
sqliteErrorMsg(pParse, "access to %s.%s.%s is prohibited",
|
||||
zDBase, pTab->zName, zCol);
|
||||
}else{
|
||||
sqliteSetString(&pParse->zErrMsg,"access to ", pTab->zName, ".",
|
||||
zCol, " is prohibited", (char*)0);
|
||||
sqliteErrorMsg(pParse, "access to %s.%s is prohibited", pTab->zName,zCol);
|
||||
}
|
||||
pParse->nErr++;
|
||||
pParse->rc = SQLITE_AUTH;
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
sqliteAuthBadReturnCode(pParse, rc);
|
||||
|
@ -179,14 +174,13 @@ int sqliteAuthCheck(
|
|||
sqlite *db = pParse->db;
|
||||
int rc;
|
||||
|
||||
if( db->xAuth==0 ){
|
||||
if( db->init.busy || db->xAuth==0 ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext);
|
||||
if( rc==SQLITE_DENY ){
|
||||
sqliteSetString(&pParse->zErrMsg, "not authorized", (char*)0);
|
||||
sqliteErrorMsg(pParse, "not authorized");
|
||||
pParse->rc = SQLITE_AUTH;
|
||||
pParse->nErr++;
|
||||
}else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){
|
||||
rc = SQLITE_DENY;
|
||||
sqliteAuthBadReturnCode(pParse, rc);
|
||||
|
|
|
@ -210,13 +210,13 @@ struct CellHdr {
|
|||
** The maximum number of database entries that can be held in a single
|
||||
** page of the database.
|
||||
*/
|
||||
#define MX_CELL ((SQLITE_PAGE_SIZE-sizeof(PageHdr))/MIN_CELL_SIZE)
|
||||
#define MX_CELL ((SQLITE_USABLE_SIZE-sizeof(PageHdr))/MIN_CELL_SIZE)
|
||||
|
||||
/*
|
||||
** The amount of usable space on a single page of the BTree. This is the
|
||||
** page size minus the overhead of the page header.
|
||||
*/
|
||||
#define USABLE_SPACE (SQLITE_PAGE_SIZE - sizeof(PageHdr))
|
||||
#define USABLE_SPACE (SQLITE_USABLE_SIZE - sizeof(PageHdr))
|
||||
|
||||
/*
|
||||
** The maximum amount of payload (in bytes) that can be stored locally for
|
||||
|
@ -261,7 +261,7 @@ struct FreeBlk {
|
|||
/*
|
||||
** The number of bytes of payload that will fit on a single overflow page.
|
||||
*/
|
||||
#define OVERFLOW_SIZE (SQLITE_PAGE_SIZE-sizeof(Pgno))
|
||||
#define OVERFLOW_SIZE (SQLITE_USABLE_SIZE-sizeof(Pgno))
|
||||
|
||||
/*
|
||||
** When the key and data for a single entry in the BTree will not fit in
|
||||
|
@ -319,7 +319,7 @@ struct FreelistInfo {
|
|||
** The pageDestructor() routine handles that chore.
|
||||
*/
|
||||
struct MemPage {
|
||||
union {
|
||||
union u_page_data {
|
||||
char aDisk[SQLITE_PAGE_SIZE]; /* Page data stored on disk */
|
||||
PageHdr hdr; /* Overlay page header */
|
||||
} u;
|
||||
|
@ -338,7 +338,7 @@ struct MemPage {
|
|||
** to the end. EXTRA_SIZE is the number of bytes of space needed to hold
|
||||
** that extra information.
|
||||
*/
|
||||
#define EXTRA_SIZE (sizeof(MemPage)-SQLITE_PAGE_SIZE)
|
||||
#define EXTRA_SIZE (sizeof(MemPage)-sizeof(union u_page_data))
|
||||
|
||||
/*
|
||||
** Everything we need to know about an open database
|
||||
|
@ -421,7 +421,7 @@ static int cellSize(Btree *pBt, Cell *pCell){
|
|||
static void defragmentPage(Btree *pBt, MemPage *pPage){
|
||||
int pc, i, n;
|
||||
FreeBlk *pFBlk;
|
||||
char newPage[SQLITE_PAGE_SIZE];
|
||||
char newPage[SQLITE_USABLE_SIZE];
|
||||
|
||||
assert( sqlitepager_iswriteable(pPage) );
|
||||
assert( pPage->isInit );
|
||||
|
@ -434,7 +434,7 @@ static void defragmentPage(Btree *pBt, MemPage *pPage){
|
|||
/* This routine should never be called on an overfull page. The
|
||||
** following asserts verify that constraint. */
|
||||
assert( Addr(pCell) > Addr(pPage) );
|
||||
assert( Addr(pCell) < Addr(pPage) + SQLITE_PAGE_SIZE );
|
||||
assert( Addr(pCell) < Addr(pPage) + SQLITE_USABLE_SIZE );
|
||||
|
||||
n = cellSize(pBt, pCell);
|
||||
pCell->h.iNext = SWAB16(pBt, pc + n);
|
||||
|
@ -442,16 +442,16 @@ static void defragmentPage(Btree *pBt, MemPage *pPage){
|
|||
pPage->apCell[i] = (Cell*)&pPage->u.aDisk[pc];
|
||||
pc += n;
|
||||
}
|
||||
assert( pPage->nFree==SQLITE_PAGE_SIZE-pc );
|
||||
assert( pPage->nFree==SQLITE_USABLE_SIZE-pc );
|
||||
memcpy(pPage->u.aDisk, newPage, pc);
|
||||
if( pPage->nCell>0 ){
|
||||
pPage->apCell[pPage->nCell-1]->h.iNext = 0;
|
||||
}
|
||||
pFBlk = (FreeBlk*)&pPage->u.aDisk[pc];
|
||||
pFBlk->iSize = SWAB16(pBt, SQLITE_PAGE_SIZE - pc);
|
||||
pFBlk->iSize = SWAB16(pBt, SQLITE_USABLE_SIZE - pc);
|
||||
pFBlk->iNext = 0;
|
||||
pPage->u.hdr.firstFree = SWAB16(pBt, pc);
|
||||
memset(&pFBlk[1], 0, SQLITE_PAGE_SIZE - pc - sizeof(FreeBlk));
|
||||
memset(&pFBlk[1], 0, SQLITE_USABLE_SIZE - pc - sizeof(FreeBlk));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -483,7 +483,7 @@ static int allocateSpace(Btree *pBt, MemPage *pPage, int nByte){
|
|||
pIdx = &pPage->u.hdr.firstFree;
|
||||
p = (FreeBlk*)&pPage->u.aDisk[SWAB16(pBt, *pIdx)];
|
||||
while( (iSize = SWAB16(pBt, p->iSize))<nByte ){
|
||||
assert( cnt++ < SQLITE_PAGE_SIZE/4 );
|
||||
assert( cnt++ < SQLITE_USABLE_SIZE/4 );
|
||||
if( p->iNext==0 ){
|
||||
defragmentPage(pBt, pPage);
|
||||
pIdx = &pPage->u.hdr.firstFree;
|
||||
|
@ -598,12 +598,12 @@ static int initPage(Bt *pBt, MemPage *pPage, Pgno pgnoThis, MemPage *pParent){
|
|||
freeSpace = USABLE_SPACE;
|
||||
idx = SWAB16(pBt, pPage->u.hdr.firstCell);
|
||||
while( idx!=0 ){
|
||||
if( idx>SQLITE_PAGE_SIZE-MIN_CELL_SIZE ) goto page_format_error;
|
||||
if( idx>SQLITE_USABLE_SIZE-MIN_CELL_SIZE ) goto page_format_error;
|
||||
if( idx<sizeof(PageHdr) ) goto page_format_error;
|
||||
if( idx!=ROUNDUP(idx) ) goto page_format_error;
|
||||
pCell = (Cell*)&pPage->u.aDisk[idx];
|
||||
sz = cellSize(pBt, pCell);
|
||||
if( idx+sz > SQLITE_PAGE_SIZE ) goto page_format_error;
|
||||
if( idx+sz > SQLITE_USABLE_SIZE ) goto page_format_error;
|
||||
freeSpace -= sz;
|
||||
pPage->apCell[pPage->nCell++] = pCell;
|
||||
idx = SWAB16(pBt, pCell->h.iNext);
|
||||
|
@ -612,7 +612,7 @@ static int initPage(Bt *pBt, MemPage *pPage, Pgno pgnoThis, MemPage *pParent){
|
|||
idx = SWAB16(pBt, pPage->u.hdr.firstFree);
|
||||
while( idx!=0 ){
|
||||
int iNext;
|
||||
if( idx>SQLITE_PAGE_SIZE-sizeof(FreeBlk) ) goto page_format_error;
|
||||
if( idx>SQLITE_USABLE_SIZE-sizeof(FreeBlk) ) goto page_format_error;
|
||||
if( idx<sizeof(PageHdr) ) goto page_format_error;
|
||||
pFBlk = (FreeBlk*)&pPage->u.aDisk[idx];
|
||||
pPage->nFree += SWAB16(pBt, pFBlk->iSize);
|
||||
|
@ -640,13 +640,13 @@ static void zeroPage(Btree *pBt, MemPage *pPage){
|
|||
PageHdr *pHdr;
|
||||
FreeBlk *pFBlk;
|
||||
assert( sqlitepager_iswriteable(pPage) );
|
||||
memset(pPage, 0, SQLITE_PAGE_SIZE);
|
||||
memset(pPage, 0, SQLITE_USABLE_SIZE);
|
||||
pHdr = &pPage->u.hdr;
|
||||
pHdr->firstCell = 0;
|
||||
pHdr->firstFree = SWAB16(pBt, sizeof(*pHdr));
|
||||
pFBlk = (FreeBlk*)&pHdr[1];
|
||||
pFBlk->iNext = 0;
|
||||
pPage->nFree = SQLITE_PAGE_SIZE - sizeof(*pHdr);
|
||||
pPage->nFree = SQLITE_USABLE_SIZE - sizeof(*pHdr);
|
||||
pFBlk->iSize = SWAB16(pBt, pPage->nFree);
|
||||
pPage->nCell = 0;
|
||||
pPage->isOverfull = 0;
|
||||
|
@ -697,7 +697,7 @@ int sqliteBtreeOpen(
|
|||
assert( sizeof(PageHdr)==8 );
|
||||
assert( sizeof(CellHdr)==12 );
|
||||
assert( sizeof(FreeBlk)==4 );
|
||||
assert( sizeof(OverflowPage)==SQLITE_PAGE_SIZE );
|
||||
assert( sizeof(OverflowPage)==SQLITE_USABLE_SIZE );
|
||||
assert( sizeof(FreelistInfo)==OVERFLOW_SIZE );
|
||||
assert( sizeof(ptr)==sizeof(char*) );
|
||||
assert( sizeof(uptr)==sizeof(ptr) );
|
||||
|
@ -793,7 +793,7 @@ static int lockBtree(Btree *pBt){
|
|||
PageOne *pP1 = pBt->page1;
|
||||
if( strcmp(pP1->zMagic,zMagicHeader)!=0 ||
|
||||
(pP1->iMagic!=MAGIC && swab32(pP1->iMagic)!=MAGIC) ){
|
||||
rc = SQLITE_CORRUPT;
|
||||
rc = SQLITE_NOTADB;
|
||||
goto page1_init_failed;
|
||||
}
|
||||
pBt->needSwab = pP1->iMagic!=MAGIC;
|
||||
|
@ -1031,10 +1031,15 @@ static int fileBtreeRollbackCkpt(Btree *pBt){
|
|||
** root page of a b-tree. If it is not, then the cursor acquired
|
||||
** will not work correctly.
|
||||
*/
|
||||
static int fileBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){
|
||||
static
|
||||
int fileBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){
|
||||
int rc;
|
||||
BtCursor *pCur, *pRing;
|
||||
|
||||
if( pBt->readOnly && wrFlag ){
|
||||
*ppCur = 0;
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
if( pBt->page1==0 ){
|
||||
rc = lockBtree(pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -2082,7 +2087,7 @@ static void relinkCellList(Btree *pBt, MemPage *pPage){
|
|||
pIdx = &pPage->u.hdr.firstCell;
|
||||
for(i=0; i<pPage->nCell; i++){
|
||||
int idx = Addr(pPage->apCell[i]) - Addr(pPage);
|
||||
assert( idx>0 && idx<SQLITE_PAGE_SIZE );
|
||||
assert( idx>0 && idx<SQLITE_USABLE_SIZE );
|
||||
*pIdx = SWAB16(pBt, idx);
|
||||
pIdx = &pPage->apCell[i]->h.iNext;
|
||||
}
|
||||
|
@ -2098,7 +2103,7 @@ static void relinkCellList(Btree *pBt, MemPage *pPage){
|
|||
static void copyPage(MemPage *pTo, MemPage *pFrom){
|
||||
uptr from, to;
|
||||
int i;
|
||||
memcpy(pTo->u.aDisk, pFrom->u.aDisk, SQLITE_PAGE_SIZE);
|
||||
memcpy(pTo->u.aDisk, pFrom->u.aDisk, SQLITE_USABLE_SIZE);
|
||||
pTo->pParent = 0;
|
||||
pTo->isInit = 1;
|
||||
pTo->nCell = pFrom->nCell;
|
||||
|
@ -2108,7 +2113,7 @@ static void copyPage(MemPage *pTo, MemPage *pFrom){
|
|||
from = Addr(pFrom);
|
||||
for(i=0; i<pTo->nCell; i++){
|
||||
uptr x = Addr(pFrom->apCell[i]);
|
||||
if( x>from && x<from+SQLITE_PAGE_SIZE ){
|
||||
if( x>from && x<from+SQLITE_USABLE_SIZE ){
|
||||
*((uptr*)&pTo->apCell[i]) = x + to - from;
|
||||
}else{
|
||||
pTo->apCell[i] = pFrom->apCell[i];
|
||||
|
@ -2203,7 +2208,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
|
|||
** underfull.
|
||||
*/
|
||||
assert( sqlitepager_iswriteable(pPage) );
|
||||
if( !pPage->isOverfull && pPage->nFree<SQLITE_PAGE_SIZE/2
|
||||
if( !pPage->isOverfull && pPage->nFree<SQLITE_USABLE_SIZE/2
|
||||
&& pPage->nCell>=2){
|
||||
relinkCellList(pBt, pPage);
|
||||
return SQLITE_OK;
|
||||
|
@ -2229,7 +2234,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
|
|||
pgnoChild = SWAB32(pBt, pPage->u.hdr.rightChild);
|
||||
rc = sqlitepager_get(pBt->pPager, pgnoChild, (void**)&pChild);
|
||||
if( rc ) return rc;
|
||||
memcpy(pPage, pChild, SQLITE_PAGE_SIZE);
|
||||
memcpy(pPage, pChild, SQLITE_USABLE_SIZE);
|
||||
pPage->isInit = 0;
|
||||
rc = initPage(pBt, pPage, sqlitepager_pagenumber(pPage), 0);
|
||||
assert( rc==SQLITE_OK );
|
||||
|
@ -2724,7 +2729,8 @@ static int fileBtreeDelete(BtCursor *pCur){
|
|||
getTempCursor(pCur, &leafCur);
|
||||
rc = fileBtreeNext(&leafCur, ¬Used);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return SQLITE_CORRUPT;
|
||||
if( rc!=SQLITE_NOMEM ) rc = SQLITE_CORRUPT;
|
||||
return rc;
|
||||
}
|
||||
rc = sqlitepager_write(leafCur.pPage);
|
||||
if( rc ) return rc;
|
||||
|
@ -2907,7 +2913,7 @@ static int copyCell(Btree *pBtFrom, BTree *pBtTo, Cell *pCell){
|
|||
if( rc==SQLITE_OK ){
|
||||
rc = sqlitepager_write(pNew);
|
||||
if( rc==SQLITE_OK ){
|
||||
memcpy(pNew, pOvfl, SQLITE_PAGE_SIZE);
|
||||
memcpy(pNew, pOvfl, SQLITE_USABLE_SIZE);
|
||||
*pPrev = SWAB32(pBtTo, new);
|
||||
if( pPrevPg ){
|
||||
sqlitepager_unref(pPrevPg);
|
||||
|
@ -2950,7 +2956,7 @@ static int copyDatabasePage(
|
|||
rc = sqlitepager_write(pPage);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
memcpy(pPage, pPageFrom, SQLITE_PAGE_SIZE);
|
||||
memcpy(pPage, pPageFrom, SQLITE_USABLE_SIZE);
|
||||
idx = SWAB16(pBt, pPage->u.hdr.firstCell);
|
||||
while( idx>0 ){
|
||||
pCell = (Cell*)&pPage->u.aDisk[idx];
|
||||
|
@ -3040,7 +3046,7 @@ static int fileBtreePageDump(Btree *pBt, int pgno, int recursive){
|
|||
if( recursive ) printf("PAGE %d:\n", pgno);
|
||||
i = 0;
|
||||
idx = SWAB16(pBt, pPage->u.hdr.firstCell);
|
||||
while( idx>0 && idx<=SQLITE_PAGE_SIZE-MIN_CELL_SIZE ){
|
||||
while( idx>0 && idx<=SQLITE_USABLE_SIZE-MIN_CELL_SIZE ){
|
||||
Cell *pCell = (Cell*)&pPage->u.aDisk[idx];
|
||||
int sz = cellSize(pBt, pCell);
|
||||
sprintf(range,"%d..%d", idx, idx+sz-1);
|
||||
|
@ -3070,7 +3076,7 @@ static int fileBtreePageDump(Btree *pBt, int pgno, int recursive){
|
|||
nFree = 0;
|
||||
i = 0;
|
||||
idx = SWAB16(pBt, pPage->u.hdr.firstFree);
|
||||
while( idx>0 && idx<SQLITE_PAGE_SIZE ){
|
||||
while( idx>0 && idx<SQLITE_USABLE_SIZE ){
|
||||
FreeBlk *p = (FreeBlk*)&pPage->u.aDisk[idx];
|
||||
sprintf(range,"%d..%d", idx, idx+p->iSize-1);
|
||||
nFree += SWAB16(pBt, p->iSize);
|
||||
|
@ -3084,7 +3090,7 @@ static int fileBtreePageDump(Btree *pBt, int pgno, int recursive){
|
|||
}
|
||||
if( recursive && pPage->u.hdr.rightChild!=0 ){
|
||||
idx = SWAB16(pBt, pPage->u.hdr.firstCell);
|
||||
while( idx>0 && idx<SQLITE_PAGE_SIZE-MIN_CELL_SIZE ){
|
||||
while( idx>0 && idx<SQLITE_USABLE_SIZE-MIN_CELL_SIZE ){
|
||||
Cell *pCell = (Cell*)&pPage->u.aDisk[idx];
|
||||
fileBtreePageDump(pBt, SWAB32(pBt, pCell->h.leftChild), 1);
|
||||
idx = SWAB16(pBt, pCell->h.iNext);
|
||||
|
@ -3129,7 +3135,7 @@ static int fileBtreeCursorDump(BtCursor *pCur, int *aResult){
|
|||
aResult[4] = pPage->nFree;
|
||||
cnt = 0;
|
||||
idx = SWAB16(pBt, pPage->u.hdr.firstFree);
|
||||
while( idx>0 && idx<SQLITE_PAGE_SIZE ){
|
||||
while( idx>0 && idx<SQLITE_USABLE_SIZE ){
|
||||
cnt++;
|
||||
idx = SWAB16(pBt, ((FreeBlk*)&pPage->u.aDisk[idx])->iNext);
|
||||
}
|
||||
|
@ -3139,7 +3145,6 @@ static int fileBtreeCursorDump(BtCursor *pCur, int *aResult){
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
/*
|
||||
** Return the pager associated with a BTree. This routine is used for
|
||||
** testing and debugging only.
|
||||
|
@ -3147,7 +3152,6 @@ static int fileBtreeCursorDump(BtCursor *pCur, int *aResult){
|
|||
static Pager *fileBtreePager(Btree *pBt){
|
||||
return pBt->pPager;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This structure is passed around through all the sanity checking routines
|
||||
|
@ -3293,7 +3297,7 @@ static int checkTreePage(
|
|||
Btree *pBt;
|
||||
char zMsg[100];
|
||||
char zContext[100];
|
||||
char hit[SQLITE_PAGE_SIZE];
|
||||
char hit[SQLITE_USABLE_SIZE];
|
||||
|
||||
/* Check that the page exists
|
||||
*/
|
||||
|
@ -3369,19 +3373,19 @@ static int checkTreePage(
|
|||
*/
|
||||
memset(hit, 0, sizeof(hit));
|
||||
memset(hit, 1, sizeof(PageHdr));
|
||||
for(i=SWAB16(pBt, pPage->u.hdr.firstCell); i>0 && i<SQLITE_PAGE_SIZE; ){
|
||||
for(i=SWAB16(pBt, pPage->u.hdr.firstCell); i>0 && i<SQLITE_USABLE_SIZE; ){
|
||||
Cell *pCell = (Cell*)&pPage->u.aDisk[i];
|
||||
int j;
|
||||
for(j=i+cellSize(pBt, pCell)-1; j>=i; j--) hit[j]++;
|
||||
i = SWAB16(pBt, pCell->h.iNext);
|
||||
}
|
||||
for(i=SWAB16(pBt,pPage->u.hdr.firstFree); i>0 && i<SQLITE_PAGE_SIZE; ){
|
||||
for(i=SWAB16(pBt,pPage->u.hdr.firstFree); i>0 && i<SQLITE_USABLE_SIZE; ){
|
||||
FreeBlk *pFBlk = (FreeBlk*)&pPage->u.aDisk[i];
|
||||
int j;
|
||||
for(j=i+SWAB16(pBt,pFBlk->iSize)-1; j>=i; j--) hit[j]++;
|
||||
i = SWAB16(pBt,pFBlk->iNext);
|
||||
}
|
||||
for(i=0; i<SQLITE_PAGE_SIZE; i++){
|
||||
for(i=0; i<SQLITE_USABLE_SIZE; i++){
|
||||
if( hit[i]==0 ){
|
||||
sprintf(zMsg, "Unused space at byte %d of page %d", i, iPage);
|
||||
checkAppendMsg(pCheck, zMsg, 0);
|
||||
|
@ -3396,9 +3400,9 @@ static int checkTreePage(
|
|||
/* Check that free space is kept to a minimum
|
||||
*/
|
||||
#if 0
|
||||
if( pParent && pParent->nCell>2 && pPage->nFree>3*SQLITE_PAGE_SIZE/4 ){
|
||||
if( pParent && pParent->nCell>2 && pPage->nFree>3*SQLITE_USABLE_SIZE/4 ){
|
||||
sprintf(zMsg, "free space (%d) greater than max (%d)", pPage->nFree,
|
||||
SQLITE_PAGE_SIZE/3);
|
||||
SQLITE_USABLE_SIZE/3);
|
||||
checkAppendMsg(pCheck, zContext, zMsg);
|
||||
}
|
||||
#endif
|
||||
|
@ -3500,7 +3504,7 @@ static int fileBtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){
|
|||
if( !pBtTo->inTrans || !pBtFrom->inTrans ) return SQLITE_ERROR;
|
||||
if( pBtTo->needSwab!=pBtFrom->needSwab ) return SQLITE_ERROR;
|
||||
if( pBtTo->pCursor ) return SQLITE_BUSY;
|
||||
memcpy(pBtTo->page1, pBtFrom->page1, SQLITE_PAGE_SIZE);
|
||||
memcpy(pBtTo->page1, pBtFrom->page1, SQLITE_USABLE_SIZE);
|
||||
rc = sqlitepager_overwrite(pBtTo->pPager, 1, pBtFrom->page1);
|
||||
nToPage = sqlitepager_pagecount(pBtTo->pPager);
|
||||
nPage = sqlitepager_pagecount(pBtFrom->pPager);
|
||||
|
@ -3555,9 +3559,9 @@ static BtOps sqliteBtreeOps = {
|
|||
fileBtreeIntegrityCheck,
|
||||
fileBtreeGetFilename,
|
||||
fileBtreeCopyFile,
|
||||
fileBtreePager,
|
||||
#ifdef SQLITE_TEST
|
||||
fileBtreePageDump,
|
||||
fileBtreePager
|
||||
#endif
|
||||
};
|
||||
static BtCursorOps sqliteBtreeCursorOps = {
|
||||
|
|
|
@ -57,9 +57,9 @@ struct BtOps {
|
|||
char *(*IntegrityCheck)(Btree*, int*, int);
|
||||
const char *(*GetFilename)(Btree*);
|
||||
int (*Copyfile)(Btree*,Btree*);
|
||||
struct Pager *(*Pager)(Btree*);
|
||||
#ifdef SQLITE_TEST
|
||||
int (*PageDump)(Btree*, int, int);
|
||||
struct Pager *(*Pager)(Btree*);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -142,13 +142,13 @@ int sqliteRbtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree);
|
|||
(btOps(pBt)->IntegrityCheck(pBt, aRoot, nRoot))
|
||||
#define sqliteBtreeGetFilename(pBt) (btOps(pBt)->GetFilename(pBt))
|
||||
#define sqliteBtreeCopyFile(pBt1, pBt2) (btOps(pBt1)->Copyfile(pBt1, pBt2))
|
||||
#define sqliteBtreePager(pBt) (btOps(pBt)->Pager(pBt))
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
#define sqliteBtreePageDump(pBt, pgno, recursive)\
|
||||
(btOps(pBt)->PageDump(pBt, pgno, recursive))
|
||||
#define sqliteBtreeCursorDump(pCur, aResult)\
|
||||
(btCOps(pCur)->CursorDump(pCur, aResult))
|
||||
#define sqliteBtreePager(pBt) (btOps(pBt)->Pager(pBt))
|
||||
int btree_native_byte_order;
|
||||
#endif /* SQLITE_TEST */
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ void sqliteBeginParse(Parse *pParse, int explainFlag){
|
|||
sqlite *db = pParse->db;
|
||||
int i;
|
||||
pParse->explain = explainFlag;
|
||||
if((db->flags & SQLITE_Initialized)==0 && pParse->initFlag==0 ){
|
||||
if((db->flags & SQLITE_Initialized)==0 && db->init.busy==0 ){
|
||||
int rc = sqliteInit(db, &pParse->zErrMsg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
pParse->rc = rc;
|
||||
|
@ -54,17 +54,6 @@ void sqliteBeginParse(Parse *pParse, int explainFlag){
|
|||
pParse->nVar = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is a fake callback procedure used when sqlite_exec() is
|
||||
** invoked with a NULL callback pointer. If we pass a NULL callback
|
||||
** pointer into sqliteVdbeExec() it will return at every OP_Callback,
|
||||
** which we do not want it to do. So we substitute a pointer to this
|
||||
** procedure in place of the NULL.
|
||||
*/
|
||||
static int fakeCallback(void *NotUsed, int n, char **az1, char **az2){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called after a single SQL statement has been
|
||||
** parsed and we want to execute the VDBE code to implement
|
||||
|
@ -76,36 +65,20 @@ static int fakeCallback(void *NotUsed, int n, char **az1, char **az2){
|
|||
** no VDBE code was generated.
|
||||
*/
|
||||
void sqliteExec(Parse *pParse){
|
||||
int rc = SQLITE_OK;
|
||||
sqlite *db = pParse->db;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int (*xCallback)(void*,int,char**,char**);
|
||||
|
||||
if( v==0 && (v = sqliteGetVdbe(pParse))!=0 ){
|
||||
sqliteVdbeAddOp(v, OP_Halt, 0, 0);
|
||||
}
|
||||
if( sqlite_malloc_failed ) return;
|
||||
xCallback = pParse->xCallback;
|
||||
if( xCallback==0 && pParse->useCallback ) xCallback = fakeCallback;
|
||||
if( v && pParse->nErr==0 ){
|
||||
FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0;
|
||||
sqliteVdbeTrace(v, trace);
|
||||
sqliteVdbeMakeReady(v, pParse->nVar, xCallback, pParse->pArg,
|
||||
pParse->explain);
|
||||
if( pParse->useCallback ){
|
||||
if( pParse->explain ){
|
||||
rc = sqliteVdbeList(v);
|
||||
db->next_cookie = db->aDb[0].schema_cookie;
|
||||
}else{
|
||||
sqliteVdbeExec(v);
|
||||
}
|
||||
rc = sqliteVdbeFinalize(v, &pParse->zErrMsg);
|
||||
if( rc ) pParse->nErr++;
|
||||
pParse->pVdbe = 0;
|
||||
pParse->rc = rc;
|
||||
if( rc ) pParse->nErr++;
|
||||
}else{
|
||||
pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE;
|
||||
}
|
||||
sqliteVdbeMakeReady(v, pParse->nVar, pParse->explain);
|
||||
pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE;
|
||||
pParse->colNamesSet = 0;
|
||||
}else if( pParse->useCallback==0 ){
|
||||
}else if( pParse->rc==SQLITE_OK ){
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
}
|
||||
pParse->nTab = 0;
|
||||
|
@ -240,7 +213,7 @@ void sqliteUnlinkAndDeleteIndex(sqlite *db, Index *pIndex){
|
|||
**
|
||||
** If iDb<=0 then reset the internal schema tables for all database
|
||||
** files. If iDb>=2 then reset the internal schema for only the
|
||||
** single file indicates.
|
||||
** single file indicated.
|
||||
*/
|
||||
void sqliteResetInternalSchema(sqlite *db, int iDb){
|
||||
HashElem *pElem;
|
||||
|
@ -280,10 +253,18 @@ void sqliteResetInternalSchema(sqlite *db, int iDb){
|
|||
** schema hash tables and therefore do not have to make any changes
|
||||
** to any of those tables.
|
||||
*/
|
||||
for(i=0; i<db->nDb; i++){
|
||||
struct Db *pDb = &db->aDb[i];
|
||||
if( pDb->pBt==0 ){
|
||||
if( pDb->pAux && pDb->xFreeAux ) pDb->xFreeAux(pDb->pAux);
|
||||
pDb->pAux = 0;
|
||||
}
|
||||
}
|
||||
for(i=j=2; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt==0 ){
|
||||
sqliteFree(db->aDb[i].zName);
|
||||
db->aDb[i].zName = 0;
|
||||
struct Db *pDb = &db->aDb[i];
|
||||
if( pDb->pBt==0 ){
|
||||
sqliteFree(pDb->zName);
|
||||
pDb->zName = 0;
|
||||
continue;
|
||||
}
|
||||
if( j<i ){
|
||||
|
@ -456,7 +437,7 @@ void sqliteStartTable(
|
|||
pParse->sFirstToken = *pStart;
|
||||
zName = sqliteTableNameFromToken(pName);
|
||||
if( zName==0 ) return;
|
||||
if( pParse->iDb==1 ) isTemp = 1;
|
||||
if( db->init.iDb==1 ) isTemp = 1;
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
assert( (isTemp & 1)==isTemp );
|
||||
{
|
||||
|
@ -493,17 +474,16 @@ void sqliteStartTable(
|
|||
if( isTemp && db->aDb[1].pBt==0 && !pParse->explain ){
|
||||
int rc = sqliteBtreeFactory(db, 0, 0, MAX_PAGES, &db->aDb[1].pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteSetString(&pParse->zErrMsg, "unable to open a temporary database "
|
||||
"file for storing temporary tables", (char*)0);
|
||||
sqliteErrorMsg(pParse, "unable to open a temporary database "
|
||||
"file for storing temporary tables");
|
||||
pParse->nErr++;
|
||||
return;
|
||||
}
|
||||
if( db->flags & SQLITE_InTrans ){
|
||||
rc = sqliteBtreeBeginTrans(db->aDb[1].pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "unable to get a write lock on "
|
||||
"the temporary database file", 0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "unable to get a write lock on "
|
||||
"the temporary database file");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -517,20 +497,16 @@ void sqliteStartTable(
|
|||
** an existing temporary table, that is not an error.
|
||||
*/
|
||||
pTable = sqliteFindTable(db, zName, 0);
|
||||
iDb = isTemp ? 1 : pParse->iDb;
|
||||
if( pTable!=0 && (pTable->iDb==iDb || !pParse->initFlag) ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "table ", 0, pName->z, pName->n,
|
||||
" already exists", 0, 0);
|
||||
iDb = isTemp ? 1 : db->init.iDb;
|
||||
if( pTable!=0 && (pTable->iDb==iDb || !db->init.busy) ){
|
||||
sqliteErrorMsg(pParse, "table %T already exists", pName);
|
||||
sqliteFree(zName);
|
||||
pParse->nErr++;
|
||||
return;
|
||||
}
|
||||
if( (pIdx = sqliteFindIndex(db, zName, 0))!=0 &&
|
||||
(pIdx->iDb==0 || !pParse->initFlag) ){
|
||||
sqliteSetString(&pParse->zErrMsg, "there is already an index named ",
|
||||
zName, (char*)0);
|
||||
(pIdx->iDb==0 || !db->init.busy) ){
|
||||
sqliteErrorMsg(pParse, "there is already an index named %s", zName);
|
||||
sqliteFree(zName);
|
||||
pParse->nErr++;
|
||||
return;
|
||||
}
|
||||
pTable = sqliteMalloc( sizeof(Table) );
|
||||
|
@ -555,7 +531,7 @@ void sqliteStartTable(
|
|||
** indices. Hence, the record number for the table must be allocated
|
||||
** now.
|
||||
*/
|
||||
if( !pParse->initFlag && (v = sqliteGetVdbe(pParse))!=0 ){
|
||||
if( !db->init.busy && (v = sqliteGetVdbe(pParse))!=0 ){
|
||||
sqliteBeginWriteOperation(pParse, 0, isTemp);
|
||||
if( !isTemp ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, db->file_format, 0);
|
||||
|
@ -588,8 +564,7 @@ void sqliteAddColumn(Parse *pParse, Token *pName){
|
|||
sqliteDequote(z);
|
||||
for(i=0; i<p->nCol; i++){
|
||||
if( sqliteStrICmp(z, p->aCol[i].zName)==0 ){
|
||||
sqliteSetString(&pParse->zErrMsg, "duplicate column name: ", z, (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "duplicate column name: %s", z);
|
||||
sqliteFree(z);
|
||||
return;
|
||||
}
|
||||
|
@ -708,9 +683,8 @@ void sqliteAddPrimaryKey(Parse *pParse, IdList *pList, int onError){
|
|||
int iCol = -1, i;
|
||||
if( pTab==0 ) goto primary_key_exit;
|
||||
if( pTab->hasPrimKey ){
|
||||
sqliteSetString(&pParse->zErrMsg, "table \"", pTab->zName,
|
||||
"\" has more than one primary key", (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse,
|
||||
"table \"%s\" has more than one primary key", pTab->zName);
|
||||
goto primary_key_exit;
|
||||
}
|
||||
pTab->hasPrimKey = 1;
|
||||
|
@ -753,34 +727,16 @@ primary_key_exit:
|
|||
*/
|
||||
int sqliteCollateType(const char *zType, int nType){
|
||||
int i;
|
||||
for(i=0; i<nType-1; i++){
|
||||
switch( zType[i] ){
|
||||
case 'b':
|
||||
case 'B': {
|
||||
if( i<nType-3 && sqliteStrNICmp(&zType[i],"blob",4)==0 ){
|
||||
return SQLITE_SO_TEXT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
case 'C': {
|
||||
if( i<nType-3 && (sqliteStrNICmp(&zType[i],"char",4)==0 ||
|
||||
sqliteStrNICmp(&zType[i],"clob",4)==0)
|
||||
){
|
||||
return SQLITE_SO_TEXT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'x':
|
||||
case 'X': {
|
||||
if( i>=2 && sqliteStrNICmp(&zType[i-2],"text",4)==0 ){
|
||||
return SQLITE_SO_TEXT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
for(i=0; i<nType-3; i++){
|
||||
int c = *(zType++) | 0x60;
|
||||
if( (c=='b' || c=='c') && sqliteStrNICmp(zType, "lob", 3)==0 ){
|
||||
return SQLITE_SO_TEXT;
|
||||
}
|
||||
if( c=='c' && sqliteStrNICmp(zType, "har", 3)==0 ){
|
||||
return SQLITE_SO_TEXT;
|
||||
}
|
||||
if( c=='t' && sqliteStrNICmp(zType, "ext", 3)==0 ){
|
||||
return SQLITE_SO_TEXT;
|
||||
}
|
||||
}
|
||||
return SQLITE_SO_NUM;
|
||||
|
@ -819,7 +775,9 @@ void sqliteAddCollateType(Parse *pParse, int collType){
|
|||
*/
|
||||
void sqliteChangeCookie(sqlite *db, Vdbe *v){
|
||||
if( db->next_cookie==db->aDb[0].schema_cookie ){
|
||||
db->next_cookie = db->aDb[0].schema_cookie + sqliteRandomByte() + 1;
|
||||
unsigned char r;
|
||||
sqliteRandomness(1, &r);
|
||||
db->next_cookie = db->aDb[0].schema_cookie + r + 1;
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
sqliteVdbeAddOp(v, OP_Integer, db->next_cookie, 0);
|
||||
sqliteVdbeAddOp(v, OP_SetCookie, 0, 0);
|
||||
|
@ -910,8 +868,8 @@ static char *createTableStmt(Table *p){
|
|||
** is added to the internal hash tables, assuming no errors have
|
||||
** occurred.
|
||||
**
|
||||
** An entry for the table is made in the master table on disk,
|
||||
** unless this is a temporary table or initFlag==1. When initFlag==1,
|
||||
** An entry for the table is made in the master table on disk, unless
|
||||
** this is a temporary table or db->init.busy==1. When db->init.busy==1
|
||||
** it means we are reading the sqlite_master table because we just
|
||||
** connected to the database or because the sqlite_master table has
|
||||
** recently changes, so the entry for this table already exists in
|
||||
|
@ -944,14 +902,14 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
|
|||
sqliteDeleteTable(0, pSelTab);
|
||||
}
|
||||
|
||||
/* If the initFlag is 1 it means we are reading the SQL off the
|
||||
/* If the db->init.busy is 1 it means we are reading the SQL off the
|
||||
** "sqlite_master" or "sqlite_temp_master" table on the disk.
|
||||
** So do not write to the disk again. Extract the root page number
|
||||
** for the table from the pParse->newTnum field. (The page number
|
||||
** for the table from the db->init.newTnum field. (The page number
|
||||
** should have been put there by the sqliteOpenCb routine.)
|
||||
*/
|
||||
if( pParse->initFlag ){
|
||||
p->tnum = pParse->newTnum;
|
||||
if( db->init.busy ){
|
||||
p->tnum = db->init.newTnum;
|
||||
}
|
||||
|
||||
/* If not initializing, then create a record for the new table
|
||||
|
@ -961,7 +919,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
|
|||
** If this is a TEMPORARY table, write the entry into the auxiliary
|
||||
** file instead of into the main database file.
|
||||
*/
|
||||
if( !pParse->initFlag ){
|
||||
if( !db->init.busy ){
|
||||
int n;
|
||||
Vdbe *v;
|
||||
|
||||
|
@ -969,24 +927,16 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
|
|||
if( v==0 ) return;
|
||||
if( p->pSelect==0 ){
|
||||
/* A regular table */
|
||||
sqliteVdbeAddOp(v, OP_CreateTable, 0, p->iDb);
|
||||
sqliteVdbeChangeP3(v, -1, (char *)&p->tnum, P3_POINTER);
|
||||
sqliteVdbeOp3(v, OP_CreateTable, 0, p->iDb, (char*)&p->tnum, P3_POINTER);
|
||||
}else{
|
||||
/* A view */
|
||||
sqliteVdbeAddOp(v, OP_Integer, 0, 0);
|
||||
}
|
||||
p->tnum = 0;
|
||||
sqliteVdbeAddOp(v, OP_Pull, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
if( p->pSelect==0 ){
|
||||
sqliteVdbeChangeP3(v, -1, "table", P3_STATIC);
|
||||
}else{
|
||||
sqliteVdbeChangeP3(v, -1, "view", P3_STATIC);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, p->zName, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, p->zName, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, p->pSelect==0?"table":"view", P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, p->zName, 0);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, p->zName, 0);
|
||||
sqliteVdbeAddOp(v, OP_Dup, 4, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
if( pSelect ){
|
||||
|
@ -1072,7 +1022,7 @@ void sqliteCreateView(
|
|||
*/
|
||||
p->pSelect = sqliteSelectDup(pSelect);
|
||||
sqliteSelectDelete(pSelect);
|
||||
if( !pParse->initFlag ){
|
||||
if( !pParse->db->init.busy ){
|
||||
sqliteViewGetColumnNames(pParse, p);
|
||||
}
|
||||
|
||||
|
@ -1084,7 +1034,7 @@ void sqliteCreateView(
|
|||
sEnd.z += sEnd.n;
|
||||
}
|
||||
sEnd.n = 0;
|
||||
n = (char *) sEnd.z - (char *) pBegin->z;
|
||||
n = sEnd.z - pBegin->z;
|
||||
z = pBegin->z;
|
||||
while( n>0 && (z[n-1]==';' || isspace(z[n-1])) ){ n--; }
|
||||
sEnd.z = &z[n-1];
|
||||
|
@ -1098,7 +1048,7 @@ void sqliteCreateView(
|
|||
/*
|
||||
** The Table structure pTable is really a VIEW. Fill in the names of
|
||||
** the columns of the view in the pTable structure. Return the number
|
||||
** of errors. If an error is seen leave an error message in pPare->zErrMsg.
|
||||
** of errors. If an error is seen leave an error message in pParse->zErrMsg.
|
||||
*/
|
||||
int sqliteViewGetColumnNames(Parse *pParse, Table *pTable){
|
||||
ExprList *pEList;
|
||||
|
@ -1124,9 +1074,7 @@ int sqliteViewGetColumnNames(Parse *pParse, Table *pTable){
|
|||
** should always fail. But we will leave it in place just to be safe.
|
||||
*/
|
||||
if( pTable->nCol<0 ){
|
||||
sqliteSetString(&pParse->zErrMsg, "view ", pTable->zName,
|
||||
" is circularly defined", (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "view %s is circularly defined", pTable->zName);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1176,12 +1124,12 @@ int sqliteViewGetColumnNames(Parse *pParse, Table *pTable){
|
|||
*/
|
||||
static void sqliteViewResetColumnNames(Table *pTable){
|
||||
int i;
|
||||
if( pTable==0 || pTable->pSelect==0 ) return;
|
||||
if( pTable->nCol==0 ) return;
|
||||
for(i=0; i<pTable->nCol; i++){
|
||||
sqliteFree(pTable->aCol[i].zName);
|
||||
sqliteFree(pTable->aCol[i].zDflt);
|
||||
sqliteFree(pTable->aCol[i].zType);
|
||||
Column *pCol;
|
||||
assert( pTable!=0 && pTable->pSelect!=0 );
|
||||
for(i=0, pCol=pTable->aCol; i<pTable->nCol; i++, pCol++){
|
||||
sqliteFree(pCol->zName);
|
||||
sqliteFree(pCol->zDflt);
|
||||
sqliteFree(pCol->zType);
|
||||
}
|
||||
sqliteFree(pTable->aCol);
|
||||
pTable->aCol = 0;
|
||||
|
@ -1215,9 +1163,7 @@ Table *sqliteTableFromToken(Parse *pParse, Token *pTok){
|
|||
pTab = sqliteFindTable(pParse->db, zName, 0);
|
||||
sqliteFree(zName);
|
||||
if( pTab==0 ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "no such table: ", 0,
|
||||
pTok->z, pTok->n, 0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "no such table: %T", pTok);
|
||||
}
|
||||
return pTab;
|
||||
}
|
||||
|
@ -1268,21 +1214,16 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){
|
|||
}
|
||||
#endif
|
||||
if( pTable->readOnly ){
|
||||
sqliteSetString(&pParse->zErrMsg, "table ", pTable->zName,
|
||||
" may not be dropped", (char*)0);
|
||||
sqliteErrorMsg(pParse, "table %s may not be dropped", pTable->zName);
|
||||
pParse->nErr++;
|
||||
return;
|
||||
}
|
||||
if( isView && pTable->pSelect==0 ){
|
||||
sqliteSetString(&pParse->zErrMsg, "use DROP TABLE to delete table ",
|
||||
pTable->zName, (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "use DROP TABLE to delete table %s", pTable->zName);
|
||||
return;
|
||||
}
|
||||
if( !isView && pTable->pSelect ){
|
||||
sqliteSetString(&pParse->zErrMsg, "use DROP VIEW to delete view ",
|
||||
pTable->zName, (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "use DROP VIEW to delete view %s", pTable->zName);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1291,7 +1232,7 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){
|
|||
*/
|
||||
v = sqliteGetVdbe(pParse);
|
||||
if( v ){
|
||||
static VdbeOp dropTable[] = {
|
||||
static VdbeOpList dropTable[] = {
|
||||
{ OP_Rewind, 0, ADDR(8), 0},
|
||||
{ OP_String, 0, 0, 0}, /* 1 */
|
||||
{ OP_MemStore, 1, 1, 0},
|
||||
|
@ -1425,19 +1366,16 @@ void sqliteCreateForeignKey(
|
|||
int iCol = p->nCol-1;
|
||||
if( iCol<0 ) goto fk_end;
|
||||
if( pToCol && pToCol->nId!=1 ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "foreign key on ", -1,
|
||||
p->aCol[iCol].zName, -1,
|
||||
" should reference only one column of table ", -1,
|
||||
pTo->z, pTo->n, 0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "foreign key on %s"
|
||||
" should reference only one column of table %T",
|
||||
p->aCol[iCol].zName, pTo);
|
||||
goto fk_end;
|
||||
}
|
||||
nCol = 1;
|
||||
}else if( pToCol && pToCol->nId!=pFromCol->nId ){
|
||||
sqliteSetString(&pParse->zErrMsg,
|
||||
sqliteErrorMsg(pParse,
|
||||
"number of columns in foreign key does not match the number of "
|
||||
"columns in the referenced table", (char*)0);
|
||||
pParse->nErr++;
|
||||
"columns in the referenced table");
|
||||
goto fk_end;
|
||||
}else{
|
||||
nCol = pFromCol->nId;
|
||||
|
@ -1473,9 +1411,9 @@ void sqliteCreateForeignKey(
|
|||
}
|
||||
}
|
||||
if( j>=p->nCol ){
|
||||
sqliteSetString(&pParse->zErrMsg, "unknown column \"",
|
||||
pFromCol->a[i].zName, "\" in foreign key definition", (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse,
|
||||
"unknown column \"%s\" in foreign key definition",
|
||||
pFromCol->a[i].zName);
|
||||
goto fk_end;
|
||||
}
|
||||
}
|
||||
|
@ -1550,8 +1488,8 @@ void sqliteCreateIndex(
|
|||
sqlite *db = pParse->db;
|
||||
|
||||
if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index;
|
||||
if( pParse->initFlag
|
||||
&& sqliteFixInit(&sFix, pParse, pParse->iDb, "index", pName)
|
||||
if( db->init.busy
|
||||
&& sqliteFixInit(&sFix, pParse, db->init.iDb, "index", pName)
|
||||
&& sqliteFixSrcList(&sFix, pTable)
|
||||
){
|
||||
goto exit_create_index;
|
||||
|
@ -1570,20 +1508,15 @@ void sqliteCreateIndex(
|
|||
}
|
||||
if( pTab==0 || pParse->nErr ) goto exit_create_index;
|
||||
if( pTab->readOnly ){
|
||||
sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
|
||||
" may not be indexed", (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
|
||||
goto exit_create_index;
|
||||
}
|
||||
if( pTab->iDb>=2 && pParse->initFlag==0 ){
|
||||
sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
|
||||
" may not have indices added", (char*)0);
|
||||
pParse->nErr++;
|
||||
if( pTab->iDb>=2 && db->init.busy==0 ){
|
||||
sqliteErrorMsg(pParse, "table %s may not have indices added", pTab->zName);
|
||||
goto exit_create_index;
|
||||
}
|
||||
if( pTab->pSelect ){
|
||||
sqliteSetString(&pParse->zErrMsg, "views may not be indexed", (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "views may not be indexed");
|
||||
goto exit_create_index;
|
||||
}
|
||||
isTemp = pTab->iDb==1;
|
||||
|
@ -1601,21 +1534,17 @@ void sqliteCreateIndex(
|
|||
** dealing with a primary key or UNIQUE constraint. We have to invent our
|
||||
** own name.
|
||||
*/
|
||||
if( pName && !pParse->initFlag ){
|
||||
if( pName && !db->init.busy ){
|
||||
Index *pISameName; /* Another index with the same name */
|
||||
Table *pTSameName; /* A table with same name as the index */
|
||||
zName = sqliteStrNDup(pName->z, pName->n);
|
||||
if( zName==0 ) goto exit_create_index;
|
||||
if( (pISameName = sqliteFindIndex(db, zName, 0))!=0 ){
|
||||
sqliteSetString(&pParse->zErrMsg, "index ", zName,
|
||||
" already exists", (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "index %s already exists", zName);
|
||||
goto exit_create_index;
|
||||
}
|
||||
if( (pTSameName = sqliteFindTable(db, zName, 0))!=0 ){
|
||||
sqliteSetString(&pParse->zErrMsg, "there is already a table named ",
|
||||
zName, (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "there is already a table named %s", zName);
|
||||
goto exit_create_index;
|
||||
}
|
||||
}else if( pName==0 ){
|
||||
|
@ -1637,7 +1566,7 @@ void sqliteCreateIndex(
|
|||
{
|
||||
const char *zDb = db->aDb[pTab->iDb].zName;
|
||||
|
||||
assert( pTab->iDb==pParse->iDb || isTemp );
|
||||
assert( pTab->iDb==db->init.iDb || isTemp );
|
||||
if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
|
@ -1673,7 +1602,7 @@ void sqliteCreateIndex(
|
|||
pIndex->nColumn = pList->nId;
|
||||
pIndex->onError = onError;
|
||||
pIndex->autoIndex = pName==0;
|
||||
pIndex->iDb = isTemp ? 1 : pParse->iDb;
|
||||
pIndex->iDb = isTemp ? 1 : db->init.iDb;
|
||||
|
||||
/* Scan the names of the columns of the table to be indexed and
|
||||
** load the column indices into the Index structure. Report an error
|
||||
|
@ -1684,9 +1613,8 @@ void sqliteCreateIndex(
|
|||
if( sqliteStrICmp(pList->a[i].zName, pTab->aCol[j].zName)==0 ) break;
|
||||
}
|
||||
if( j>=pTab->nCol ){
|
||||
sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
|
||||
" has no column named ", pList->a[i].zName, (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "table %s has no column named %s",
|
||||
pTab->zName, pList->a[i].zName);
|
||||
sqliteFree(pIndex);
|
||||
goto exit_create_index;
|
||||
}
|
||||
|
@ -1726,20 +1654,20 @@ void sqliteCreateIndex(
|
|||
pOther->pNext = pIndex;
|
||||
}
|
||||
|
||||
/* If the initFlag is 1 it means we are reading the SQL off the
|
||||
/* If the db->init.busy is 1 it means we are reading the SQL off the
|
||||
** "sqlite_master" table on the disk. So do not write to the disk
|
||||
** again. Extract the table number from the pParse->newTnum field.
|
||||
** again. Extract the table number from the db->init.newTnum field.
|
||||
*/
|
||||
if( pParse->initFlag && pTable!=0 ){
|
||||
pIndex->tnum = pParse->newTnum;
|
||||
if( db->init.busy && pTable!=0 ){
|
||||
pIndex->tnum = db->init.newTnum;
|
||||
}
|
||||
|
||||
/* If the initFlag is 0 then create the index on disk. This
|
||||
/* If the db->init.busy is 0 then create the index on disk. This
|
||||
** involves writing the index into the master table and filling in the
|
||||
** index with the current table contents.
|
||||
**
|
||||
** The initFlag is 0 when the user first enters a CREATE INDEX
|
||||
** command. The initFlag is 1 when a database is opened and
|
||||
** The db->init.busy is 0 when the user first enters a CREATE INDEX
|
||||
** command. db->init.busy is 1 when a database is opened and
|
||||
** CREATE INDEX statements are read out of the master table. In
|
||||
** the latter case the index already exists on disk, which is why
|
||||
** we don't want to recreate it.
|
||||
|
@ -1749,7 +1677,7 @@ void sqliteCreateIndex(
|
|||
** has just been created, it contains no data and the index initialization
|
||||
** step can be skipped.
|
||||
*/
|
||||
else if( pParse->initFlag==0 ){
|
||||
else if( db->init.busy==0 ){
|
||||
int n;
|
||||
Vdbe *v;
|
||||
int lbl1, lbl2;
|
||||
|
@ -1763,19 +1691,17 @@ void sqliteCreateIndex(
|
|||
sqliteOpenMasterTable(v, isTemp);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, "index", P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pIndex->zName, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
addr = sqliteVdbeAddOp(v, OP_CreateIndex, 0, isTemp);
|
||||
sqliteVdbeChangeP3(v, addr, (char*)&pIndex->tnum, P3_POINTER);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, "index", P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, pIndex->zName, 0);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, pTab->zName, 0);
|
||||
sqliteVdbeOp3(v, OP_CreateIndex, 0, isTemp,(char*)&pIndex->tnum,P3_POINTER);
|
||||
pIndex->tnum = 0;
|
||||
if( pTable ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Integer, isTemp, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0);
|
||||
sqliteVdbeCode(v,
|
||||
OP_Dup, 0, 0,
|
||||
OP_Integer, isTemp, 0,
|
||||
OP_OpenWrite, 1, 0,
|
||||
0);
|
||||
}
|
||||
addr = sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
if( pStart && pEnd ){
|
||||
|
@ -1786,8 +1712,7 @@ void sqliteCreateIndex(
|
|||
sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0);
|
||||
if( pTable ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenRead, 2, pTab->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_OpenRead, 2, pTab->tnum, pTab->zName, 0);
|
||||
lbl2 = sqliteVdbeMakeLabel(v);
|
||||
sqliteVdbeAddOp(v, OP_Rewind, 2, lbl2);
|
||||
lbl1 = sqliteVdbeAddOp(v, OP_Recno, 2, 0);
|
||||
|
@ -1801,8 +1726,8 @@ void sqliteCreateIndex(
|
|||
}
|
||||
sqliteVdbeAddOp(v, OP_MakeIdxKey, pIndex->nColumn, 0);
|
||||
if( db->file_format>=4 ) sqliteAddIdxKeyType(v, pIndex);
|
||||
sqliteVdbeAddOp(v, OP_IdxPut, 1, pIndex->onError!=OE_None);
|
||||
sqliteVdbeChangeP3(v, -1, "indexed columns are not unique", P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_IdxPut, 1, pIndex->onError!=OE_None,
|
||||
"indexed columns are not unique", P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Next, 2, lbl1);
|
||||
sqliteVdbeResolveLabel(v, lbl2);
|
||||
sqliteVdbeAddOp(v, OP_Close, 2, 0);
|
||||
|
@ -1870,7 +1795,7 @@ void sqliteDropIndex(Parse *pParse, SrcList *pName){
|
|||
/* Generate code to remove the index and from the master table */
|
||||
v = sqliteGetVdbe(pParse);
|
||||
if( v ){
|
||||
static VdbeOp dropIndex[] = {
|
||||
static VdbeOpList dropIndex[] = {
|
||||
{ OP_Rewind, 0, ADDR(9), 0},
|
||||
{ OP_String, 0, 0, 0}, /* 1 */
|
||||
{ OP_MemStore, 1, 1, 0},
|
||||
|
@ -2102,8 +2027,10 @@ void sqliteBeginTransaction(Parse *pParse, int onError){
|
|||
return;
|
||||
}
|
||||
sqliteBeginWriteOperation(pParse, 0, 0);
|
||||
db->flags |= SQLITE_InTrans;
|
||||
db->onError = onError;
|
||||
if( !pParse->explain ){
|
||||
db->flags |= SQLITE_InTrans;
|
||||
db->onError = onError;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2119,9 +2046,13 @@ void sqliteCommitTransaction(Parse *pParse){
|
|||
sqliteErrorMsg(pParse, "cannot commit - no transaction is active");
|
||||
return;
|
||||
}
|
||||
db->flags &= ~SQLITE_InTrans;
|
||||
if( !pParse->explain ){
|
||||
db->flags &= ~SQLITE_InTrans;
|
||||
}
|
||||
sqliteEndWriteOperation(pParse);
|
||||
db->onError = OE_Default;
|
||||
if( !pParse->explain ){
|
||||
db->onError = OE_Default;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2142,8 +2073,10 @@ void sqliteRollbackTransaction(Parse *pParse){
|
|||
if( v ){
|
||||
sqliteVdbeAddOp(v, OP_Rollback, 0, 0);
|
||||
}
|
||||
db->flags &= ~SQLITE_InTrans;
|
||||
db->onError = OE_Default;
|
||||
if( !pParse->explain ){
|
||||
db->flags &= ~SQLITE_InTrans;
|
||||
db->onError = OE_Default;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -37,7 +37,6 @@ void sqliteCopy(
|
|||
int i;
|
||||
Vdbe *v;
|
||||
int addr, end;
|
||||
Index *pIdx;
|
||||
char *zFile = 0;
|
||||
const char *zDb;
|
||||
sqlite *db = pParse->db;
|
||||
|
@ -58,18 +57,9 @@ void sqliteCopy(
|
|||
v = sqliteGetVdbe(pParse);
|
||||
if( v ){
|
||||
sqliteBeginWriteOperation(pParse, 1, pTab->iDb);
|
||||
addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0);
|
||||
sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
|
||||
addr = sqliteVdbeOp3(v, OP_FileOpen, 0, 0, pFilename->z, pFilename->n);
|
||||
sqliteVdbeDequoteP3(v, addr);
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, 0, pTab->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
assert( pIdx->iDb==1 || pIdx->iDb==pTab->iDb );
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, i, pIdx->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||
}
|
||||
sqliteOpenTableAndIndices(pParse, pTab, 0);
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, 0, 0); /* Initialize the row count */
|
||||
}
|
||||
|
@ -107,7 +97,7 @@ void sqliteCopy(
|
|||
sqliteVdbeAddOp(v, OP_Noop, 0, 0);
|
||||
sqliteEndWriteOperation(pParse);
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, 0, 1);
|
||||
sqliteVdbeChangeP3(v, -1, "rows inserted", P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 1, 0);
|
||||
}
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
** Willmann-Bell, Inc
|
||||
** Richmond, Virginia (USA)
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_DATETIME_FUNCS
|
||||
#include "os.h"
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
|
@ -55,6 +54,8 @@
|
|||
#include <assert.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_DATETIME_FUNCS
|
||||
|
||||
/*
|
||||
** A structure for holding a single date and time.
|
||||
*/
|
||||
|
@ -73,17 +74,50 @@ struct DateTime {
|
|||
|
||||
|
||||
/*
|
||||
** Convert N digits from zDate into an integer. Return
|
||||
** -1 if zDate does not begin with N digits.
|
||||
** Convert zDate into one or more integers. Additional arguments
|
||||
** come in groups of 5 as follows:
|
||||
**
|
||||
** N number of digits in the integer
|
||||
** min minimum allowed value of the integer
|
||||
** max maximum allowed value of the integer
|
||||
** nextC first character after the integer
|
||||
** pVal where to write the integers value.
|
||||
**
|
||||
** Conversions continue until one with nextC==0 is encountered.
|
||||
** The function returns the number of successful conversions.
|
||||
*/
|
||||
static int getDigits(const char *zDate, int N){
|
||||
int val = 0;
|
||||
while( N-- ){
|
||||
if( !isdigit(*zDate) ) return -1;
|
||||
val = val*10 + *zDate - '0';
|
||||
static int getDigits(const char *zDate, ...){
|
||||
va_list ap;
|
||||
int val;
|
||||
int N;
|
||||
int min;
|
||||
int max;
|
||||
int nextC;
|
||||
int *pVal;
|
||||
int cnt = 0;
|
||||
va_start(ap, zDate);
|
||||
do{
|
||||
N = va_arg(ap, int);
|
||||
min = va_arg(ap, int);
|
||||
max = va_arg(ap, int);
|
||||
nextC = va_arg(ap, int);
|
||||
pVal = va_arg(ap, int*);
|
||||
val = 0;
|
||||
while( N-- ){
|
||||
if( !isdigit(*zDate) ){
|
||||
return cnt;
|
||||
}
|
||||
val = val*10 + *zDate - '0';
|
||||
zDate++;
|
||||
}
|
||||
if( val<min || val>max || (nextC!=0 && nextC!=*zDate) ){
|
||||
return cnt;
|
||||
}
|
||||
*pVal = val;
|
||||
zDate++;
|
||||
}
|
||||
return val;
|
||||
cnt++;
|
||||
}while( nextC );
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -91,38 +125,9 @@ static int getDigits(const char *zDate, int N){
|
|||
** the number of digits converted.
|
||||
*/
|
||||
static int getValue(const char *z, double *pR){
|
||||
double r = 0.0;
|
||||
double rDivide = 1.0;
|
||||
int isNeg = 0;
|
||||
int nChar = 0;
|
||||
if( *z=='+' ){
|
||||
z++;
|
||||
nChar++;
|
||||
}else if( *z=='-' ){
|
||||
z++;
|
||||
isNeg = 1;
|
||||
nChar++;
|
||||
}
|
||||
if( !isdigit(*z) ) return 0;
|
||||
while( isdigit(*z) ){
|
||||
r = r*10.0 + *z - '0';
|
||||
nChar++;
|
||||
z++;
|
||||
}
|
||||
if( *z=='.' && isdigit(z[1]) ){
|
||||
z++;
|
||||
nChar++;
|
||||
while( isdigit(*z) ){
|
||||
r = r*10.0 + *z - '0';
|
||||
rDivide *= 10.0;
|
||||
nChar++;
|
||||
z++;
|
||||
}
|
||||
r /= rDivide;
|
||||
}
|
||||
if( *z!=0 && !isspace(*z) ) return 0;
|
||||
*pR = isNeg ? -r : r;
|
||||
return nChar;
|
||||
const char *zEnd;
|
||||
*pR = sqliteAtoF(z, &zEnd);
|
||||
return zEnd - z;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -150,14 +155,10 @@ static int parseTimezone(const char *zDate, DateTime *p){
|
|||
return *zDate!=0;
|
||||
}
|
||||
zDate++;
|
||||
nHr = getDigits(zDate, 2);
|
||||
if( nHr<0 || nHr>14 ) return 1;
|
||||
zDate += 2;
|
||||
if( zDate[0]!=':' ) return 1;
|
||||
zDate++;
|
||||
nMn = getDigits(zDate, 2);
|
||||
if( nMn<0 || nMn>59 ) return 1;
|
||||
zDate += 2;
|
||||
if( getDigits(zDate, 2, 0, 14, ':', &nHr, 2, 0, 59, 0, &nMn)!=2 ){
|
||||
return 1;
|
||||
}
|
||||
zDate += 5;
|
||||
p->tz = sgn*(nMn + nHr*60);
|
||||
while( isspace(*zDate) ){ zDate++; }
|
||||
return *zDate!=0;
|
||||
|
@ -173,16 +174,16 @@ static int parseTimezone(const char *zDate, DateTime *p){
|
|||
static int parseHhMmSs(const char *zDate, DateTime *p){
|
||||
int h, m, s;
|
||||
double ms = 0.0;
|
||||
h = getDigits(zDate, 2);
|
||||
if( h<0 || zDate[2]!=':' ) return 1;
|
||||
zDate += 3;
|
||||
m = getDigits(zDate, 2);
|
||||
if( m<0 || m>59 ) return 1;
|
||||
zDate += 2;
|
||||
if( getDigits(zDate, 2, 0, 24, ':', &h, 2, 0, 59, 0, &m)!=2 ){
|
||||
return 1;
|
||||
}
|
||||
zDate += 5;
|
||||
if( *zDate==':' ){
|
||||
s = getDigits(&zDate[1], 2);
|
||||
if( s<0 || s>59 ) return 1;
|
||||
zDate += 3;
|
||||
zDate++;
|
||||
if( getDigits(zDate, 2, 0, 59, 0, &s)!=1 ){
|
||||
return 1;
|
||||
}
|
||||
zDate += 2;
|
||||
if( *zDate=='.' && isdigit(zDate[1]) ){
|
||||
double rScale = 1.0;
|
||||
zDate++;
|
||||
|
@ -259,20 +260,21 @@ static void computeJD(DateTime *p){
|
|||
** date.
|
||||
*/
|
||||
static int parseYyyyMmDd(const char *zDate, DateTime *p){
|
||||
int Y, M, D;
|
||||
int Y, M, D, neg;
|
||||
|
||||
Y = getDigits(zDate, 4);
|
||||
if( Y<0 || zDate[4]!='-' ) return 1;
|
||||
zDate += 5;
|
||||
M = getDigits(zDate, 2);
|
||||
if( M<=0 || M>12 || zDate[2]!='-' ) return 1;
|
||||
zDate += 3;
|
||||
D = getDigits(zDate, 2);
|
||||
if( D<=0 || D>31 ) return 1;
|
||||
zDate += 2;
|
||||
if( zDate[0]=='-' ){
|
||||
zDate++;
|
||||
neg = 1;
|
||||
}else{
|
||||
neg = 0;
|
||||
}
|
||||
if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 ){
|
||||
return 1;
|
||||
}
|
||||
zDate += 10;
|
||||
while( isspace(*zDate) ){ zDate++; }
|
||||
if( isdigit(*zDate) ){
|
||||
if( parseHhMmSs(zDate, p) ) return 1;
|
||||
if( parseHhMmSs(zDate, p)==0 ){
|
||||
/* We got the time */
|
||||
}else if( *zDate==0 ){
|
||||
p->validHMS = 0;
|
||||
}else{
|
||||
|
@ -280,7 +282,7 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
|
|||
}
|
||||
p->validJD = 0;
|
||||
p->validYMD = 1;
|
||||
p->Y = Y;
|
||||
p->Y = neg ? -Y : Y;
|
||||
p->M = M;
|
||||
p->D = D;
|
||||
if( p->validTZ ){
|
||||
|
@ -306,15 +308,12 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
|
|||
** as there is a year and date.
|
||||
*/
|
||||
static int parseDateOrTime(const char *zDate, DateTime *p){
|
||||
int i;
|
||||
memset(p, 0, sizeof(*p));
|
||||
for(i=0; isdigit(zDate[i]); i++){}
|
||||
if( i==4 && zDate[i]=='-' ){
|
||||
return parseYyyyMmDd(zDate, p);
|
||||
}else if( i==2 && zDate[i]==':' ){
|
||||
return parseHhMmSs(zDate, p);
|
||||
if( parseYyyyMmDd(zDate,p)==0 ){
|
||||
return 0;
|
||||
}else if( i==0 && sqliteStrICmp(zDate,"now")==0 ){
|
||||
}else if( parseHhMmSs(zDate, p)==0 ){
|
||||
return 0;
|
||||
}else if( sqliteStrICmp(zDate,"now")==0){
|
||||
double r;
|
||||
if( sqliteOsCurrentTime(&r)==0 ){
|
||||
p->rJD = r;
|
||||
|
@ -323,7 +322,7 @@ static int parseDateOrTime(const char *zDate, DateTime *p){
|
|||
}
|
||||
return 1;
|
||||
}else if( sqliteIsNumber(zDate) ){
|
||||
p->rJD = sqliteAtoF(zDate);
|
||||
p->rJD = sqliteAtoF(zDate, 0);
|
||||
p->validJD = 1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -336,17 +335,23 @@ static int parseDateOrTime(const char *zDate, DateTime *p){
|
|||
static void computeYMD(DateTime *p){
|
||||
int Z, A, B, C, D, E, X1;
|
||||
if( p->validYMD ) return;
|
||||
Z = p->rJD + 0.5;
|
||||
A = (Z - 1867216.25)/36524.25;
|
||||
A = Z + 1 + A - (A/4);
|
||||
B = A + 1524;
|
||||
C = (B - 122.1)/365.25;
|
||||
D = 365.25*C;
|
||||
E = (B-D)/30.6001;
|
||||
X1 = 30.6001*E;
|
||||
p->D = B - D - X1;
|
||||
p->M = E<14 ? E-1 : E-13;
|
||||
p->Y = p->M>2 ? C - 4716 : C - 4715;
|
||||
if( !p->validJD ){
|
||||
p->Y = 2000;
|
||||
p->M = 1;
|
||||
p->D = 1;
|
||||
}else{
|
||||
Z = p->rJD + 0.5;
|
||||
A = (Z - 1867216.25)/36524.25;
|
||||
A = Z + 1 + A - (A/4);
|
||||
B = A + 1524;
|
||||
C = (B - 122.1)/365.25;
|
||||
D = 365.25*C;
|
||||
E = (B-D)/30.6001;
|
||||
X1 = 30.6001*E;
|
||||
p->D = B - D - X1;
|
||||
p->M = E<14 ? E-1 : E-13;
|
||||
p->Y = p->M>2 ? C - 4716 : C - 4715;
|
||||
}
|
||||
p->validYMD = 1;
|
||||
}
|
||||
|
||||
|
@ -452,8 +457,9 @@ static int parseModifier(const char *zMod, DateTime *p){
|
|||
int rc = 1;
|
||||
int n;
|
||||
double r;
|
||||
char z[30];
|
||||
for(n=0; n<sizeof(z)-1 && zMod[n]; n++){
|
||||
char *z, zBuf[30];
|
||||
z = zBuf;
|
||||
for(n=0; n<sizeof(zBuf)-1 && zMod[n]; n++){
|
||||
z[n] = tolower(zMod[n]);
|
||||
}
|
||||
z[n] = 0;
|
||||
|
@ -526,22 +532,22 @@ static int parseModifier(const char *zMod, DateTime *p){
|
|||
** or month or year.
|
||||
*/
|
||||
if( strncmp(z, "start of ", 9)!=0 ) break;
|
||||
zMod = &z[9];
|
||||
z += 9;
|
||||
computeYMD(p);
|
||||
p->validHMS = 1;
|
||||
p->h = p->m = 0;
|
||||
p->s = 0.0;
|
||||
p->validTZ = 0;
|
||||
p->validJD = 0;
|
||||
if( strcmp(zMod,"month")==0 ){
|
||||
if( strcmp(z,"month")==0 ){
|
||||
p->D = 1;
|
||||
rc = 0;
|
||||
}else if( strcmp(zMod,"year")==0 ){
|
||||
}else if( strcmp(z,"year")==0 ){
|
||||
computeYMD(p);
|
||||
p->M = 1;
|
||||
p->D = 1;
|
||||
rc = 0;
|
||||
}else if( strcmp(zMod,"day")==0 ){
|
||||
}else if( strcmp(z,"day")==0 ){
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
|
@ -560,11 +566,33 @@ static int parseModifier(const char *zMod, DateTime *p){
|
|||
case '9': {
|
||||
n = getValue(z, &r);
|
||||
if( n<=0 ) break;
|
||||
zMod = &z[n];
|
||||
while( isspace(zMod[0]) ) zMod++;
|
||||
n = strlen(zMod);
|
||||
if( z[n]==':' ){
|
||||
/* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the
|
||||
** specified number of hours, minutes, seconds, and fractional seconds
|
||||
** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be
|
||||
** omitted.
|
||||
*/
|
||||
const char *z2 = z;
|
||||
DateTime tx;
|
||||
int day;
|
||||
if( !isdigit(*z2) ) z2++;
|
||||
memset(&tx, 0, sizeof(tx));
|
||||
if( parseHhMmSs(z2, &tx) ) break;
|
||||
computeJD(&tx);
|
||||
tx.rJD -= 0.5;
|
||||
day = (int)tx.rJD;
|
||||
tx.rJD -= day;
|
||||
if( z[0]=='-' ) tx.rJD = -tx.rJD;
|
||||
computeJD(p);
|
||||
clearYMD_HMS_TZ(p);
|
||||
p->rJD += tx.rJD;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
z += n;
|
||||
while( isspace(z[0]) ) z++;
|
||||
n = strlen(z);
|
||||
if( n>10 || n<3 ) break;
|
||||
strcpy(z, zMod);
|
||||
if( z[n-1]=='s' ){ z[n-1] = 0; n--; }
|
||||
computeJD(p);
|
||||
rc = 0;
|
||||
|
|
|
@ -189,7 +189,7 @@ void sqliteDeleteFrom(
|
|||
}
|
||||
|
||||
/* The usual case: There is a WHERE clause so we have to scan through
|
||||
** the table an pick which records to delete.
|
||||
** the table and pick which records to delete.
|
||||
*/
|
||||
else{
|
||||
/* Begin the database scan
|
||||
|
@ -253,12 +253,7 @@ void sqliteDeleteFrom(
|
|||
** cursors are opened only once on the outside the loop.
|
||||
*/
|
||||
pParse->nTab = iCur + 1;
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, iCur, pTab->tnum);
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, pParse->nTab++, pIdx->tnum);
|
||||
}
|
||||
sqliteOpenTableAndIndices(pParse, pTab, iCur);
|
||||
|
||||
/* This is the beginning of the delete loop when there are no
|
||||
** row triggers */
|
||||
|
@ -299,13 +294,14 @@ void sqliteDeleteFrom(
|
|||
pParse->nTab = iCur;
|
||||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_SetCounts, 0, 0);
|
||||
sqliteEndWriteOperation(pParse);
|
||||
|
||||
/*
|
||||
** Return the number of rows that were deleted.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, 0, 1);
|
||||
sqliteVdbeChangeP3(v, -1, "rows deleted", P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 1, 0);
|
||||
}
|
||||
|
@ -347,7 +343,8 @@ void sqliteGenerateRowDelete(
|
|||
int addr;
|
||||
addr = sqliteVdbeAddOp(v, OP_NotExists, iCur, 0);
|
||||
sqliteGenerateRowIndexDelete(db, v, pTab, iCur, 0);
|
||||
sqliteVdbeAddOp(v, OP_Delete, iCur, count);
|
||||
sqliteVdbeAddOp(v, OP_Delete, iCur,
|
||||
(count?OPFLAG_NCHANGE:0) | OPFLAG_CSCHANGE);
|
||||
sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v));
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
** $Id$
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
** How This Encoder Works
|
||||
|
@ -26,7 +27,7 @@
|
|||
** 0x00. This is accomplished by using an escape character to encode
|
||||
** 0x27 and 0x00 as a two-byte sequence. The escape character is always
|
||||
** 0x01. An 0x00 is encoded as the two byte sequence 0x01 0x01. The
|
||||
** 0x27 character is encoded as the two byte sequence 0x01 0x03. Finally,
|
||||
** 0x27 character is encoded as the two byte sequence 0x01 0x28. Finally,
|
||||
** the escape character itself is encoded as the two-character sequence
|
||||
** 0x01 0x02.
|
||||
**
|
||||
|
@ -34,7 +35,7 @@
|
|||
**
|
||||
** 0x00 -> 0x01 0x01
|
||||
** 0x01 -> 0x01 0x02
|
||||
** 0x27 -> 0x01 0x03
|
||||
** 0x27 -> 0x01 0x28
|
||||
**
|
||||
** If that were all the encoder did, it would work, but in certain cases
|
||||
** it could double the size of the encoded string. For example, to
|
||||
|
@ -80,7 +81,7 @@
|
|||
** the offset in step 7 below.
|
||||
**
|
||||
** (6) Convert each 0x01 0x01 sequence into a single character 0x00.
|
||||
** Convert 0x01 0x02 into 0x01. Convert 0x01 0x03 into 0x27.
|
||||
** Convert 0x01 0x02 into 0x01. Convert 0x01 0x28 into 0x27.
|
||||
**
|
||||
** (7) Subtract the offset value that was the first character of
|
||||
** the encoded buffer from all characters in the output buffer.
|
||||
|
@ -114,13 +115,20 @@
|
|||
**
|
||||
** The return value is the number of characters in the encoded
|
||||
** string, excluding the "\000" terminator.
|
||||
**
|
||||
** If out==NULL then no output is generated but the routine still returns
|
||||
** the number of characters that would have been generated if out had
|
||||
** not been NULL.
|
||||
*/
|
||||
int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out){
|
||||
int i, j, e, m;
|
||||
unsigned char x;
|
||||
int cnt[256];
|
||||
if( n<=0 ){
|
||||
out[0] = 'x';
|
||||
out[1] = 0;
|
||||
if( out ){
|
||||
out[0] = 'x';
|
||||
out[1] = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
memset(cnt, 0, sizeof(cnt));
|
||||
|
@ -136,24 +144,21 @@ int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out){
|
|||
if( m==0 ) break;
|
||||
}
|
||||
}
|
||||
if( out==0 ){
|
||||
return n+m+1;
|
||||
}
|
||||
out[0] = e;
|
||||
j = 1;
|
||||
for(i=0; i<n; i++){
|
||||
int c = (in[i] - e)&0xff;
|
||||
if( c==0 ){
|
||||
x = in[i] - e;
|
||||
if( x==0 || x==1 || x=='\''){
|
||||
out[j++] = 1;
|
||||
out[j++] = 1;
|
||||
}else if( c==1 ){
|
||||
out[j++] = 1;
|
||||
out[j++] = 2;
|
||||
}else if( c=='\'' ){
|
||||
out[j++] = 1;
|
||||
out[j++] = 3;
|
||||
}else{
|
||||
out[j++] = c;
|
||||
x++;
|
||||
}
|
||||
out[j++] = x;
|
||||
}
|
||||
out[j] = 0;
|
||||
assert( j==n+m+1 );
|
||||
return j;
|
||||
}
|
||||
|
||||
|
@ -168,38 +173,32 @@ int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out){
|
|||
** to decode a string in place.
|
||||
*/
|
||||
int sqlite_decode_binary(const unsigned char *in, unsigned char *out){
|
||||
int i, c, e;
|
||||
int i, e;
|
||||
unsigned char c;
|
||||
e = *(in++);
|
||||
i = 0;
|
||||
while( (c = *(in++))!=0 ){
|
||||
if( c==1 ){
|
||||
c = *(in++);
|
||||
if( c==1 ){
|
||||
c = 0;
|
||||
}else if( c==2 ){
|
||||
c = 1;
|
||||
}else if( c==3 ){
|
||||
c = '\'';
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
c = *(in++) - 1;
|
||||
}
|
||||
out[i++] = (c + e)&0xff;
|
||||
out[i++] = c + e;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
#ifdef ENCODER_TEST
|
||||
#include <stdio.h>
|
||||
/*
|
||||
** The subroutines above are not tested by the usual test suite. To test
|
||||
** these routines, compile just this one file with a -DENCODER_TEST=1 option
|
||||
** and run the result.
|
||||
*/
|
||||
int main(int argc, char **argv){
|
||||
int i, j, n, m, nOut;
|
||||
int i, j, n, m, nOut, nByteIn, nByteOut;
|
||||
unsigned char in[30000];
|
||||
unsigned char out[33000];
|
||||
|
||||
nByteIn = nByteOut = 0;
|
||||
for(i=0; i<sizeof(in); i++){
|
||||
printf("Test %d: ", i+1);
|
||||
n = rand() % (i+1);
|
||||
|
@ -213,11 +212,17 @@ int main(int argc, char **argv){
|
|||
}else{
|
||||
for(j=0; j<n; j++) in[j] = rand() & 0xff;
|
||||
}
|
||||
nByteIn += n;
|
||||
nOut = sqlite_encode_binary(in, n, out);
|
||||
nByteOut += nOut;
|
||||
if( nOut!=strlen(out) ){
|
||||
printf(" ERROR return value is %d instead of %d\n", nOut, strlen(out));
|
||||
exit(1);
|
||||
}
|
||||
if( nOut!=sqlite_encode_binary(in, n, 0) ){
|
||||
printf(" ERROR actual output size disagrees with predicted size\n");
|
||||
exit(1);
|
||||
}
|
||||
m = (256*n + 1262)/253;
|
||||
printf("size %d->%d (max %d)", n, strlen(out)+1, m);
|
||||
if( strlen(out)+1>m ){
|
||||
|
@ -241,5 +246,9 @@ int main(int argc, char **argv){
|
|||
}
|
||||
printf(" OK\n");
|
||||
}
|
||||
fprintf(stderr,"Finished. Total encoding: %d->%d bytes\n",
|
||||
nByteIn, nByteOut);
|
||||
fprintf(stderr,"Avg size increase: %.3f%%\n",
|
||||
(nByteOut-nByteIn)*100.0/(double)nByteIn);
|
||||
}
|
||||
#endif /* ENCODER_TEST */
|
||||
|
|
|
@ -26,8 +26,7 @@ Expr *sqliteExpr(int op, Expr *pLeft, Expr *pRight, Token *pToken){
|
|||
Expr *pNew;
|
||||
pNew = sqliteMalloc( sizeof(Expr) );
|
||||
if( pNew==0 ){
|
||||
sqliteExprDelete(pLeft);
|
||||
sqliteExprDelete(pRight);
|
||||
/* When malloc fails, we leak memory from pLeft and pRight */
|
||||
return 0;
|
||||
}
|
||||
pNew->op = op;
|
||||
|
@ -38,9 +37,9 @@ Expr *sqliteExpr(int op, Expr *pLeft, Expr *pRight, Token *pToken){
|
|||
pNew->token = *pToken;
|
||||
pNew->span = *pToken;
|
||||
}else{
|
||||
pNew->token.dyn = 0;
|
||||
pNew->token.z = 0;
|
||||
pNew->token.n = 0;
|
||||
assert( pNew->token.dyn==0 );
|
||||
assert( pNew->token.z==0 );
|
||||
assert( pNew->token.n==0 );
|
||||
if( pLeft && pRight ){
|
||||
sqliteExprSpan(pNew, &pLeft->span, &pRight->span);
|
||||
}else{
|
||||
|
@ -55,14 +54,15 @@ Expr *sqliteExpr(int op, Expr *pLeft, Expr *pRight, Token *pToken){
|
|||
** text between the two given tokens.
|
||||
*/
|
||||
void sqliteExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){
|
||||
if( pExpr && pRight && pRight->z && pLeft && pLeft->z ){
|
||||
assert( pRight!=0 );
|
||||
assert( pLeft!=0 );
|
||||
/* Note: pExpr might be NULL due to a prior malloc failure */
|
||||
if( pExpr && pRight->z && pLeft->z ){
|
||||
if( pLeft->dyn==0 && pRight->dyn==0 ){
|
||||
pExpr->span.z = pLeft->z;
|
||||
pExpr->span.n = pRight->n + Addr(pRight->z) - Addr(pLeft->z);
|
||||
}else{
|
||||
pExpr->span.z = 0;
|
||||
pExpr->span.n = 0;
|
||||
pExpr->span.dyn = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,18 +75,16 @@ Expr *sqliteExprFunction(ExprList *pList, Token *pToken){
|
|||
Expr *pNew;
|
||||
pNew = sqliteMalloc( sizeof(Expr) );
|
||||
if( pNew==0 ){
|
||||
sqliteExprListDelete(pList);
|
||||
/* sqliteExprListDelete(pList); // Leak pList when malloc fails */
|
||||
return 0;
|
||||
}
|
||||
pNew->op = TK_FUNCTION;
|
||||
pNew->pList = pList;
|
||||
pNew->token.dyn = 0;
|
||||
if( pToken ){
|
||||
assert( pToken->dyn==0 );
|
||||
pNew->token = *pToken;
|
||||
}else{
|
||||
pNew->token.z = 0;
|
||||
pNew->token.n = 0;
|
||||
}
|
||||
pNew->span = pNew->token;
|
||||
return pNew;
|
||||
|
@ -97,12 +95,12 @@ Expr *sqliteExprFunction(ExprList *pList, Token *pToken){
|
|||
*/
|
||||
void sqliteExprDelete(Expr *p){
|
||||
if( p==0 ) return;
|
||||
if( p->span.dyn && p->span.z ) sqliteFree((char*)p->span.z);
|
||||
if( p->token.dyn && p->token.z ) sqliteFree((char*)p->token.z);
|
||||
if( p->pLeft ) sqliteExprDelete(p->pLeft);
|
||||
if( p->pRight ) sqliteExprDelete(p->pRight);
|
||||
if( p->pList ) sqliteExprListDelete(p->pList);
|
||||
if( p->pSelect ) sqliteSelectDelete(p->pSelect);
|
||||
if( p->span.dyn ) sqliteFree((char*)p->span.z);
|
||||
if( p->token.dyn ) sqliteFree((char*)p->token.z);
|
||||
sqliteExprDelete(p->pLeft);
|
||||
sqliteExprDelete(p->pRight);
|
||||
sqliteExprListDelete(p->pList);
|
||||
sqliteSelectDelete(p->pSelect);
|
||||
sqliteFree(p);
|
||||
}
|
||||
|
||||
|
@ -129,13 +127,9 @@ Expr *sqliteExprDup(Expr *p){
|
|||
pNew->token.z = sqliteStrDup(p->token.z);
|
||||
pNew->token.dyn = 1;
|
||||
}else{
|
||||
pNew->token.z = 0;
|
||||
pNew->token.n = 0;
|
||||
pNew->token.dyn = 0;
|
||||
assert( pNew->token.z==0 );
|
||||
}
|
||||
pNew->span.z = 0;
|
||||
pNew->span.n = 0;
|
||||
pNew->span.dyn = 0;
|
||||
pNew->pLeft = sqliteExprDup(p->pLeft);
|
||||
pNew->pRight = sqliteExprDup(p->pRight);
|
||||
pNew->pList = sqliteExprListDup(p->pList);
|
||||
|
@ -149,23 +143,22 @@ void sqliteTokenCopy(Token *pTo, Token *pFrom){
|
|||
pTo->z = sqliteStrNDup(pFrom->z, pFrom->n);
|
||||
pTo->dyn = 1;
|
||||
}else{
|
||||
pTo->n = 0;
|
||||
pTo->z = 0;
|
||||
pTo->dyn = 0;
|
||||
}
|
||||
}
|
||||
ExprList *sqliteExprListDup(ExprList *p){
|
||||
ExprList *pNew;
|
||||
struct ExprList_item *pItem;
|
||||
int i;
|
||||
if( p==0 ) return 0;
|
||||
pNew = sqliteMalloc( sizeof(*pNew) );
|
||||
if( pNew==0 ) return 0;
|
||||
pNew->nExpr = pNew->nAlloc = p->nExpr;
|
||||
pNew->a = sqliteMalloc( p->nExpr*sizeof(p->a[0]) );
|
||||
if( pNew->a==0 ) return 0;
|
||||
for(i=0; i<p->nExpr; i++){
|
||||
pNew->a = pItem = sqliteMalloc( p->nExpr*sizeof(p->a[0]) );
|
||||
if( pItem==0 ) return 0; /* Leaks memory after a malloc failure */
|
||||
for(i=0; i<p->nExpr; i++, pItem++){
|
||||
Expr *pNewExpr, *pOldExpr;
|
||||
pNew->a[i].pExpr = pNewExpr = sqliteExprDup(pOldExpr = p->a[i].pExpr);
|
||||
pItem->pExpr = pNewExpr = sqliteExprDup(pOldExpr = p->a[i].pExpr);
|
||||
if( pOldExpr->span.z!=0 && pNewExpr ){
|
||||
/* Always make a copy of the span for top-level expressions in the
|
||||
** expression list. The logic in SELECT processing that determines
|
||||
|
@ -174,10 +167,10 @@ ExprList *sqliteExprListDup(ExprList *p){
|
|||
}
|
||||
assert( pNewExpr==0 || pNewExpr->span.z!=0
|
||||
|| pOldExpr->span.z==0 || sqlite_malloc_failed );
|
||||
pNew->a[i].zName = sqliteStrDup(p->a[i].zName);
|
||||
pNew->a[i].sortOrder = p->a[i].sortOrder;
|
||||
pNew->a[i].isAgg = p->a[i].isAgg;
|
||||
pNew->a[i].done = 0;
|
||||
pItem->zName = sqliteStrDup(p->a[i].zName);
|
||||
pItem->sortOrder = p->a[i].sortOrder;
|
||||
pItem->isAgg = p->a[i].isAgg;
|
||||
pItem->done = 0;
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
|
@ -187,19 +180,21 @@ SrcList *sqliteSrcListDup(SrcList *p){
|
|||
int nByte;
|
||||
if( p==0 ) return 0;
|
||||
nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
|
||||
pNew = sqliteMalloc( nByte );
|
||||
pNew = sqliteMallocRaw( nByte );
|
||||
if( pNew==0 ) return 0;
|
||||
pNew->nSrc = pNew->nAlloc = p->nSrc;
|
||||
for(i=0; i<p->nSrc; i++){
|
||||
pNew->a[i].zDatabase = sqliteStrDup(p->a[i].zDatabase);
|
||||
pNew->a[i].zName = sqliteStrDup(p->a[i].zName);
|
||||
pNew->a[i].zAlias = sqliteStrDup(p->a[i].zAlias);
|
||||
pNew->a[i].jointype = p->a[i].jointype;
|
||||
pNew->a[i].iCursor = p->a[i].iCursor;
|
||||
pNew->a[i].pTab = 0;
|
||||
pNew->a[i].pSelect = sqliteSelectDup(p->a[i].pSelect);
|
||||
pNew->a[i].pOn = sqliteExprDup(p->a[i].pOn);
|
||||
pNew->a[i].pUsing = sqliteIdListDup(p->a[i].pUsing);
|
||||
struct SrcList_item *pNewItem = &pNew->a[i];
|
||||
struct SrcList_item *pOldItem = &p->a[i];
|
||||
pNewItem->zDatabase = sqliteStrDup(pOldItem->zDatabase);
|
||||
pNewItem->zName = sqliteStrDup(pOldItem->zName);
|
||||
pNewItem->zAlias = sqliteStrDup(pOldItem->zAlias);
|
||||
pNewItem->jointype = pOldItem->jointype;
|
||||
pNewItem->iCursor = pOldItem->iCursor;
|
||||
pNewItem->pTab = 0;
|
||||
pNewItem->pSelect = sqliteSelectDup(pOldItem->pSelect);
|
||||
pNewItem->pOn = sqliteExprDup(pOldItem->pOn);
|
||||
pNewItem->pUsing = sqliteIdListDup(pOldItem->pUsing);
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
|
@ -207,21 +202,23 @@ IdList *sqliteIdListDup(IdList *p){
|
|||
IdList *pNew;
|
||||
int i;
|
||||
if( p==0 ) return 0;
|
||||
pNew = sqliteMalloc( sizeof(*pNew) );
|
||||
pNew = sqliteMallocRaw( sizeof(*pNew) );
|
||||
if( pNew==0 ) return 0;
|
||||
pNew->nId = pNew->nAlloc = p->nId;
|
||||
pNew->a = sqliteMalloc( p->nId*sizeof(p->a[0]) );
|
||||
pNew->a = sqliteMallocRaw( p->nId*sizeof(p->a[0]) );
|
||||
if( pNew->a==0 ) return 0;
|
||||
for(i=0; i<p->nId; i++){
|
||||
pNew->a[i].zName = sqliteStrDup(p->a[i].zName);
|
||||
pNew->a[i].idx = p->a[i].idx;
|
||||
struct IdList_item *pNewItem = &pNew->a[i];
|
||||
struct IdList_item *pOldItem = &p->a[i];
|
||||
pNewItem->zName = sqliteStrDup(pOldItem->zName);
|
||||
pNewItem->idx = pOldItem->idx;
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
Select *sqliteSelectDup(Select *p){
|
||||
Select *pNew;
|
||||
if( p==0 ) return 0;
|
||||
pNew = sqliteMalloc( sizeof(*p) );
|
||||
pNew = sqliteMallocRaw( sizeof(*p) );
|
||||
if( pNew==0 ) return 0;
|
||||
pNew->isDistinct = p->isDistinct;
|
||||
pNew->pEList = sqliteExprListDup(p->pEList);
|
||||
|
@ -246,32 +243,31 @@ Select *sqliteSelectDup(Select *p){
|
|||
** initially NULL, then create a new expression list.
|
||||
*/
|
||||
ExprList *sqliteExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){
|
||||
int i;
|
||||
if( pList==0 ){
|
||||
pList = sqliteMalloc( sizeof(ExprList) );
|
||||
if( pList==0 ){
|
||||
sqliteExprDelete(pExpr);
|
||||
/* sqliteExprDelete(pExpr); // Leak memory if malloc fails */
|
||||
return 0;
|
||||
}
|
||||
pList->nAlloc = 0;
|
||||
assert( pList->nAlloc==0 );
|
||||
}
|
||||
if( pList->nAlloc<=pList->nExpr ){
|
||||
struct ExprList_item *a;
|
||||
pList->nAlloc = pList->nAlloc*2 + 4;
|
||||
a = sqliteRealloc(pList->a, pList->nAlloc*sizeof(pList->a[0]));
|
||||
if( a==0 ){
|
||||
sqliteExprDelete(pExpr);
|
||||
pList->a = sqliteRealloc(pList->a, pList->nAlloc*sizeof(pList->a[0]));
|
||||
if( pList->a==0 ){
|
||||
/* sqliteExprDelete(pExpr); // Leak memory if malloc fails */
|
||||
pList->nExpr = pList->nAlloc = 0;
|
||||
return pList;
|
||||
}
|
||||
pList->a = a;
|
||||
}
|
||||
if( pList->a && (pExpr || pName) ){
|
||||
i = pList->nExpr++;
|
||||
memset(&pList->a[i], 0, sizeof(pList->a[i]));
|
||||
pList->a[i].pExpr = pExpr;
|
||||
assert( pList->a!=0 );
|
||||
if( pExpr || pName ){
|
||||
struct ExprList_item *pItem = &pList->a[pList->nExpr++];
|
||||
memset(pItem, 0, sizeof(*pItem));
|
||||
pItem->pExpr = pExpr;
|
||||
if( pName ){
|
||||
sqliteSetNString(&pList->a[i].zName, pName->z, pName->n, 0);
|
||||
sqliteDequote(pList->a[i].zName);
|
||||
sqliteSetNString(&pItem->zName, pName->z, pName->n, 0);
|
||||
sqliteDequote(pItem->zName);
|
||||
}
|
||||
}
|
||||
return pList;
|
||||
|
@ -283,6 +279,8 @@ ExprList *sqliteExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){
|
|||
void sqliteExprListDelete(ExprList *pList){
|
||||
int i;
|
||||
if( pList==0 ) return;
|
||||
assert( pList->a!=0 || (pList->nExpr==0 && pList->nAlloc==0) );
|
||||
assert( pList->nExpr<=pList->nAlloc );
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
sqliteExprDelete(pList->a[i].pExpr);
|
||||
sqliteFree(pList->a[i].zName);
|
||||
|
@ -379,6 +377,221 @@ int sqliteIsRowid(const char *z){
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
|
||||
** that name in the set of source tables in pSrcList and make the pExpr
|
||||
** expression node refer back to that source column. The following changes
|
||||
** are made to pExpr:
|
||||
**
|
||||
** pExpr->iDb Set the index in db->aDb[] of the database holding
|
||||
** the table.
|
||||
** pExpr->iTable Set to the cursor number for the table obtained
|
||||
** from pSrcList.
|
||||
** pExpr->iColumn Set to the column number within the table.
|
||||
** pExpr->dataType Set to the appropriate data type for the column.
|
||||
** pExpr->op Set to TK_COLUMN.
|
||||
** pExpr->pLeft Any expression this points to is deleted
|
||||
** pExpr->pRight Any expression this points to is deleted.
|
||||
**
|
||||
** The pDbToken is the name of the database (the "X"). This value may be
|
||||
** NULL meaning that name is of the form Y.Z or Z. Any available database
|
||||
** can be used. The pTableToken is the name of the table (the "Y"). This
|
||||
** value can be NULL if pDbToken is also NULL. If pTableToken is NULL it
|
||||
** means that the form of the name is Z and that columns from any table
|
||||
** can be used.
|
||||
**
|
||||
** If the name cannot be resolved unambiguously, leave an error message
|
||||
** in pParse and return non-zero. Return zero on success.
|
||||
*/
|
||||
static int lookupName(
|
||||
Parse *pParse, /* The parsing context */
|
||||
Token *pDbToken, /* Name of the database containing table, or NULL */
|
||||
Token *pTableToken, /* Name of table containing column, or NULL */
|
||||
Token *pColumnToken, /* Name of the column. */
|
||||
SrcList *pSrcList, /* List of tables used to resolve column names */
|
||||
ExprList *pEList, /* List of expressions used to resolve "AS" */
|
||||
Expr *pExpr /* Make this EXPR node point to the selected column */
|
||||
){
|
||||
char *zDb = 0; /* Name of the database. The "X" in X.Y.Z */
|
||||
char *zTab = 0; /* Name of the table. The "Y" in X.Y.Z or Y.Z */
|
||||
char *zCol = 0; /* Name of the column. The "Z" */
|
||||
int i, j; /* Loop counters */
|
||||
int cnt = 0; /* Number of matching column names */
|
||||
int cntTab = 0; /* Number of matching table names */
|
||||
sqlite *db = pParse->db; /* The database */
|
||||
|
||||
assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */
|
||||
if( pDbToken && pDbToken->z ){
|
||||
zDb = sqliteStrNDup(pDbToken->z, pDbToken->n);
|
||||
sqliteDequote(zDb);
|
||||
}else{
|
||||
zDb = 0;
|
||||
}
|
||||
if( pTableToken && pTableToken->z ){
|
||||
zTab = sqliteStrNDup(pTableToken->z, pTableToken->n);
|
||||
sqliteDequote(zTab);
|
||||
}else{
|
||||
assert( zDb==0 );
|
||||
zTab = 0;
|
||||
}
|
||||
zCol = sqliteStrNDup(pColumnToken->z, pColumnToken->n);
|
||||
sqliteDequote(zCol);
|
||||
if( sqlite_malloc_failed ){
|
||||
return 1; /* Leak memory (zDb and zTab) if malloc fails */
|
||||
}
|
||||
assert( zTab==0 || pEList==0 );
|
||||
|
||||
pExpr->iTable = -1;
|
||||
for(i=0; i<pSrcList->nSrc; i++){
|
||||
struct SrcList_item *pItem = &pSrcList->a[i];
|
||||
Table *pTab = pItem->pTab;
|
||||
Column *pCol;
|
||||
|
||||
if( pTab==0 ) continue;
|
||||
assert( pTab->nCol>0 );
|
||||
if( zTab ){
|
||||
if( pItem->zAlias ){
|
||||
char *zTabName = pItem->zAlias;
|
||||
if( sqliteStrICmp(zTabName, zTab)!=0 ) continue;
|
||||
}else{
|
||||
char *zTabName = pTab->zName;
|
||||
if( zTabName==0 || sqliteStrICmp(zTabName, zTab)!=0 ) continue;
|
||||
if( zDb!=0 && sqliteStrICmp(db->aDb[pTab->iDb].zName, zDb)!=0 ){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( 0==(cntTab++) ){
|
||||
pExpr->iTable = pItem->iCursor;
|
||||
pExpr->iDb = pTab->iDb;
|
||||
}
|
||||
for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
|
||||
if( sqliteStrICmp(pCol->zName, zCol)==0 ){
|
||||
cnt++;
|
||||
pExpr->iTable = pItem->iCursor;
|
||||
pExpr->iDb = pTab->iDb;
|
||||
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
|
||||
pExpr->iColumn = j==pTab->iPKey ? -1 : j;
|
||||
pExpr->dataType = pCol->sortOrder & SQLITE_SO_TYPEMASK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have not already resolved the name, then maybe
|
||||
** it is a new.* or old.* trigger argument reference
|
||||
*/
|
||||
if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){
|
||||
TriggerStack *pTriggerStack = pParse->trigStack;
|
||||
Table *pTab = 0;
|
||||
if( pTriggerStack->newIdx != -1 && sqliteStrICmp("new", zTab) == 0 ){
|
||||
pExpr->iTable = pTriggerStack->newIdx;
|
||||
assert( pTriggerStack->pTab );
|
||||
pTab = pTriggerStack->pTab;
|
||||
}else if( pTriggerStack->oldIdx != -1 && sqliteStrICmp("old", zTab) == 0 ){
|
||||
pExpr->iTable = pTriggerStack->oldIdx;
|
||||
assert( pTriggerStack->pTab );
|
||||
pTab = pTriggerStack->pTab;
|
||||
}
|
||||
|
||||
if( pTab ){
|
||||
int j;
|
||||
Column *pCol = pTab->aCol;
|
||||
|
||||
pExpr->iDb = pTab->iDb;
|
||||
cntTab++;
|
||||
for(j=0; j < pTab->nCol; j++, pCol++) {
|
||||
if( sqliteStrICmp(pCol->zName, zCol)==0 ){
|
||||
cnt++;
|
||||
pExpr->iColumn = j==pTab->iPKey ? -1 : j;
|
||||
pExpr->dataType = pCol->sortOrder & SQLITE_SO_TYPEMASK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Perhaps the name is a reference to the ROWID
|
||||
*/
|
||||
if( cnt==0 && cntTab==1 && sqliteIsRowid(zCol) ){
|
||||
cnt = 1;
|
||||
pExpr->iColumn = -1;
|
||||
pExpr->dataType = SQLITE_SO_NUM;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z
|
||||
** might refer to an result-set alias. This happens, for example, when
|
||||
** we are resolving names in the WHERE clause of the following command:
|
||||
**
|
||||
** SELECT a+b AS x FROM table WHERE x<10;
|
||||
**
|
||||
** In cases like this, replace pExpr with a copy of the expression that
|
||||
** forms the result set entry ("a+b" in the example) and return immediately.
|
||||
** Note that the expression in the result set should have already been
|
||||
** resolved by the time the WHERE clause is resolved.
|
||||
*/
|
||||
if( cnt==0 && pEList!=0 ){
|
||||
for(j=0; j<pEList->nExpr; j++){
|
||||
char *zAs = pEList->a[j].zName;
|
||||
if( zAs!=0 && sqliteStrICmp(zAs, zCol)==0 ){
|
||||
assert( pExpr->pLeft==0 && pExpr->pRight==0 );
|
||||
pExpr->op = TK_AS;
|
||||
pExpr->iColumn = j;
|
||||
pExpr->pLeft = sqliteExprDup(pEList->a[j].pExpr);
|
||||
sqliteFree(zCol);
|
||||
assert( zTab==0 && zDb==0 );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If X and Y are NULL (in other words if only the column name Z is
|
||||
** supplied) and the value of Z is enclosed in double-quotes, then
|
||||
** Z is a string literal if it doesn't match any column names. In that
|
||||
** case, we need to return right away and not make any changes to
|
||||
** pExpr.
|
||||
*/
|
||||
if( cnt==0 && zTab==0 && pColumnToken->z[0]=='"' ){
|
||||
sqliteFree(zCol);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** cnt==0 means there was not match. cnt>1 means there were two or
|
||||
** more matches. Either way, we have an error.
|
||||
*/
|
||||
if( cnt!=1 ){
|
||||
char *z = 0;
|
||||
char *zErr;
|
||||
zErr = cnt==0 ? "no such column: %s" : "ambiguous column name: %s";
|
||||
if( zDb ){
|
||||
sqliteSetString(&z, zDb, ".", zTab, ".", zCol, 0);
|
||||
}else if( zTab ){
|
||||
sqliteSetString(&z, zTab, ".", zCol, 0);
|
||||
}else{
|
||||
z = sqliteStrDup(zCol);
|
||||
}
|
||||
sqliteErrorMsg(pParse, zErr, z);
|
||||
sqliteFree(z);
|
||||
}
|
||||
|
||||
/* Clean up and return
|
||||
*/
|
||||
sqliteFree(zDb);
|
||||
sqliteFree(zTab);
|
||||
sqliteFree(zCol);
|
||||
sqliteExprDelete(pExpr->pLeft);
|
||||
pExpr->pLeft = 0;
|
||||
sqliteExprDelete(pExpr->pRight);
|
||||
pExpr->pRight = 0;
|
||||
pExpr->op = TK_COLUMN;
|
||||
sqliteAuthRead(pParse, pExpr, pSrcList);
|
||||
return cnt!=1;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine walks an expression tree and resolves references to
|
||||
** table columns. Nodes of the form ID.ID or ID resolve into an
|
||||
|
@ -412,15 +625,15 @@ int sqliteIsRowid(const char *z){
|
|||
*/
|
||||
int sqliteExprResolveIds(
|
||||
Parse *pParse, /* The parser context */
|
||||
SrcList *pTabList, /* List of tables used to resolve column names */
|
||||
SrcList *pSrcList, /* List of tables used to resolve column names */
|
||||
ExprList *pEList, /* List of expressions used to resolve "AS" */
|
||||
Expr *pExpr /* The expression to be analyzed. */
|
||||
){
|
||||
int i;
|
||||
|
||||
if( pExpr==0 || pTabList==0 ) return 0;
|
||||
for(i=0; i<pTabList->nSrc; i++){
|
||||
assert( pTabList->a[i].iCursor>=0 && pTabList->a[i].iCursor<pParse->nTab );
|
||||
if( pExpr==0 || pSrcList==0 ) return 0;
|
||||
for(i=0; i<pSrcList->nSrc; i++){
|
||||
assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursor<pParse->nTab );
|
||||
}
|
||||
switch( pExpr->op ){
|
||||
/* Double-quoted strings (ex: "abc") are used as identifiers if
|
||||
|
@ -431,79 +644,11 @@ int sqliteExprResolveIds(
|
|||
if( pExpr->token.z[0]=='\'' ) break;
|
||||
/* Fall thru into the TK_ID case if this is a double-quoted string */
|
||||
}
|
||||
/* A lone identifier. Try and match it as follows:
|
||||
**
|
||||
** 1. To the name of a column of one of the tables in pTabList
|
||||
**
|
||||
** 2. To the right side of an AS keyword in the column list of
|
||||
** a SELECT statement. (For example, match against 'x' in
|
||||
** "SELECT a+b AS 'x' FROM t1".)
|
||||
**
|
||||
** 3. One of the special names "ROWID", "OID", or "_ROWID_".
|
||||
/* A lone identifier is the name of a columnd.
|
||||
*/
|
||||
case TK_ID: {
|
||||
int cnt = 0; /* Number of matches */
|
||||
char *z;
|
||||
int iDb = -1;
|
||||
|
||||
assert( pExpr->token.z );
|
||||
z = sqliteStrNDup(pExpr->token.z, pExpr->token.n);
|
||||
sqliteDequote(z);
|
||||
if( z==0 ) return 1;
|
||||
for(i=0; i<pTabList->nSrc; i++){
|
||||
int j;
|
||||
Table *pTab = pTabList->a[i].pTab;
|
||||
if( pTab==0 ) continue;
|
||||
iDb = pTab->iDb;
|
||||
assert( pTab->nCol>0 );
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){
|
||||
cnt++;
|
||||
pExpr->iTable = pTabList->a[i].iCursor;
|
||||
pExpr->iDb = pTab->iDb;
|
||||
if( j==pTab->iPKey ){
|
||||
/* Substitute the record number for the INTEGER PRIMARY KEY */
|
||||
pExpr->iColumn = -1;
|
||||
pExpr->dataType = SQLITE_SO_NUM;
|
||||
}else{
|
||||
pExpr->iColumn = j;
|
||||
pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK;
|
||||
}
|
||||
pExpr->op = TK_COLUMN;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( cnt==0 && pEList!=0 ){
|
||||
int j;
|
||||
for(j=0; j<pEList->nExpr; j++){
|
||||
char *zAs = pEList->a[j].zName;
|
||||
if( zAs!=0 && sqliteStrICmp(zAs, z)==0 ){
|
||||
cnt++;
|
||||
assert( pExpr->pLeft==0 && pExpr->pRight==0 );
|
||||
pExpr->op = TK_AS;
|
||||
pExpr->iColumn = j;
|
||||
pExpr->pLeft = sqliteExprDup(pEList->a[j].pExpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if( cnt==0 && iDb>=0 && sqliteIsRowid(z) ){
|
||||
pExpr->iColumn = -1;
|
||||
pExpr->iTable = pTabList->a[0].iCursor;
|
||||
pExpr->iDb = iDb;
|
||||
cnt = 1 + (pTabList->nSrc>1);
|
||||
pExpr->op = TK_COLUMN;
|
||||
pExpr->dataType = SQLITE_SO_NUM;
|
||||
}
|
||||
sqliteFree(z);
|
||||
if( cnt==0 && pExpr->token.z[0]!='"' ){
|
||||
sqliteErrorMsg(pParse, "no such column: %T", &pExpr->token);
|
||||
if( lookupName(pParse, 0, 0, &pExpr->token, pSrcList, pEList, pExpr) ){
|
||||
return 1;
|
||||
}else if( cnt>1 ){
|
||||
sqliteErrorMsg(pParse, "ambiguous column name: %T", &pExpr->token);
|
||||
return 1;
|
||||
}
|
||||
if( pExpr->op==TK_COLUMN ){
|
||||
sqliteAuthRead(pParse, pExpr, pTabList);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -512,134 +657,32 @@ int sqliteExprResolveIds(
|
|||
** Or a database, table and column: ID.ID.ID
|
||||
*/
|
||||
case TK_DOT: {
|
||||
int cnt = 0; /* Number of matches */
|
||||
int cntTab = 0; /* Number of matching tables */
|
||||
int i; /* Loop counter */
|
||||
Expr *pLeft, *pRight; /* Left and right subbranches of the expr */
|
||||
char *zLeft, *zRight; /* Text of an identifier */
|
||||
char *zDb; /* Name of database holding table */
|
||||
sqlite *db = pParse->db;
|
||||
Token *pColumn;
|
||||
Token *pTable;
|
||||
Token *pDb;
|
||||
Expr *pRight;
|
||||
|
||||
pRight = pExpr->pRight;
|
||||
if( pRight->op==TK_ID ){
|
||||
pLeft = pExpr->pLeft;
|
||||
zDb = 0;
|
||||
pDb = 0;
|
||||
pTable = &pExpr->pLeft->token;
|
||||
pColumn = &pRight->token;
|
||||
}else{
|
||||
Expr *pDb = pExpr->pLeft;
|
||||
assert( pDb && pDb->op==TK_ID && pDb->token.z );
|
||||
zDb = sqliteStrNDup(pDb->token.z, pDb->token.n);
|
||||
pLeft = pRight->pLeft;
|
||||
pRight = pRight->pRight;
|
||||
assert( pRight->op==TK_DOT );
|
||||
pDb = &pExpr->pLeft->token;
|
||||
pTable = &pRight->pLeft->token;
|
||||
pColumn = &pRight->pRight->token;
|
||||
}
|
||||
assert( pLeft && pLeft->op==TK_ID && pLeft->token.z );
|
||||
assert( pRight && pRight->op==TK_ID && pRight->token.z );
|
||||
zLeft = sqliteStrNDup(pLeft->token.z, pLeft->token.n);
|
||||
zRight = sqliteStrNDup(pRight->token.z, pRight->token.n);
|
||||
if( zLeft==0 || zRight==0 ){
|
||||
sqliteFree(zLeft);
|
||||
sqliteFree(zRight);
|
||||
sqliteFree(zDb);
|
||||
if( lookupName(pParse, pDb, pTable, pColumn, pSrcList, 0, pExpr) ){
|
||||
return 1;
|
||||
}
|
||||
sqliteDequote(zDb);
|
||||
sqliteDequote(zLeft);
|
||||
sqliteDequote(zRight);
|
||||
pExpr->iTable = -1;
|
||||
for(i=0; i<pTabList->nSrc; i++){
|
||||
int j;
|
||||
char *zTab;
|
||||
Table *pTab = pTabList->a[i].pTab;
|
||||
if( pTab==0 ) continue;
|
||||
assert( pTab->nCol>0 );
|
||||
if( pTabList->a[i].zAlias ){
|
||||
zTab = pTabList->a[i].zAlias;
|
||||
if( sqliteStrICmp(zTab, zLeft)!=0 ) continue;
|
||||
}else{
|
||||
zTab = pTab->zName;
|
||||
if( zTab==0 || sqliteStrICmp(zTab, zLeft)!=0 ) continue;
|
||||
if( zDb!=0 && sqliteStrICmp(db->aDb[pTab->iDb].zName, zDb)!=0 ){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if( 0==(cntTab++) ){
|
||||
pExpr->iTable = pTabList->a[i].iCursor;
|
||||
pExpr->iDb = pTab->iDb;
|
||||
}
|
||||
for(j=0; j<pTab->nCol; j++){
|
||||
if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
|
||||
cnt++;
|
||||
pExpr->iTable = pTabList->a[i].iCursor;
|
||||
pExpr->iDb = pTab->iDb;
|
||||
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
|
||||
pExpr->iColumn = j==pTab->iPKey ? -1 : j;
|
||||
pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have not already resolved this *.* expression, then maybe
|
||||
* it is a new.* or old.* trigger argument reference */
|
||||
if( cnt == 0 && pParse->trigStack != 0 ){
|
||||
TriggerStack *pTriggerStack = pParse->trigStack;
|
||||
int t = 0;
|
||||
if( pTriggerStack->newIdx != -1 && sqliteStrICmp("new", zLeft) == 0 ){
|
||||
pExpr->iTable = pTriggerStack->newIdx;
|
||||
assert( pTriggerStack->pTab );
|
||||
pExpr->iDb = pTriggerStack->pTab->iDb;
|
||||
cntTab++;
|
||||
t = 1;
|
||||
}
|
||||
if( pTriggerStack->oldIdx != -1 && sqliteStrICmp("old", zLeft) == 0 ){
|
||||
pExpr->iTable = pTriggerStack->oldIdx;
|
||||
assert( pTriggerStack->pTab );
|
||||
pExpr->iDb = pTriggerStack->pTab->iDb;
|
||||
cntTab++;
|
||||
t = 1;
|
||||
}
|
||||
|
||||
if( t ){
|
||||
int j;
|
||||
Table *pTab = pTriggerStack->pTab;
|
||||
for(j=0; j < pTab->nCol; j++) {
|
||||
if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
|
||||
cnt++;
|
||||
pExpr->iColumn = j==pTab->iPKey ? -1 : j;
|
||||
pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( cnt==0 && cntTab==1 && sqliteIsRowid(zRight) ){
|
||||
cnt = 1;
|
||||
pExpr->iColumn = -1;
|
||||
pExpr->dataType = SQLITE_SO_NUM;
|
||||
}
|
||||
sqliteFree(zDb);
|
||||
sqliteFree(zLeft);
|
||||
sqliteFree(zRight);
|
||||
if( cnt==0 ){
|
||||
sqliteErrorMsg(pParse, "no such column: %T.%T",
|
||||
&pLeft->token, &pRight->token);
|
||||
return 1;
|
||||
}else if( cnt>1 ){
|
||||
sqliteErrorMsg(pParse, "ambiguous column name: %T.%T",
|
||||
&pLeft->token, &pRight->token);
|
||||
return 1;
|
||||
}
|
||||
sqliteExprDelete(pExpr->pLeft);
|
||||
pExpr->pLeft = 0;
|
||||
sqliteExprDelete(pExpr->pRight);
|
||||
pExpr->pRight = 0;
|
||||
pExpr->op = TK_COLUMN;
|
||||
sqliteAuthRead(pParse, pExpr, pTabList);
|
||||
break;
|
||||
}
|
||||
|
||||
case TK_IN: {
|
||||
Vdbe *v = sqliteGetVdbe(pParse);
|
||||
if( v==0 ) return 1;
|
||||
if( sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pLeft) ){
|
||||
if( sqliteExprResolveIds(pParse, pSrcList, pEList, pExpr->pLeft) ){
|
||||
return 1;
|
||||
}
|
||||
if( pExpr->pSelect ){
|
||||
|
@ -677,9 +720,10 @@ int sqliteExprResolveIds(
|
|||
case TK_FLOAT:
|
||||
case TK_INTEGER:
|
||||
case TK_STRING: {
|
||||
int addr = sqliteVdbeAddOp(v, OP_SetInsert, iSet, 0);
|
||||
int addr;
|
||||
assert( pE2->token.z );
|
||||
sqliteVdbeChangeP3(v, addr, pE2->token.z, pE2->token.n);
|
||||
addr = sqliteVdbeOp3(v, OP_SetInsert, iSet, 0,
|
||||
pE2->token.z, pE2->token.n);
|
||||
sqliteVdbeDequoteP3(v, addr);
|
||||
break;
|
||||
}
|
||||
|
@ -709,11 +753,11 @@ int sqliteExprResolveIds(
|
|||
/* For all else, just recursively walk the tree */
|
||||
default: {
|
||||
if( pExpr->pLeft
|
||||
&& sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pLeft) ){
|
||||
&& sqliteExprResolveIds(pParse, pSrcList, pEList, pExpr->pLeft) ){
|
||||
return 1;
|
||||
}
|
||||
if( pExpr->pRight
|
||||
&& sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pRight) ){
|
||||
&& sqliteExprResolveIds(pParse, pSrcList, pEList, pExpr->pRight) ){
|
||||
return 1;
|
||||
}
|
||||
if( pExpr->pList ){
|
||||
|
@ -721,7 +765,7 @@ int sqliteExprResolveIds(
|
|||
ExprList *pList = pExpr->pList;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
Expr *pArg = pList->a[i].pExpr;
|
||||
if( sqliteExprResolveIds(pParse, pTabList, pEList, pArg) ){
|
||||
if( sqliteExprResolveIds(pParse, pSrcList, pEList, pArg) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -782,7 +826,6 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
|
|||
case TK_FUNCTION: {
|
||||
int n = pExpr->pList ? pExpr->pList->nExpr : 0; /* Number of arguments */
|
||||
int no_such_func = 0; /* True if no such function exists */
|
||||
int is_type_of = 0; /* True if is the special TypeOf() function */
|
||||
int wrong_num_args = 0; /* True if wrong number of arguments */
|
||||
int is_agg = 0; /* True if is an aggregate function */
|
||||
int i;
|
||||
|
@ -795,11 +838,7 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
|
|||
if( pDef==0 ){
|
||||
pDef = sqliteFindFunction(pParse->db, zId, nId, -1, 0);
|
||||
if( pDef==0 ){
|
||||
if( n==1 && nId==6 && sqliteStrNICmp(zId, "typeof", 6)==0 ){
|
||||
is_type_of = 1;
|
||||
}else {
|
||||
no_such_func = 1;
|
||||
}
|
||||
no_such_func = 1;
|
||||
}else{
|
||||
wrong_num_args = 1;
|
||||
}
|
||||
|
@ -807,38 +846,27 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
|
|||
is_agg = pDef->xFunc==0;
|
||||
}
|
||||
if( is_agg && !allowAgg ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "misuse of aggregate function ", -1,
|
||||
zId, nId, "()", 2, 0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "misuse of aggregate function %.*s()", nId, zId);
|
||||
nErr++;
|
||||
is_agg = 0;
|
||||
}else if( no_such_func ){
|
||||
sqliteSetNString(&pParse->zErrMsg, "no such function: ", -1, zId,nId,0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "no such function: %.*s", nId, zId);
|
||||
nErr++;
|
||||
}else if( wrong_num_args ){
|
||||
sqliteSetNString(&pParse->zErrMsg,
|
||||
"wrong number of arguments to function ", -1, zId, nId, "()", 2, 0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse,"wrong number of arguments to function %.*s()",
|
||||
nId, zId);
|
||||
nErr++;
|
||||
}
|
||||
if( is_agg ) pExpr->op = TK_AGG_FUNCTION;
|
||||
if( is_agg && pIsAgg ) *pIsAgg = 1;
|
||||
if( is_agg ){
|
||||
pExpr->op = TK_AGG_FUNCTION;
|
||||
if( pIsAgg ) *pIsAgg = 1;
|
||||
}
|
||||
for(i=0; nErr==0 && i<n; i++){
|
||||
nErr = sqliteExprCheck(pParse, pExpr->pList->a[i].pExpr,
|
||||
allowAgg && !is_agg, pIsAgg);
|
||||
}
|
||||
if( pDef==0 ){
|
||||
if( is_type_of ){
|
||||
pExpr->op = TK_STRING;
|
||||
if( sqliteExprType(pExpr->pList->a[0].pExpr)==SQLITE_SO_NUM ){
|
||||
pExpr->token.z = "numeric";
|
||||
pExpr->token.n = 7;
|
||||
}else{
|
||||
pExpr->token.z = "text";
|
||||
pExpr->token.n = 4;
|
||||
}
|
||||
}
|
||||
/* Already reported an error */
|
||||
}else if( pDef->dataType>=0 ){
|
||||
if( pDef->dataType<n ){
|
||||
pExpr->dataType =
|
||||
|
@ -973,8 +1001,6 @@ int sqliteExprType(Expr *p){
|
|||
return SQLITE_SO_NUM;
|
||||
}
|
||||
|
||||
/* Run */
|
||||
|
||||
/*
|
||||
** Generate code into the current Vdbe to evaluate the given
|
||||
** expression and leave the result on the top of stack.
|
||||
|
@ -1019,26 +1045,17 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
|||
}
|
||||
break;
|
||||
}
|
||||
case TK_STRING:
|
||||
case TK_FLOAT:
|
||||
case TK_INTEGER: {
|
||||
if( !sqliteFitsIn32Bits(pExpr->token.z) ){
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
}else{
|
||||
if( pExpr->op==TK_INTEGER && sqliteFitsIn32Bits(pExpr->token.z) ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, atoi(pExpr->token.z), 0);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
}
|
||||
sqliteVdbeChangeP3(v, -1, pExpr->token.z, pExpr->token.n);
|
||||
break;
|
||||
}
|
||||
case TK_FLOAT: {
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
assert( pExpr->token.z );
|
||||
sqliteVdbeChangeP3(v, -1, pExpr->token.z, pExpr->token.n);
|
||||
break;
|
||||
}
|
||||
case TK_STRING: {
|
||||
int addr = sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
assert( pExpr->token.z );
|
||||
sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n);
|
||||
sqliteVdbeDequoteP3(v, addr);
|
||||
sqliteVdbeDequoteP3(v, -1);
|
||||
break;
|
||||
}
|
||||
case TK_NULL: {
|
||||
|
@ -1087,23 +1104,6 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
|||
sqliteVdbeAddOp(v, OP_Concat, 2, 0);
|
||||
break;
|
||||
}
|
||||
case TK_UPLUS: {
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
if( pLeft && pLeft->op==TK_INTEGER ){
|
||||
if( sqliteFitsIn32Bits(pLeft->token.z) ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, atoi(pLeft->token.z), 0);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
}
|
||||
sqliteVdbeChangeP3(v, -1, pLeft->token.z, pLeft->token.n);
|
||||
}else if( pLeft && pLeft->op==TK_FLOAT ){
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pLeft->token.z, pLeft->token.n);
|
||||
}else{
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TK_UMINUS: {
|
||||
assert( pExpr->pLeft );
|
||||
if( pExpr->pLeft->op==TK_FLOAT || pExpr->pLeft->op==TK_INTEGER ){
|
||||
|
@ -1144,7 +1144,6 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
|||
case TK_GLOB:
|
||||
case TK_LIKE:
|
||||
case TK_FUNCTION: {
|
||||
int i;
|
||||
ExprList *pList = pExpr->pList;
|
||||
int nExpr = pList ? pList->nExpr : 0;
|
||||
FuncDef *pDef;
|
||||
|
@ -1153,11 +1152,8 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
|||
getFunctionName(pExpr, &zId, &nId);
|
||||
pDef = sqliteFindFunction(pParse->db, zId, nId, nExpr, 0);
|
||||
assert( pDef!=0 );
|
||||
for(i=0; i<nExpr; i++){
|
||||
sqliteExprCode(pParse, pList->a[i].pExpr);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Function, nExpr, 0);
|
||||
sqliteVdbeChangeP3(v, -1, (char*)pDef, P3_POINTER);
|
||||
nExpr = sqliteExprCodeExprList(pParse, pList, pDef->includeTypes);
|
||||
sqliteVdbeOp3(v, OP_Function, nExpr, 0, (char*)pDef, P3_POINTER);
|
||||
break;
|
||||
}
|
||||
case TK_SELECT: {
|
||||
|
@ -1170,7 +1166,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
|||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
addr = sqliteVdbeCurrentAddr(v);
|
||||
sqliteVdbeAddOp(v, OP_NotNull, -1, addr+4);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 2, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr+6);
|
||||
if( pExpr->pSelect ){
|
||||
|
@ -1192,6 +1188,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
|||
sqliteVdbeAddOp(v, OP_And, 0, 0);
|
||||
break;
|
||||
}
|
||||
case TK_UPLUS:
|
||||
case TK_AS: {
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
break;
|
||||
|
@ -1246,21 +1243,49 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
|||
if( pExpr->iColumn == OE_Rollback ||
|
||||
pExpr->iColumn == OE_Abort ||
|
||||
pExpr->iColumn == OE_Fail ){
|
||||
char * msg = sqliteStrNDup(pExpr->token.z, pExpr->token.n);
|
||||
sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn);
|
||||
sqliteDequote(msg);
|
||||
sqliteVdbeChangeP3(v, -1, msg, 0);
|
||||
sqliteFree(msg);
|
||||
sqliteVdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn,
|
||||
pExpr->token.z, pExpr->token.n);
|
||||
sqliteVdbeDequoteP3(v, -1);
|
||||
} else {
|
||||
assert( pExpr->iColumn == OE_Ignore );
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, pParse->trigStack->ignoreJump);
|
||||
sqliteVdbeChangeP3(v, -1, "(IGNORE jump)", 0);
|
||||
sqliteVdbeOp3(v, OP_Goto, 0, pParse->trigStack->ignoreJump,
|
||||
"(IGNORE jump)", 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that pushes the value of every element of the given
|
||||
** expression list onto the stack. If the includeTypes flag is true,
|
||||
** then also push a string that is the datatype of each element onto
|
||||
** the stack after the value.
|
||||
**
|
||||
** Return the number of elements pushed onto the stack.
|
||||
*/
|
||||
int sqliteExprCodeExprList(
|
||||
Parse *pParse, /* Parsing context */
|
||||
ExprList *pList, /* The expression list to be coded */
|
||||
int includeTypes /* TRUE to put datatypes on the stack too */
|
||||
){
|
||||
struct ExprList_item *pItem;
|
||||
int i, n;
|
||||
Vdbe *v;
|
||||
if( pList==0 ) return 0;
|
||||
v = sqliteGetVdbe(pParse);
|
||||
n = pList->nExpr;
|
||||
for(pItem=pList->a, i=0; i<n; i++, pItem++){
|
||||
sqliteExprCode(pParse, pItem->pExpr);
|
||||
if( includeTypes ){
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0,
|
||||
sqliteExprType(pItem->pExpr)==SQLITE_SO_NUM ? "numeric" : "text",
|
||||
P3_STATIC);
|
||||
}
|
||||
}
|
||||
return includeTypes ? n*2 : n;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code for a boolean expression such that a jump is made
|
||||
** to the label "dest" if the expression is true but execution
|
||||
|
|
|
@ -28,35 +28,36 @@
|
|||
/*
|
||||
** Implementation of the non-aggregate min() and max() functions
|
||||
*/
|
||||
static void minFunc(sqlite_func *context, int argc, const char **argv){
|
||||
static void minmaxFunc(sqlite_func *context, int argc, const char **argv){
|
||||
const char *zBest;
|
||||
int i;
|
||||
int (*xCompare)(const char*, const char*);
|
||||
int mask; /* 0 for min() or 0xffffffff for max() */
|
||||
|
||||
if( argc==0 ) return;
|
||||
mask = (int)sqlite_user_data(context);
|
||||
zBest = argv[0];
|
||||
if( zBest==0 ) return;
|
||||
for(i=1; i<argc; i++){
|
||||
if( argv[1][0]=='n' ){
|
||||
xCompare = sqliteCompare;
|
||||
}else{
|
||||
xCompare = strcmp;
|
||||
}
|
||||
for(i=2; i<argc; i+=2){
|
||||
if( argv[i]==0 ) return;
|
||||
if( sqliteCompare(argv[i], zBest)<0 ){
|
||||
if( (xCompare(argv[i], zBest)^mask)<0 ){
|
||||
zBest = argv[i];
|
||||
}
|
||||
}
|
||||
sqlite_set_result_string(context, zBest, -1);
|
||||
}
|
||||
static void maxFunc(sqlite_func *context, int argc, const char **argv){
|
||||
const char *zBest;
|
||||
int i;
|
||||
|
||||
if( argc==0 ) return;
|
||||
zBest = argv[0];
|
||||
if( zBest==0 ) return;
|
||||
for(i=1; i<argc; i++){
|
||||
if( argv[i]==0 ) return;
|
||||
if( sqliteCompare(argv[i], zBest)>0 ){
|
||||
zBest = argv[i];
|
||||
}
|
||||
}
|
||||
sqlite_set_result_string(context, zBest, -1);
|
||||
/*
|
||||
** Return the type of the argument.
|
||||
*/
|
||||
static void typeofFunc(sqlite_func *context, int argc, const char **argv){
|
||||
assert( argc==2 );
|
||||
sqlite_set_result_string(context, argv[1], -1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -147,7 +148,7 @@ static void roundFunc(sqlite_func *context, int argc, const char **argv){
|
|||
n = argc==2 ? atoi(argv[1]) : 0;
|
||||
if( n>30 ) n = 30;
|
||||
if( n<0 ) n = 0;
|
||||
r = sqliteAtoF(argv[0]);
|
||||
r = sqliteAtoF(argv[0], 0);
|
||||
sprintf(zBuf,"%.*f",n,r);
|
||||
sqlite_set_result_string(context, zBuf, -1);
|
||||
}
|
||||
|
@ -178,8 +179,8 @@ static void lowerFunc(sqlite_func *context, int argc, const char **argv){
|
|||
|
||||
/*
|
||||
** Implementation of the IFNULL(), NVL(), and COALESCE() functions.
|
||||
** All three do the same thing. They return the first argument
|
||||
** non-NULL argument.
|
||||
** All three do the same thing. They return the first non-NULL
|
||||
** argument.
|
||||
*/
|
||||
static void ifnullFunc(sqlite_func *context, int argc, const char **argv){
|
||||
int i;
|
||||
|
@ -195,7 +196,9 @@ static void ifnullFunc(sqlite_func *context, int argc, const char **argv){
|
|||
** Implementation of random(). Return a random integer.
|
||||
*/
|
||||
static void randomFunc(sqlite_func *context, int argc, const char **argv){
|
||||
sqlite_set_result_int(context, sqliteRandomInteger());
|
||||
int r;
|
||||
sqliteRandomness(sizeof(r), &r);
|
||||
sqlite_set_result_int(context, r);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -207,6 +210,25 @@ static void last_insert_rowid(sqlite_func *context, int arg, const char **argv){
|
|||
sqlite_set_result_int(context, sqlite_last_insert_rowid(db));
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the change_count() SQL function. The return
|
||||
** value is the same as the sqlite_changes() API function.
|
||||
*/
|
||||
static void change_count(sqlite_func *context, int arg, const char **argv){
|
||||
sqlite *db = sqlite_user_data(context);
|
||||
sqlite_set_result_int(context, sqlite_changes(db));
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the last_statement_change_count() SQL function. The
|
||||
** return value is the same as the sqlite_last_statement_changes() API function.
|
||||
*/
|
||||
static void last_statement_change_count(sqlite_func *context, int arg,
|
||||
const char **argv){
|
||||
sqlite *db = sqlite_user_data(context);
|
||||
sqlite_set_result_int(context, sqlite_last_statement_changes(db));
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the like() SQL function. This function implements
|
||||
** the build-in LIKE operator. The first argument to the function is the
|
||||
|
@ -341,13 +363,13 @@ static void soundexFunc(sqlite_func *context, int argc, const char **argv){
|
|||
** generating test data.
|
||||
*/
|
||||
static void randStr(sqlite_func *context, int argc, const char **argv){
|
||||
static const char zSrc[] =
|
||||
static const unsigned char zSrc[] =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789"
|
||||
".-!,:*^+=_|?/<> ";
|
||||
int iMin, iMax, n, r, i;
|
||||
char zBuf[1000];
|
||||
unsigned char zBuf[1000];
|
||||
if( argc>=1 ){
|
||||
iMin = atoi(argv[0]);
|
||||
if( iMin<0 ) iMin = 0;
|
||||
|
@ -358,19 +380,20 @@ static void randStr(sqlite_func *context, int argc, const char **argv){
|
|||
if( argc>=2 ){
|
||||
iMax = atoi(argv[1]);
|
||||
if( iMax<iMin ) iMax = iMin;
|
||||
if( iMax>=sizeof(zBuf) ) iMax = sizeof(zBuf);
|
||||
if( iMax>=sizeof(zBuf) ) iMax = sizeof(zBuf)-1;
|
||||
}else{
|
||||
iMax = 50;
|
||||
}
|
||||
n = iMin;
|
||||
if( iMax>iMin ){
|
||||
r = sqliteRandomInteger() & 0x7fffffff;
|
||||
sqliteRandomness(sizeof(r), &r);
|
||||
r &= 0x7fffffff;
|
||||
n += r%(iMax + 1 - iMin);
|
||||
}
|
||||
r = 0;
|
||||
assert( n<sizeof(zBuf) );
|
||||
sqliteRandomness(n, zBuf);
|
||||
for(i=0; i<n; i++){
|
||||
r = (r + sqliteRandomByte())% (sizeof(zSrc)-1);
|
||||
zBuf[i] = zSrc[r];
|
||||
zBuf[i] = zSrc[zBuf[i]%(sizeof(zSrc)-1)];
|
||||
}
|
||||
zBuf[n] = 0;
|
||||
sqlite_set_result_string(context, zBuf, n);
|
||||
|
@ -395,7 +418,7 @@ static void sumStep(sqlite_func *context, int argc, const char **argv){
|
|||
if( argc<1 ) return;
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
if( p && argv[0] ){
|
||||
p->sum += sqliteAtoF(argv[0]);
|
||||
p->sum += sqliteAtoF(argv[0], 0);
|
||||
p->cnt++;
|
||||
}
|
||||
}
|
||||
|
@ -433,7 +456,7 @@ static void stdDevStep(sqlite_func *context, int argc, const char **argv){
|
|||
if( argc<1 ) return;
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
if( p && argv[0] ){
|
||||
x = sqliteAtoF(argv[0]);
|
||||
x = sqliteAtoF(argv[0], 0);
|
||||
p->sum += x;
|
||||
p->sum2 += x*x;
|
||||
p->cnt++;
|
||||
|
@ -488,39 +511,32 @@ struct MinMaxCtx {
|
|||
/*
|
||||
** Routines to implement min() and max() aggregate functions.
|
||||
*/
|
||||
static void minStep(sqlite_func *context, int argc, const char **argv){
|
||||
static void minmaxStep(sqlite_func *context, int argc, const char **argv){
|
||||
MinMaxCtx *p;
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
if( p==0 || argc<1 || argv[0]==0 ) return;
|
||||
if( p->z==0 || sqliteCompare(argv[0],p->z)<0 ){
|
||||
int len;
|
||||
if( p->z && p->z!=p->zBuf ){
|
||||
sqliteFree(p->z);
|
||||
}
|
||||
len = strlen(argv[0]);
|
||||
if( len < sizeof(p->zBuf) ){
|
||||
p->z = p->zBuf;
|
||||
}else{
|
||||
p->z = sqliteMalloc( len+1 );
|
||||
if( p->z==0 ) return;
|
||||
}
|
||||
strcpy(p->z, argv[0]);
|
||||
int (*xCompare)(const char*, const char*);
|
||||
int mask; /* 0 for min() or 0xffffffff for max() */
|
||||
|
||||
assert( argc==2 );
|
||||
if( argv[1][0]=='n' ){
|
||||
xCompare = sqliteCompare;
|
||||
}else{
|
||||
xCompare = strcmp;
|
||||
}
|
||||
}
|
||||
static void maxStep(sqlite_func *context, int argc, const char **argv){
|
||||
MinMaxCtx *p;
|
||||
mask = (int)sqlite_user_data(context);
|
||||
p = sqlite_aggregate_context(context, sizeof(*p));
|
||||
if( p==0 || argc<1 || argv[0]==0 ) return;
|
||||
if( p->z==0 || sqliteCompare(argv[0],p->z)>0 ){
|
||||
if( p->z==0 || (xCompare(argv[0],p->z)^mask)<0 ){
|
||||
int len;
|
||||
if( p->z && p->z!=p->zBuf ){
|
||||
if( !p->zBuf[0] ){
|
||||
sqliteFree(p->z);
|
||||
}
|
||||
len = strlen(argv[0]);
|
||||
if( len < sizeof(p->zBuf) ){
|
||||
p->z = p->zBuf;
|
||||
if( len < sizeof(p->zBuf)-1 ){
|
||||
p->z = &p->zBuf[1];
|
||||
p->zBuf[0] = 1;
|
||||
}else{
|
||||
p->z = sqliteMalloc( len+1 );
|
||||
p->zBuf[0] = 0;
|
||||
if( p->z==0 ) return;
|
||||
}
|
||||
strcpy(p->z, argv[0]);
|
||||
|
@ -532,7 +548,7 @@ static void minMaxFinalize(sqlite_func *context){
|
|||
if( p && p->z ){
|
||||
sqlite_set_result_string(context, p->z, strlen(p->z));
|
||||
}
|
||||
if( p && p->z && p->z!=p->zBuf ){
|
||||
if( p && !p->zBuf[0] ){
|
||||
sqliteFree(p->z);
|
||||
}
|
||||
}
|
||||
|
@ -545,71 +561,86 @@ static void minMaxFinalize(sqlite_func *context){
|
|||
void sqliteRegisterBuiltinFunctions(sqlite *db){
|
||||
static struct {
|
||||
char *zName;
|
||||
int nArg;
|
||||
int dataType;
|
||||
signed char nArg;
|
||||
signed char dataType;
|
||||
u8 argType; /* 0: none. 1: db 2: (-1) */
|
||||
void (*xFunc)(sqlite_func*,int,const char**);
|
||||
} aFuncs[] = {
|
||||
{ "min", -1, SQLITE_ARGS, minFunc },
|
||||
{ "min", 0, 0, 0 },
|
||||
{ "max", -1, SQLITE_ARGS, maxFunc },
|
||||
{ "max", 0, 0, 0 },
|
||||
{ "length", 1, SQLITE_NUMERIC, lengthFunc },
|
||||
{ "substr", 3, SQLITE_TEXT, substrFunc },
|
||||
{ "abs", 1, SQLITE_NUMERIC, absFunc },
|
||||
{ "round", 1, SQLITE_NUMERIC, roundFunc },
|
||||
{ "round", 2, SQLITE_NUMERIC, roundFunc },
|
||||
{ "upper", 1, SQLITE_TEXT, upperFunc },
|
||||
{ "lower", 1, SQLITE_TEXT, lowerFunc },
|
||||
{ "coalesce", -1, SQLITE_ARGS, ifnullFunc },
|
||||
{ "coalesce", 0, 0, 0 },
|
||||
{ "coalesce", 1, 0, 0 },
|
||||
{ "ifnull", 2, SQLITE_ARGS, ifnullFunc },
|
||||
{ "random", -1, SQLITE_NUMERIC, randomFunc },
|
||||
{ "like", 2, SQLITE_NUMERIC, likeFunc },
|
||||
{ "glob", 2, SQLITE_NUMERIC, globFunc },
|
||||
{ "nullif", 2, SQLITE_ARGS, nullifFunc },
|
||||
{ "sqlite_version",0,SQLITE_TEXT, versionFunc},
|
||||
{ "quote", 1, SQLITE_ARGS, quoteFunc },
|
||||
{ "min", -1, SQLITE_ARGS, 0, minmaxFunc },
|
||||
{ "min", 0, 0, 0, 0 },
|
||||
{ "max", -1, SQLITE_ARGS, 2, minmaxFunc },
|
||||
{ "max", 0, 0, 2, 0 },
|
||||
{ "typeof", 1, SQLITE_TEXT, 0, typeofFunc },
|
||||
{ "length", 1, SQLITE_NUMERIC, 0, lengthFunc },
|
||||
{ "substr", 3, SQLITE_TEXT, 0, substrFunc },
|
||||
{ "abs", 1, SQLITE_NUMERIC, 0, absFunc },
|
||||
{ "round", 1, SQLITE_NUMERIC, 0, roundFunc },
|
||||
{ "round", 2, SQLITE_NUMERIC, 0, roundFunc },
|
||||
{ "upper", 1, SQLITE_TEXT, 0, upperFunc },
|
||||
{ "lower", 1, SQLITE_TEXT, 0, lowerFunc },
|
||||
{ "coalesce", -1, SQLITE_ARGS, 0, ifnullFunc },
|
||||
{ "coalesce", 0, 0, 0, 0 },
|
||||
{ "coalesce", 1, 0, 0, 0 },
|
||||
{ "ifnull", 2, SQLITE_ARGS, 0, ifnullFunc },
|
||||
{ "random", -1, SQLITE_NUMERIC, 0, randomFunc },
|
||||
{ "like", 2, SQLITE_NUMERIC, 0, likeFunc },
|
||||
{ "glob", 2, SQLITE_NUMERIC, 0, globFunc },
|
||||
{ "nullif", 2, SQLITE_ARGS, 0, nullifFunc },
|
||||
{ "sqlite_version",0,SQLITE_TEXT, 0, versionFunc},
|
||||
{ "quote", 1, SQLITE_ARGS, 0, quoteFunc },
|
||||
{ "last_insert_rowid", 0, SQLITE_NUMERIC, 1, last_insert_rowid },
|
||||
{ "change_count", 0, SQLITE_NUMERIC, 1, change_count },
|
||||
{ "last_statement_change_count",
|
||||
0, SQLITE_NUMERIC, 1, last_statement_change_count },
|
||||
#ifdef SQLITE_SOUNDEX
|
||||
{ "soundex", 1, SQLITE_TEXT, soundexFunc},
|
||||
{ "soundex", 1, SQLITE_TEXT, 0, soundexFunc},
|
||||
#endif
|
||||
#ifdef SQLITE_TEST
|
||||
{ "randstr", 2, SQLITE_TEXT, randStr },
|
||||
{ "randstr", 2, SQLITE_TEXT, 0, randStr },
|
||||
#endif
|
||||
};
|
||||
static struct {
|
||||
char *zName;
|
||||
int nArg;
|
||||
int dataType;
|
||||
signed char nArg;
|
||||
signed char dataType;
|
||||
u8 argType;
|
||||
void (*xStep)(sqlite_func*,int,const char**);
|
||||
void (*xFinalize)(sqlite_func*);
|
||||
} aAggs[] = {
|
||||
{ "min", 1, 0, minStep, minMaxFinalize },
|
||||
{ "max", 1, 0, maxStep, minMaxFinalize },
|
||||
{ "sum", 1, SQLITE_NUMERIC, sumStep, sumFinalize },
|
||||
{ "avg", 1, SQLITE_NUMERIC, sumStep, avgFinalize },
|
||||
{ "count", 0, SQLITE_NUMERIC, countStep, countFinalize },
|
||||
{ "count", 1, SQLITE_NUMERIC, countStep, countFinalize },
|
||||
{ "min", 1, 0, 0, minmaxStep, minMaxFinalize },
|
||||
{ "max", 1, 0, 2, minmaxStep, minMaxFinalize },
|
||||
{ "sum", 1, SQLITE_NUMERIC, 0, sumStep, sumFinalize },
|
||||
{ "avg", 1, SQLITE_NUMERIC, 0, sumStep, avgFinalize },
|
||||
{ "count", 0, SQLITE_NUMERIC, 0, countStep, countFinalize },
|
||||
{ "count", 1, SQLITE_NUMERIC, 0, countStep, countFinalize },
|
||||
#if 0
|
||||
{ "stddev", 1, SQLITE_NUMERIC, stdDevStep, stdDevFinalize },
|
||||
{ "stddev", 1, SQLITE_NUMERIC, 0, stdDevStep, stdDevFinalize },
|
||||
#endif
|
||||
};
|
||||
static const char *azTypeFuncs[] = { "min", "max", "typeof" };
|
||||
int i;
|
||||
|
||||
for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
|
||||
void *pArg = aFuncs[i].argType==2 ? (void*)(-1) : db;
|
||||
sqlite_create_function(db, aFuncs[i].zName,
|
||||
aFuncs[i].nArg, aFuncs[i].xFunc, 0);
|
||||
aFuncs[i].nArg, aFuncs[i].xFunc, pArg);
|
||||
if( aFuncs[i].xFunc ){
|
||||
sqlite_function_type(db, aFuncs[i].zName, aFuncs[i].dataType);
|
||||
}
|
||||
}
|
||||
sqlite_create_function(db, "last_insert_rowid", 0,
|
||||
last_insert_rowid, db);
|
||||
sqlite_function_type(db, "last_insert_rowid", SQLITE_NUMERIC);
|
||||
for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
|
||||
void *pArg = aAggs[i].argType==2 ? (void*)(-1) : db;
|
||||
sqlite_create_aggregate(db, aAggs[i].zName,
|
||||
aAggs[i].nArg, aAggs[i].xStep, aAggs[i].xFinalize, 0);
|
||||
aAggs[i].nArg, aAggs[i].xStep, aAggs[i].xFinalize, pArg);
|
||||
sqlite_function_type(db, aAggs[i].zName, aAggs[i].dataType);
|
||||
}
|
||||
for(i=0; i<sizeof(azTypeFuncs)/sizeof(azTypeFuncs[0]); i++){
|
||||
int n = strlen(azTypeFuncs[i]);
|
||||
FuncDef *p = sqliteHashFind(&db->aFunc, azTypeFuncs[i], n);
|
||||
while( p ){
|
||||
p->includeTypes = 1;
|
||||
p = p->pNext;
|
||||
}
|
||||
}
|
||||
sqliteRegisterDateTimeFunctions(db);
|
||||
}
|
||||
|
|
|
@ -330,14 +330,7 @@ void sqliteInsert(
|
|||
/* Open tables and indices if there are no row triggers */
|
||||
if( !row_triggers_exist ){
|
||||
base = pParse->nTab;
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||
}
|
||||
idx = sqliteOpenTableAndIndices(pParse, pTab, base);
|
||||
pParse->nTab += idx;
|
||||
}
|
||||
|
||||
|
@ -391,8 +384,7 @@ void sqliteInsert(
|
|||
}
|
||||
}
|
||||
if( pColumn && j>=pColumn->nId ){
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zDflt, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[i].zDflt, P3_STATIC);
|
||||
}else if( useTempTable ){
|
||||
sqliteVdbeAddOp(v, OP_Column, srcTab, j);
|
||||
}else if( pSelect ){
|
||||
|
@ -416,14 +408,7 @@ void sqliteInsert(
|
|||
*/
|
||||
if( row_triggers_exist && !isView ){
|
||||
base = pParse->nTab;
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||
}
|
||||
idx = sqliteOpenTableAndIndices(pParse, pTab, base);
|
||||
pParse->nTab += idx;
|
||||
}
|
||||
|
||||
|
@ -472,8 +457,7 @@ void sqliteInsert(
|
|||
}
|
||||
}
|
||||
if( pColumn && j>=pColumn->nId ){
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zDflt, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[i].zDflt, P3_STATIC);
|
||||
}else if( useTempTable ){
|
||||
sqliteVdbeAddOp(v, OP_Column, srcTab, j);
|
||||
}else if( pSelect ){
|
||||
|
@ -535,14 +519,14 @@ void sqliteInsert(
|
|||
}
|
||||
}
|
||||
|
||||
sqliteVdbeAddOp(v, OP_SetCounts, 0, 0);
|
||||
sqliteEndWriteOperation(pParse);
|
||||
|
||||
/*
|
||||
** Return the number of rows inserted.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, "rows inserted", P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_ColumnName, 0, 1, "rows inserted", P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_MemLoad, iCntMem, 0);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 1, 0);
|
||||
}
|
||||
|
@ -698,8 +682,7 @@ void sqliteGenerateConstraintChecks(
|
|||
break;
|
||||
}
|
||||
case OE_Replace: {
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zDflt, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[i].zDflt, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Push, nCol-i, 0);
|
||||
break;
|
||||
}
|
||||
|
@ -741,8 +724,8 @@ void sqliteGenerateConstraintChecks(
|
|||
case OE_Rollback:
|
||||
case OE_Abort:
|
||||
case OE_Fail: {
|
||||
sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError);
|
||||
sqliteVdbeChangeP3(v, -1, "PRIMARY KEY must be unique", P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError,
|
||||
"PRIMARY KEY must be unique", P3_STATIC);
|
||||
break;
|
||||
}
|
||||
case OE_Replace: {
|
||||
|
@ -839,8 +822,7 @@ void sqliteGenerateConstraintChecks(
|
|||
}
|
||||
strcpy(&zErrMsg[n1],
|
||||
pIdx->nColumn>1 ? " are not unique" : " is not unique");
|
||||
sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError);
|
||||
sqliteVdbeChangeP3(v, -1, sqliteStrDup(zErrMsg), P3_DYNAMIC);
|
||||
sqliteVdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError, zErrMsg, 0);
|
||||
break;
|
||||
}
|
||||
case OE_Ignore: {
|
||||
|
@ -906,8 +888,32 @@ void sqliteCompleteInsertion(
|
|||
sqliteVdbeAddOp(v, OP_Dup, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_PutIntKey, base, pParse->trigStack?0:1);
|
||||
sqliteVdbeAddOp(v, OP_PutIntKey, base,
|
||||
(pParse->trigStack?0:OPFLAG_NCHANGE) |
|
||||
(isUpdate?0:OPFLAG_LASTROWID) | OPFLAG_CSCHANGE);
|
||||
if( isUpdate && recnoChng ){
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will open write cursors for a table and for all
|
||||
** indices of that table. The "base" parameter is the cursor number used
|
||||
** for the table. Indices are opened on subsequent cursors.
|
||||
**
|
||||
** Return the total number of cursors opened. This is always at least
|
||||
** 1 (for the main table) plus more for each cursor.
|
||||
*/
|
||||
int sqliteOpenTableAndIndices(Parse *pParse, Table *pTab, int base){
|
||||
int i;
|
||||
Index *pIdx;
|
||||
Vdbe *v = sqliteGetVdbe(pParse);
|
||||
assert( v!=0 );
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeOp3(v, OP_OpenWrite, base, pTab->tnum, pTab->zName, P3_STATIC);
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeOp3(v, OP_OpenWrite, i+base, pIdx->tnum, pIdx->zName, P3_STATIC);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
|
|
@ -33,8 +33,9 @@ typedef struct {
|
|||
** Fill the InitData structure with an error message that indicates
|
||||
** that the database is corrupt.
|
||||
*/
|
||||
static void corruptSchema(InitData *pData){
|
||||
sqliteSetString(pData->pzErrMsg, "malformed database schema", (char*)0);
|
||||
static void corruptSchema(InitData *pData, const char *zExtra){
|
||||
sqliteSetString(pData->pzErrMsg, "malformed database schema",
|
||||
zExtra!=0 && zExtra[0]!=0 ? " - " : (char*)0, zExtra, (char*)0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -54,36 +55,39 @@ static void corruptSchema(InitData *pData){
|
|||
static
|
||||
int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){
|
||||
InitData *pData = (InitData*)pInit;
|
||||
Parse sParse;
|
||||
int nErr = 0;
|
||||
|
||||
assert( argc==5 );
|
||||
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
|
||||
if( argv[0]==0 ){
|
||||
corruptSchema(pData);
|
||||
corruptSchema(pData, 0);
|
||||
return 1;
|
||||
}
|
||||
switch( argv[0][0] ){
|
||||
case 'v':
|
||||
case 'i':
|
||||
case 't': { /* CREATE TABLE, CREATE INDEX, or CREATE VIEW statements */
|
||||
sqlite *db = pData->db;
|
||||
if( argv[2]==0 || argv[4]==0 ){
|
||||
corruptSchema(pData);
|
||||
corruptSchema(pData, 0);
|
||||
return 1;
|
||||
}
|
||||
if( argv[3] && argv[3][0] ){
|
||||
/* Call the parser to process a CREATE TABLE, INDEX or VIEW.
|
||||
** But because sParse.initFlag is set to 1, no VDBE code is generated
|
||||
** But because db->init.busy is set to 1, no VDBE code is generated
|
||||
** or executed. All the parser does is build the internal data
|
||||
** structures that describe the table, index, or view.
|
||||
*/
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
sParse.db = pData->db;
|
||||
sParse.initFlag = 1;
|
||||
sParse.iDb = atoi(argv[4]);
|
||||
sParse.newTnum = atoi(argv[2]);
|
||||
sParse.useCallback = 1;
|
||||
sqliteRunParser(&sParse, argv[3], pData->pzErrMsg);
|
||||
char *zErr;
|
||||
assert( db->init.busy );
|
||||
db->init.iDb = atoi(argv[4]);
|
||||
assert( db->init.iDb>=0 && db->init.iDb<db->nDb );
|
||||
db->init.newTnum = atoi(argv[2]);
|
||||
if( sqlite_exec(db, argv[3], 0, 0, &zErr) ){
|
||||
corruptSchema(pData, zErr);
|
||||
sqlite_freemem(zErr);
|
||||
}
|
||||
db->init.iDb = 0;
|
||||
}else{
|
||||
/* If the SQL column is blank it means this is an index that
|
||||
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
|
||||
|
@ -95,8 +99,8 @@ int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){
|
|||
Index *pIndex;
|
||||
|
||||
iDb = atoi(argv[4]);
|
||||
assert( iDb>=0 && iDb<pData->db->nDb );
|
||||
pIndex = sqliteFindIndex(pData->db, argv[1], pData->db->aDb[iDb].zName);
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
pIndex = sqliteFindIndex(db, argv[1], db->aDb[iDb].zName);
|
||||
if( pIndex==0 || pIndex->tnum!=0 ){
|
||||
/* This can occur if there exists an index on a TEMP table which
|
||||
** has the same name as another index on a permanent index. Since
|
||||
|
@ -127,6 +131,9 @@ int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){
|
|||
** format version 1 or 2 to version 3. The correct operation of
|
||||
** this routine relys on the fact that no indices are used when
|
||||
** copying a table out to a temporary file.
|
||||
**
|
||||
** The change from version 2 to version 3 occurred between SQLite
|
||||
** version 2.5.6 and 2.6.0 on 2002-July-18.
|
||||
*/
|
||||
static
|
||||
int upgrade_3_callback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
|
@ -150,8 +157,8 @@ int upgrade_3_callback(void *pInit, int argc, char **argv, char **NotUsed){
|
|||
"DROP TABLE sqlite_x;",
|
||||
0, 0, &zErr, argv[0], argv[0], argv[0]);
|
||||
if( zErr ){
|
||||
sqliteSetString(pData->pzErrMsg, zErr, (char*)0);
|
||||
sqlite_freemem(zErr);
|
||||
if( *pData->pzErrMsg ) sqlite_freemem(*pData->pzErrMsg);
|
||||
*pData->pzErrMsg = zErr;
|
||||
}
|
||||
|
||||
/* If an error occurred in the SQL above, then the transaction will
|
||||
|
@ -185,7 +192,6 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
|
|||
char *azArg[6];
|
||||
char zDbNum[30];
|
||||
int meta[SQLITE_N_BTREE_META];
|
||||
Parse sParse;
|
||||
InitData initData;
|
||||
|
||||
/*
|
||||
|
@ -242,6 +248,7 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
|
|||
|
||||
/* Construct the schema tables: sqlite_master and sqlite_temp_master
|
||||
*/
|
||||
sqliteSafetyOff(db);
|
||||
azArg[0] = "table";
|
||||
azArg[1] = MASTER_NAME;
|
||||
azArg[2] = "2";
|
||||
|
@ -266,6 +273,7 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
|
|||
pTab->readOnly = 1;
|
||||
}
|
||||
}
|
||||
sqliteSafetyOn(db);
|
||||
|
||||
/* Create a cursor to hold the database open
|
||||
*/
|
||||
|
@ -292,6 +300,9 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
|
|||
if( size==0 ){ size = MAX_PAGES; }
|
||||
db->cache_size = size;
|
||||
db->safety_level = meta[4];
|
||||
if( meta[6]>0 && meta[6]<=2 && db->temp_store==0 ){
|
||||
db->temp_store = meta[6];
|
||||
}
|
||||
if( db->safety_level==0 ) db->safety_level = 2;
|
||||
|
||||
/*
|
||||
|
@ -327,31 +338,28 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
|
|||
|
||||
/* Read the schema information out of the schema tables
|
||||
*/
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
sParse.db = db;
|
||||
sParse.xCallback = sqliteInitCallback;
|
||||
sParse.pArg = (void*)&initData;
|
||||
sParse.initFlag = 1;
|
||||
sParse.useCallback = 1;
|
||||
assert( db->init.busy );
|
||||
sqliteSafetyOff(db);
|
||||
if( iDb==0 ){
|
||||
sqliteRunParser(&sParse,
|
||||
rc = sqlite_exec(db,
|
||||
db->file_format>=2 ? init_script : older_init_script,
|
||||
pzErrMsg);
|
||||
sqliteInitCallback, &initData, 0);
|
||||
}else{
|
||||
char *zSql = 0;
|
||||
sqliteSetString(&zSql,
|
||||
"SELECT type, name, rootpage, sql, ", zDbNum, " FROM \"",
|
||||
db->aDb[iDb].zName, "\".sqlite_master", (char*)0);
|
||||
sqliteRunParser(&sParse, zSql, pzErrMsg);
|
||||
rc = sqlite_exec(db, zSql, sqliteInitCallback, &initData, 0);
|
||||
sqliteFree(zSql);
|
||||
}
|
||||
sqliteSafetyOn(db);
|
||||
sqliteBtreeCloseCursor(curMain);
|
||||
if( sqlite_malloc_failed ){
|
||||
sqliteSetString(pzErrMsg, "out of memory", (char*)0);
|
||||
sParse.rc = SQLITE_NOMEM;
|
||||
rc = SQLITE_NOMEM;
|
||||
sqliteResetInternalSchema(db, 0);
|
||||
}
|
||||
if( sParse.rc==SQLITE_OK ){
|
||||
if( rc==SQLITE_OK ){
|
||||
DbSetProperty(db, iDb, DB_SchemaLoaded);
|
||||
if( iDb==0 ){
|
||||
DbSetProperty(db, 1, DB_SchemaLoaded);
|
||||
|
@ -359,7 +367,7 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
|
|||
}else{
|
||||
sqliteResetInternalSchema(db, iDb);
|
||||
}
|
||||
return sParse.rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -378,17 +386,58 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
|
|||
int sqliteInit(sqlite *db, char **pzErrMsg){
|
||||
int i, rc;
|
||||
|
||||
if( db->init.busy ) return SQLITE_OK;
|
||||
assert( (db->flags & SQLITE_Initialized)==0 );
|
||||
rc = SQLITE_OK;
|
||||
db->init.busy = 1;
|
||||
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
|
||||
if( DbHasProperty(db, i, DB_SchemaLoaded) ) continue;
|
||||
assert( i!=1 ); /* Should have been initialized together with 0 */
|
||||
rc = sqliteInitOne(db, i, pzErrMsg);
|
||||
if( rc ){
|
||||
sqliteResetInternalSchema(db, i);
|
||||
}
|
||||
}
|
||||
db->init.busy = 0;
|
||||
if( rc==SQLITE_OK ){
|
||||
db->flags |= SQLITE_Initialized;
|
||||
sqliteCommitInternalChanges(db);
|
||||
}else{
|
||||
}
|
||||
|
||||
/* If the database is in formats 1 or 2, then upgrade it to
|
||||
** version 3. This will reconstruct all indices. If the
|
||||
** upgrade fails for any reason (ex: out of disk space, database
|
||||
** is read only, interrupt received, etc.) then fail the init.
|
||||
*/
|
||||
if( rc==SQLITE_OK && db->file_format<3 ){
|
||||
char *zErr = 0;
|
||||
InitData initData;
|
||||
int meta[SQLITE_N_BTREE_META];
|
||||
|
||||
db->magic = SQLITE_MAGIC_OPEN;
|
||||
initData.db = db;
|
||||
initData.pzErrMsg = &zErr;
|
||||
db->file_format = 3;
|
||||
rc = sqlite_exec(db,
|
||||
"BEGIN; SELECT name FROM sqlite_master WHERE type='table';",
|
||||
upgrade_3_callback,
|
||||
&initData,
|
||||
&zErr);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqliteBtreeGetMeta(db->aDb[0].pBt, meta);
|
||||
meta[2] = 4;
|
||||
sqliteBtreeUpdateMeta(db->aDb[0].pBt, meta);
|
||||
sqlite_exec(db, "COMMIT", 0, 0, 0);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteSetString(pzErrMsg,
|
||||
"unable to upgrade database to the version 2.6 format",
|
||||
zErr ? ": " : 0, zErr, (char*)0);
|
||||
}
|
||||
sqlite_freemem(zErr);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
db->flags &= ~SQLITE_Initialized;
|
||||
}
|
||||
return rc;
|
||||
|
@ -432,6 +481,7 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
|
|||
db->magic = SQLITE_MAGIC_BUSY;
|
||||
db->nDb = 2;
|
||||
db->aDb = db->aDbStatic;
|
||||
/* db->flags |= SQLITE_ShortColNames; */
|
||||
sqliteHashInit(&db->aFunc, SQLITE_HASH_STRING, 1);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
sqliteHashInit(&db->aDb[i].tblHash, SQLITE_HASH_STRING, 0);
|
||||
|
@ -475,42 +525,6 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
|
|||
*pzErrMsg = 0;
|
||||
}
|
||||
|
||||
/* If the database is in formats 1 or 2, then upgrade it to
|
||||
** version 3. This will reconstruct all indices. If the
|
||||
** upgrade fails for any reason (ex: out of disk space, database
|
||||
** is read only, interrupt received, etc.) then refuse to open.
|
||||
*/
|
||||
if( rc==SQLITE_OK && db->file_format<3 ){
|
||||
char *zErr = 0;
|
||||
InitData initData;
|
||||
int meta[SQLITE_N_BTREE_META];
|
||||
|
||||
initData.db = db;
|
||||
initData.pzErrMsg = &zErr;
|
||||
db->file_format = 3;
|
||||
rc = sqlite_exec(db,
|
||||
"BEGIN; SELECT name FROM sqlite_master WHERE type='table';",
|
||||
upgrade_3_callback,
|
||||
&initData,
|
||||
&zErr);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqliteBtreeGetMeta(db->aDb[0].pBt, meta);
|
||||
meta[2] = 4;
|
||||
sqliteBtreeUpdateMeta(db->aDb[0].pBt, meta);
|
||||
sqlite_exec(db, "COMMIT", 0, 0, 0);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteSetString(pzErrMsg,
|
||||
"unable to upgrade database to the version 2.6 format",
|
||||
zErr ? ": " : 0, zErr, (char*)0);
|
||||
sqlite_freemem(zErr);
|
||||
sqliteStrRealloc(pzErrMsg);
|
||||
sqlite_close(db);
|
||||
return 0;
|
||||
}
|
||||
sqlite_freemem(zErr);
|
||||
}
|
||||
|
||||
/* Return a pointer to the newly opened database structure */
|
||||
return db;
|
||||
|
||||
|
@ -534,6 +548,16 @@ int sqlite_changes(sqlite *db){
|
|||
return db->nChange;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of changes produced by the last INSERT, UPDATE, or
|
||||
** DELETE statement to complete execution. The count does not include
|
||||
** changes due to SQL statements executed in trigger programs that were
|
||||
** triggered by that statement
|
||||
*/
|
||||
int sqlite_last_statement_changes(sqlite *db){
|
||||
return db->lsChange;
|
||||
}
|
||||
|
||||
/*
|
||||
** Close an existing SQLite database
|
||||
*/
|
||||
|
@ -547,13 +571,10 @@ void sqlite_close(sqlite *db){
|
|||
}
|
||||
db->magic = SQLITE_MAGIC_CLOSED;
|
||||
for(j=0; j<db->nDb; j++){
|
||||
if( db->aDb[j].pBt ){
|
||||
sqliteBtreeClose(db->aDb[j].pBt);
|
||||
db->aDb[j].pBt = 0;
|
||||
}
|
||||
if( j>=2 ){
|
||||
sqliteFree(db->aDb[j].zName);
|
||||
db->aDb[j].zName = 0;
|
||||
struct Db *pDb = &db->aDb[j];
|
||||
if( pDb->pBt ){
|
||||
sqliteBtreeClose(pDb->pBt);
|
||||
pDb->pBt = 0;
|
||||
}
|
||||
}
|
||||
sqliteResetInternalSchema(db, 0);
|
||||
|
@ -581,84 +602,8 @@ void sqliteRollbackAll(sqlite *db){
|
|||
db->aDb[i].inTrans = 0;
|
||||
}
|
||||
}
|
||||
sqliteRollbackInternalChanges(db);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine does the work of either sqlite_exec() or sqlite_compile().
|
||||
** It works like sqlite_exec() if pVm==NULL and it works like sqlite_compile()
|
||||
** otherwise.
|
||||
*/
|
||||
static int sqliteMain(
|
||||
sqlite *db, /* The database on which the SQL executes */
|
||||
const char *zSql, /* The SQL to be executed */
|
||||
sqlite_callback xCallback, /* Invoke this callback routine */
|
||||
void *pArg, /* First argument to xCallback() */
|
||||
const char **pzTail, /* OUT: Next statement after the first */
|
||||
sqlite_vm **ppVm, /* OUT: The virtual machine */
|
||||
char **pzErrMsg /* OUT: Write error messages here */
|
||||
){
|
||||
Parse sParse;
|
||||
|
||||
if( pzErrMsg ) *pzErrMsg = 0;
|
||||
if( sqliteSafetyOn(db) ) goto exec_misuse;
|
||||
if( (db->flags & SQLITE_Initialized)==0 ){
|
||||
int rc, cnt = 1;
|
||||
while( (rc = sqliteInit(db, pzErrMsg))==SQLITE_BUSY
|
||||
&& db->xBusyCallback && db->xBusyCallback(db->pBusyArg, "", cnt++)!=0 ){}
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteStrRealloc(pzErrMsg);
|
||||
sqliteSafetyOff(db);
|
||||
return rc;
|
||||
}
|
||||
if( pzErrMsg ){
|
||||
sqliteFree(*pzErrMsg);
|
||||
*pzErrMsg = 0;
|
||||
}
|
||||
}
|
||||
if( db->file_format<3 ){
|
||||
sqliteSafetyOff(db);
|
||||
sqliteSetString(pzErrMsg, "obsolete database file format", (char*)0);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if( db->pVdbe==0 ){ db->nChange = 0; }
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
sParse.db = db;
|
||||
sParse.xCallback = xCallback;
|
||||
sParse.pArg = pArg;
|
||||
sParse.useCallback = ppVm==0;
|
||||
if( db->xTrace ) db->xTrace(db->pTraceArg, zSql);
|
||||
sqliteRunParser(&sParse, zSql, pzErrMsg);
|
||||
if( sqlite_malloc_failed ){
|
||||
sqliteSetString(pzErrMsg, "out of memory", (char*)0);
|
||||
sParse.rc = SQLITE_NOMEM;
|
||||
sqliteRollbackAll(db);
|
||||
sqliteResetInternalSchema(db, 0);
|
||||
db->flags &= ~SQLITE_InTrans;
|
||||
}
|
||||
if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK;
|
||||
if( sParse.rc!=SQLITE_OK && pzErrMsg && *pzErrMsg==0 ){
|
||||
sqliteSetString(pzErrMsg, sqlite_error_string(sParse.rc), (char*)0);
|
||||
}
|
||||
sqliteStrRealloc(pzErrMsg);
|
||||
if( sParse.rc==SQLITE_SCHEMA ){
|
||||
sqliteResetInternalSchema(db, 0);
|
||||
}
|
||||
if( sParse.useCallback==0 ){
|
||||
assert( ppVm );
|
||||
*ppVm = (sqlite_vm*)sParse.pVdbe;
|
||||
if( pzTail ) *pzTail = sParse.zTail;
|
||||
}
|
||||
if( sqliteSafetyOff(db) ) goto exec_misuse;
|
||||
return sParse.rc;
|
||||
|
||||
exec_misuse:
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = 0;
|
||||
sqliteSetString(pzErrMsg, sqlite_error_string(SQLITE_MISUSE), (char*)0);
|
||||
sqliteStrRealloc(pzErrMsg);
|
||||
}
|
||||
return SQLITE_MISUSE;
|
||||
sqliteResetInternalSchema(db, 0);
|
||||
/* sqliteRollbackInternalChanges(db); */
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -678,9 +623,62 @@ int sqlite_exec(
|
|||
void *pArg, /* First argument to xCallback() */
|
||||
char **pzErrMsg /* Write error messages here */
|
||||
){
|
||||
return sqliteMain(db, zSql, xCallback, pArg, 0, 0, pzErrMsg);
|
||||
int rc = SQLITE_OK;
|
||||
const char *zLeftover;
|
||||
sqlite_vm *pVm;
|
||||
int nRetry = 0;
|
||||
int nChange = 0;
|
||||
int nCallback;
|
||||
|
||||
if( zSql==0 ) return SQLITE_OK;
|
||||
while( rc==SQLITE_OK && zSql[0] ){
|
||||
pVm = 0;
|
||||
rc = sqlite_compile(db, zSql, &zLeftover, &pVm, pzErrMsg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
assert( pVm==0 || sqlite_malloc_failed );
|
||||
return rc;
|
||||
}
|
||||
if( pVm==0 ){
|
||||
/* This happens if the zSql input contained only whitespace */
|
||||
break;
|
||||
}
|
||||
db->nChange += nChange;
|
||||
nCallback = 0;
|
||||
while(1){
|
||||
int nArg;
|
||||
char **azArg, **azCol;
|
||||
rc = sqlite_step(pVm, &nArg, (const char***)&azArg,(const char***)&azCol);
|
||||
if( rc==SQLITE_ROW ){
|
||||
if( xCallback!=0 && xCallback(pArg, nArg, azArg, azCol) ){
|
||||
sqlite_finalize(pVm, 0);
|
||||
return SQLITE_ABORT;
|
||||
}
|
||||
nCallback++;
|
||||
}else{
|
||||
if( rc==SQLITE_DONE && nCallback==0
|
||||
&& (db->flags & SQLITE_NullCallback)!=0 && xCallback!=0 ){
|
||||
xCallback(pArg, nArg, azArg, azCol);
|
||||
}
|
||||
rc = sqlite_finalize(pVm, pzErrMsg);
|
||||
if( rc==SQLITE_SCHEMA && nRetry<2 ){
|
||||
nRetry++;
|
||||
rc = SQLITE_OK;
|
||||
break;
|
||||
}
|
||||
if( db->pVdbe==0 ){
|
||||
nChange = db->nChange;
|
||||
}
|
||||
nRetry = 0;
|
||||
zSql = zLeftover;
|
||||
while( isspace(zSql[0]) ) zSql++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Compile a single statement of SQL into a virtual machine. Return one
|
||||
** of the SQLITE_ success/failure codes. Also write an error message into
|
||||
|
@ -693,7 +691,88 @@ int sqlite_compile(
|
|||
sqlite_vm **ppVm, /* OUT: The virtual machine */
|
||||
char **pzErrMsg /* OUT: Write error messages here */
|
||||
){
|
||||
return sqliteMain(db, zSql, 0, 0, pzTail, ppVm, pzErrMsg);
|
||||
Parse sParse;
|
||||
|
||||
if( pzErrMsg ) *pzErrMsg = 0;
|
||||
if( sqliteSafetyOn(db) ) goto exec_misuse;
|
||||
if( !db->init.busy ){
|
||||
if( (db->flags & SQLITE_Initialized)==0 ){
|
||||
int rc, cnt = 1;
|
||||
while( (rc = sqliteInit(db, pzErrMsg))==SQLITE_BUSY
|
||||
&& db->xBusyCallback
|
||||
&& db->xBusyCallback(db->pBusyArg, "", cnt++)!=0 ){}
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteStrRealloc(pzErrMsg);
|
||||
sqliteSafetyOff(db);
|
||||
return rc;
|
||||
}
|
||||
if( pzErrMsg ){
|
||||
sqliteFree(*pzErrMsg);
|
||||
*pzErrMsg = 0;
|
||||
}
|
||||
}
|
||||
if( db->file_format<3 ){
|
||||
sqliteSafetyOff(db);
|
||||
sqliteSetString(pzErrMsg, "obsolete database file format", (char*)0);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
assert( (db->flags & SQLITE_Initialized)!=0 || db->init.busy );
|
||||
if( db->pVdbe==0 ){ db->nChange = 0; }
|
||||
memset(&sParse, 0, sizeof(sParse));
|
||||
sParse.db = db;
|
||||
sqliteRunParser(&sParse, zSql, pzErrMsg);
|
||||
if( db->xTrace && !db->init.busy ){
|
||||
/* Trace only the statment that was compiled.
|
||||
** Make a copy of that part of the SQL string since zSQL is const
|
||||
** and we must pass a zero terminated string to the trace function
|
||||
** The copy is unnecessary if the tail pointer is pointing at the
|
||||
** beginnig or end of the SQL string.
|
||||
*/
|
||||
if( sParse.zTail && sParse.zTail!=zSql && *sParse.zTail ){
|
||||
char *tmpSql = sqliteStrNDup(zSql, sParse.zTail - zSql);
|
||||
if( tmpSql ){
|
||||
db->xTrace(db->pTraceArg, tmpSql);
|
||||
free(tmpSql);
|
||||
}else{
|
||||
/* If a memory error occurred during the copy,
|
||||
** trace entire SQL string and fall through to the
|
||||
** sqlite_malloc_failed test to report the error.
|
||||
*/
|
||||
db->xTrace(db->pTraceArg, zSql);
|
||||
}
|
||||
}else{
|
||||
db->xTrace(db->pTraceArg, zSql);
|
||||
}
|
||||
}
|
||||
if( sqlite_malloc_failed ){
|
||||
sqliteSetString(pzErrMsg, "out of memory", (char*)0);
|
||||
sParse.rc = SQLITE_NOMEM;
|
||||
sqliteRollbackAll(db);
|
||||
sqliteResetInternalSchema(db, 0);
|
||||
db->flags &= ~SQLITE_InTrans;
|
||||
}
|
||||
if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK;
|
||||
if( sParse.rc!=SQLITE_OK && pzErrMsg && *pzErrMsg==0 ){
|
||||
sqliteSetString(pzErrMsg, sqlite_error_string(sParse.rc), (char*)0);
|
||||
}
|
||||
sqliteStrRealloc(pzErrMsg);
|
||||
if( sParse.rc==SQLITE_SCHEMA ){
|
||||
sqliteResetInternalSchema(db, 0);
|
||||
}
|
||||
assert( ppVm );
|
||||
*ppVm = (sqlite_vm*)sParse.pVdbe;
|
||||
if( pzTail ) *pzTail = sParse.zTail;
|
||||
if( sqliteSafetyOff(db) ) goto exec_misuse;
|
||||
return sParse.rc;
|
||||
|
||||
exec_misuse:
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = 0;
|
||||
sqliteSetString(pzErrMsg, sqlite_error_string(SQLITE_MISUSE), (char*)0);
|
||||
sqliteStrRealloc(pzErrMsg);
|
||||
}
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
|
||||
|
||||
|
@ -729,7 +808,7 @@ int sqlite_reset(
|
|||
char **pzErrMsg /* OUT: Write error messages here */
|
||||
){
|
||||
int rc = sqliteVdbeReset((Vdbe*)pVm, pzErrMsg);
|
||||
sqliteVdbeMakeReady((Vdbe*)pVm, -1, 0, 0, 0);
|
||||
sqliteVdbeMakeReady((Vdbe*)pVm, -1, 0);
|
||||
sqliteStrRealloc(pzErrMsg);
|
||||
return rc;
|
||||
}
|
||||
|
@ -767,6 +846,7 @@ const char *sqlite_error_string(int rc){
|
|||
case SQLITE_AUTH: z = "authorization denied"; break;
|
||||
case SQLITE_FORMAT: z = "auxiliary database format error"; break;
|
||||
case SQLITE_RANGE: z = "bind index out of range"; break;
|
||||
case SQLITE_NOTADB: z = "file is encrypted or is not a database";break;
|
||||
default: z = "unknown error"; break;
|
||||
}
|
||||
return z;
|
||||
|
@ -784,22 +864,23 @@ static int sqliteDefaultBusyCallback(
|
|||
int count /* Number of times table has been busy */
|
||||
){
|
||||
#if SQLITE_MIN_SLEEP_MS==1
|
||||
int delay = 10;
|
||||
int prior_delay = 0;
|
||||
static const char delays[] =
|
||||
{ 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 50, 100};
|
||||
static const short int totals[] =
|
||||
{ 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228, 287};
|
||||
# define NDELAY (sizeof(delays)/sizeof(delays[0]))
|
||||
int timeout = (int)(long)Timeout;
|
||||
int i;
|
||||
int delay, prior;
|
||||
|
||||
for(i=1; i<count; i++){
|
||||
prior_delay += delay;
|
||||
delay = delay*2;
|
||||
if( delay>=1000 ){
|
||||
delay = 1000;
|
||||
prior_delay += 1000*(count - i - 1);
|
||||
break;
|
||||
}
|
||||
if( count <= NDELAY ){
|
||||
delay = delays[count-1];
|
||||
prior = totals[count-1];
|
||||
}else{
|
||||
delay = delays[NDELAY-1];
|
||||
prior = totals[NDELAY-1] + delay*(count-NDELAY-1);
|
||||
}
|
||||
if( prior_delay + delay > timeout ){
|
||||
delay = timeout - prior_delay;
|
||||
if( prior + delay > timeout ){
|
||||
delay = timeout - prior;
|
||||
if( delay<=0 ) return 0;
|
||||
}
|
||||
sqliteOsSleep(delay);
|
||||
|
@ -856,9 +937,9 @@ void sqlite_progress_handler(
|
|||
** This routine installs a default busy handler that waits for the
|
||||
** specified number of milliseconds before returning 0.
|
||||
*/
|
||||
void sqlite_busy_timeout(sqlite *db, long ms){
|
||||
void sqlite_busy_timeout(sqlite *db, int ms){
|
||||
if( ms>0 ){
|
||||
sqlite_busy_handler(db, sqliteDefaultBusyCallback, (void*)ms);
|
||||
sqlite_busy_handler(db, sqliteDefaultBusyCallback, (void*)(long)ms);
|
||||
}else{
|
||||
sqlite_busy_handler(db, 0, 0);
|
||||
}
|
||||
|
@ -903,7 +984,7 @@ const char *sqlite_libencoding(void){ return sqlite_encoding; }
|
|||
** sqlite_create_aggregate(), and vice versa.
|
||||
**
|
||||
** If nArg is -1 it means that this function will accept any number
|
||||
** of arguments, including 0.
|
||||
** of arguments, including 0. The maximum allowed value of nArg is 127.
|
||||
*/
|
||||
int sqlite_create_function(
|
||||
sqlite *db, /* Add the function to this database connection */
|
||||
|
@ -915,6 +996,7 @@ int sqlite_create_function(
|
|||
FuncDef *p;
|
||||
int nName;
|
||||
if( db==0 || zName==0 || sqliteSafetyCheck(db) ) return 1;
|
||||
if( nArg<-1 || nArg>127 ) return 1;
|
||||
nName = strlen(zName);
|
||||
if( nName>255 ) return 1;
|
||||
p = sqliteFindFunction(db, zName, nName, nArg, 1);
|
||||
|
@ -936,6 +1018,7 @@ int sqlite_create_aggregate(
|
|||
FuncDef *p;
|
||||
int nName;
|
||||
if( db==0 || zName==0 || sqliteSafetyCheck(db) ) return 1;
|
||||
if( nArg<-1 || nArg>127 ) return 1;
|
||||
nName = strlen(zName);
|
||||
if( nName>255 ) return 1;
|
||||
p = sqliteFindFunction(db, zName, nName, nArg, 1);
|
||||
|
@ -976,6 +1059,24 @@ void *sqlite_trace(sqlite *db, void (*xTrace)(void*,const char*), void *pArg){
|
|||
return pOld;
|
||||
}
|
||||
|
||||
/*** EXPERIMENTAL ***
|
||||
**
|
||||
** Register a function to be invoked when a transaction comments.
|
||||
** If either function returns non-zero, then the commit becomes a
|
||||
** rollback.
|
||||
*/
|
||||
void *sqlite_commit_hook(
|
||||
sqlite *db, /* Attach the hook to this database */
|
||||
int (*xCallback)(void*), /* Function to invoke on each commit */
|
||||
void *pArg /* Argument to the function */
|
||||
){
|
||||
void *pOld = db->pCommitArg;
|
||||
db->xCommitCallback = xCallback;
|
||||
db->pCommitArg = pArg;
|
||||
return pOld;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This routine is called to create a connection to a database BTree
|
||||
** driver. If zFilename is the name of a file, then that file is
|
||||
|
|
|
@ -13,7 +13,6 @@ char *sqliteOpcodeNames[] = { "???",
|
|||
"Push",
|
||||
"ColumnName",
|
||||
"Callback",
|
||||
"NullCallback",
|
||||
"Concat",
|
||||
"Add",
|
||||
"Subtract",
|
||||
|
@ -26,7 +25,7 @@ char *sqliteOpcodeNames[] = { "???",
|
|||
"ShiftLeft",
|
||||
"ShiftRight",
|
||||
"AddImm",
|
||||
"IsNumeric",
|
||||
"ForceInt",
|
||||
"MustBeInt",
|
||||
"Eq",
|
||||
"Ne",
|
||||
|
@ -78,6 +77,7 @@ char *sqliteOpcodeNames[] = { "???",
|
|||
"PutIntKey",
|
||||
"PutStrKey",
|
||||
"Delete",
|
||||
"SetCounts",
|
||||
"KeyAsData",
|
||||
"RowKey",
|
||||
"RowData",
|
||||
|
@ -107,6 +107,8 @@ char *sqliteOpcodeNames[] = { "???",
|
|||
"ListReset",
|
||||
"ListPush",
|
||||
"ListPop",
|
||||
"ContextPush",
|
||||
"ContextPop",
|
||||
"SortPut",
|
||||
"SortMakeRec",
|
||||
"SortMakeKey",
|
||||
|
@ -133,4 +135,6 @@ char *sqliteOpcodeNames[] = { "???",
|
|||
"SetFirst",
|
||||
"SetNext",
|
||||
"Vacuum",
|
||||
"StackDepth",
|
||||
"StackReset",
|
||||
};
|
||||
|
|
|
@ -12,71 +12,71 @@
|
|||
#define OP_Push 11
|
||||
#define OP_ColumnName 12
|
||||
#define OP_Callback 13
|
||||
#define OP_NullCallback 14
|
||||
#define OP_Concat 15
|
||||
#define OP_Add 16
|
||||
#define OP_Subtract 17
|
||||
#define OP_Multiply 18
|
||||
#define OP_Divide 19
|
||||
#define OP_Remainder 20
|
||||
#define OP_Function 21
|
||||
#define OP_BitAnd 22
|
||||
#define OP_BitOr 23
|
||||
#define OP_ShiftLeft 24
|
||||
#define OP_ShiftRight 25
|
||||
#define OP_AddImm 26
|
||||
#define OP_IsNumeric 27
|
||||
#define OP_MustBeInt 28
|
||||
#define OP_Eq 29
|
||||
#define OP_Ne 30
|
||||
#define OP_Lt 31
|
||||
#define OP_Le 32
|
||||
#define OP_Gt 33
|
||||
#define OP_Ge 34
|
||||
#define OP_StrEq 35
|
||||
#define OP_StrNe 36
|
||||
#define OP_StrLt 37
|
||||
#define OP_StrLe 38
|
||||
#define OP_StrGt 39
|
||||
#define OP_StrGe 40
|
||||
#define OP_And 41
|
||||
#define OP_Or 42
|
||||
#define OP_Negative 43
|
||||
#define OP_AbsValue 44
|
||||
#define OP_Not 45
|
||||
#define OP_BitNot 46
|
||||
#define OP_Noop 47
|
||||
#define OP_If 48
|
||||
#define OP_IfNot 49
|
||||
#define OP_IsNull 50
|
||||
#define OP_NotNull 51
|
||||
#define OP_MakeRecord 52
|
||||
#define OP_MakeIdxKey 53
|
||||
#define OP_MakeKey 54
|
||||
#define OP_IncrKey 55
|
||||
#define OP_Checkpoint 56
|
||||
#define OP_Transaction 57
|
||||
#define OP_Commit 58
|
||||
#define OP_Rollback 59
|
||||
#define OP_ReadCookie 60
|
||||
#define OP_SetCookie 61
|
||||
#define OP_VerifyCookie 62
|
||||
#define OP_OpenRead 63
|
||||
#define OP_OpenWrite 64
|
||||
#define OP_OpenTemp 65
|
||||
#define OP_OpenPseudo 66
|
||||
#define OP_Close 67
|
||||
#define OP_MoveLt 68
|
||||
#define OP_MoveTo 69
|
||||
#define OP_Distinct 70
|
||||
#define OP_NotFound 71
|
||||
#define OP_Found 72
|
||||
#define OP_IsUnique 73
|
||||
#define OP_NotExists 74
|
||||
#define OP_NewRecno 75
|
||||
#define OP_PutIntKey 76
|
||||
#define OP_PutStrKey 77
|
||||
#define OP_Delete 78
|
||||
#define OP_Concat 14
|
||||
#define OP_Add 15
|
||||
#define OP_Subtract 16
|
||||
#define OP_Multiply 17
|
||||
#define OP_Divide 18
|
||||
#define OP_Remainder 19
|
||||
#define OP_Function 20
|
||||
#define OP_BitAnd 21
|
||||
#define OP_BitOr 22
|
||||
#define OP_ShiftLeft 23
|
||||
#define OP_ShiftRight 24
|
||||
#define OP_AddImm 25
|
||||
#define OP_ForceInt 26
|
||||
#define OP_MustBeInt 27
|
||||
#define OP_Eq 28
|
||||
#define OP_Ne 29
|
||||
#define OP_Lt 30
|
||||
#define OP_Le 31
|
||||
#define OP_Gt 32
|
||||
#define OP_Ge 33
|
||||
#define OP_StrEq 34
|
||||
#define OP_StrNe 35
|
||||
#define OP_StrLt 36
|
||||
#define OP_StrLe 37
|
||||
#define OP_StrGt 38
|
||||
#define OP_StrGe 39
|
||||
#define OP_And 40
|
||||
#define OP_Or 41
|
||||
#define OP_Negative 42
|
||||
#define OP_AbsValue 43
|
||||
#define OP_Not 44
|
||||
#define OP_BitNot 45
|
||||
#define OP_Noop 46
|
||||
#define OP_If 47
|
||||
#define OP_IfNot 48
|
||||
#define OP_IsNull 49
|
||||
#define OP_NotNull 50
|
||||
#define OP_MakeRecord 51
|
||||
#define OP_MakeIdxKey 52
|
||||
#define OP_MakeKey 53
|
||||
#define OP_IncrKey 54
|
||||
#define OP_Checkpoint 55
|
||||
#define OP_Transaction 56
|
||||
#define OP_Commit 57
|
||||
#define OP_Rollback 58
|
||||
#define OP_ReadCookie 59
|
||||
#define OP_SetCookie 60
|
||||
#define OP_VerifyCookie 61
|
||||
#define OP_OpenRead 62
|
||||
#define OP_OpenWrite 63
|
||||
#define OP_OpenTemp 64
|
||||
#define OP_OpenPseudo 65
|
||||
#define OP_Close 66
|
||||
#define OP_MoveLt 67
|
||||
#define OP_MoveTo 68
|
||||
#define OP_Distinct 69
|
||||
#define OP_NotFound 70
|
||||
#define OP_Found 71
|
||||
#define OP_IsUnique 72
|
||||
#define OP_NotExists 73
|
||||
#define OP_NewRecno 74
|
||||
#define OP_PutIntKey 75
|
||||
#define OP_PutStrKey 76
|
||||
#define OP_Delete 77
|
||||
#define OP_SetCounts 78
|
||||
#define OP_KeyAsData 79
|
||||
#define OP_RowKey 80
|
||||
#define OP_RowData 81
|
||||
|
@ -106,29 +106,33 @@
|
|||
#define OP_ListReset 105
|
||||
#define OP_ListPush 106
|
||||
#define OP_ListPop 107
|
||||
#define OP_SortPut 108
|
||||
#define OP_SortMakeRec 109
|
||||
#define OP_SortMakeKey 110
|
||||
#define OP_Sort 111
|
||||
#define OP_SortNext 112
|
||||
#define OP_SortCallback 113
|
||||
#define OP_SortReset 114
|
||||
#define OP_FileOpen 115
|
||||
#define OP_FileRead 116
|
||||
#define OP_FileColumn 117
|
||||
#define OP_MemStore 118
|
||||
#define OP_MemLoad 119
|
||||
#define OP_MemIncr 120
|
||||
#define OP_AggReset 121
|
||||
#define OP_AggInit 122
|
||||
#define OP_AggFunc 123
|
||||
#define OP_AggFocus 124
|
||||
#define OP_AggSet 125
|
||||
#define OP_AggGet 126
|
||||
#define OP_AggNext 127
|
||||
#define OP_SetInsert 128
|
||||
#define OP_SetFound 129
|
||||
#define OP_SetNotFound 130
|
||||
#define OP_SetFirst 131
|
||||
#define OP_SetNext 132
|
||||
#define OP_Vacuum 133
|
||||
#define OP_ContextPush 108
|
||||
#define OP_ContextPop 109
|
||||
#define OP_SortPut 110
|
||||
#define OP_SortMakeRec 111
|
||||
#define OP_SortMakeKey 112
|
||||
#define OP_Sort 113
|
||||
#define OP_SortNext 114
|
||||
#define OP_SortCallback 115
|
||||
#define OP_SortReset 116
|
||||
#define OP_FileOpen 117
|
||||
#define OP_FileRead 118
|
||||
#define OP_FileColumn 119
|
||||
#define OP_MemStore 120
|
||||
#define OP_MemLoad 121
|
||||
#define OP_MemIncr 122
|
||||
#define OP_AggReset 123
|
||||
#define OP_AggInit 124
|
||||
#define OP_AggFunc 125
|
||||
#define OP_AggFocus 126
|
||||
#define OP_AggSet 127
|
||||
#define OP_AggGet 128
|
||||
#define OP_AggNext 129
|
||||
#define OP_SetInsert 130
|
||||
#define OP_SetFound 131
|
||||
#define OP_SetNotFound 132
|
||||
#define OP_SetFirst 133
|
||||
#define OP_SetNext 134
|
||||
#define OP_Vacuum 135
|
||||
#define OP_StackDepth 136
|
||||
#define OP_StackReset 137
|
||||
|
|
|
@ -34,9 +34,6 @@
|
|||
# ifndef O_BINARY
|
||||
# define O_BINARY 0
|
||||
# endif
|
||||
# ifndef EISDIR
|
||||
# define EISDIR 21
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -467,9 +464,11 @@ int sqliteOsOpenReadWrite(
|
|||
id->dirfd = -1;
|
||||
id->fd = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY, 0644);
|
||||
if( id->fd<0 ){
|
||||
if (errno == EISDIR) {
|
||||
#ifdef EISDIR
|
||||
if( errno==EISDIR ){
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
#endif
|
||||
id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
|
||||
if( id->fd<0 ){
|
||||
return SQLITE_CANTOPEN;
|
||||
|
@ -779,6 +778,13 @@ int sqliteOsOpenDirectory(
|
|||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the following global variable points to a string which is the
|
||||
** name of a directory, then that directory will be used to store
|
||||
** temporary files.
|
||||
*/
|
||||
const char *sqlite_temp_directory = 0;
|
||||
|
||||
/*
|
||||
** Create a temporary file name in zBuf. zBuf must be big enough to
|
||||
** hold at least SQLITE_TEMPNAME_SIZE characters.
|
||||
|
@ -786,19 +792,22 @@ int sqliteOsOpenDirectory(
|
|||
int sqliteOsTempFileName(char *zBuf){
|
||||
#if OS_UNIX
|
||||
static const char *azDirs[] = {
|
||||
0,
|
||||
"/var/tmp",
|
||||
"/usr/tmp",
|
||||
"/tmp",
|
||||
".",
|
||||
};
|
||||
static char zChars[] =
|
||||
static unsigned char zChars[] =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789";
|
||||
int i, j;
|
||||
struct stat buf;
|
||||
const char *zDir = ".";
|
||||
azDirs[0] = sqlite_temp_directory;
|
||||
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
|
||||
if( azDirs[i]==0 ) continue;
|
||||
if( stat(azDirs[i], &buf) ) continue;
|
||||
if( !S_ISDIR(buf.st_mode) ) continue;
|
||||
if( access(azDirs[i], 07) ) continue;
|
||||
|
@ -808,9 +817,9 @@ int sqliteOsTempFileName(char *zBuf){
|
|||
do{
|
||||
sprintf(zBuf, "%s/"TEMP_FILE_PREFIX, zDir);
|
||||
j = strlen(zBuf);
|
||||
for(i=0; i<15; i++){
|
||||
int n = sqliteRandomByte() % (sizeof(zChars)-1);
|
||||
zBuf[j++] = zChars[n];
|
||||
sqliteRandomness(15, &zBuf[j]);
|
||||
for(i=0; i<15; i++, j++){
|
||||
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
|
||||
}
|
||||
zBuf[j] = 0;
|
||||
}while( access(zBuf,0)==0 );
|
||||
|
@ -821,16 +830,22 @@ int sqliteOsTempFileName(char *zBuf){
|
|||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789";
|
||||
int i, j;
|
||||
char *zDir;
|
||||
char zTempPath[SQLITE_TEMPNAME_SIZE];
|
||||
GetTempPath(SQLITE_TEMPNAME_SIZE-30, zTempPath);
|
||||
for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
|
||||
zTempPath[i] = 0;
|
||||
if( sqlite_temp_directory==0 ){
|
||||
GetTempPath(SQLITE_TEMPNAME_SIZE-30, zTempPath);
|
||||
for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
|
||||
zTempPath[i] = 0;
|
||||
zDir = zTempPath;
|
||||
}else{
|
||||
zDir = sqlite_temp_directory;
|
||||
}
|
||||
for(;;){
|
||||
sprintf(zBuf, "%s\\"TEMP_FILE_PREFIX, zTempPath);
|
||||
sprintf(zBuf, "%s\\"TEMP_FILE_PREFIX, zDir);
|
||||
j = strlen(zBuf);
|
||||
for(i=0; i<15; i++){
|
||||
int n = sqliteRandomByte() % (sizeof(zChars) - 1);
|
||||
zBuf[j++] = zChars[n];
|
||||
sqliteRandomness(15, &zBuf[j]);
|
||||
for(i=0; i<15; i++, j++){
|
||||
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
|
||||
}
|
||||
zBuf[j] = 0;
|
||||
if( !sqliteOsFileExists(zBuf) ) break;
|
||||
|
@ -842,13 +857,16 @@ int sqliteOsTempFileName(char *zBuf){
|
|||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789";
|
||||
int i, j;
|
||||
char *zDir;
|
||||
char zTempPath[SQLITE_TEMPNAME_SIZE];
|
||||
char zdirName[32];
|
||||
CInfoPBRec infoRec;
|
||||
Str31 dirName;
|
||||
memset(&infoRec, 0, sizeof(infoRec));
|
||||
memset(zTempPath, 0, SQLITE_TEMPNAME_SIZE);
|
||||
if( FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder,
|
||||
if( sqlite_temp_directory!=0 ){
|
||||
zDir = sqlite_temp_directory;
|
||||
}else if( FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder,
|
||||
&(infoRec.dirInfo.ioVRefNum), &(infoRec.dirInfo.ioDrParID)) == noErr ){
|
||||
infoRec.dirInfo.ioNamePtr = dirName;
|
||||
do{
|
||||
|
@ -865,15 +883,18 @@ int sqliteOsTempFileName(char *zBuf){
|
|||
break;
|
||||
}
|
||||
} while( infoRec.dirInfo.ioDrDirID != fsRtDirID );
|
||||
zDir = zTempPath;
|
||||
}
|
||||
if( *zTempPath == 0 )
|
||||
if( zDir[0]==0 ){
|
||||
getcwd(zTempPath, SQLITE_TEMPNAME_SIZE-24);
|
||||
zDir = zTempPath;
|
||||
}
|
||||
for(;;){
|
||||
sprintf(zBuf, "%s"TEMP_FILE_PREFIX, zTempPath);
|
||||
sprintf(zBuf, "%s"TEMP_FILE_PREFIX, zDir);
|
||||
j = strlen(zBuf);
|
||||
for(i=0; i<15; i++){
|
||||
int n = sqliteRandomByte() % sizeof(zChars);
|
||||
zBuf[j++] = zChars[n];
|
||||
sqliteRandomness(15, &zBuf[j]);
|
||||
for(i=0; i<15; i++, j++){
|
||||
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
|
||||
}
|
||||
zBuf[j] = 0;
|
||||
if( !sqliteOsFileExists(zBuf) ) break;
|
||||
|
@ -1220,7 +1241,7 @@ int sqliteOsFileSize(OsFile *id, off_t *pSize){
|
|||
** the LockFileEx() API.
|
||||
*/
|
||||
int isNT(void){
|
||||
static osType = 0; /* 0=unknown 1=win95 2=winNT */
|
||||
static int osType = 0; /* 0=unknown 1=win95 2=winNT */
|
||||
if( osType==0 ){
|
||||
OSVERSIONINFO sInfo;
|
||||
sInfo.dwOSVersionInfoSize = sizeof(sInfo);
|
||||
|
@ -1331,9 +1352,11 @@ int sqliteOsReadLock(OsFile *id){
|
|||
if( id->locked>0 ){
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
int lk = (sqliteRandomInteger() & 0x7ffffff)%N_LOCKBYTE+1;
|
||||
int lk;
|
||||
int res;
|
||||
int cnt = 100;
|
||||
sqliteRandomness(sizeof(lk), &lk);
|
||||
lk = (lk & 0x7fffffff)%N_LOCKBYTE + 1;
|
||||
while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){
|
||||
Sleep(1);
|
||||
}
|
||||
|
@ -1365,10 +1388,12 @@ int sqliteOsReadLock(OsFile *id){
|
|||
if( id->locked>0 || id->refNumRF == -1 ){
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
int lk = (sqliteRandomInteger() & 0x7ffffff)%N_LOCKBYTE+1;
|
||||
int lk;
|
||||
OSErr res;
|
||||
int cnt = 5;
|
||||
ParamBlockRec params;
|
||||
sqliteRandomness(sizeof(lk), &lk);
|
||||
lk = (lk & 0x7fffffff)%N_LOCKBYTE + 1;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.ioParam.ioRefNum = id->refNumRF;
|
||||
params.ioParam.ioPosMode = fsFromStart;
|
||||
|
@ -1783,7 +1808,7 @@ char *sqliteOsFullPathname(const char *zRelative){
|
|||
}
|
||||
|
||||
/*
|
||||
** The following variable, if set to a now-zero value, become the result
|
||||
** The following variable, if set to a non-zero value, becomes the result
|
||||
** returned from sqliteOsCurrentTime(). This is used for testing.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
|
|
|
@ -39,7 +39,9 @@
|
|||
*/
|
||||
#ifndef SQLITE_DISABLE_LFS
|
||||
# define _LARGE_FILE 1
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
# ifndef _FILE_OFFSET_BITS
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
# endif
|
||||
# define _LARGEFILE_SOURCE 1
|
||||
#endif
|
||||
|
||||
|
@ -118,9 +120,6 @@
|
|||
#endif
|
||||
|
||||
#if OS_WIN
|
||||
# if defined(__CYGWIN__)
|
||||
# define __CYGWIN_USE_BIG_TYPES__
|
||||
# endif
|
||||
#include <windows.h>
|
||||
#include <winbase.h>
|
||||
typedef struct OsFile OsFile;
|
||||
|
|
|
@ -84,6 +84,19 @@ static Pager *mainPager = 0;
|
|||
** Each in-memory image of a page begins with the following header.
|
||||
** This header is only visible to this pager module. The client
|
||||
** code that calls pager sees only the data that follows the header.
|
||||
**
|
||||
** Client code should call sqlitepager_write() on a page prior to making
|
||||
** any modifications to that page. The first time sqlitepager_write()
|
||||
** is called, the original page contents are written into the rollback
|
||||
** journal and PgHdr.inJournal and PgHdr.needSync are set. Later, once
|
||||
** the journal page has made it onto the disk surface, PgHdr.needSync
|
||||
** is cleared. The modified page cannot be written back into the original
|
||||
** database file until the journal pages has been synced to disk and the
|
||||
** PgHdr.needSync has been cleared.
|
||||
**
|
||||
** The PgHdr.dirty flag is set when sqlitepager_write() is called and
|
||||
** is cleared again when the page content is written back to the original
|
||||
** database file.
|
||||
*/
|
||||
typedef struct PgHdr PgHdr;
|
||||
struct PgHdr {
|
||||
|
@ -104,6 +117,16 @@ struct PgHdr {
|
|||
/* Pager.nExtra bytes of local data follow the page data */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** A macro used for invoking the codec if there is one
|
||||
*/
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
# define CODEC(P,D,N,X) if( P->xCodec ){ P->xCodec(P->pCodecArg,D,N,X); }
|
||||
#else
|
||||
# define CODEC(P,D,N,X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Convert a pointer to a PgHdr into a pointer to its data
|
||||
** and back again.
|
||||
|
@ -145,9 +168,11 @@ struct Pager {
|
|||
int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */
|
||||
int mxPage; /* Maximum number of pages to hold in cache */
|
||||
int nHit, nMiss, nOvfl; /* Cache hits, missing, and LRU overflows */
|
||||
void (*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */
|
||||
void *pCodecArg; /* First argument to xCodec() */
|
||||
u8 journalOpen; /* True if journal file descriptors is valid */
|
||||
u8 journalStarted; /* True if initial magic of journal is synced */
|
||||
u8 useJournal; /* Do not use a rollback journal on this file */
|
||||
u8 journalStarted; /* True if header of journal is synced */
|
||||
u8 useJournal; /* Use a rollback journal on this file */
|
||||
u8 ckptOpen; /* True if the checkpoint journal is open */
|
||||
u8 ckptInUse; /* True we are in a checkpoint */
|
||||
u8 ckptAutoopen; /* Open ckpt journal when main journal is opened*/
|
||||
|
@ -188,8 +213,8 @@ struct Pager {
|
|||
*/
|
||||
typedef struct PageRecord PageRecord;
|
||||
struct PageRecord {
|
||||
Pgno pgno; /* The page number */
|
||||
char aData[SQLITE_PAGE_SIZE]; /* Original data for page pgno */
|
||||
Pgno pgno; /* The page number */
|
||||
char aData[SQLITE_PAGE_SIZE]; /* Original data for page pgno */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -279,7 +304,13 @@ int journal_format = 3;
|
|||
#endif
|
||||
|
||||
/*
|
||||
** Read a 32-bit integer from the given file descriptor
|
||||
** Read a 32-bit integer from the given file descriptor. Store the integer
|
||||
** that is read in *pRes. Return SQLITE_OK if everything worked, or an
|
||||
** error code is something goes wrong.
|
||||
**
|
||||
** If the journal format is 2 or 3, read a big-endian integer. If the
|
||||
** journal format is 1, read an integer in the native byte-order of the
|
||||
** host machine.
|
||||
*/
|
||||
static int read32bits(int format, OsFile *fd, u32 *pRes){
|
||||
u32 res;
|
||||
|
@ -295,8 +326,13 @@ static int read32bits(int format, OsFile *fd, u32 *pRes){
|
|||
}
|
||||
|
||||
/*
|
||||
** Write a 32-bit integer into the given file descriptor. Writing
|
||||
** is always done using the new journal format.
|
||||
** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK
|
||||
** on success or an error code is something goes wrong.
|
||||
**
|
||||
** If the journal format is 2 or 3, write the integer as 4 big-endian
|
||||
** bytes. If the journal format is 1, write the integer in the native
|
||||
** byte order. In normal operation, only formats 2 and 3 are used.
|
||||
** Journal format 1 is only used for testing.
|
||||
*/
|
||||
static int write32bits(OsFile *fd, u32 val){
|
||||
unsigned char ac[4];
|
||||
|
@ -313,6 +349,9 @@ static int write32bits(OsFile *fd, u32 val){
|
|||
/*
|
||||
** Write a 32-bit integer into a page header right before the
|
||||
** page data. This will overwrite the PgHdr.pDirty pointer.
|
||||
**
|
||||
** The integer is big-endian for formats 2 and 3 and native byte order
|
||||
** for journal format 1.
|
||||
*/
|
||||
static void store32bits(u32 val, PgHdr *p, int offset){
|
||||
unsigned char *ac;
|
||||
|
@ -469,6 +508,10 @@ static int pager_unwritelock(Pager *pPager){
|
|||
|
||||
/*
|
||||
** Compute and return a checksum for the page of data.
|
||||
**
|
||||
** This is not a real checksum. It is really just the sum of the
|
||||
** random initial value and the page number. We considered do a checksum
|
||||
** of the database, but that was found to be too slow.
|
||||
*/
|
||||
static u32 pager_cksum(Pager *pPager, Pgno pgno, const char *aData){
|
||||
u32 cksum = pPager->cksumInit + pgno;
|
||||
|
@ -529,6 +572,7 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int format){
|
|||
memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
|
||||
pPg->dirty = 0;
|
||||
pPg->needSync = 0;
|
||||
CODEC(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@ -537,21 +581,53 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int format){
|
|||
** Playback the journal and thus restore the database file to
|
||||
** the state it was in before we started making changes.
|
||||
**
|
||||
** The journal file format is as follows: There is an initial
|
||||
** file-type string for sanity checking. Then there is a single
|
||||
** Pgno number which is the number of pages in the database before
|
||||
** changes were made. The database is truncated to this size.
|
||||
** Next come zero or more page records where each page record
|
||||
** consists of a Pgno and SQLITE_PAGE_SIZE bytes of data. See
|
||||
** the PageRecord structure for details.
|
||||
** The journal file format is as follows:
|
||||
**
|
||||
** * 8 byte prefix. One of the aJournalMagic123 vectors defined
|
||||
** above. The format of the journal file is determined by which
|
||||
** of the three prefix vectors is seen.
|
||||
** * 4 byte big-endian integer which is the number of valid page records
|
||||
** in the journal. If this value is 0xffffffff, then compute the
|
||||
** number of page records from the journal size. This field appears
|
||||
** in format 3 only.
|
||||
** * 4 byte big-endian integer which is the initial value for the
|
||||
** sanity checksum. This field appears in format 3 only.
|
||||
** * 4 byte integer which is the number of pages to truncate the
|
||||
** database to during a rollback.
|
||||
** * Zero or more pages instances, each as follows:
|
||||
** + 4 byte page number.
|
||||
** + SQLITE_PAGE_SIZE bytes of data.
|
||||
** + 4 byte checksum (format 3 only)
|
||||
**
|
||||
** When we speak of the journal header, we mean the first 4 bullets above.
|
||||
** Each entry in the journal is an instance of the 5th bullet. Note that
|
||||
** bullets 2 and 3 only appear in format-3 journals.
|
||||
**
|
||||
** Call the value from the second bullet "nRec". nRec is the number of
|
||||
** valid page entries in the journal. In most cases, you can compute the
|
||||
** value of nRec from the size of the journal file. But if a power
|
||||
** failure occurred while the journal was being written, it could be the
|
||||
** case that the size of the journal file had already been increased but
|
||||
** the extra entries had not yet made it safely to disk. In such a case,
|
||||
** the value of nRec computed from the file size would be too large. For
|
||||
** that reason, we always use the nRec value in the header.
|
||||
**
|
||||
** If the nRec value is 0xffffffff it means that nRec should be computed
|
||||
** from the file size. This value is used when the user selects the
|
||||
** no-sync option for the journal. A power failure could lead to corruption
|
||||
** in this case. But for things like temporary table (which will be
|
||||
** deleted when the power is restored) we don't care.
|
||||
**
|
||||
** Journal formats 1 and 2 do not have an nRec value in the header so we
|
||||
** have to compute nRec from the file size. This has risks (as described
|
||||
** above) which is why all persistent tables have been changed to use
|
||||
** format 3.
|
||||
**
|
||||
** If the file opened as the journal file is not a well-formed
|
||||
** journal file (as determined by looking at the magic number
|
||||
** at the beginning) then this routine returns SQLITE_PROTOCOL.
|
||||
** If any other errors occur during playback, the database will
|
||||
** likely be corrupted, so the PAGER_ERR_CORRUPT bit is set in
|
||||
** pPager->errMask and SQLITE_CORRUPT is returned. If it all
|
||||
** works, then this routine returns SQLITE_OK.
|
||||
** journal file then the database will likely already be
|
||||
** corrupted, so the PAGER_ERR_CORRUPT bit is set in pPager->errMask
|
||||
** and SQLITE_CORRUPT is returned. If it all works, then this routine
|
||||
** returns SQLITE_OK.
|
||||
*/
|
||||
static int pager_playback(Pager *pPager, int useJournalSize){
|
||||
off_t szJ; /* Size of the journal file in bytes */
|
||||
|
@ -571,6 +647,13 @@ static int pager_playback(Pager *pPager, int useJournalSize){
|
|||
if( rc!=SQLITE_OK ){
|
||||
goto end_playback;
|
||||
}
|
||||
|
||||
/* If the journal file is too small to contain a complete header,
|
||||
** it must mean that the process that created the journal was just
|
||||
** beginning to write the journal file when it died. In that case,
|
||||
** the database file should have still been completely unchanged.
|
||||
** Nothing needs to be rolled back. We can safely ignore this journal.
|
||||
*/
|
||||
if( szJ < sizeof(aMagic)+sizeof(Pgno) ){
|
||||
goto end_playback;
|
||||
}
|
||||
|
@ -594,6 +677,15 @@ static int pager_playback(Pager *pPager, int useJournalSize){
|
|||
goto end_playback;
|
||||
}
|
||||
if( format>=JOURNAL_FORMAT_3 ){
|
||||
if( szJ < sizeof(aMagic) + 3*sizeof(u32) ){
|
||||
/* Ignore the journal if it is too small to contain a complete
|
||||
** header. We already did this test once above, but at the prior
|
||||
** test, we did not know the journal format and so we had to assume
|
||||
** the smallest possible header. Now we know the header is bigger
|
||||
** than the minimum so we test again.
|
||||
*/
|
||||
goto end_playback;
|
||||
}
|
||||
rc = read32bits(format, &pPager->jfd, (u32*)&nRec);
|
||||
if( rc ) goto end_playback;
|
||||
rc = read32bits(format, &pPager->jfd, &pPager->cksumInit);
|
||||
|
@ -630,7 +722,7 @@ static int pager_playback(Pager *pPager, int useJournalSize){
|
|||
|
||||
/* Pages that have been written to the journal but never synced
|
||||
** where not restored by the loop above. We have to restore those
|
||||
** pages by reading the back from the original database.
|
||||
** pages by reading them back from the original database.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
PgHdr *pPg;
|
||||
|
@ -640,6 +732,8 @@ static int pager_playback(Pager *pPager, int useJournalSize){
|
|||
if( (int)pPg->pgno <= pPager->origDbSize ){
|
||||
sqliteOsSeek(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)(pPg->pgno-1));
|
||||
rc = sqliteOsRead(&pPager->fd, zBuf, SQLITE_PAGE_SIZE);
|
||||
TRACE2("REFETCH %d\n", pPg->pgno);
|
||||
CODEC(pPager, zBuf, pPg->pgno, 2);
|
||||
if( rc ) break;
|
||||
}else{
|
||||
memset(zBuf, 0, SQLITE_PAGE_SIZE);
|
||||
|
@ -747,6 +841,7 @@ end_ckpt_playback:
|
|||
void sqlitepager_set_cachesize(Pager *pPager, int mxPage){
|
||||
if( mxPage>=0 ){
|
||||
pPager->noSync = pPager->tempFile;
|
||||
if( pPager->noSync==0 ) pPager->needSync = 0;
|
||||
}else{
|
||||
pPager->noSync = 1;
|
||||
mxPage = -mxPage;
|
||||
|
@ -772,8 +867,9 @@ void sqlitepager_set_cachesize(Pager *pPager, int mxPage){
|
|||
** when it is rolled back.
|
||||
**
|
||||
** FULL The journal is synced twice before writes begin on the
|
||||
** database (with some additional information being written
|
||||
** in between the two syncs. If we assume that writing a
|
||||
** database (with some additional information - the nRec field
|
||||
** of the journal header - being written in between the two
|
||||
** syncs). If we assume that writing a
|
||||
** single disk sector is atomic, then this mode provides
|
||||
** assurance that the journal will not be corrupted to the
|
||||
** point of causing damage to the database during rollback.
|
||||
|
@ -784,6 +880,7 @@ void sqlitepager_set_cachesize(Pager *pPager, int mxPage){
|
|||
void sqlitepager_set_safety_level(Pager *pPager, int level){
|
||||
pPager->noSync = level==1 || pPager->tempFile;
|
||||
pPager->fullSync = level==3 && !pPager->tempFile;
|
||||
if( pPager->noSync==0 ) pPager->needSync = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -933,7 +1030,7 @@ int sqlitepager_pagecount(Pager *pPager){
|
|||
/*
|
||||
** Forward declaration
|
||||
*/
|
||||
static int syncAllPages(Pager*);
|
||||
static int syncJournal(Pager*);
|
||||
|
||||
/*
|
||||
** Truncate the file to the number of pages specified.
|
||||
|
@ -950,7 +1047,7 @@ int sqlitepager_truncate(Pager *pPager, Pgno nPage){
|
|||
if( nPage>=(unsigned)pPager->dbSize ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
syncAllPages(pPager);
|
||||
syncJournal(pPager);
|
||||
rc = sqliteOsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)nPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
pPager->dbSize = nPage;
|
||||
|
@ -1056,23 +1153,26 @@ int sqlitepager_ref(void *pData){
|
|||
}
|
||||
|
||||
/*
|
||||
** Sync the journal and then write all free dirty pages to the database
|
||||
** file.
|
||||
** Sync the journal. In other words, make sure all the pages that have
|
||||
** been written to the journal have actually reached the surface of the
|
||||
** disk. It is not safe to modify the original database file until after
|
||||
** the journal has been synced. If the original database is modified before
|
||||
** the journal is synced and a power failure occurs, the unsynced journal
|
||||
** data would be lost and we would be unable to completely rollback the
|
||||
** database changes. Database corruption would occur.
|
||||
**
|
||||
** This routine also updates the nRec field in the header of the journal.
|
||||
** (See comments on the pager_playback() routine for additional information.)
|
||||
** If the sync mode is FULL, two syncs will occur. First the whole journal
|
||||
** is synced, then the nRec field is updated, then a second sync occurs.
|
||||
**
|
||||
** Writing all free dirty pages to the database after the sync is a
|
||||
** non-obvious optimization. fsync() is an expensive operation so we
|
||||
** want to minimize the number ot times it is called. After an fsync() call,
|
||||
** we are free to write dirty pages back to the database. It is best
|
||||
** to go ahead and write as many dirty pages as possible to minimize
|
||||
** the risk of having to do another fsync() later on. Writing dirty
|
||||
** free pages in this way was observed to make database operations go
|
||||
** up to 10 times faster.
|
||||
** For temporary databases, we do not care if we are able to rollback
|
||||
** after a power failure, so sync occurs.
|
||||
**
|
||||
** If we are writing to temporary database, there is no need to preserve
|
||||
** the integrity of the journal file, so we can save time and skip the
|
||||
** fsync().
|
||||
** This routine clears the needSync field of every page current held in
|
||||
** memory.
|
||||
*/
|
||||
static int syncAllPages(Pager *pPager){
|
||||
static int syncJournal(Pager *pPager){
|
||||
PgHdr *pPg;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
|
@ -1082,9 +1182,13 @@ static int syncAllPages(Pager *pPager){
|
|||
if( pPager->needSync ){
|
||||
if( !pPager->tempFile ){
|
||||
assert( pPager->journalOpen );
|
||||
assert( !pPager->noSync );
|
||||
/* assert( !pPager->noSync ); // noSync might be set if synchronous
|
||||
** was turned off after the transaction was started. Ticket #615 */
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
/* Make sure the pPager->nRec counter we are keeping agrees
|
||||
** with the nRec computed from the size of the journal file.
|
||||
*/
|
||||
off_t hdrSz, pgSz, jSz;
|
||||
hdrSz = JOURNAL_HDR_SZ(journal_format);
|
||||
pgSz = JOURNAL_PG_SZ(journal_format);
|
||||
|
@ -1094,6 +1198,7 @@ static int syncAllPages(Pager *pPager){
|
|||
}
|
||||
#endif
|
||||
if( journal_format>=3 ){
|
||||
/* Write the nRec value into the journal file header */
|
||||
off_t szJ;
|
||||
if( pPager->fullSync ){
|
||||
TRACE1("SYNC\n");
|
||||
|
@ -1152,7 +1257,10 @@ static int pager_write_pagelist(PgHdr *pList){
|
|||
while( pList ){
|
||||
assert( pList->dirty );
|
||||
sqliteOsSeek(&pPager->fd, (pList->pgno-1)*(off_t)SQLITE_PAGE_SIZE);
|
||||
CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
|
||||
TRACE2("STORE %d\n", pList->pgno);
|
||||
rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pList), SQLITE_PAGE_SIZE);
|
||||
CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 0);
|
||||
if( rc ) return rc;
|
||||
pList->dirty = 0;
|
||||
pList = pList->pDirty;
|
||||
|
@ -1304,7 +1412,7 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
|||
** it can't be helped.
|
||||
*/
|
||||
if( pPg==0 ){
|
||||
int rc = syncAllPages(pPager);
|
||||
int rc = syncJournal(pPager);
|
||||
if( rc!=0 ){
|
||||
sqlitepager_rollback(pPager);
|
||||
return SQLITE_IOERR;
|
||||
|
@ -1413,6 +1521,8 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
|||
int rc;
|
||||
sqliteOsSeek(&pPager->fd, (pgno-1)*(off_t)SQLITE_PAGE_SIZE);
|
||||
rc = sqliteOsRead(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
|
||||
TRACE2("FETCH %d\n", pPg->pgno);
|
||||
CODEC(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3);
|
||||
if( rc!=SQLITE_OK ){
|
||||
off_t fileSize;
|
||||
if( sqliteOsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK
|
||||
|
@ -1557,7 +1667,7 @@ static int pager_open_journal(Pager *pPager){
|
|||
rc = write32bits(&pPager->jfd, pPager->noSync ? 0xffffffff : 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
pPager->cksumInit = (u32)sqliteRandomInteger();
|
||||
sqliteRandomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
|
||||
rc = write32bits(&pPager->jfd, pPager->cksumInit);
|
||||
}
|
||||
}else if( journal_format==JOURNAL_FORMAT_2 ){
|
||||
|
@ -1700,7 +1810,10 @@ int sqlitepager_write(void *pData){
|
|||
szPg = SQLITE_PAGE_SIZE+4;
|
||||
}
|
||||
store32bits(pPg->pgno, pPg, -4);
|
||||
CODEC(pPager, pData, pPg->pgno, 7);
|
||||
rc = sqliteOsWrite(&pPager->jfd, &((char*)pData)[-4], szPg);
|
||||
TRACE3("JOURNAL %d %d\n", pPg->pgno, pPg->needSync);
|
||||
CODEC(pPager, pData, pPg->pgno, 0);
|
||||
if( journal_format>=JOURNAL_FORMAT_3 ){
|
||||
*(u32*)PGHDR_TO_EXTRA(pPg) = saved;
|
||||
}
|
||||
|
@ -1718,7 +1831,6 @@ int sqlitepager_write(void *pData){
|
|||
pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
|
||||
page_add_to_ckpt_list(pPg);
|
||||
}
|
||||
TRACE3("JOURNAL %d %d\n", pPg->pgno, pPg->needSync);
|
||||
}else{
|
||||
pPg->needSync = !pPager->journalStarted && !pPager->noSync;
|
||||
TRACE3("APPEND %d %d\n", pPg->pgno, pPg->needSync);
|
||||
|
@ -1736,7 +1848,10 @@ int sqlitepager_write(void *pData){
|
|||
if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){
|
||||
assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
|
||||
store32bits(pPg->pgno, pPg, -4);
|
||||
CODEC(pPager, pData, pPg->pgno, 7);
|
||||
rc = sqliteOsWrite(&pPager->cpfd, &((char*)pData)[-4], SQLITE_PAGE_SIZE+4);
|
||||
TRACE2("CKPT-JOURNAL %d\n", pPg->pgno);
|
||||
CODEC(pPager, pData, pPg->pgno, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlitepager_rollback(pPager);
|
||||
pPager->errMask |= PAGER_ERR_FULL;
|
||||
|
@ -1896,7 +2011,8 @@ int sqlitepager_commit(Pager *pPager){
|
|||
return rc;
|
||||
}
|
||||
assert( pPager->journalOpen );
|
||||
if( pPager->needSync && sqliteOsSync(&pPager->jfd)!=SQLITE_OK ){
|
||||
rc = syncJournal(pPager);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto commit_abort;
|
||||
}
|
||||
pPg = pager_get_all_dirty_pages(pPager);
|
||||
|
@ -2077,6 +2193,18 @@ const char *sqlitepager_filename(Pager *pPager){
|
|||
return pPager->zFilename;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the codec for this pager
|
||||
*/
|
||||
void sqlitepager_set_codec(
|
||||
Pager *pPager,
|
||||
void (*xCodec)(void*,void*,Pgno,int),
|
||||
void *pCodecArg
|
||||
){
|
||||
pPager->xCodec = xCodec;
|
||||
pPager->pCodecArg = pCodecArg;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
/*
|
||||
** Print a listing of all referenced pages and their ref count.
|
||||
|
|
|
@ -19,12 +19,35 @@
|
|||
/*
|
||||
** The size of one page
|
||||
**
|
||||
** You can change this value to another (reasonable) power of two
|
||||
** such as 512, 2048, 4096, or 8192 and things will still work. But
|
||||
** experiments show that a page size of 1024 gives the best speed.
|
||||
** (The speed differences are minimal.)
|
||||
** You can change this value to another (reasonable) value you want.
|
||||
** It need not be a power of two, though the interface to the disk
|
||||
** will likely be faster if it is.
|
||||
**
|
||||
** Experiments show that a page size of 1024 gives the best speed
|
||||
** for common usages. The speed differences for different sizes
|
||||
** such as 512, 2048, 4096, an so forth, is minimal. Note, however,
|
||||
** that changing the page size results in a completely imcompatible
|
||||
** file format.
|
||||
*/
|
||||
#ifndef SQLITE_PAGE_SIZE
|
||||
#define SQLITE_PAGE_SIZE 1024
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Number of extra bytes of data allocated at the end of each page and
|
||||
** stored on disk but not used by the higher level btree layer. Changing
|
||||
** this value results in a completely incompatible file format.
|
||||
*/
|
||||
#ifndef SQLITE_PAGE_RESERVE
|
||||
#define SQLITE_PAGE_RESERVE 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The total number of usable bytes stored on disk for each page.
|
||||
** The usable bytes come at the beginning of the page and the reserve
|
||||
** bytes come at the end.
|
||||
*/
|
||||
#define SQLITE_USABLE_SIZE (SQLITE_PAGE_SIZE-SQLITE_PAGE_RESERVE)
|
||||
|
||||
/*
|
||||
** Maximum number of pages in one database. (This is a limitation of
|
||||
|
@ -75,6 +98,7 @@ int *sqlitepager_stats(Pager*);
|
|||
void sqlitepager_set_safety_level(Pager*,int);
|
||||
const char *sqlitepager_filename(Pager*);
|
||||
int sqlitepager_rename(Pager*, const char *zNewName);
|
||||
void sqlitepager_set_codec(Pager*,void(*)(void*,void*,Pgno,int),void*);
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
void sqlitepager_refdump(Pager*);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,130 +1,130 @@
|
|||
#define TK_ABORT 1
|
||||
#define TK_AFTER 2
|
||||
#define TK_AGG_FUNCTION 3
|
||||
#define TK_ALL 4
|
||||
#define TK_AND 5
|
||||
#define TK_AS 6
|
||||
#define TK_ASC 7
|
||||
#define TK_ATTACH 8
|
||||
#define TK_BEFORE 9
|
||||
#define TK_BEGIN 10
|
||||
#define TK_BETWEEN 11
|
||||
#define TK_BITAND 12
|
||||
#define TK_BITNOT 13
|
||||
#define TK_BITOR 14
|
||||
#define TK_BY 15
|
||||
#define TK_CASCADE 16
|
||||
#define TK_CASE 17
|
||||
#define TK_CHECK 18
|
||||
#define TK_CLUSTER 19
|
||||
#define TK_COLLATE 20
|
||||
#define TK_COLUMN 21
|
||||
#define TK_END_OF_FILE 1
|
||||
#define TK_ILLEGAL 2
|
||||
#define TK_SPACE 3
|
||||
#define TK_UNCLOSED_STRING 4
|
||||
#define TK_COMMENT 5
|
||||
#define TK_FUNCTION 6
|
||||
#define TK_COLUMN 7
|
||||
#define TK_AGG_FUNCTION 8
|
||||
#define TK_SEMI 9
|
||||
#define TK_EXPLAIN 10
|
||||
#define TK_BEGIN 11
|
||||
#define TK_TRANSACTION 12
|
||||
#define TK_COMMIT 13
|
||||
#define TK_END 14
|
||||
#define TK_ROLLBACK 15
|
||||
#define TK_CREATE 16
|
||||
#define TK_TABLE 17
|
||||
#define TK_TEMP 18
|
||||
#define TK_LP 19
|
||||
#define TK_RP 20
|
||||
#define TK_AS 21
|
||||
#define TK_COMMA 22
|
||||
#define TK_COMMENT 23
|
||||
#define TK_COMMIT 24
|
||||
#define TK_CONCAT 25
|
||||
#define TK_CONFLICT 26
|
||||
#define TK_CONSTRAINT 27
|
||||
#define TK_COPY 28
|
||||
#define TK_CREATE 29
|
||||
#define TK_DATABASE 30
|
||||
#define TK_DEFAULT 31
|
||||
#define TK_DEFERRABLE 32
|
||||
#define TK_DEFERRED 33
|
||||
#define TK_DELETE 34
|
||||
#define TK_ID 23
|
||||
#define TK_ABORT 24
|
||||
#define TK_AFTER 25
|
||||
#define TK_ASC 26
|
||||
#define TK_ATTACH 27
|
||||
#define TK_BEFORE 28
|
||||
#define TK_CASCADE 29
|
||||
#define TK_CLUSTER 30
|
||||
#define TK_CONFLICT 31
|
||||
#define TK_COPY 32
|
||||
#define TK_DATABASE 33
|
||||
#define TK_DEFERRED 34
|
||||
#define TK_DELIMITERS 35
|
||||
#define TK_DESC 36
|
||||
#define TK_DETACH 37
|
||||
#define TK_DISTINCT 38
|
||||
#define TK_DOT 39
|
||||
#define TK_DROP 40
|
||||
#define TK_EACH 41
|
||||
#define TK_ELSE 42
|
||||
#define TK_END 43
|
||||
#define TK_END_OF_FILE 44
|
||||
#define TK_EQ 45
|
||||
#define TK_EXCEPT 46
|
||||
#define TK_EXPLAIN 47
|
||||
#define TK_FAIL 48
|
||||
#define TK_FLOAT 49
|
||||
#define TK_FOR 50
|
||||
#define TK_FOREIGN 51
|
||||
#define TK_FROM 52
|
||||
#define TK_FUNCTION 53
|
||||
#define TK_GE 54
|
||||
#define TK_GLOB 55
|
||||
#define TK_GROUP 56
|
||||
#define TK_GT 57
|
||||
#define TK_HAVING 58
|
||||
#define TK_ID 59
|
||||
#define TK_IGNORE 60
|
||||
#define TK_ILLEGAL 61
|
||||
#define TK_IMMEDIATE 62
|
||||
#define TK_IN 63
|
||||
#define TK_INDEX 64
|
||||
#define TK_INITIALLY 65
|
||||
#define TK_INSERT 66
|
||||
#define TK_INSTEAD 67
|
||||
#define TK_INTEGER 68
|
||||
#define TK_INTERSECT 69
|
||||
#define TK_INTO 70
|
||||
#define TK_IS 71
|
||||
#define TK_ISNULL 72
|
||||
#define TK_JOIN 73
|
||||
#define TK_JOIN_KW 74
|
||||
#define TK_KEY 75
|
||||
#define TK_LE 76
|
||||
#define TK_LIKE 77
|
||||
#define TK_LIMIT 78
|
||||
#define TK_LP 79
|
||||
#define TK_LSHIFT 80
|
||||
#define TK_LT 81
|
||||
#define TK_MATCH 82
|
||||
#define TK_MINUS 83
|
||||
#define TK_NE 84
|
||||
#define TK_NOT 85
|
||||
#define TK_NOTNULL 86
|
||||
#define TK_NULL 87
|
||||
#define TK_OF 88
|
||||
#define TK_OFFSET 89
|
||||
#define TK_ON 90
|
||||
#define TK_OR 91
|
||||
#define TK_ORDER 92
|
||||
#define TK_PLUS 93
|
||||
#define TK_PRAGMA 94
|
||||
#define TK_PRIMARY 95
|
||||
#define TK_RAISE 96
|
||||
#define TK_EACH 38
|
||||
#define TK_FAIL 39
|
||||
#define TK_FOR 40
|
||||
#define TK_GLOB 41
|
||||
#define TK_IGNORE 42
|
||||
#define TK_IMMEDIATE 43
|
||||
#define TK_INITIALLY 44
|
||||
#define TK_INSTEAD 45
|
||||
#define TK_LIKE 46
|
||||
#define TK_MATCH 47
|
||||
#define TK_KEY 48
|
||||
#define TK_OF 49
|
||||
#define TK_OFFSET 50
|
||||
#define TK_PRAGMA 51
|
||||
#define TK_RAISE 52
|
||||
#define TK_REPLACE 53
|
||||
#define TK_RESTRICT 54
|
||||
#define TK_ROW 55
|
||||
#define TK_STATEMENT 56
|
||||
#define TK_TRIGGER 57
|
||||
#define TK_VACUUM 58
|
||||
#define TK_VIEW 59
|
||||
#define TK_OR 60
|
||||
#define TK_AND 61
|
||||
#define TK_NOT 62
|
||||
#define TK_EQ 63
|
||||
#define TK_NE 64
|
||||
#define TK_ISNULL 65
|
||||
#define TK_NOTNULL 66
|
||||
#define TK_IS 67
|
||||
#define TK_BETWEEN 68
|
||||
#define TK_IN 69
|
||||
#define TK_GT 70
|
||||
#define TK_GE 71
|
||||
#define TK_LT 72
|
||||
#define TK_LE 73
|
||||
#define TK_BITAND 74
|
||||
#define TK_BITOR 75
|
||||
#define TK_LSHIFT 76
|
||||
#define TK_RSHIFT 77
|
||||
#define TK_PLUS 78
|
||||
#define TK_MINUS 79
|
||||
#define TK_STAR 80
|
||||
#define TK_SLASH 81
|
||||
#define TK_REM 82
|
||||
#define TK_CONCAT 83
|
||||
#define TK_UMINUS 84
|
||||
#define TK_UPLUS 85
|
||||
#define TK_BITNOT 86
|
||||
#define TK_STRING 87
|
||||
#define TK_JOIN_KW 88
|
||||
#define TK_INTEGER 89
|
||||
#define TK_CONSTRAINT 90
|
||||
#define TK_DEFAULT 91
|
||||
#define TK_FLOAT 92
|
||||
#define TK_NULL 93
|
||||
#define TK_PRIMARY 94
|
||||
#define TK_UNIQUE 95
|
||||
#define TK_CHECK 96
|
||||
#define TK_REFERENCES 97
|
||||
#define TK_REM 98
|
||||
#define TK_REPLACE 99
|
||||
#define TK_RESTRICT 100
|
||||
#define TK_ROLLBACK 101
|
||||
#define TK_ROW 102
|
||||
#define TK_RP 103
|
||||
#define TK_RSHIFT 104
|
||||
#define TK_SELECT 105
|
||||
#define TK_SEMI 106
|
||||
#define TK_SET 107
|
||||
#define TK_SLASH 108
|
||||
#define TK_SPACE 109
|
||||
#define TK_STAR 110
|
||||
#define TK_STATEMENT 111
|
||||
#define TK_STRING 112
|
||||
#define TK_TABLE 113
|
||||
#define TK_TEMP 114
|
||||
#define TK_THEN 115
|
||||
#define TK_TRANSACTION 116
|
||||
#define TK_TRIGGER 117
|
||||
#define TK_UMINUS 118
|
||||
#define TK_UNCLOSED_STRING 119
|
||||
#define TK_UNION 120
|
||||
#define TK_UNIQUE 121
|
||||
#define TK_UPDATE 122
|
||||
#define TK_UPLUS 123
|
||||
#define TK_USING 124
|
||||
#define TK_VACUUM 125
|
||||
#define TK_VALUES 126
|
||||
#define TK_VARIABLE 127
|
||||
#define TK_VIEW 128
|
||||
#define TK_WHEN 129
|
||||
#define TK_WHERE 130
|
||||
#define TK_COLLATE 98
|
||||
#define TK_ON 99
|
||||
#define TK_DELETE 100
|
||||
#define TK_UPDATE 101
|
||||
#define TK_INSERT 102
|
||||
#define TK_SET 103
|
||||
#define TK_DEFERRABLE 104
|
||||
#define TK_FOREIGN 105
|
||||
#define TK_DROP 106
|
||||
#define TK_UNION 107
|
||||
#define TK_ALL 108
|
||||
#define TK_INTERSECT 109
|
||||
#define TK_EXCEPT 110
|
||||
#define TK_SELECT 111
|
||||
#define TK_DISTINCT 112
|
||||
#define TK_DOT 113
|
||||
#define TK_FROM 114
|
||||
#define TK_JOIN 115
|
||||
#define TK_USING 116
|
||||
#define TK_ORDER 117
|
||||
#define TK_BY 118
|
||||
#define TK_GROUP 119
|
||||
#define TK_HAVING 120
|
||||
#define TK_LIMIT 121
|
||||
#define TK_WHERE 122
|
||||
#define TK_INTO 123
|
||||
#define TK_VALUES 124
|
||||
#define TK_VARIABLE 125
|
||||
#define TK_CASE 126
|
||||
#define TK_WHEN 127
|
||||
#define TK_THEN 128
|
||||
#define TK_ELSE 129
|
||||
#define TK_INDEX 130
|
||||
|
|
|
@ -23,13 +23,11 @@
|
|||
%syntax_error {
|
||||
if( pParse->zErrMsg==0 ){
|
||||
if( TOKEN.z[0] ){
|
||||
sqliteSetNString(&pParse->zErrMsg,
|
||||
"near \"", -1, TOKEN.z, TOKEN.n, "\": syntax error", -1, 0);
|
||||
sqliteErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
|
||||
}else{
|
||||
sqliteSetString(&pParse->zErrMsg, "incomplete SQL statement", (char*)0);
|
||||
sqliteErrorMsg(pParse, "incomplete SQL statement");
|
||||
}
|
||||
}
|
||||
pParse->nErr++;
|
||||
}
|
||||
%name sqliteParser
|
||||
%include {
|
||||
|
@ -65,13 +63,10 @@ struct TrigEvent { int a; IdList * b; };
|
|||
%nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
|
||||
COLUMN AGG_FUNCTION.
|
||||
|
||||
// Input is zero or more commands.
|
||||
// Input is a single SQL command
|
||||
input ::= cmdlist.
|
||||
|
||||
// A list of commands is zero or more commands
|
||||
//
|
||||
cmdlist ::= ecmd.
|
||||
cmdlist ::= cmdlist ecmd.
|
||||
cmdlist ::= ecmd.
|
||||
ecmd ::= explain cmdx SEMI.
|
||||
ecmd ::= SEMI.
|
||||
cmdx ::= cmd. { sqliteExec(pParse); }
|
||||
|
@ -132,6 +127,22 @@ id(A) ::= ID(X). {A = X;}
|
|||
OF OFFSET PRAGMA RAISE REPLACE RESTRICT ROW STATEMENT
|
||||
TEMP TRIGGER VACUUM VIEW.
|
||||
|
||||
// Define operator precedence early so that this is the first occurance
|
||||
// of the operator tokens in the grammer. Keeping the operators together
|
||||
// causes them to be assigned integer values that are close together,
|
||||
// which keeps parser tables smaller.
|
||||
//
|
||||
%left OR.
|
||||
%left AND.
|
||||
%right NOT.
|
||||
%left EQ NE ISNULL NOTNULL IS LIKE GLOB BETWEEN IN.
|
||||
%left GT GE LT LE.
|
||||
%left BITAND BITOR LSHIFT RSHIFT.
|
||||
%left PLUS MINUS.
|
||||
%left STAR SLASH REM.
|
||||
%left CONCAT.
|
||||
%right UMINUS UPLUS BITNOT.
|
||||
|
||||
// And "ids" is an identifer-or-string.
|
||||
//
|
||||
%type ids {Token}
|
||||
|
@ -366,7 +377,8 @@ seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). {
|
|||
else { sqliteIdListDelete(U); }
|
||||
}
|
||||
}
|
||||
seltablist(A) ::= stl_prefix(X) LP select(S) RP as(Z) on_opt(N) using_opt(U). {
|
||||
seltablist(A) ::= stl_prefix(X) LP seltablist_paren(S) RP
|
||||
as(Z) on_opt(N) using_opt(U). {
|
||||
A = sqliteSrcListAppend(X,0,0);
|
||||
A->a[A->nSrc-1].pSelect = S;
|
||||
if( Z.n ) sqliteSrcListAddAlias(A,&Z);
|
||||
|
@ -380,6 +392,17 @@ seltablist(A) ::= stl_prefix(X) LP select(S) RP as(Z) on_opt(N) using_opt(U). {
|
|||
}
|
||||
}
|
||||
|
||||
// A seltablist_paren nonterminal represents anything in a FROM that
|
||||
// is contained inside parentheses. This can be either a subquery or
|
||||
// a grouping of table and subqueries.
|
||||
//
|
||||
%type seltablist_paren {Select*}
|
||||
%destructor seltablist_paren {sqliteSelectDelete($$);}
|
||||
seltablist_paren(A) ::= select(S). {A = S;}
|
||||
seltablist_paren(A) ::= seltablist(F). {
|
||||
A = sqliteSelectNew(0,F,0,0,0,0,0,-1,0);
|
||||
}
|
||||
|
||||
%type dbnm {Token}
|
||||
dbnm(A) ::= . {A.z=0; A.n=0;}
|
||||
dbnm(A) ::= DOT nm(X). {A = X;}
|
||||
|
@ -505,16 +528,6 @@ inscollist(A) ::= nm(Y). {A = sqliteIdListAppend(0,&Y);}
|
|||
|
||||
/////////////////////////// Expression Processing /////////////////////////////
|
||||
//
|
||||
%left OR.
|
||||
%left AND.
|
||||
%right NOT.
|
||||
%left EQ NE ISNULL NOTNULL IS LIKE GLOB BETWEEN IN.
|
||||
%left GT GE LT LE.
|
||||
%left BITAND BITOR LSHIFT RSHIFT.
|
||||
%left PLUS MINUS.
|
||||
%left STAR SLASH REM.
|
||||
%left CONCAT.
|
||||
%right UMINUS UPLUS BITNOT.
|
||||
|
||||
%type expr {Expr*}
|
||||
%destructor expr {sqliteExprDelete($$);}
|
||||
|
@ -664,6 +677,20 @@ expr(A) ::= expr(X) NOT IN LP select(Y) RP(E). {
|
|||
A = sqliteExpr(TK_NOT, A, 0, 0);
|
||||
sqliteExprSpan(A,&X->span,&E);
|
||||
}
|
||||
expr(A) ::= expr(X) IN nm(Y) dbnm(D). {
|
||||
SrcList *pSrc = sqliteSrcListAppend(0, &Y, &D);
|
||||
A = sqliteExpr(TK_IN, X, 0, 0);
|
||||
if( A ) A->pSelect = sqliteSelectNew(0,pSrc,0,0,0,0,0,-1,0);
|
||||
sqliteExprSpan(A,&X->span,D.z?&D:&Y);
|
||||
}
|
||||
expr(A) ::= expr(X) NOT IN nm(Y) dbnm(D). {
|
||||
SrcList *pSrc = sqliteSrcListAppend(0, &Y, &D);
|
||||
A = sqliteExpr(TK_IN, X, 0, 0);
|
||||
if( A ) A->pSelect = sqliteSelectNew(0,pSrc,0,0,0,0,0,-1,0);
|
||||
A = sqliteExpr(TK_NOT, A, 0, 0);
|
||||
sqliteExprSpan(A,&X->span,D.z?&D:&Y);
|
||||
}
|
||||
|
||||
|
||||
/* CASE expressions */
|
||||
expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). {
|
||||
|
@ -812,11 +839,11 @@ trigger_cmd(A) ::= UPDATE orconf(R) nm(X) SET setlist(Y) where_opt(Z).
|
|||
{ A = sqliteTriggerUpdateStep(&X, Y, Z, R); }
|
||||
|
||||
// INSERT
|
||||
trigger_cmd(A) ::= INSERT orconf(R) INTO nm(X) inscollist_opt(F)
|
||||
trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F)
|
||||
VALUES LP itemlist(Y) RP.
|
||||
{A = sqliteTriggerInsertStep(&X, F, Y, 0, R);}
|
||||
|
||||
trigger_cmd(A) ::= INSERT orconf(R) INTO nm(X) inscollist_opt(F) select(S).
|
||||
trigger_cmd(A) ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) select(S).
|
||||
{A = sqliteTriggerInsertStep(&X, F, 0, S, R);}
|
||||
|
||||
// DELETE
|
||||
|
@ -854,9 +881,12 @@ cmd ::= DROP TRIGGER nm(X) dbnm(D). {
|
|||
}
|
||||
|
||||
//////////////////////// ATTACH DATABASE file AS name /////////////////////////
|
||||
cmd ::= ATTACH database_kw_opt ids(F) AS nm(D). {
|
||||
sqliteAttach(pParse, &F, &D);
|
||||
cmd ::= ATTACH database_kw_opt ids(F) AS nm(D) key_opt(K). {
|
||||
sqliteAttach(pParse, &F, &D, &K);
|
||||
}
|
||||
%type key_opt {Token}
|
||||
key_opt(A) ::= USING ids(X). { A = X; }
|
||||
key_opt(A) ::= . { A.z = 0; A.n = 0; }
|
||||
|
||||
database_kw_opt ::= DATABASE.
|
||||
database_kw_opt ::= .
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
/*
|
||||
** Interpret the given string as a boolean value.
|
||||
*/
|
||||
static int getBoolean(char *z){
|
||||
static int getBoolean(const char *z){
|
||||
static char *azTrue[] = { "yes", "on", "true" };
|
||||
int i;
|
||||
if( z[0]==0 ) return 0;
|
||||
|
@ -34,7 +34,8 @@ static int getBoolean(char *z){
|
|||
|
||||
/*
|
||||
** Interpret the given string as a safety level. Return 0 for OFF,
|
||||
** 1 for ON or NORMAL and 2 for FULL.
|
||||
** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or
|
||||
** unrecognized string argument.
|
||||
**
|
||||
** Note that the values returned are one less that the values that
|
||||
** should be passed into sqliteBtreeSetSafetyLevel(). The is done
|
||||
|
@ -70,8 +71,8 @@ static int getSafetyLevel(char *z){
|
|||
** backed temporary databases, 2 for the Red-Black tree in memory database
|
||||
** and 0 to use the compile-time default.
|
||||
*/
|
||||
static int getTempStore(char *z){
|
||||
if( z[0]>='0' || z[0]<='2' ){
|
||||
static int getTempStore(const char *z){
|
||||
if( z[0]>='0' && z[0]<='2' ){
|
||||
return z[0] - '0';
|
||||
}else if( sqliteStrICmp(z, "file")==0 ){
|
||||
return 1;
|
||||
|
@ -82,6 +83,68 @@ static int getTempStore(char *z){
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If the TEMP database is open, close it and mark the database schema
|
||||
** as needing reloading. This must be done when using the TEMP_STORE
|
||||
** or DEFAULT_TEMP_STORE pragmas.
|
||||
*/
|
||||
static int changeTempStorage(Parse *pParse, const char *zStorageType){
|
||||
int ts = getTempStore(zStorageType);
|
||||
sqlite *db = pParse->db;
|
||||
if( db->temp_store==ts ) return SQLITE_OK;
|
||||
if( db->aDb[1].pBt!=0 ){
|
||||
if( db->flags & SQLITE_InTrans ){
|
||||
sqliteErrorMsg(pParse, "temporary storage cannot be changed "
|
||||
"from within a transaction");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
sqliteBtreeClose(db->aDb[1].pBt);
|
||||
db->aDb[1].pBt = 0;
|
||||
sqliteResetInternalSchema(db, 0);
|
||||
}
|
||||
db->temp_store = ts;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if zRight and zLeft refer to a pragma that queries
|
||||
** or changes one of the flags in db->flags. Return 1 if so and 0 if not.
|
||||
** Also, implement the pragma.
|
||||
*/
|
||||
static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
|
||||
static const struct {
|
||||
const char *zName; /* Name of the pragma */
|
||||
int mask; /* Mask for the db->flags value */
|
||||
} aPragma[] = {
|
||||
{ "vdbe_trace", SQLITE_VdbeTrace },
|
||||
{ "full_column_names", SQLITE_FullColNames },
|
||||
{ "short_column_names", SQLITE_ShortColNames },
|
||||
{ "show_datatypes", SQLITE_ReportTypes },
|
||||
{ "count_changes", SQLITE_CountRows },
|
||||
{ "empty_result_callbacks", SQLITE_NullCallback },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aPragma)/sizeof(aPragma[0]); i++){
|
||||
if( sqliteStrICmp(zLeft, aPragma[i].zName)==0 ){
|
||||
sqlite *db = pParse->db;
|
||||
Vdbe *v;
|
||||
if( strcmp(zLeft,zRight)==0 && (v = sqliteGetVdbe(pParse))!=0 ){
|
||||
sqliteVdbeOp3(v, OP_ColumnName, 0, 1, aPragma[i].zName, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_ColumnName, 1, 0, "boolean", P3_STATIC);
|
||||
sqliteVdbeCode(v, OP_Integer, (db->flags & aPragma[i].mask)!=0, 0,
|
||||
OP_Callback, 1, 0,
|
||||
0);
|
||||
}else if( getBoolean(zRight) ){
|
||||
db->flags |= aPragma[i].mask;
|
||||
}else{
|
||||
db->flags &= ~aPragma[i].mask;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Process a pragma statement.
|
||||
**
|
||||
|
@ -132,20 +195,21 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
** and a positive value means synchronous is on.
|
||||
*/
|
||||
if( sqliteStrICmp(zLeft,"default_cache_size")==0 ){
|
||||
static VdbeOp getCacheSize[] = {
|
||||
static VdbeOpList getCacheSize[] = {
|
||||
{ OP_ReadCookie, 0, 2, 0},
|
||||
{ OP_AbsValue, 0, 0, 0},
|
||||
{ OP_Dup, 0, 0, 0},
|
||||
{ OP_Integer, 0, 0, 0},
|
||||
{ OP_Ne, 0, 6, 0},
|
||||
{ OP_Integer, MAX_PAGES,0, 0},
|
||||
{ OP_ColumnName, 0, 0, "cache_size"},
|
||||
{ OP_Integer, 0, 0, 0}, /* 5 */
|
||||
{ OP_ColumnName, 0, 1, "cache_size"},
|
||||
{ OP_Callback, 1, 0, 0},
|
||||
};
|
||||
int addr;
|
||||
if( pRight->z==pLeft->z ){
|
||||
sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
|
||||
addr = sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
|
||||
sqliteVdbeChangeP1(v, addr+5, MAX_PAGES);
|
||||
}else{
|
||||
int addr;
|
||||
int size = atoi(zRight);
|
||||
if( size<0 ) size = -size;
|
||||
sqliteBeginWriteOperation(pParse, 0, 0);
|
||||
|
@ -176,8 +240,8 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
** N should be a positive integer.
|
||||
*/
|
||||
if( sqliteStrICmp(zLeft,"cache_size")==0 ){
|
||||
static VdbeOp getCacheSize[] = {
|
||||
{ OP_ColumnName, 0, 0, "cache_size"},
|
||||
static VdbeOpList getCacheSize[] = {
|
||||
{ OP_ColumnName, 0, 1, "cache_size"},
|
||||
{ OP_Callback, 1, 0, 0},
|
||||
};
|
||||
if( pRight->z==pLeft->z ){
|
||||
|
@ -214,8 +278,8 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
** zero, but with a write performance penalty. The default mode is NORMAL.
|
||||
*/
|
||||
if( sqliteStrICmp(zLeft,"default_synchronous")==0 ){
|
||||
static VdbeOp getSync[] = {
|
||||
{ OP_ColumnName, 0, 0, "synchronous"},
|
||||
static VdbeOpList getSync[] = {
|
||||
{ OP_ColumnName, 0, 1, "synchronous"},
|
||||
{ OP_ReadCookie, 0, 3, 0},
|
||||
{ OP_Dup, 0, 0, 0},
|
||||
{ OP_If, 0, 0, 0}, /* 3 */
|
||||
|
@ -267,8 +331,8 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
** opened.
|
||||
*/
|
||||
if( sqliteStrICmp(zLeft,"synchronous")==0 ){
|
||||
static VdbeOp getSync[] = {
|
||||
{ OP_ColumnName, 0, 0, "synchronous"},
|
||||
static VdbeOpList getSync[] = {
|
||||
{ OP_ColumnName, 0, 1, "synchronous"},
|
||||
{ OP_Callback, 1, 0, 0},
|
||||
};
|
||||
if( pRight->z==pLeft->z ){
|
||||
|
@ -285,6 +349,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
}
|
||||
}else
|
||||
|
||||
#ifndef NDEBUG
|
||||
if( sqliteStrICmp(zLeft, "trigger_overhead_test")==0 ){
|
||||
if( getBoolean(zRight) ){
|
||||
always_code_trigger_setup = 1;
|
||||
|
@ -292,72 +357,35 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
always_code_trigger_setup = 0;
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
|
||||
if( sqliteStrICmp(zLeft, "vdbe_trace")==0 ){
|
||||
if( getBoolean(zRight) ){
|
||||
db->flags |= SQLITE_VdbeTrace;
|
||||
}else{
|
||||
db->flags &= ~SQLITE_VdbeTrace;
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "full_column_names")==0 ){
|
||||
if( getBoolean(zRight) ){
|
||||
db->flags |= SQLITE_FullColNames;
|
||||
}else{
|
||||
db->flags &= ~SQLITE_FullColNames;
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "show_datatypes")==0 ){
|
||||
if( getBoolean(zRight) ){
|
||||
db->flags |= SQLITE_ReportTypes;
|
||||
}else{
|
||||
db->flags &= ~SQLITE_ReportTypes;
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "count_changes")==0 ){
|
||||
if( getBoolean(zRight) ){
|
||||
db->flags |= SQLITE_CountRows;
|
||||
}else{
|
||||
db->flags &= ~SQLITE_CountRows;
|
||||
}
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "empty_result_callbacks")==0 ){
|
||||
if( getBoolean(zRight) ){
|
||||
db->flags |= SQLITE_NullCallback;
|
||||
}else{
|
||||
db->flags &= ~SQLITE_NullCallback;
|
||||
}
|
||||
if( flagPragma(pParse, zLeft, zRight) ){
|
||||
/* The flagPragma() call also generates any necessary code */
|
||||
}else
|
||||
|
||||
if( sqliteStrICmp(zLeft, "table_info")==0 ){
|
||||
Table *pTab;
|
||||
pTab = sqliteFindTable(db, zRight, 0);
|
||||
if( pTab ){
|
||||
static VdbeOp tableInfoPreface[] = {
|
||||
static VdbeOpList tableInfoPreface[] = {
|
||||
{ OP_ColumnName, 0, 0, "cid"},
|
||||
{ OP_ColumnName, 1, 0, "name"},
|
||||
{ OP_ColumnName, 2, 0, "type"},
|
||||
{ OP_ColumnName, 3, 0, "notnull"},
|
||||
{ OP_ColumnName, 4, 0, "dflt_value"},
|
||||
{ OP_ColumnName, 5, 0, "pk"},
|
||||
{ OP_ColumnName, 5, 1, "pk"},
|
||||
};
|
||||
int i;
|
||||
sqliteVdbeAddOpList(v, ArraySize(tableInfoPreface), tableInfoPreface);
|
||||
sqliteViewGetColumnNames(pParse, pTab);
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zName, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1,
|
||||
pTab->aCol[i].zType ? pTab->aCol[i].zType : "numeric", P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[i].zName, 0);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0,
|
||||
pTab->aCol[i].zType ? pTab->aCol[i].zType : "numeric", 0);
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->aCol[i].notNull, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zDflt, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0,
|
||||
pTab->aCol[i].zDflt, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->aCol[i].isPrimKey, 0);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 6, 0);
|
||||
}
|
||||
|
@ -369,10 +397,10 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
Table *pTab;
|
||||
pIdx = sqliteFindIndex(db, zRight, 0);
|
||||
if( pIdx ){
|
||||
static VdbeOp tableInfoPreface[] = {
|
||||
static VdbeOpList tableInfoPreface[] = {
|
||||
{ OP_ColumnName, 0, 0, "seqno"},
|
||||
{ OP_ColumnName, 1, 0, "cid"},
|
||||
{ OP_ColumnName, 2, 0, "name"},
|
||||
{ OP_ColumnName, 2, 1, "name"},
|
||||
};
|
||||
int i;
|
||||
pTab = pIdx->pTable;
|
||||
|
@ -381,9 +409,8 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
int cnum = pIdx->aiColumn[i];
|
||||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_Integer, cnum, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
assert( pTab->nCol>cnum );
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[cnum].zName, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[cnum].zName, 0);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 3, 0);
|
||||
}
|
||||
}
|
||||
|
@ -399,17 +426,16 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
}
|
||||
if( pTab && pIdx ){
|
||||
int i = 0;
|
||||
static VdbeOp indexListPreface[] = {
|
||||
static VdbeOpList indexListPreface[] = {
|
||||
{ OP_ColumnName, 0, 0, "seq"},
|
||||
{ OP_ColumnName, 1, 0, "name"},
|
||||
{ OP_ColumnName, 2, 0, "unique"},
|
||||
{ OP_ColumnName, 2, 1, "unique"},
|
||||
};
|
||||
|
||||
sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface);
|
||||
while(pIdx){
|
||||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, pIdx->zName, 0);
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->onError!=OE_None, 0);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 3, 0);
|
||||
++i;
|
||||
|
@ -428,12 +454,12 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
}
|
||||
if( pTab && pFK ){
|
||||
int i = 0;
|
||||
static VdbeOp indexListPreface[] = {
|
||||
static VdbeOpList indexListPreface[] = {
|
||||
{ OP_ColumnName, 0, 0, "id"},
|
||||
{ OP_ColumnName, 1, 0, "seq"},
|
||||
{ OP_ColumnName, 2, 0, "table"},
|
||||
{ OP_ColumnName, 3, 0, "from"},
|
||||
{ OP_ColumnName, 4, 0, "to"},
|
||||
{ OP_ColumnName, 4, 1, "to"},
|
||||
};
|
||||
|
||||
sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface);
|
||||
|
@ -442,13 +468,10 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
for(j=0; j<pFK->nCol; j++){
|
||||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_Integer, j, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pFK->zTo, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->aCol[pFK->aCol[j].iFrom].zName,
|
||||
P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, pFK->aCol[j].zCol, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, pFK->zTo, 0);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0,
|
||||
pTab->aCol[pFK->aCol[j].iFrom].zName, 0);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, pFK->aCol[j].zCol, 0);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 5, 0);
|
||||
}
|
||||
++i;
|
||||
|
@ -459,10 +482,10 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
|
||||
if( sqliteStrICmp(zLeft, "database_list")==0 ){
|
||||
int i;
|
||||
static VdbeOp indexListPreface[] = {
|
||||
static VdbeOpList indexListPreface[] = {
|
||||
{ OP_ColumnName, 0, 0, "seq"},
|
||||
{ OP_ColumnName, 1, 0, "name"},
|
||||
{ OP_ColumnName, 2, 0, "file"},
|
||||
{ OP_ColumnName, 2, 1, "file"},
|
||||
};
|
||||
|
||||
sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface);
|
||||
|
@ -470,11 +493,9 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
if( db->aDb[i].pBt==0 ) continue;
|
||||
assert( db->aDb[i].zName!=0 );
|
||||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, db->aDb[i].zName, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, sqliteBtreeGetFilename(db->aDb[i].pBt),
|
||||
P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0, db->aDb[i].zName, 0);
|
||||
sqliteVdbeOp3(v, OP_String, 0, 0,
|
||||
sqliteBtreeGetFilename(db->aDb[i].pBt), 0);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 3, 0);
|
||||
}
|
||||
}else
|
||||
|
@ -492,20 +513,15 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
** override this setting
|
||||
*/
|
||||
if( sqliteStrICmp(zLeft, "temp_store")==0 ){
|
||||
static VdbeOp getTmpDbLoc[] = {
|
||||
{ OP_ColumnName, 0, 0, "temp_store"},
|
||||
static VdbeOpList getTmpDbLoc[] = {
|
||||
{ OP_ColumnName, 0, 1, "temp_store"},
|
||||
{ OP_Callback, 1, 0, 0},
|
||||
};
|
||||
if( pRight->z==pLeft->z ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, db->temp_store, 0);
|
||||
sqliteVdbeAddOpList(v, ArraySize(getTmpDbLoc), getTmpDbLoc);
|
||||
}else{
|
||||
if (&db->aDb[1].pBt != 0) {
|
||||
sqliteErrorMsg(pParse, "The temporary database already exists - "
|
||||
"its location cannot now be changed");
|
||||
} else {
|
||||
db->temp_store = getTempStore(zRight);
|
||||
}
|
||||
changeTempStorage(pParse, zRight);
|
||||
}
|
||||
}else
|
||||
|
||||
|
@ -513,30 +529,25 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
** PRAGMA default_temp_store
|
||||
** PRAGMA default_temp_store = "default"|"memory"|"file"
|
||||
**
|
||||
** Return or set the value of the persistent temp_store flag (as
|
||||
** well as the value currently in force).
|
||||
** Return or set the value of the persistent temp_store flag. Any
|
||||
** change does not take effect until the next time the database is
|
||||
** opened.
|
||||
**
|
||||
** Note that it is possible for the library compile-time options to
|
||||
** override this setting
|
||||
*/
|
||||
if( sqliteStrICmp(zLeft, "default_temp_store")==0 ){
|
||||
static VdbeOp getTmpDbLoc[] = {
|
||||
{ OP_ColumnName, 0, 0, "temp_store"},
|
||||
static VdbeOpList getTmpDbLoc[] = {
|
||||
{ OP_ColumnName, 0, 1, "temp_store"},
|
||||
{ OP_ReadCookie, 0, 5, 0},
|
||||
{ OP_Callback, 1, 0, 0}};
|
||||
if( pRight->z==pLeft->z ){
|
||||
sqliteVdbeAddOpList(v, ArraySize(getTmpDbLoc), getTmpDbLoc);
|
||||
}else{
|
||||
if (&db->aDb[1].pBt != 0) {
|
||||
sqliteErrorMsg(pParse, "The temporary database already exists - "
|
||||
"its location cannot now be changed");
|
||||
} else {
|
||||
sqliteBeginWriteOperation(pParse, 0, 0);
|
||||
db->temp_store = getTempStore(zRight);
|
||||
sqliteVdbeAddOp(v, OP_Integer, db->temp_store, 0);
|
||||
sqliteVdbeAddOp(v, OP_SetCookie, 0, 5);
|
||||
sqliteEndWriteOperation(pParse);
|
||||
}
|
||||
sqliteBeginWriteOperation(pParse, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Integer, getTempStore(zRight), 0);
|
||||
sqliteVdbeAddOp(v, OP_SetCookie, 0, 5);
|
||||
sqliteEndWriteOperation(pParse);
|
||||
}
|
||||
}else
|
||||
|
||||
|
@ -557,15 +568,15 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
/* Code that initializes the integrity check program. Set the
|
||||
** error count 0
|
||||
*/
|
||||
static VdbeOp initCode[] = {
|
||||
static VdbeOpList initCode[] = {
|
||||
{ OP_Integer, 0, 0, 0},
|
||||
{ OP_MemStore, 0, 1, 0},
|
||||
{ OP_ColumnName, 0, 0, "integrity_check"},
|
||||
{ OP_ColumnName, 0, 1, "integrity_check"},
|
||||
};
|
||||
|
||||
/* Code to do an BTree integrity check on a single database file.
|
||||
*/
|
||||
static VdbeOp checkDb[] = {
|
||||
static VdbeOpList checkDb[] = {
|
||||
{ OP_SetInsert, 0, 0, "2"},
|
||||
{ OP_Integer, 0, 0, 0}, /* 1 */
|
||||
{ OP_OpenRead, 0, 2, 0},
|
||||
|
@ -590,7 +601,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
** messages have been generated, output OK. Otherwise output the
|
||||
** error message
|
||||
*/
|
||||
static VdbeOp endCode[] = {
|
||||
static VdbeOpList endCode[] = {
|
||||
{ OP_MemLoad, 0, 0, 0},
|
||||
{ OP_Integer, 0, 0, 0},
|
||||
{ OP_Ne, 0, 0, 0}, /* 2 */
|
||||
|
@ -625,13 +636,11 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
|
||||
if( pTab->pIndex==0 ) continue;
|
||||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenRead, 1, pTab->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_OpenRead, 1, pTab->tnum, pTab->zName, 0);
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
if( pIdx->tnum==0 ) continue;
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenRead, j+2, pIdx->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_OpenRead, j+2, pIdx->tnum, pIdx->zName, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Integer, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MemStore, 1, 1);
|
||||
|
@ -639,7 +648,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
sqliteVdbeAddOp(v, OP_MemIncr, 1, 0);
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
int k, jmp2;
|
||||
static VdbeOp idxErr[] = {
|
||||
static VdbeOpList idxErr[] = {
|
||||
{ OP_MemIncr, 0, 0, 0},
|
||||
{ OP_String, 0, 0, "rowid "},
|
||||
{ OP_Recno, 1, 0, 0},
|
||||
|
@ -667,7 +676,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
sqliteVdbeAddOp(v, OP_Next, 1, loopTop+1);
|
||||
sqliteVdbeChangeP2(v, loopTop, sqliteVdbeCurrentAddr(v));
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
static VdbeOp cntIdx[] = {
|
||||
static VdbeOpList cntIdx[] = {
|
||||
{ OP_Integer, 0, 0, 0},
|
||||
{ OP_MemStore, 2, 1, 0},
|
||||
{ OP_Rewind, 0, 0, 0}, /* 2 */
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
/*
|
||||
** The "printf" code that follows dates from the 1980's. It is in
|
||||
** the public domain. The original comments are included here for
|
||||
** completeness. They are slightly out-of-date.
|
||||
** completeness. They are very out-of-date but might be useful as
|
||||
** an historical reference. Most of the "enhancements" have been backed
|
||||
** out so that the functionality is now the same as standard printf().
|
||||
**
|
||||
**************************************************************************
|
||||
**
|
||||
** The following modules is an enhanced replacement for the "printf" subroutines
|
||||
** found in the standard C library. The following enhancements are
|
||||
|
@ -48,76 +52,80 @@
|
|||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** Undefine COMPATIBILITY to make some slight changes in the way things
|
||||
** work. I think the changes are an improvement, but they are not
|
||||
** backwards compatible.
|
||||
*/
|
||||
/* #define COMPATIBILITY / * Compatible with SUN OS 4.1 */
|
||||
|
||||
/*
|
||||
** Conversion types fall into various categories as defined by the
|
||||
** following enumeration.
|
||||
*/
|
||||
enum et_type { /* The type of the format field */
|
||||
etRADIX, /* Integer types. %d, %x, %o, and so forth */
|
||||
etFLOAT, /* Floating point. %f */
|
||||
etEXP, /* Exponentional notation. %e and %E */
|
||||
etGENERIC, /* Floating or exponential, depending on exponent. %g */
|
||||
etSIZE, /* Return number of characters processed so far. %n */
|
||||
etSTRING, /* Strings. %s */
|
||||
etDYNSTRING, /* Dynamically allocated strings. %z */
|
||||
etPERCENT, /* Percent symbol. %% */
|
||||
etCHARX, /* Characters. %c */
|
||||
etERROR, /* Used to indicate no such conversion type */
|
||||
#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */
|
||||
#define etFLOAT 2 /* Floating point. %f */
|
||||
#define etEXP 3 /* Exponentional notation. %e and %E */
|
||||
#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */
|
||||
#define etSIZE 5 /* Return number of characters processed so far. %n */
|
||||
#define etSTRING 6 /* Strings. %s */
|
||||
#define etDYNSTRING 7 /* Dynamically allocated strings. %z */
|
||||
#define etPERCENT 8 /* Percent symbol. %% */
|
||||
#define etCHARX 9 /* Characters. %c */
|
||||
#define etERROR 10 /* Used to indicate no such conversion type */
|
||||
/* The rest are extensions, not normally found in printf() */
|
||||
etCHARLIT, /* Literal characters. %' */
|
||||
etSQLESCAPE, /* Strings with '\'' doubled. %q */
|
||||
etSQLESCAPE2, /* Strings with '\'' doubled and enclosed in '',
|
||||
#define etCHARLIT 11 /* Literal characters. %' */
|
||||
#define etSQLESCAPE 12 /* Strings with '\'' doubled. %q */
|
||||
#define etSQLESCAPE2 13 /* Strings with '\'' doubled and enclosed in '',
|
||||
NULL pointers replaced by SQL NULL. %Q */
|
||||
etORDINAL /* 1st, 2nd, 3rd and so forth */
|
||||
};
|
||||
#define etTOKEN 14 /* a pointer to a Token structure */
|
||||
#define etSRCLIST 15 /* a pointer to a SrcList */
|
||||
|
||||
|
||||
/*
|
||||
** An "etByte" is an 8-bit unsigned value.
|
||||
*/
|
||||
typedef unsigned char etByte;
|
||||
|
||||
/*
|
||||
** Each builtin conversion character (ex: the 'd' in "%d") is described
|
||||
** by an instance of the following structure
|
||||
*/
|
||||
typedef struct et_info { /* Information about each format field */
|
||||
int fmttype; /* The format field code letter */
|
||||
int base; /* The base for radix conversion */
|
||||
char *charset; /* The character set for conversion */
|
||||
int flag_signed; /* Is the quantity signed? */
|
||||
char *prefix; /* Prefix on non-zero values in alt format */
|
||||
enum et_type type; /* Conversion paradigm */
|
||||
char fmttype; /* The format field code letter */
|
||||
etByte base; /* The base for radix conversion */
|
||||
etByte flags; /* One or more of FLAG_ constants below */
|
||||
etByte type; /* Conversion paradigm */
|
||||
char *charset; /* The character set for conversion */
|
||||
char *prefix; /* Prefix on non-zero values in alt format */
|
||||
} et_info;
|
||||
|
||||
/*
|
||||
** Allowed values for et_info.flags
|
||||
*/
|
||||
#define FLAG_SIGNED 1 /* True if the value to convert is signed */
|
||||
#define FLAG_INTERN 2 /* True if for internal use only */
|
||||
|
||||
|
||||
/*
|
||||
** The following table is searched linearly, so it is good to put the
|
||||
** most frequently used conversion types first.
|
||||
*/
|
||||
static et_info fmtinfo[] = {
|
||||
{ 'd', 10, "0123456789", 1, 0, etRADIX, },
|
||||
{ 's', 0, 0, 0, 0, etSTRING, },
|
||||
{ 'z', 0, 0, 0, 0, etDYNSTRING, },
|
||||
{ 'q', 0, 0, 0, 0, etSQLESCAPE, },
|
||||
{ 'Q', 0, 0, 0, 0, etSQLESCAPE2, },
|
||||
{ 'c', 0, 0, 0, 0, etCHARX, },
|
||||
{ 'o', 8, "01234567", 0, "0", etRADIX, },
|
||||
{ 'u', 10, "0123456789", 0, 0, etRADIX, },
|
||||
{ 'x', 16, "0123456789abcdef", 0, "x0", etRADIX, },
|
||||
{ 'X', 16, "0123456789ABCDEF", 0, "X0", etRADIX, },
|
||||
{ 'r', 10, "0123456789", 0, 0, etORDINAL, },
|
||||
{ 'f', 0, 0, 1, 0, etFLOAT, },
|
||||
{ 'e', 0, "e", 1, 0, etEXP, },
|
||||
{ 'E', 0, "E", 1, 0, etEXP, },
|
||||
{ 'g', 0, "e", 1, 0, etGENERIC, },
|
||||
{ 'G', 0, "E", 1, 0, etGENERIC, },
|
||||
{ 'i', 10, "0123456789", 1, 0, etRADIX, },
|
||||
{ 'n', 0, 0, 0, 0, etSIZE, },
|
||||
{ '%', 0, 0, 0, 0, etPERCENT, },
|
||||
{ 'b', 2, "01", 0, "b0", etRADIX, }, /* Binary */
|
||||
{ 'p', 10, "0123456789", 0, 0, etRADIX, }, /* Pointers */
|
||||
{ '\'', 0, 0, 0, 0, etCHARLIT, }, /* Literal char */
|
||||
{ 'd', 10, 1, etRADIX, "0123456789", 0 },
|
||||
{ 's', 0, 0, etSTRING, 0, 0 },
|
||||
{ 'z', 0, 2, etDYNSTRING, 0, 0 },
|
||||
{ 'q', 0, 0, etSQLESCAPE, 0, 0 },
|
||||
{ 'Q', 0, 0, etSQLESCAPE2, 0, 0 },
|
||||
{ 'c', 0, 0, etCHARX, 0, 0 },
|
||||
{ 'o', 8, 0, etRADIX, "01234567", "0" },
|
||||
{ 'u', 10, 0, etRADIX, "0123456789", 0 },
|
||||
{ 'x', 16, 0, etRADIX, "0123456789abcdef", "x0" },
|
||||
{ 'X', 16, 0, etRADIX, "0123456789ABCDEF", "X0" },
|
||||
{ 'f', 0, 1, etFLOAT, 0, 0 },
|
||||
{ 'e', 0, 1, etEXP, "e", 0 },
|
||||
{ 'E', 0, 1, etEXP, "E", 0 },
|
||||
{ 'g', 0, 1, etGENERIC, "e", 0 },
|
||||
{ 'G', 0, 1, etGENERIC, "E", 0 },
|
||||
{ 'i', 10, 1, etRADIX, "0123456789", 0 },
|
||||
{ 'n', 0, 0, etSIZE, 0, 0 },
|
||||
{ '%', 0, 0, etPERCENT, 0, 0 },
|
||||
{ 'p', 10, 0, etRADIX, "0123456789", 0 },
|
||||
{ 'T', 0, 2, etTOKEN, 0, 0 },
|
||||
{ 'S', 0, 2, etSRCLIST, 0, 0 },
|
||||
};
|
||||
#define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0]))
|
||||
|
||||
|
@ -181,52 +189,49 @@ static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
|||
** will run.
|
||||
*/
|
||||
static int vxprintf(
|
||||
void (*func)(void*,char*,int),
|
||||
void *arg,
|
||||
const char *format,
|
||||
va_list ap
|
||||
void (*func)(void*,const char*,int), /* Consumer of text */
|
||||
void *arg, /* First argument to the consumer */
|
||||
int useExtended, /* Allow extended %-conversions */
|
||||
const char *fmt, /* Format string */
|
||||
va_list ap /* arguments */
|
||||
){
|
||||
register const char *fmt; /* The format string. */
|
||||
register int c; /* Next character in the format string */
|
||||
register char *bufpt; /* Pointer to the conversion buffer */
|
||||
register int precision; /* Precision of the current field */
|
||||
register int length; /* Length of the field */
|
||||
register int idx; /* A general purpose loop counter */
|
||||
int count; /* Total number of characters output */
|
||||
int width; /* Width of the current field */
|
||||
int flag_leftjustify; /* True if "-" flag is present */
|
||||
int flag_plussign; /* True if "+" flag is present */
|
||||
int flag_blanksign; /* True if " " flag is present */
|
||||
int flag_alternateform; /* True if "#" flag is present */
|
||||
int flag_zeropad; /* True if field width constant starts with zero */
|
||||
int flag_long; /* True if "l" flag is present */
|
||||
int flag_center; /* True if "=" flag is present */
|
||||
unsigned long longvalue; /* Value for integer types */
|
||||
int c; /* Next character in the format string */
|
||||
char *bufpt; /* Pointer to the conversion buffer */
|
||||
int precision; /* Precision of the current field */
|
||||
int length; /* Length of the field */
|
||||
int idx; /* A general purpose loop counter */
|
||||
int count; /* Total number of characters output */
|
||||
int width; /* Width of the current field */
|
||||
etByte flag_leftjustify; /* True if "-" flag is present */
|
||||
etByte flag_plussign; /* True if "+" flag is present */
|
||||
etByte flag_blanksign; /* True if " " flag is present */
|
||||
etByte flag_alternateform; /* True if "#" flag is present */
|
||||
etByte flag_zeropad; /* True if field width constant starts with zero */
|
||||
etByte flag_long; /* True if "l" flag is present */
|
||||
unsigned long longvalue; /* Value for integer types */
|
||||
LONGDOUBLE_TYPE realvalue; /* Value for real types */
|
||||
et_info *infop; /* Pointer to the appropriate info structure */
|
||||
char buf[etBUFSIZE]; /* Conversion buffer */
|
||||
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
|
||||
int errorflag = 0; /* True if an error is encountered */
|
||||
enum et_type xtype; /* Conversion paradigm */
|
||||
char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
|
||||
static char spaces[] = " "
|
||||
" ";
|
||||
et_info *infop; /* Pointer to the appropriate info structure */
|
||||
char buf[etBUFSIZE]; /* Conversion buffer */
|
||||
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
|
||||
etByte errorflag = 0; /* True if an error is encountered */
|
||||
etByte xtype; /* Conversion paradigm */
|
||||
char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
|
||||
static char spaces[] = " ";
|
||||
#define etSPACESIZE (sizeof(spaces)-1)
|
||||
#ifndef etNOFLOATINGPOINT
|
||||
int exp; /* exponent of real numbers */
|
||||
double rounder; /* Used for rounding floating point values */
|
||||
int flag_dp; /* True if decimal point should be shown */
|
||||
int flag_rtz; /* True if trailing zeros should be removed */
|
||||
int flag_exp; /* True to force display of the exponent */
|
||||
int nsd; /* Number of significant digits returned */
|
||||
int exp; /* exponent of real numbers */
|
||||
double rounder; /* Used for rounding floating point values */
|
||||
etByte flag_dp; /* True if decimal point should be shown */
|
||||
etByte flag_rtz; /* True if trailing zeros should be removed */
|
||||
etByte flag_exp; /* True to force display of the exponent */
|
||||
int nsd; /* Number of significant digits returned */
|
||||
#endif
|
||||
|
||||
fmt = format; /* Put in a register for speed */
|
||||
count = length = 0;
|
||||
bufpt = 0;
|
||||
for(; (c=(*fmt))!=0; ++fmt){
|
||||
if( c!='%' ){
|
||||
register int amt;
|
||||
int amt;
|
||||
bufpt = (char *)fmt;
|
||||
amt = 1;
|
||||
while( (c=(*++fmt))!='%' && c!=0 ) amt++;
|
||||
|
@ -242,7 +247,7 @@ static int vxprintf(
|
|||
}
|
||||
/* Find out what flags are present */
|
||||
flag_leftjustify = flag_plussign = flag_blanksign =
|
||||
flag_alternateform = flag_zeropad = flag_center = 0;
|
||||
flag_alternateform = flag_zeropad = 0;
|
||||
do{
|
||||
switch( c ){
|
||||
case '-': flag_leftjustify = 1; c = 0; break;
|
||||
|
@ -250,11 +255,9 @@ static int vxprintf(
|
|||
case ' ': flag_blanksign = 1; c = 0; break;
|
||||
case '#': flag_alternateform = 1; c = 0; break;
|
||||
case '0': flag_zeropad = 1; c = 0; break;
|
||||
case '=': flag_center = 1; c = 0; break;
|
||||
default: break;
|
||||
}
|
||||
}while( c==0 && (c=(*++fmt))!=0 );
|
||||
if( flag_center ) flag_leftjustify = 0;
|
||||
/* Get the field width */
|
||||
width = 0;
|
||||
if( c=='*' ){
|
||||
|
@ -279,10 +282,7 @@ static int vxprintf(
|
|||
c = *++fmt;
|
||||
if( c=='*' ){
|
||||
precision = va_arg(ap,int);
|
||||
#ifndef etCOMPATIBILITY
|
||||
/* This is sensible, but SUN OS 4.1 doesn't do it. */
|
||||
if( precision<0 ) precision = -precision;
|
||||
#endif
|
||||
c = *++fmt;
|
||||
}else{
|
||||
while( c>='0' && c<='9' ){
|
||||
|
@ -304,18 +304,16 @@ static int vxprintf(
|
|||
}
|
||||
/* Fetch the info entry for the field */
|
||||
infop = 0;
|
||||
xtype = etERROR;
|
||||
for(idx=0; idx<etNINFO; idx++){
|
||||
if( c==fmtinfo[idx].fmttype ){
|
||||
infop = &fmtinfo[idx];
|
||||
if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
|
||||
xtype = infop->type;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* No info entry found. It must be an error. */
|
||||
if( infop==0 ){
|
||||
xtype = etERROR;
|
||||
}else{
|
||||
xtype = infop->type;
|
||||
}
|
||||
zExtra = 0;
|
||||
|
||||
/*
|
||||
|
@ -337,11 +335,10 @@ static int vxprintf(
|
|||
** infop Pointer to the appropriate info struct.
|
||||
*/
|
||||
switch( xtype ){
|
||||
case etORDINAL:
|
||||
case etRADIX:
|
||||
if( flag_long ) longvalue = va_arg(ap,long);
|
||||
else longvalue = va_arg(ap,int);
|
||||
#ifdef etCOMPATIBILITY
|
||||
#if 1
|
||||
/* For the format %#x, the value zero is printed "0" not "0x0".
|
||||
** I think this is stupid. */
|
||||
if( longvalue==0 ) flag_alternateform = 0;
|
||||
|
@ -350,7 +347,7 @@ static int vxprintf(
|
|||
** but leave the prefix for hex. */
|
||||
if( longvalue==0 && infop->base==8 ) flag_alternateform = 0;
|
||||
#endif
|
||||
if( infop->flag_signed ){
|
||||
if( infop->flags & FLAG_SIGNED ){
|
||||
if( *(long*)&longvalue<0 ){
|
||||
longvalue = -*(long*)&longvalue;
|
||||
prefix = '-';
|
||||
|
@ -361,26 +358,7 @@ static int vxprintf(
|
|||
if( flag_zeropad && precision<width-(prefix!=0) ){
|
||||
precision = width-(prefix!=0);
|
||||
}
|
||||
bufpt = &buf[etBUFSIZE];
|
||||
if( xtype==etORDINAL ){
|
||||
long a,b;
|
||||
a = longvalue%10;
|
||||
b = longvalue%100;
|
||||
bufpt -= 2;
|
||||
if( a==0 || a>3 || (b>10 && b<14) ){
|
||||
bufpt[0] = 't';
|
||||
bufpt[1] = 'h';
|
||||
}else if( a==1 ){
|
||||
bufpt[0] = 's';
|
||||
bufpt[1] = 't';
|
||||
}else if( a==2 ){
|
||||
bufpt[0] = 'n';
|
||||
bufpt[1] = 'd';
|
||||
}else if( a==3 ){
|
||||
bufpt[0] = 'r';
|
||||
bufpt[1] = 'd';
|
||||
}
|
||||
}
|
||||
bufpt = &buf[etBUFSIZE-1];
|
||||
{
|
||||
register char *cset; /* Use registers for speed */
|
||||
register int base;
|
||||
|
@ -391,7 +369,7 @@ static int vxprintf(
|
|||
longvalue = longvalue/base;
|
||||
}while( longvalue>0 );
|
||||
}
|
||||
length = &buf[etBUFSIZE]-bufpt;
|
||||
length = &buf[etBUFSIZE-1]-bufpt;
|
||||
for(idx=precision-length; idx>0; idx--){
|
||||
*(--bufpt) = '0'; /* Zero pad */
|
||||
}
|
||||
|
@ -403,7 +381,7 @@ static int vxprintf(
|
|||
for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x;
|
||||
}
|
||||
}
|
||||
length = &buf[etBUFSIZE]-bufpt;
|
||||
length = &buf[etBUFSIZE-1]-bufpt;
|
||||
break;
|
||||
case etFLOAT:
|
||||
case etEXP:
|
||||
|
@ -422,7 +400,7 @@ static int vxprintf(
|
|||
}
|
||||
if( infop->type==etGENERIC && precision>0 ) precision--;
|
||||
rounder = 0.0;
|
||||
#ifdef COMPATIBILITY
|
||||
#if 0
|
||||
/* Rounding works like BSD when the constant 0.4999 is used. Wierd! */
|
||||
for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
|
||||
#else
|
||||
|
@ -433,12 +411,11 @@ static int vxprintf(
|
|||
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
|
||||
exp = 0;
|
||||
if( realvalue>0.0 ){
|
||||
int k = 0;
|
||||
while( realvalue>=1e8 && k++<100 ){ realvalue *= 1e-8; exp+=8; }
|
||||
while( realvalue>=10.0 && k++<100 ){ realvalue *= 0.1; exp++; }
|
||||
while( realvalue<1e-8 && k++<100 ){ realvalue *= 1e8; exp-=8; }
|
||||
while( realvalue<1.0 && k++<100 ){ realvalue *= 10.0; exp--; }
|
||||
if( k>=100 ){
|
||||
while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
|
||||
while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
|
||||
while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
|
||||
while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
|
||||
if( exp>350 || exp<-350 ){
|
||||
bufpt = "NaN";
|
||||
length = 3;
|
||||
break;
|
||||
|
@ -590,6 +567,25 @@ static int vxprintf(
|
|||
if( precision>=0 && precision<length ) length = precision;
|
||||
}
|
||||
break;
|
||||
case etTOKEN: {
|
||||
Token *pToken = va_arg(ap, Token*);
|
||||
(*func)(arg, pToken->z, pToken->n);
|
||||
length = width = 0;
|
||||
break;
|
||||
}
|
||||
case etSRCLIST: {
|
||||
SrcList *pSrc = va_arg(ap, SrcList*);
|
||||
int k = va_arg(ap, int);
|
||||
struct SrcList_item *pItem = &pSrc->a[k];
|
||||
assert( k>=0 && k<pSrc->nSrc );
|
||||
if( pItem->zDatabase && pItem->zDatabase[0] ){
|
||||
(*func)(arg, pItem->zDatabase, strlen(pItem->zDatabase));
|
||||
(*func)(arg, ".", 1);
|
||||
}
|
||||
(*func)(arg, pItem->zName, strlen(pItem->zName));
|
||||
length = width = 0;
|
||||
break;
|
||||
}
|
||||
case etERROR:
|
||||
buf[0] = '%';
|
||||
buf[1] = c;
|
||||
|
@ -609,11 +605,6 @@ static int vxprintf(
|
|||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
if( flag_center ){
|
||||
nspace = nspace/2;
|
||||
width -= nspace;
|
||||
flag_leftjustify = 1;
|
||||
}
|
||||
count += nspace;
|
||||
while( nspace>=etSPACESIZE ){
|
||||
(*func)(arg,spaces,etSPACESIZE);
|
||||
|
@ -639,11 +630,7 @@ static int vxprintf(
|
|||
}
|
||||
}
|
||||
if( zExtra ){
|
||||
if( xtype==etDYNSTRING ){
|
||||
free(zExtra);
|
||||
}else{
|
||||
sqliteFree(zExtra);
|
||||
}
|
||||
sqliteFree(zExtra);
|
||||
}
|
||||
}/* End for loop over the format string */
|
||||
return errorflag ? -1 : count;
|
||||
|
@ -657,7 +644,9 @@ struct sgMprintf {
|
|||
char *zBase; /* A base allocation */
|
||||
char *zText; /* The string collected so far */
|
||||
int nChar; /* Length of the string so far */
|
||||
int nTotal; /* Output size if unconstrained */
|
||||
int nAlloc; /* Amount of space allocated in zText */
|
||||
void *(*xRealloc)(void*,int); /* Function used to realloc memory */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -666,24 +655,25 @@ struct sgMprintf {
|
|||
** This routine add nNewChar characters of text in zNewText to
|
||||
** the sgMprintf structure pointed to by "arg".
|
||||
*/
|
||||
static void mout(void *arg, char *zNewText, int nNewChar){
|
||||
static void mout(void *arg, const char *zNewText, int nNewChar){
|
||||
struct sgMprintf *pM = (struct sgMprintf*)arg;
|
||||
pM->nTotal += nNewChar;
|
||||
if( pM->nChar + nNewChar + 1 > pM->nAlloc ){
|
||||
pM->nAlloc = pM->nChar + nNewChar*2 + 1;
|
||||
if( pM->zText==pM->zBase ){
|
||||
pM->zText = sqliteMalloc(pM->nAlloc);
|
||||
if( pM->zText && pM->nChar ) memcpy(pM->zText,pM->zBase,pM->nChar);
|
||||
if( pM->xRealloc==0 ){
|
||||
nNewChar = pM->nAlloc - pM->nChar - 1;
|
||||
}else{
|
||||
char *z = sqliteRealloc(pM->zText, pM->nAlloc);
|
||||
if( z==0 ){
|
||||
sqliteFree(pM->zText);
|
||||
pM->nChar = 0;
|
||||
pM->nAlloc = 0;
|
||||
pM->nAlloc = pM->nChar + nNewChar*2 + 1;
|
||||
if( pM->zText==pM->zBase ){
|
||||
pM->zText = pM->xRealloc(0, pM->nAlloc);
|
||||
if( pM->zText && pM->nChar ){
|
||||
memcpy(pM->zText, pM->zBase, pM->nChar);
|
||||
}
|
||||
}else{
|
||||
pM->zText = pM->xRealloc(pM->zText, pM->nAlloc);
|
||||
}
|
||||
pM->zText = z;
|
||||
}
|
||||
}
|
||||
if( pM->zText ){
|
||||
if( pM->zText && nNewChar>0 ){
|
||||
memcpy(&pM->zText[pM->nChar], zNewText, nNewChar);
|
||||
pM->nChar += nNewChar;
|
||||
pM->zText[pM->nChar] = 0;
|
||||
|
@ -691,114 +681,104 @@ static void mout(void *arg, char *zNewText, int nNewChar){
|
|||
}
|
||||
|
||||
/*
|
||||
** sqlite_mprintf() works like printf(), but allocations memory to hold the
|
||||
** resulting string and returns a pointer to the allocated memory. Use
|
||||
** sqliteFree() to release the memory allocated.
|
||||
** This routine is a wrapper around xprintf() that invokes mout() as
|
||||
** the consumer.
|
||||
*/
|
||||
char *sqliteMPrintf(const char *zFormat, ...){
|
||||
va_list ap;
|
||||
struct sgMprintf sMprintf;
|
||||
char zBuf[200];
|
||||
|
||||
sMprintf.nChar = 0;
|
||||
sMprintf.nAlloc = sizeof(zBuf);
|
||||
sMprintf.zText = zBuf;
|
||||
sMprintf.zBase = zBuf;
|
||||
va_start(ap,zFormat);
|
||||
vxprintf(mout,&sMprintf,zFormat,ap);
|
||||
va_end(ap);
|
||||
sMprintf.zText[sMprintf.nChar] = 0;
|
||||
return sqliteRealloc(sMprintf.zText, sMprintf.nChar+1);
|
||||
static char *base_vprintf(
|
||||
void *(*xRealloc)(void*,int), /* Routine to realloc memory. May be NULL */
|
||||
int useInternal, /* Use internal %-conversions if true */
|
||||
char *zInitBuf, /* Initially write here, before mallocing */
|
||||
int nInitBuf, /* Size of zInitBuf[] */
|
||||
const char *zFormat, /* format string */
|
||||
va_list ap /* arguments */
|
||||
){
|
||||
struct sgMprintf sM;
|
||||
sM.zBase = sM.zText = zInitBuf;
|
||||
sM.nChar = sM.nTotal = 0;
|
||||
sM.nAlloc = nInitBuf;
|
||||
sM.xRealloc = xRealloc;
|
||||
vxprintf(mout, &sM, useInternal, zFormat, ap);
|
||||
if( xRealloc ){
|
||||
if( sM.zText==sM.zBase ){
|
||||
sM.zText = xRealloc(0, sM.nChar+1);
|
||||
memcpy(sM.zText, sM.zBase, sM.nChar+1);
|
||||
}else if( sM.nAlloc>sM.nChar+10 ){
|
||||
sM.zText = xRealloc(sM.zText, sM.nChar+1);
|
||||
}
|
||||
}
|
||||
return sM.zText;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite_mprintf() works like printf(), but allocations memory to hold the
|
||||
** resulting string and returns a pointer to the allocated memory. Use
|
||||
** sqliteFree() to release the memory allocated.
|
||||
** Realloc that is a real function, not a macro.
|
||||
*/
|
||||
static void *printf_realloc(void *old, int size){
|
||||
return sqliteRealloc(old,size);
|
||||
}
|
||||
|
||||
/*
|
||||
** Print into memory obtained from sqliteMalloc(). Use the internal
|
||||
** %-conversion extensions.
|
||||
*/
|
||||
char *sqliteVMPrintf(const char *zFormat, va_list ap){
|
||||
char zBase[1000];
|
||||
return base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap);
|
||||
}
|
||||
|
||||
/*
|
||||
** Print into memory obtained from sqliteMalloc(). Use the internal
|
||||
** %-conversion extensions.
|
||||
*/
|
||||
char *sqliteMPrintf(const char *zFormat, ...){
|
||||
va_list ap;
|
||||
char *z;
|
||||
char zBase[1000];
|
||||
va_start(ap, zFormat);
|
||||
z = base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap);
|
||||
va_end(ap);
|
||||
return z;
|
||||
}
|
||||
|
||||
/*
|
||||
** Print into memory obtained from malloc(). Do not use the internal
|
||||
** %-conversion extensions. This routine is for use by external users.
|
||||
*/
|
||||
char *sqlite_mprintf(const char *zFormat, ...){
|
||||
va_list ap;
|
||||
struct sgMprintf sMprintf;
|
||||
char *zNew;
|
||||
char *z;
|
||||
char zBuf[200];
|
||||
|
||||
sMprintf.nChar = 0;
|
||||
sMprintf.nAlloc = sizeof(zBuf);
|
||||
sMprintf.zText = zBuf;
|
||||
sMprintf.zBase = zBuf;
|
||||
va_start(ap,zFormat);
|
||||
vxprintf(mout,&sMprintf,zFormat,ap);
|
||||
z = base_vprintf((void*(*)(void*,int))realloc, 0,
|
||||
zBuf, sizeof(zBuf), zFormat, ap);
|
||||
va_end(ap);
|
||||
sMprintf.zText[sMprintf.nChar] = 0;
|
||||
zNew = malloc( sMprintf.nChar+1 );
|
||||
if( zNew ) strcpy(zNew,sMprintf.zText);
|
||||
if( sMprintf.zText!=sMprintf.zBase ){
|
||||
sqliteFree(sMprintf.zText);
|
||||
}
|
||||
return zNew;
|
||||
return z;
|
||||
}
|
||||
|
||||
/* This is the varargs version of sqlite_mprintf.
|
||||
*/
|
||||
char *sqlite_vmprintf(const char *zFormat, va_list ap){
|
||||
struct sgMprintf sMprintf;
|
||||
char *zNew;
|
||||
char zBuf[200];
|
||||
sMprintf.nChar = 0;
|
||||
sMprintf.zText = zBuf;
|
||||
sMprintf.nAlloc = sizeof(zBuf);
|
||||
sMprintf.zBase = zBuf;
|
||||
vxprintf(mout,&sMprintf,zFormat,ap);
|
||||
sMprintf.zText[sMprintf.nChar] = 0;
|
||||
zNew = malloc( sMprintf.nChar+1 );
|
||||
if( zNew ) strcpy(zNew,sMprintf.zText);
|
||||
if( sMprintf.zText!=sMprintf.zBase ){
|
||||
sqliteFree(sMprintf.zText);
|
||||
}
|
||||
return zNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function implements the callback from vxprintf.
|
||||
**
|
||||
** This routine add nNewChar characters of text in zNewText to
|
||||
** the sgMprintf structure pointed to by "arg". Unlike mout() above,
|
||||
** this routine does not allocate new space when the buffer fills.
|
||||
** It just truncates.
|
||||
*/
|
||||
static void sout(void *arg, char *zNewText, int nNewChar){
|
||||
struct sgMprintf *pM = (struct sgMprintf*)arg;
|
||||
if( pM->nChar + nNewChar + 1 > pM->nAlloc ){
|
||||
nNewChar = pM->nAlloc - pM->nChar - 1;
|
||||
if( nNewChar<=0 ) return;
|
||||
}
|
||||
memcpy(&pM->zText[pM->nChar], zNewText, nNewChar);
|
||||
pM->nChar += nNewChar;
|
||||
pM->zText[pM->nChar] = 0;
|
||||
return base_vprintf((void*(*)(void*,int))realloc, 0,
|
||||
zBuf, sizeof(zBuf), zFormat, ap);
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite_sprintf() works like sprintf() except that it ignores the
|
||||
** sqlite_snprintf() works like snprintf() except that it ignores the
|
||||
** current locale settings. This is important for SQLite because we
|
||||
** are not able to use a "," as the decimal point in place of "." as
|
||||
** specified by some locales.
|
||||
*/
|
||||
int sqlite_snprintf(int n, char *zBuf, const char *zFormat, ...){
|
||||
char *sqlite_snprintf(int n, char *zBuf, const char *zFormat, ...){
|
||||
char *z;
|
||||
va_list ap;
|
||||
struct sgMprintf sMprintf;
|
||||
|
||||
sMprintf.nChar = 0;
|
||||
sMprintf.nAlloc = n;
|
||||
sMprintf.zText = zBuf;
|
||||
sMprintf.zBase = zBuf;
|
||||
va_start(ap,zFormat);
|
||||
vxprintf(sout,&sMprintf,zFormat,ap);
|
||||
z = base_vprintf(0, 0, zBuf, n, zFormat, ap);
|
||||
va_end(ap);
|
||||
return sMprintf.nChar;
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** The following four routines implement the varargs versions of the
|
||||
** sqlite_exec() and sqlite_get_table() interfaces. See the sqlite.h
|
||||
|
|
|
@ -35,15 +35,15 @@
|
|||
** on RC4, which we know works very well.
|
||||
*/
|
||||
static int randomByte(){
|
||||
int t;
|
||||
unsigned char t;
|
||||
|
||||
/* All threads share a single random number generator.
|
||||
** This structure is the current state of the generator.
|
||||
*/
|
||||
static struct {
|
||||
int isInit; /* True if initialized */
|
||||
int i, j; /* State variables */
|
||||
int s[256]; /* State variables */
|
||||
unsigned char isInit; /* True if initialized */
|
||||
unsigned char i, j; /* State variables */
|
||||
unsigned char s[256]; /* State variables */
|
||||
} prng;
|
||||
|
||||
/* Initialize the state of the random number generator once,
|
||||
|
@ -65,8 +65,7 @@ static int randomByte(){
|
|||
prng.s[i] = i;
|
||||
}
|
||||
for(i=0; i<256; i++){
|
||||
int t;
|
||||
prng.j = (prng.j + prng.s[i] + k[i]) & 0xff;
|
||||
prng.j += prng.s[i] + k[i];
|
||||
t = prng.s[prng.j];
|
||||
prng.s[prng.j] = prng.s[i];
|
||||
prng.s[i] = t;
|
||||
|
@ -76,38 +75,23 @@ static int randomByte(){
|
|||
|
||||
/* Generate and return single random byte
|
||||
*/
|
||||
prng.i = (prng.i + 1) & 0xff;
|
||||
prng.j = (prng.j + prng.s[prng.i]) & 0xff;
|
||||
prng.i++;
|
||||
t = prng.s[prng.i];
|
||||
prng.j += t;
|
||||
prng.s[prng.i] = prng.s[prng.j];
|
||||
prng.s[prng.j] = t;
|
||||
t = prng.s[prng.i] + prng.s[prng.j];
|
||||
return prng.s[t & 0xff];
|
||||
t += prng.s[prng.i];
|
||||
return prng.s[t];
|
||||
}
|
||||
|
||||
/*
|
||||
** Return an random 8-bit integer.
|
||||
** Return N random bytes.
|
||||
*/
|
||||
int sqliteRandomByte(){
|
||||
int r;
|
||||
void sqliteRandomness(int N, void *pBuf){
|
||||
unsigned char *zBuf = pBuf;
|
||||
sqliteOsEnterMutex();
|
||||
r = randomByte();
|
||||
sqliteOsLeaveMutex();
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a random 32-bit integer. The integer is generated by making
|
||||
** 4 calls to sqliteRandomByte().
|
||||
*/
|
||||
int sqliteRandomInteger(){
|
||||
int r;
|
||||
int i;
|
||||
sqliteOsEnterMutex();
|
||||
r = randomByte();
|
||||
for(i=1; i<4; i++){
|
||||
r = (r<<8) + randomByte();
|
||||
while( N-- ){
|
||||
*(zBuf++) = randomByte();
|
||||
}
|
||||
sqliteOsLeaveMutex();
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -42,6 +42,9 @@ Select *sqliteSelectNew(
|
|||
sqliteExprDelete(pHaving);
|
||||
sqliteExprListDelete(pOrderBy);
|
||||
}else{
|
||||
if( pEList==0 ){
|
||||
pEList = sqliteExprListAppend(0, sqliteExpr(TK_ALL,0,0,0), 0);
|
||||
}
|
||||
pNew->pEList = pEList;
|
||||
pNew->pSrc = pSrc;
|
||||
pNew->pWhere = pWhere;
|
||||
|
@ -335,9 +338,7 @@ static void pushOntoSorter(Parse *pParse, Vdbe *v, ExprList *pOrderBy){
|
|||
sqliteExprCode(pParse, pOrderBy->a[i].pExpr);
|
||||
}
|
||||
zSortOrder[pOrderBy->nExpr] = 0;
|
||||
sqliteVdbeAddOp(v, OP_SortMakeKey, pOrderBy->nExpr, 0);
|
||||
sqliteVdbeChangeP3(v, -1, zSortOrder, strlen(zSortOrder));
|
||||
sqliteFree(zSortOrder);
|
||||
sqliteVdbeOp3(v, OP_SortMakeKey, pOrderBy->nExpr, 0, zSortOrder, P3_DYNAMIC);
|
||||
sqliteVdbeAddOp(v, OP_SortPut, 0, 0);
|
||||
}
|
||||
|
||||
|
@ -360,8 +361,7 @@ void sqliteAddKeyType(Vdbe *v, ExprList *pEList){
|
|||
zType[i] = sqliteExprType(pEList->a[i].pExpr)==SQLITE_SO_NUM ? 'n' : 't';
|
||||
}
|
||||
zType[i] = 0;
|
||||
sqliteVdbeChangeP3(v, -1, zType, nColumn);
|
||||
sqliteFree(zType);
|
||||
sqliteVdbeChangeP3(v, -1, zType, P3_DYNAMIC);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -564,18 +564,19 @@ static void generateSortTail(
|
|||
int eDest, /* Write the sorted results here */
|
||||
int iParm /* Optional parameter associated with eDest */
|
||||
){
|
||||
int end = sqliteVdbeMakeLabel(v);
|
||||
int end1 = sqliteVdbeMakeLabel(v);
|
||||
int end2 = sqliteVdbeMakeLabel(v);
|
||||
int addr;
|
||||
if( eDest==SRT_Sorter ) return;
|
||||
sqliteVdbeAddOp(v, OP_Sort, 0, 0);
|
||||
addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end);
|
||||
addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end1);
|
||||
if( p->iOffset>=0 ){
|
||||
sqliteVdbeAddOp(v, OP_MemIncr, p->iOffset, addr+4);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr);
|
||||
}
|
||||
if( p->iLimit>=0 ){
|
||||
sqliteVdbeAddOp(v, OP_MemIncr, p->iLimit, end);
|
||||
sqliteVdbeAddOp(v, OP_MemIncr, p->iLimit, end2);
|
||||
}
|
||||
switch( eDest ){
|
||||
case SRT_Callback: {
|
||||
|
@ -601,7 +602,7 @@ static void generateSortTail(
|
|||
case SRT_Mem: {
|
||||
assert( nColumn==1 );
|
||||
sqliteVdbeAddOp(v, OP_MemStore, iParm, 1);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, end);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, end1);
|
||||
break;
|
||||
}
|
||||
case SRT_Subroutine: {
|
||||
|
@ -619,7 +620,9 @@ static void generateSortTail(
|
|||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr);
|
||||
sqliteVdbeResolveLabel(v, end);
|
||||
sqliteVdbeResolveLabel(v, end2);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
sqliteVdbeResolveLabel(v, end1);
|
||||
sqliteVdbeAddOp(v, OP_SortReset, 0, 0);
|
||||
}
|
||||
|
||||
|
@ -645,9 +648,6 @@ static void generateColumnTypes(
|
|||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int i, j;
|
||||
if( pParse->useCallback && (pParse->db->flags & SQLITE_ReportTypes)==0 ){
|
||||
return;
|
||||
}
|
||||
for(i=0; i<pEList->nExpr; i++){
|
||||
Expr *p = pEList->a[i].pExpr;
|
||||
char *zType = 0;
|
||||
|
@ -672,15 +672,14 @@ static void generateColumnTypes(
|
|||
zType = "NUMERIC";
|
||||
}
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, i + pEList->nExpr, 0);
|
||||
sqliteVdbeChangeP3(v, -1, zType, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_ColumnName, i + pEList->nExpr, 0, zType, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will tell the VDBE the names of columns
|
||||
** in the result set. This information is used to provide the
|
||||
** azCol[] vaolues in the callback.
|
||||
** azCol[] values in the callback.
|
||||
*/
|
||||
static void generateColumnNames(
|
||||
Parse *pParse, /* Parser context */
|
||||
|
@ -689,21 +688,24 @@ static void generateColumnNames(
|
|||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int i, j;
|
||||
sqlite *db = pParse->db;
|
||||
int fullNames, shortNames;
|
||||
|
||||
assert( v!=0 );
|
||||
if( pParse->colNamesSet || v==0 || sqlite_malloc_failed ) return;
|
||||
pParse->colNamesSet = 1;
|
||||
fullNames = (db->flags & SQLITE_FullColNames)!=0;
|
||||
shortNames = (db->flags & SQLITE_ShortColNames)!=0;
|
||||
for(i=0; i<pEList->nExpr; i++){
|
||||
Expr *p;
|
||||
char *zType = 0;
|
||||
int showFullNames;
|
||||
int p2 = i==pEList->nExpr-1;
|
||||
p = pEList->a[i].pExpr;
|
||||
if( p==0 ) continue;
|
||||
if( pEList->a[i].zName ){
|
||||
char *zName = pEList->a[i].zName;
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
|
||||
sqliteVdbeChangeP3(v, -1, zName, strlen(zName));
|
||||
sqliteVdbeOp3(v, OP_ColumnName, i, p2, zName, 0);
|
||||
continue;
|
||||
}
|
||||
showFullNames = (pParse->db->flags & SQLITE_FullColNames)!=0;
|
||||
if( p->op==TK_COLUMN && pTabList ){
|
||||
Table *pTab;
|
||||
char *zCol;
|
||||
|
@ -715,39 +717,31 @@ static void generateColumnNames(
|
|||
assert( iCol==-1 || (iCol>=0 && iCol<pTab->nCol) );
|
||||
if( iCol<0 ){
|
||||
zCol = "_ROWID_";
|
||||
zType = "INTEGER";
|
||||
}else{
|
||||
zCol = pTab->aCol[iCol].zName;
|
||||
zType = pTab->aCol[iCol].zType;
|
||||
}
|
||||
if( p->span.z && p->span.z[0] && !showFullNames ){
|
||||
int addr = sqliteVdbeAddOp(v,OP_ColumnName, i, 0);
|
||||
sqliteVdbeChangeP3(v, -1, p->span.z, p->span.n);
|
||||
if( !shortNames && !fullNames && p->span.z && p->span.z[0] ){
|
||||
int addr = sqliteVdbeOp3(v,OP_ColumnName, i, p2, p->span.z, p->span.n);
|
||||
sqliteVdbeCompressSpace(v, addr);
|
||||
}else if( pTabList->nSrc>1 || showFullNames ){
|
||||
}else if( fullNames || (!shortNames && pTabList->nSrc>1) ){
|
||||
char *zName = 0;
|
||||
char *zTab;
|
||||
|
||||
zTab = pTabList->a[j].zAlias;
|
||||
if( showFullNames || zTab==0 ) zTab = pTab->zName;
|
||||
if( fullNames || zTab==0 ) zTab = pTab->zName;
|
||||
sqliteSetString(&zName, zTab, ".", zCol, 0);
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
|
||||
sqliteVdbeChangeP3(v, -1, zName, strlen(zName));
|
||||
sqliteFree(zName);
|
||||
sqliteVdbeOp3(v, OP_ColumnName, i, p2, zName, P3_DYNAMIC);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
|
||||
sqliteVdbeChangeP3(v, -1, zCol, 0);
|
||||
sqliteVdbeOp3(v, OP_ColumnName, i, p2, zCol, 0);
|
||||
}
|
||||
}else if( p->span.z && p->span.z[0] ){
|
||||
int addr = sqliteVdbeAddOp(v,OP_ColumnName, i, 0);
|
||||
sqliteVdbeChangeP3(v, -1, p->span.z, p->span.n);
|
||||
int addr = sqliteVdbeOp3(v,OP_ColumnName, i, p2, p->span.z, p->span.n);
|
||||
sqliteVdbeCompressSpace(v, addr);
|
||||
}else{
|
||||
char zName[30];
|
||||
assert( p->op!=TK_COLUMN || pTabList==0 );
|
||||
sprintf(zName, "column%d", i+1);
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
|
||||
sqliteVdbeChangeP3(v, -1, zName, strlen(zName));
|
||||
sqliteVdbeOp3(v, OP_ColumnName, i, p2, zName, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -777,8 +771,9 @@ static int fillInColumnList(Parse*, Select*);
|
|||
*/
|
||||
Table *sqliteResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
|
||||
Table *pTab;
|
||||
int i;
|
||||
int i, j;
|
||||
ExprList *pEList;
|
||||
Column *aCol;
|
||||
|
||||
if( fillInColumnList(pParse, pSelect) ){
|
||||
return 0;
|
||||
|
@ -791,17 +786,27 @@ Table *sqliteResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
|
|||
pEList = pSelect->pEList;
|
||||
pTab->nCol = pEList->nExpr;
|
||||
assert( pTab->nCol>0 );
|
||||
pTab->aCol = sqliteMalloc( sizeof(pTab->aCol[0])*pTab->nCol );
|
||||
pTab->aCol = aCol = sqliteMalloc( sizeof(pTab->aCol[0])*pTab->nCol );
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
Expr *p;
|
||||
Expr *p, *pR;
|
||||
if( pEList->a[i].zName ){
|
||||
pTab->aCol[i].zName = sqliteStrDup(pEList->a[i].zName);
|
||||
}else if( (p=pEList->a[i].pExpr)->span.z && p->span.z[0] ){
|
||||
aCol[i].zName = sqliteStrDup(pEList->a[i].zName);
|
||||
}else if( (p=pEList->a[i].pExpr)->op==TK_DOT
|
||||
&& (pR=p->pRight)!=0 && pR->token.z && pR->token.z[0] ){
|
||||
int cnt;
|
||||
sqliteSetNString(&aCol[i].zName, pR->token.z, pR->token.n, 0);
|
||||
for(j=cnt=0; j<i; j++){
|
||||
if( sqliteStrICmp(aCol[j].zName, aCol[i].zName)==0 ){
|
||||
int n;
|
||||
char zBuf[30];
|
||||
sprintf(zBuf,"_%d",++cnt);
|
||||
n = strlen(zBuf);
|
||||
sqliteSetNString(&aCol[i].zName, pR->token.z, pR->token.n, zBuf, n,0);
|
||||
j = -1;
|
||||
}
|
||||
}
|
||||
}else if( p->span.z && p->span.z[0] ){
|
||||
sqliteSetNString(&pTab->aCol[i].zName, p->span.z, p->span.n, 0);
|
||||
}else if( p->op==TK_DOT && p->pRight && p->pRight->token.z &&
|
||||
p->pRight->token.z[0] ){
|
||||
sqliteSetNString(&pTab->aCol[i].zName,
|
||||
p->pRight->token.z, p->pRight->token.n, 0);
|
||||
}else{
|
||||
char zBuf[30];
|
||||
sprintf(zBuf, "column%d", i+1);
|
||||
|
@ -1262,7 +1267,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p){
|
|||
** |
|
||||
** `-----> SELECT b FROM t2
|
||||
** |
|
||||
** `------> SELECT c FROM t1
|
||||
** `------> SELECT a FROM t1
|
||||
**
|
||||
** The arrows in the diagram above represent the Select.pPrior pointer.
|
||||
** So if this routine is called with p equal to the t3 query, then
|
||||
|
@ -1485,14 +1490,6 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
|||
" do not have the same number of result columns", selectOpName(p->op));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Issue a null callback if that is what the user wants.
|
||||
*/
|
||||
if( eDest==SRT_Callback &&
|
||||
(pParse->useCallback==0 || (pParse->db->flags & SQLITE_NullCallback)!=0)
|
||||
){
|
||||
sqliteVdbeAddOp(v, OP_NullCallback, p->pEList->nExpr, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1512,25 +1509,29 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
|||
static void substExprList(ExprList*,int,ExprList*); /* Forward Decl */
|
||||
static void substExpr(Expr *pExpr, int iTable, ExprList *pEList){
|
||||
if( pExpr==0 ) return;
|
||||
if( pExpr->op==TK_COLUMN && pExpr->iTable==iTable && pExpr->iColumn>=0 ){
|
||||
Expr *pNew;
|
||||
assert( pEList!=0 && pExpr->iColumn<pEList->nExpr );
|
||||
assert( pExpr->pLeft==0 && pExpr->pRight==0 && pExpr->pList==0 );
|
||||
pNew = pEList->a[pExpr->iColumn].pExpr;
|
||||
assert( pNew!=0 );
|
||||
pExpr->op = pNew->op;
|
||||
pExpr->dataType = pNew->dataType;
|
||||
assert( pExpr->pLeft==0 );
|
||||
pExpr->pLeft = sqliteExprDup(pNew->pLeft);
|
||||
assert( pExpr->pRight==0 );
|
||||
pExpr->pRight = sqliteExprDup(pNew->pRight);
|
||||
assert( pExpr->pList==0 );
|
||||
pExpr->pList = sqliteExprListDup(pNew->pList);
|
||||
pExpr->iTable = pNew->iTable;
|
||||
pExpr->iColumn = pNew->iColumn;
|
||||
pExpr->iAgg = pNew->iAgg;
|
||||
sqliteTokenCopy(&pExpr->token, &pNew->token);
|
||||
sqliteTokenCopy(&pExpr->span, &pNew->span);
|
||||
if( pExpr->op==TK_COLUMN && pExpr->iTable==iTable ){
|
||||
if( pExpr->iColumn<0 ){
|
||||
pExpr->op = TK_NULL;
|
||||
}else{
|
||||
Expr *pNew;
|
||||
assert( pEList!=0 && pExpr->iColumn<pEList->nExpr );
|
||||
assert( pExpr->pLeft==0 && pExpr->pRight==0 && pExpr->pList==0 );
|
||||
pNew = pEList->a[pExpr->iColumn].pExpr;
|
||||
assert( pNew!=0 );
|
||||
pExpr->op = pNew->op;
|
||||
pExpr->dataType = pNew->dataType;
|
||||
assert( pExpr->pLeft==0 );
|
||||
pExpr->pLeft = sqliteExprDup(pNew->pLeft);
|
||||
assert( pExpr->pRight==0 );
|
||||
pExpr->pRight = sqliteExprDup(pNew->pRight);
|
||||
assert( pExpr->pList==0 );
|
||||
pExpr->pList = sqliteExprListDup(pNew->pList);
|
||||
pExpr->iTable = pNew->iTable;
|
||||
pExpr->iColumn = pNew->iColumn;
|
||||
pExpr->iAgg = pNew->iAgg;
|
||||
sqliteTokenCopy(&pExpr->token, &pNew->token);
|
||||
sqliteTokenCopy(&pExpr->span, &pNew->span);
|
||||
}
|
||||
}else{
|
||||
substExpr(pExpr->pLeft, iTable, pEList);
|
||||
substExpr(pExpr->pRight, iTable, pEList);
|
||||
|
@ -1835,18 +1836,23 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
|
|||
Vdbe *v;
|
||||
int seekOp;
|
||||
int cont;
|
||||
ExprList eList;
|
||||
ExprList *pEList, *pList, eList;
|
||||
struct ExprList_item eListItem;
|
||||
SrcList *pSrc;
|
||||
|
||||
|
||||
/* Check to see if this query is a simple min() or max() query. Return
|
||||
** zero if it is not.
|
||||
*/
|
||||
if( p->pGroupBy || p->pHaving || p->pWhere ) return 0;
|
||||
if( p->pSrc->nSrc!=1 ) return 0;
|
||||
if( p->pEList->nExpr!=1 ) return 0;
|
||||
pExpr = p->pEList->a[0].pExpr;
|
||||
pSrc = p->pSrc;
|
||||
if( pSrc->nSrc!=1 ) return 0;
|
||||
pEList = p->pEList;
|
||||
if( pEList->nExpr!=1 ) return 0;
|
||||
pExpr = pEList->a[0].pExpr;
|
||||
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
|
||||
if( pExpr->pList==0 || pExpr->pList->nExpr!=1 ) return 0;
|
||||
pList = pExpr->pList;
|
||||
if( pList==0 || pList->nExpr!=1 ) return 0;
|
||||
if( pExpr->token.n!=3 ) return 0;
|
||||
if( sqliteStrNICmp(pExpr->token.z,"min",3)==0 ){
|
||||
seekOp = OP_Rewind;
|
||||
|
@ -1855,10 +1861,10 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
|
|||
}else{
|
||||
return 0;
|
||||
}
|
||||
pExpr = pExpr->pList->a[0].pExpr;
|
||||
pExpr = pList->a[0].pExpr;
|
||||
if( pExpr->op!=TK_COLUMN ) return 0;
|
||||
iCol = pExpr->iColumn;
|
||||
pTab = p->pSrc->a[0].pTab;
|
||||
pTab = pSrc->a[0].pTab;
|
||||
|
||||
/* If we get to here, it means the query is of the correct form.
|
||||
** Check to make sure we have an index and make pIdx point to the
|
||||
|
@ -1886,24 +1892,30 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
|
|||
generateColumnTypes(pParse, p->pSrc, p->pEList);
|
||||
}
|
||||
|
||||
/* If the output is destined for a temporary table, open that table.
|
||||
*/
|
||||
if( eDest==SRT_TempTable ){
|
||||
sqliteVdbeAddOp(v, OP_OpenTemp, iParm, 0);
|
||||
}
|
||||
|
||||
/* Generating code to find the min or the max. Basically all we have
|
||||
** to do is find the first or the last entry in the chosen index. If
|
||||
** the min() or max() is on the INTEGER PRIMARY KEY, then find the first
|
||||
** or last entry in the main table.
|
||||
*/
|
||||
sqliteCodeVerifySchema(pParse, pTab->iDb);
|
||||
base = p->pSrc->a[0].iCursor;
|
||||
base = pSrc->a[0].iCursor;
|
||||
computeLimitRegisters(pParse, p);
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
if( pSrc->a[0].pSelect==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeOp3(v, OP_OpenRead, base, pTab->tnum, pTab->zName, 0);
|
||||
}
|
||||
cont = sqliteVdbeMakeLabel(v);
|
||||
if( pIdx==0 ){
|
||||
sqliteVdbeAddOp(v, seekOp, base, 0);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenRead, base+1, pIdx->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_OpenRead, base+1, pIdx->tnum, pIdx->zName, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, seekOp, base+1, 0);
|
||||
sqliteVdbeAddOp(v, OP_IdxRecno, base+1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Close, base+1, 0);
|
||||
|
@ -1916,6 +1928,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
|
|||
selectInnerLoop(pParse, p, &eList, 0, 0, 0, -1, eDest, iParm, cont, cont);
|
||||
sqliteVdbeResolveLabel(v, cont);
|
||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1935,7 +1948,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
|
|||
**
|
||||
** SRT_Union Store results as a key in a temporary table iParm
|
||||
**
|
||||
** SRT_Except Remove results form the temporary table iParm.
|
||||
** SRT_Except Remove results from the temporary table iParm.
|
||||
**
|
||||
** SRT_Table Store results in temporary table iParm
|
||||
**
|
||||
|
@ -2156,14 +2169,6 @@ int sqliteSelect(
|
|||
generateColumnNames(pParse, pTabList, pEList);
|
||||
}
|
||||
|
||||
/* Check for the special case of a min() or max() function by itself
|
||||
** in the result set.
|
||||
*/
|
||||
if( simpleMinMaxQuery(pParse, p, eDest, iParm) ){
|
||||
rc = 0;
|
||||
goto select_end;
|
||||
}
|
||||
|
||||
/* Generate code for all sub-queries in the FROM clause
|
||||
*/
|
||||
for(i=0; i<pTabList->nSrc; i++){
|
||||
|
@ -2193,6 +2198,14 @@ int sqliteSelect(
|
|||
isDistinct = p->isDistinct;
|
||||
}
|
||||
|
||||
/* Check for the special case of a min() or max() function by itself
|
||||
** in the result set.
|
||||
*/
|
||||
if( simpleMinMaxQuery(pParse, p, eDest, iParm) ){
|
||||
rc = 0;
|
||||
goto select_end;
|
||||
}
|
||||
|
||||
/* Check to see if this is a subquery that can be "flattened" into its parent.
|
||||
** If flattening is a possiblity, do so and return immediately.
|
||||
*/
|
||||
|
@ -2262,8 +2275,7 @@ int sqliteSelect(
|
|||
for(i=0; i<pParse->nAgg; i++){
|
||||
FuncDef *pFunc;
|
||||
if( (pFunc = pParse->aAgg[i].pFunc)!=0 && pFunc->xFinalize!=0 ){
|
||||
sqliteVdbeAddOp(v, OP_AggInit, 0, i);
|
||||
sqliteVdbeChangeP3(v, -1, (char*)pFunc, P3_POINTER);
|
||||
sqliteVdbeOp3(v, OP_AggInit, 0, i, (char*)pFunc, P3_POINTER);
|
||||
}
|
||||
}
|
||||
if( pGroupBy==0 ){
|
||||
|
@ -2308,6 +2320,7 @@ int sqliteSelect(
|
|||
** processing.
|
||||
*/
|
||||
else{
|
||||
AggExpr *pAgg;
|
||||
if( pGroupBy ){
|
||||
int lbl1;
|
||||
for(i=0; i<pGroupBy->nExpr; i++){
|
||||
|
@ -2317,29 +2330,27 @@ int sqliteSelect(
|
|||
if( pParse->db->file_format>=4 ) sqliteAddKeyType(v, pGroupBy);
|
||||
lbl1 = sqliteVdbeMakeLabel(v);
|
||||
sqliteVdbeAddOp(v, OP_AggFocus, 0, lbl1);
|
||||
for(i=0; i<pParse->nAgg; i++){
|
||||
if( pParse->aAgg[i].isAgg ) continue;
|
||||
sqliteExprCode(pParse, pParse->aAgg[i].pExpr);
|
||||
for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
|
||||
if( pAgg->isAgg ) continue;
|
||||
sqliteExprCode(pParse, pAgg->pExpr);
|
||||
sqliteVdbeAddOp(v, OP_AggSet, 0, i);
|
||||
}
|
||||
sqliteVdbeResolveLabel(v, lbl1);
|
||||
}
|
||||
for(i=0; i<pParse->nAgg; i++){
|
||||
for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
|
||||
Expr *pE;
|
||||
int j;
|
||||
if( !pParse->aAgg[i].isAgg ) continue;
|
||||
pE = pParse->aAgg[i].pExpr;
|
||||
int nExpr;
|
||||
FuncDef *pDef;
|
||||
if( !pAgg->isAgg ) continue;
|
||||
assert( pAgg->pFunc!=0 );
|
||||
assert( pAgg->pFunc->xStep!=0 );
|
||||
pDef = pAgg->pFunc;
|
||||
pE = pAgg->pExpr;
|
||||
assert( pE!=0 );
|
||||
assert( pE->op==TK_AGG_FUNCTION );
|
||||
if( pE->pList ){
|
||||
for(j=0; j<pE->pList->nExpr; j++){
|
||||
sqliteExprCode(pParse, pE->pList->a[j].pExpr);
|
||||
}
|
||||
}
|
||||
nExpr = sqliteExprCodeExprList(pParse, pE->pList, pDef->includeTypes);
|
||||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_AggFunc, 0, pE->pList ? pE->pList->nExpr : 0);
|
||||
assert( pParse->aAgg[i].pFunc!=0 );
|
||||
assert( pParse->aAgg[i].pFunc->xStep!=0 );
|
||||
sqliteVdbeChangeP3(v, -1, (char*)pParse->aAgg[i].pFunc, P3_POINTER);
|
||||
sqliteVdbeOp3(v, OP_AggFunc, 0, nExpr, (char*)pDef, P3_POINTER);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2375,13 +2386,16 @@ int sqliteSelect(
|
|||
generateSortTail(p, v, pEList->nExpr, eDest, iParm);
|
||||
}
|
||||
|
||||
|
||||
/* Issue a null callback if that is what the user wants.
|
||||
/* If this was a subquery, we have now converted the subquery into a
|
||||
** temporary table. So delete the subquery structure from the parent
|
||||
** to prevent this subquery from being evaluated again and to force the
|
||||
** the use of the temporary table.
|
||||
*/
|
||||
if( eDest==SRT_Callback &&
|
||||
(pParse->useCallback==0 || (pParse->db->flags & SQLITE_NullCallback)!=0)
|
||||
){
|
||||
sqliteVdbeAddOp(v, OP_NullCallback, pEList->nExpr, 0);
|
||||
if( pParent ){
|
||||
assert( pParent->pSrc->nSrc>parentTab );
|
||||
assert( pParent->pSrc->a[parentTab].pSelect==p );
|
||||
sqliteSelectDelete(p);
|
||||
pParent->pSrc->a[parentTab].pSelect = 0;
|
||||
}
|
||||
|
||||
/* The SELECT was successfully coded. Set the return code to 0
|
||||
|
|
|
@ -167,6 +167,7 @@ int sqlite_exec(
|
|||
#define SQLITE_AUTH 23 /* Authorization denied */
|
||||
#define SQLITE_FORMAT 24 /* Auxiliary database format error */
|
||||
#define SQLITE_RANGE 25 /* 2nd parameter to sqlite_bind out of range */
|
||||
#define SQLITE_NOTADB 26 /* File opened that is not a database file */
|
||||
#define SQLITE_ROW 100 /* sqlite_step() has another row ready */
|
||||
#define SQLITE_DONE 101 /* sqlite_step() has finished executing */
|
||||
|
||||
|
@ -203,6 +204,32 @@ int sqlite_last_insert_rowid(sqlite*);
|
|||
*/
|
||||
int sqlite_changes(sqlite*);
|
||||
|
||||
/*
|
||||
** This function returns the number of database rows that were changed
|
||||
** by the last INSERT, UPDATE, or DELETE statment executed by sqlite_exec(),
|
||||
** or by the last VM to run to completion. The change count is not updated
|
||||
** by SQL statements other than INSERT, UPDATE or DELETE.
|
||||
**
|
||||
** Changes are counted, even if they are later undone by a ROLLBACK or
|
||||
** ABORT. Changes associated with trigger programs that execute as a
|
||||
** result of the INSERT, UPDATE, or DELETE statement are not counted.
|
||||
**
|
||||
** If a callback invokes sqlite_exec() recursively, then the changes
|
||||
** in the inner, recursive call are counted together with the changes
|
||||
** in the outer call.
|
||||
**
|
||||
** SQLite implements the command "DELETE FROM table" without a WHERE clause
|
||||
** by dropping and recreating the table. (This is much faster than going
|
||||
** through and deleting individual elements form the table.) Because of
|
||||
** this optimization, the change count for "DELETE FROM table" will be
|
||||
** zero regardless of the number of elements that were originally in the
|
||||
** table. To get an accurate count of the number of rows deleted, use
|
||||
** "DELETE FROM table WHERE 1" instead.
|
||||
**
|
||||
******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ******
|
||||
*/
|
||||
int sqlite_last_statement_changes(sqlite*);
|
||||
|
||||
/* If the parameter to this routine is one of the return value constants
|
||||
** defined above, then this routine returns a constant text string which
|
||||
** descripts (in English) the meaning of the return value.
|
||||
|
@ -262,7 +289,7 @@ void sqlite_busy_handler(sqlite*, int(*)(void*,const char*,int), void*);
|
|||
** Calling this routine with an argument less than or equal to zero
|
||||
** turns off all busy handlers.
|
||||
*/
|
||||
void sqlite_busy_timeout(sqlite*, long ms);
|
||||
void sqlite_busy_timeout(sqlite*, int ms);
|
||||
|
||||
/*
|
||||
** This next routine is really just a wrapper around sqlite_exec().
|
||||
|
@ -439,13 +466,12 @@ int sqlite_create_aggregate(
|
|||
** Use the following routine to define the datatype returned by a
|
||||
** user-defined function. The second argument can be one of the
|
||||
** constants SQLITE_NUMERIC, SQLITE_TEXT, or SQLITE_ARGS or it
|
||||
** can be an integer greater than or equal to zero. The datatype
|
||||
** will be numeric or text (the only two types supported) if the
|
||||
** argument is SQLITE_NUMERIC or SQLITE_TEXT. If the argument is
|
||||
** SQLITE_ARGS, then the datatype is numeric if any argument to the
|
||||
** function is numeric and is text otherwise. If the second argument
|
||||
** is an integer, then the datatype of the result is the same as the
|
||||
** parameter to the function that corresponds to that integer.
|
||||
** can be an integer greater than or equal to zero. When the datatype
|
||||
** parameter is non-negative, the type of the result will be the
|
||||
** same as the datatype-th argument. If datatype==SQLITE_NUMERIC
|
||||
** then the result is always numeric. If datatype==SQLITE_TEXT then
|
||||
** the result is always text. If datatype==SQLITE_ARGS then the result
|
||||
** is numeric if any argument is numeric and is text otherwise.
|
||||
*/
|
||||
int sqlite_function_type(
|
||||
sqlite *db, /* The database there the function is registered */
|
||||
|
@ -753,9 +779,88 @@ int sqlite_bind(sqlite_vm*, int idx, const char *value, int len, int copy);
|
|||
** query is immediately terminated and any database changes rolled back. If the
|
||||
** query was part of a larger transaction, then the transaction is not rolled
|
||||
** back and remains active. The sqlite_exec() call returns SQLITE_ABORT.
|
||||
**
|
||||
******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ******
|
||||
*/
|
||||
void sqlite_progress_handler(sqlite*, int, int(*)(void*), void*);
|
||||
|
||||
/*
|
||||
** Register a callback function to be invoked whenever a new transaction
|
||||
** is committed. The pArg argument is passed through to the callback.
|
||||
** callback. If the callback function returns non-zero, then the commit
|
||||
** is converted into a rollback.
|
||||
**
|
||||
** If another function was previously registered, its pArg value is returned.
|
||||
** Otherwise NULL is returned.
|
||||
**
|
||||
** Registering a NULL function disables the callback.
|
||||
**
|
||||
******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ******
|
||||
*/
|
||||
void *sqlite_commit_hook(sqlite*, int(*)(void*), void*);
|
||||
|
||||
/*
|
||||
** Open an encrypted SQLite database. If pKey==0 or nKey==0, this routine
|
||||
** is the same as sqlite_open().
|
||||
**
|
||||
** The code to implement this API is not available in the public release
|
||||
** of SQLite.
|
||||
*/
|
||||
sqlite *sqlite_open_encrypted(
|
||||
const char *zFilename, /* Name of the encrypted database */
|
||||
const void *pKey, /* Pointer to the key */
|
||||
int nKey, /* Number of bytes in the key */
|
||||
int *pErrcode, /* Write error code here */
|
||||
char **pzErrmsg /* Write error message here */
|
||||
);
|
||||
|
||||
/*
|
||||
** Change the key on an open database. If the current database is not
|
||||
** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the
|
||||
** database is decrypted.
|
||||
**
|
||||
** The code to implement this API is not available in the public release
|
||||
** of SQLite.
|
||||
*/
|
||||
int sqlite_rekey(
|
||||
sqlite *db, /* Database to be rekeyed */
|
||||
const void *pKey, int nKey /* The new key */
|
||||
);
|
||||
|
||||
/*
|
||||
** Encode a binary buffer "in" of size n bytes so that it contains
|
||||
** no instances of characters '\'' or '\000'. The output is
|
||||
** null-terminated and can be used as a string value in an INSERT
|
||||
** or UPDATE statement. Use sqlite_decode_binary() to convert the
|
||||
** string back into its original binary.
|
||||
**
|
||||
** The result is written into a preallocated output buffer "out".
|
||||
** "out" must be able to hold at least 2 +(257*n)/254 bytes.
|
||||
** In other words, the output will be expanded by as much as 3
|
||||
** bytes for every 254 bytes of input plus 2 bytes of fixed overhead.
|
||||
** (This is approximately 2 + 1.0118*n or about a 1.2% size increase.)
|
||||
**
|
||||
** The return value is the number of characters in the encoded
|
||||
** string, excluding the "\000" terminator.
|
||||
**
|
||||
** If out==NULL then no output is generated but the routine still returns
|
||||
** the number of characters that would have been generated if out had
|
||||
** not been NULL.
|
||||
*/
|
||||
int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out);
|
||||
|
||||
/*
|
||||
** Decode the string "in" into binary data and write it into "out".
|
||||
** This routine reverses the encoding created by sqlite_encode_binary().
|
||||
** The output will always be a few bytes less than the input. The number
|
||||
** of bytes of output is returned. If the input is not a well-formed
|
||||
** encoding, -1 is returned.
|
||||
**
|
||||
** The "in" and "out" parameters may point to the same buffer in order
|
||||
** to decode a string in place.
|
||||
*/
|
||||
int sqlite_decode_binary(const unsigned char *in, unsigned char *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of the 'extern "C"' block */
|
||||
#endif
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include "config.h"
|
||||
#include "sqlite.h"
|
||||
#include "hash.h"
|
||||
#include "vdbe.h"
|
||||
#include "parse.h"
|
||||
#include "btree.h"
|
||||
#include <stdio.h>
|
||||
|
@ -106,6 +105,9 @@
|
|||
#ifndef UINT8_TYPE
|
||||
# define UINT8_TYPE unsigned char
|
||||
#endif
|
||||
#ifndef INT8_TYPE
|
||||
# define INT8_TYPE signed char
|
||||
#endif
|
||||
#ifndef INTPTR_TYPE
|
||||
# if SQLITE_PTR_SZ==4
|
||||
# define INTPTR_TYPE int
|
||||
|
@ -116,9 +118,15 @@
|
|||
typedef UINT32_TYPE u32; /* 4-byte unsigned integer */
|
||||
typedef UINT16_TYPE u16; /* 2-byte unsigned integer */
|
||||
typedef UINT8_TYPE u8; /* 1-byte unsigned integer */
|
||||
typedef UINT8_TYPE i8; /* 1-byte signed integer */
|
||||
typedef INTPTR_TYPE ptr; /* Big enough to hold a pointer */
|
||||
typedef unsigned INTPTR_TYPE uptr; /* Big enough to hold a pointer */
|
||||
|
||||
/*
|
||||
** Defer sourcing vdbe.h until after the "u8" typedef is defined.
|
||||
*/
|
||||
#include "vdbe.h"
|
||||
|
||||
/*
|
||||
** Most C compilers these days recognize "long double", don't they?
|
||||
** Just in case we encounter one that does not, we will create a macro
|
||||
|
@ -174,6 +182,7 @@ typedef unsigned INTPTR_TYPE uptr; /* Big enough to hold a pointer */
|
|||
# define sqliteStrNDup(X,Y) sqliteStrNDup_(X,Y,__FILE__,__LINE__)
|
||||
void sqliteStrRealloc(char**);
|
||||
#else
|
||||
# define sqliteRealloc_(X,Y) sqliteRealloc(X,Y)
|
||||
# define sqliteStrRealloc(X)
|
||||
#endif
|
||||
|
||||
|
@ -254,6 +263,8 @@ struct Db {
|
|||
Hash aFKey; /* Foreign keys indexed by to-table */
|
||||
u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */
|
||||
u16 flags; /* Flags associated with this database */
|
||||
void *pAux; /* Auxiliary data. Usually NULL */
|
||||
void (*xFreeAux)(void*); /* Routine to free pAux */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -307,6 +318,24 @@ struct Db {
|
|||
** are stored. If 1, then a file is created to hold those tables. If
|
||||
** 2, then they are held in memory. 0 means use the default value in
|
||||
** the TEMP_STORE macro.
|
||||
**
|
||||
** The sqlite.lastRowid records the last insert rowid generated by an
|
||||
** insert statement. Inserts on views do not affect its value. Each
|
||||
** trigger has its own context, so that lastRowid can be updated inside
|
||||
** triggers as usual. The previous value will be restored once the trigger
|
||||
** exits. Upon entering a before or instead of trigger, lastRowid is no
|
||||
** longer (since after version 2.8.12) reset to -1.
|
||||
**
|
||||
** The sqlite.nChange does not count changes within triggers and keeps no
|
||||
** context. It is reset at start of sqlite_exec.
|
||||
** The sqlite.lsChange represents the number of changes made by the last
|
||||
** insert, update, or delete statement. It remains constant throughout the
|
||||
** length of a statement and is then updated by OP_SetCounts. It keeps a
|
||||
** context stack just like lastRowid so that the count of changes
|
||||
** within a trigger is not seen outside the trigger. Changes to views do not
|
||||
** affect the value of lsChange.
|
||||
** The sqlite.csChange keeps track of the number of current changes (since
|
||||
** the last statement) and is used to update sqlite_lsChange.
|
||||
*/
|
||||
struct sqlite {
|
||||
int nDb; /* Number of backends currently in use */
|
||||
|
@ -316,18 +345,27 @@ struct sqlite {
|
|||
u8 file_format; /* What file format version is this database? */
|
||||
u8 safety_level; /* How aggressive at synching data to disk */
|
||||
u8 want_to_close; /* Close after all VDBEs are deallocated */
|
||||
u8 temp_store; /* 1=file, 2=memory, 0=compile-time default */
|
||||
u8 onError; /* Default conflict algorithm */
|
||||
int next_cookie; /* Next value of aDb[0].schema_cookie */
|
||||
int cache_size; /* Number of pages to use in the cache */
|
||||
int temp_store; /* 1=file, 2=memory, 0=compile-time default */
|
||||
int nTable; /* Number of tables in the database */
|
||||
void *pBusyArg; /* 1st Argument to the busy callback */
|
||||
int (*xBusyCallback)(void *,const char*,int); /* The busy callback */
|
||||
void *pCommitArg; /* Argument to xCommitCallback() */
|
||||
int (*xCommitCallback)(void*);/* Invoked at every commit. */
|
||||
Hash aFunc; /* All functions that can be in SQL exprs */
|
||||
int lastRowid; /* ROWID of most recent insert */
|
||||
int lastRowid; /* ROWID of most recent insert (see above) */
|
||||
int priorNewRowid; /* Last randomly generated ROWID */
|
||||
int onError; /* Default conflict algorithm */
|
||||
int magic; /* Magic number for detect library misuse */
|
||||
int nChange; /* Number of rows changed */
|
||||
int nChange; /* Number of rows changed (see above) */
|
||||
int lsChange; /* Last statement change count (see above) */
|
||||
int csChange; /* Current statement change count (see above) */
|
||||
struct sqliteInitInfo { /* Information used during initialization */
|
||||
int iDb; /* When back is being initialized */
|
||||
int newTnum; /* Rootpage of table being initialized */
|
||||
u8 busy; /* TRUE if currently initializing */
|
||||
} init;
|
||||
struct Vdbe *pVdbe; /* List of active virtual machines */
|
||||
void (*xTrace)(void*,const char*); /* Trace function */
|
||||
void *pTraceArg; /* Argument to the trace function */
|
||||
|
@ -356,10 +394,11 @@ struct sqlite {
|
|||
#define SQLITE_InTrans 0x00000008 /* True if in a transaction */
|
||||
#define SQLITE_InternChanges 0x00000010 /* Uncommitted Hash table changes */
|
||||
#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */
|
||||
#define SQLITE_CountRows 0x00000040 /* Count rows changed by INSERT, */
|
||||
#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */
|
||||
#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */
|
||||
/* DELETE, or UPDATE and return */
|
||||
/* the count using a callback. */
|
||||
#define SQLITE_NullCallback 0x00000080 /* Invoke the callback once if the */
|
||||
#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */
|
||||
/* result set is empty */
|
||||
#define SQLITE_ReportTypes 0x00000200 /* Include information on datatypes */
|
||||
/* in 4th argument of callback */
|
||||
|
@ -384,10 +423,12 @@ struct FuncDef {
|
|||
void (*xFunc)(sqlite_func*,int,const char**); /* Regular function */
|
||||
void (*xStep)(sqlite_func*,int,const char**); /* Aggregate function step */
|
||||
void (*xFinalize)(sqlite_func*); /* Aggregate function finializer */
|
||||
int nArg; /* Number of arguments */
|
||||
int dataType; /* Datatype of the result */
|
||||
void *pUserData; /* User data parameter */
|
||||
FuncDef *pNext; /* Next function with same name */
|
||||
signed char nArg; /* Number of arguments. -1 means unlimited */
|
||||
signed char dataType; /* Arg that determines datatype. -1=NUMERIC, */
|
||||
/* -2=TEXT. -3=SQLITE_ARGS */
|
||||
u8 includeTypes; /* Add datatypes to args of xFunc and xStep */
|
||||
void *pUserData; /* User data parameter */
|
||||
FuncDef *pNext; /* Next function with same name */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -401,6 +442,7 @@ struct Column {
|
|||
u8 notNull; /* True if there is a NOT NULL constraint */
|
||||
u8 isPrimKey; /* True if this column is part of the PRIMARY KEY */
|
||||
u8 sortOrder; /* Some combination of SQLITE_SO_... values */
|
||||
u8 dottedName; /* True if zName contains a "." character */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -585,6 +627,10 @@ struct Index {
|
|||
/*
|
||||
** Each token coming out of the lexer is an instance of
|
||||
** this structure. Tokens are also used as part of an expression.
|
||||
**
|
||||
** Note if Token.z==0 then Token.dyn and Token.n are undefined and
|
||||
** may contain random values. Do not make any assuptions about Token.dyn
|
||||
** and Token.n when Token.z==0.
|
||||
*/
|
||||
struct Token {
|
||||
const char *z; /* Text of the token. Not NULL-terminated! */
|
||||
|
@ -859,8 +905,6 @@ struct AggExpr {
|
|||
struct Parse {
|
||||
sqlite *db; /* The main database structure */
|
||||
int rc; /* Return code from execution */
|
||||
sqlite_callback xCallback; /* The callback function */
|
||||
void *pArg; /* First argument to the callback function */
|
||||
char *zErrMsg; /* An error message */
|
||||
Token sErrToken; /* The token at which the error occurred */
|
||||
Token sFirstToken; /* The first token parsed */
|
||||
|
@ -870,13 +914,9 @@ struct Parse {
|
|||
Vdbe *pVdbe; /* An engine for executing database bytecode */
|
||||
u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */
|
||||
u8 explain; /* True if the EXPLAIN flag is found on the query */
|
||||
u8 initFlag; /* True if reparsing CREATE TABLEs */
|
||||
u8 nameClash; /* A permanent table name clashes with temp table name */
|
||||
u8 useAgg; /* If true, extract field values from the aggregator
|
||||
** while generating expressions. Normally false */
|
||||
u8 iDb; /* Index of database whose schema is being parsed */
|
||||
u8 useCallback; /* True if callbacks should be used to report results */
|
||||
int newTnum; /* Table number to use when reparsing CREATE TABLEs */
|
||||
int nErr; /* Number of errors seen */
|
||||
int nTab; /* Number of previously allocated VDBE cursors */
|
||||
int nMem; /* Number of memory cells used so far */
|
||||
|
@ -898,6 +938,13 @@ struct AuthContext {
|
|||
Parse *pParse; /* The Parse structure */
|
||||
};
|
||||
|
||||
/*
|
||||
** Bitfield flags for P2 value in OP_PutIntKey and OP_Delete
|
||||
*/
|
||||
#define OPFLAG_NCHANGE 1 /* Set to update db->nChange */
|
||||
#define OPFLAG_LASTROWID 2 /* Set to update db->lastRowid */
|
||||
#define OPFLAG_CSCHANGE 4 /* Set to update db->csChange */
|
||||
|
||||
/*
|
||||
* Each trigger present in the database schema is stored as an instance of
|
||||
* struct Trigger.
|
||||
|
@ -1067,7 +1114,8 @@ void sqliteRealToSortable(double r, char *);
|
|||
char *sqliteStrNDup(const char*, int);
|
||||
# define sqliteCheckMemory(a,b)
|
||||
#endif
|
||||
char *sqliteMPrintf(const char *,...);
|
||||
char *sqliteMPrintf(const char*, ...);
|
||||
char *sqliteVMPrintf(const char*, va_list);
|
||||
void sqliteSetString(char **, const char *, ...);
|
||||
void sqliteSetNString(char **, ...);
|
||||
void sqliteErrorMsg(Parse*, const char*, ...);
|
||||
|
@ -1126,6 +1174,7 @@ void sqliteUpdate(Parse*, SrcList*, ExprList*, Expr*, int);
|
|||
WhereInfo *sqliteWhereBegin(Parse*, SrcList*, Expr*, int, ExprList**);
|
||||
void sqliteWhereEnd(WhereInfo*);
|
||||
void sqliteExprCode(Parse*, Expr*);
|
||||
int sqliteExprCodeExprList(Parse*, ExprList*, int);
|
||||
void sqliteExprIfTrue(Parse*, Expr*, int, int);
|
||||
void sqliteExprIfFalse(Parse*, Expr*, int, int);
|
||||
Table *sqliteFindTable(sqlite*,const char*, const char*);
|
||||
|
@ -1145,8 +1194,7 @@ int sqliteFuncId(Token*);
|
|||
int sqliteExprResolveIds(Parse*, SrcList*, ExprList*, Expr*);
|
||||
int sqliteExprAnalyzeAggregates(Parse*, Expr*);
|
||||
Vdbe *sqliteGetVdbe(Parse*);
|
||||
int sqliteRandomByte(void);
|
||||
int sqliteRandomInteger(void);
|
||||
void sqliteRandomness(int, void*);
|
||||
void sqliteRollbackAll(sqlite*);
|
||||
void sqliteCodeVerifySchema(Parse*, int);
|
||||
void sqliteBeginTransaction(Parse*, int);
|
||||
|
@ -1159,6 +1207,7 @@ void sqliteGenerateRowDelete(sqlite*, Vdbe*, Table*, int, int);
|
|||
void sqliteGenerateRowIndexDelete(sqlite*, Vdbe*, Table*, int, char*);
|
||||
void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
|
||||
void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int, int);
|
||||
int sqliteOpenTableAndIndices(Parse*, Table*, int);
|
||||
void sqliteBeginWriteOperation(Parse*, int, int);
|
||||
void sqliteEndWriteOperation(Parse*);
|
||||
Expr *sqliteExprDup(Expr*);
|
||||
|
@ -1202,7 +1251,7 @@ void sqliteDeferForeignKey(Parse*, int);
|
|||
# define sqliteAuthContextPush(a,b,c)
|
||||
# define sqliteAuthContextPop(a) ((void)(a))
|
||||
#endif
|
||||
void sqliteAttach(Parse*, Token*, Token*);
|
||||
void sqliteAttach(Parse*, Token*, Token*, Token*);
|
||||
void sqliteDetach(Parse*, Token*);
|
||||
int sqliteBtreeFactory(const sqlite *db, const char *zFilename,
|
||||
int mode, int nPg, Btree **ppBtree);
|
||||
|
@ -1212,6 +1261,6 @@ int sqliteFixSelect(DbFixer*, Select*);
|
|||
int sqliteFixExpr(DbFixer*, Expr*);
|
||||
int sqliteFixExprList(DbFixer*, ExprList*);
|
||||
int sqliteFixTriggerStep(DbFixer*, TriggerStep*);
|
||||
double sqliteAtoF(const char *z);
|
||||
int sqlite_snprintf(int,char*,const char*,...);
|
||||
double sqliteAtoF(const char *z, const char **);
|
||||
char *sqlite_snprintf(int,char*,const char*,...);
|
||||
int sqliteFitsIn32Bits(const char *);
|
||||
|
|
|
@ -474,7 +474,7 @@ abort_parse:
|
|||
pParse->zErrMsg = 0;
|
||||
if( !nErr ) nErr++;
|
||||
}
|
||||
if( pParse->pVdbe && (pParse->useCallback || pParse->nErr>0) ){
|
||||
if( pParse->pVdbe && pParse->nErr>0 ){
|
||||
sqliteVdbeDelete(pParse->pVdbe);
|
||||
pParse->pVdbe = 0;
|
||||
}
|
||||
|
|
|
@ -65,8 +65,8 @@ void sqliteBeginTrigger(
|
|||
*/
|
||||
if( sqlite_malloc_failed ) goto trigger_cleanup;
|
||||
assert( pTableName->nSrc==1 );
|
||||
if( pParse->initFlag
|
||||
&& sqliteFixInit(&sFix, pParse, pParse->iDb, "trigger", pName)
|
||||
if( db->init.busy
|
||||
&& sqliteFixInit(&sFix, pParse, db->init.iDb, "trigger", pName)
|
||||
&& sqliteFixSrcList(&sFix, pTableName)
|
||||
){
|
||||
goto trigger_cleanup;
|
||||
|
@ -76,7 +76,7 @@ void sqliteBeginTrigger(
|
|||
goto trigger_cleanup;
|
||||
}
|
||||
iDb = isTemp ? 1 : tab->iDb;
|
||||
if( iDb>=2 && !pParse->initFlag ){
|
||||
if( iDb>=2 && !db->init.busy ){
|
||||
sqliteErrorMsg(pParse, "triggers may not be added to auxiliary "
|
||||
"database %s", db->aDb[tab->iDb].zName);
|
||||
goto trigger_cleanup;
|
||||
|
@ -181,8 +181,8 @@ void sqliteFinishTrigger(
|
|||
/* if we are not initializing, and this trigger is not on a TEMP table,
|
||||
** build the sqlite_master entry
|
||||
*/
|
||||
if( !pParse->initFlag ){
|
||||
static VdbeOp insertTrig[] = {
|
||||
if( !db->init.busy ){
|
||||
static VdbeOpList insertTrig[] = {
|
||||
{ OP_NewRecno, 0, 0, 0 },
|
||||
{ OP_String, 0, 0, "trigger" },
|
||||
{ OP_String, 0, 0, 0 }, /* 2: trigger name */
|
||||
|
@ -450,7 +450,7 @@ void sqliteDropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
|
|||
*/
|
||||
if( pTable!=0 && !nested && (v = sqliteGetVdbe(pParse))!=0 ){
|
||||
int base;
|
||||
static VdbeOp dropTrigger[] = {
|
||||
static VdbeOpList dropTrigger[] = {
|
||||
{ OP_Rewind, 0, ADDR(9), 0},
|
||||
{ OP_String, 0, 0, 0}, /* 1 */
|
||||
{ OP_Column, 0, 1, 0},
|
||||
|
@ -746,7 +746,9 @@ int sqliteCodeRowTrigger(
|
|||
sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1);
|
||||
sqliteExprDelete(whenExpr);
|
||||
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_ContextPush, 0, 0);
|
||||
codeTriggerProgram(pParse, pTrigger->step_list, orconf);
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_ContextPop, 0, 0);
|
||||
|
||||
/* Pop the entry off the trigger stack */
|
||||
pParse->trigStack = pParse->trigStack->pNext;
|
||||
|
|
|
@ -32,7 +32,8 @@ void sqliteUpdate(
|
|||
){
|
||||
int i, j; /* Loop counters */
|
||||
Table *pTab; /* The table to be updated */
|
||||
int addr; /* VDBE instruction address of the start of the loop */
|
||||
int loopStart; /* VDBE instruction address of the start of the loop */
|
||||
int jumpInst; /* Addr of VDBE instruction to jump out of loop */
|
||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||
Vdbe *v; /* The virtual database engine */
|
||||
Index *pIdx; /* For looping over indices */
|
||||
|
@ -49,6 +50,7 @@ void sqliteUpdate(
|
|||
Expr *pRecnoExpr; /* Expression defining the new record number */
|
||||
int openAll; /* True if all indices need to be opened */
|
||||
int isView; /* Trying to update a view */
|
||||
int iStackDepth; /* Index of memory cell holding stack depth */
|
||||
AuthContext sContext; /* The authorization context */
|
||||
|
||||
int before_triggers; /* True if there are any BEFORE triggers */
|
||||
|
@ -62,6 +64,7 @@ void sqliteUpdate(
|
|||
if( pParse->nErr || sqlite_malloc_failed ) goto update_cleanup;
|
||||
db = pParse->db;
|
||||
assert( pTabList->nSrc==1 );
|
||||
iStackDepth = pParse->nMem++;
|
||||
|
||||
/* Locate the table which we want to update.
|
||||
*/
|
||||
|
@ -248,7 +251,11 @@ void sqliteUpdate(
|
|||
/* The top of the update loop for when there are triggers.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
|
||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_StackDepth, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_MemStore, iStackDepth, 1);
|
||||
loopStart = sqliteVdbeAddOp(v, OP_MemLoad, iStackDepth, 0);
|
||||
sqliteVdbeAddOp(v, OP_StackReset, 0, 0);
|
||||
jumpInst = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||
|
||||
/* Open a cursor and make it point to the record that is
|
||||
|
@ -295,7 +302,7 @@ void sqliteUpdate(
|
|||
/* Fire the BEFORE and INSTEAD OF triggers
|
||||
*/
|
||||
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab,
|
||||
newIdx, oldIdx, onError, addr) ){
|
||||
newIdx, oldIdx, onError, loopStart) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
}
|
||||
|
@ -336,10 +343,10 @@ void sqliteUpdate(
|
|||
*/
|
||||
if( !row_triggers_exist ){
|
||||
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
|
||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
|
||||
jumpInst = loopStart = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_NotExists, iCur, addr);
|
||||
sqliteVdbeAddOp(v, OP_NotExists, iCur, loopStart);
|
||||
|
||||
/* If the record number will change, push the record number as it
|
||||
** will be after the update. (The old record number is currently
|
||||
|
@ -368,7 +375,7 @@ void sqliteUpdate(
|
|||
/* Do constraint checks
|
||||
*/
|
||||
sqliteGenerateConstraintChecks(pParse, pTab, iCur, aIdxUsed, chngRecno, 1,
|
||||
onError, addr);
|
||||
onError, loopStart);
|
||||
|
||||
/* Delete the old indices for the current record.
|
||||
*/
|
||||
|
@ -404,7 +411,7 @@ void sqliteUpdate(
|
|||
pParse->nTab = iCur;
|
||||
}
|
||||
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab,
|
||||
newIdx, oldIdx, onError, addr) ){
|
||||
newIdx, oldIdx, onError, loopStart) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
}
|
||||
|
@ -412,8 +419,8 @@ void sqliteUpdate(
|
|||
/* Repeat the above with the next record to be updated, until
|
||||
** all record selected by the WHERE clause have been updated.
|
||||
*/
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr);
|
||||
sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v));
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, loopStart);
|
||||
sqliteVdbeChangeP2(v, jumpInst, sqliteVdbeCurrentAddr(v));
|
||||
sqliteVdbeAddOp(v, OP_ListReset, 0, 0);
|
||||
|
||||
/* Close all tables if there were no FOR EACH ROW triggers */
|
||||
|
@ -430,14 +437,14 @@ void sqliteUpdate(
|
|||
sqliteVdbeAddOp(v, OP_Close, oldIdx, 0);
|
||||
}
|
||||
|
||||
sqliteVdbeAddOp(v, OP_SetCounts, 0, 0);
|
||||
sqliteEndWriteOperation(pParse);
|
||||
|
||||
/*
|
||||
** Return the number of rows that were changed.
|
||||
*/
|
||||
if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
|
||||
sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, "rows updated", P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_ColumnName, 0, 1, "rows updated", P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 1, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -417,120 +417,11 @@ void sqliteSetNString(char **pz, ...){
|
|||
*/
|
||||
void sqliteErrorMsg(Parse *pParse, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
int nByte;
|
||||
int i, j;
|
||||
char *z;
|
||||
static char zNull[] = "NULL";
|
||||
|
||||
pParse->nErr++;
|
||||
nByte = 1 + strlen(zFormat);
|
||||
va_start(ap, zFormat);
|
||||
for(i=0; zFormat[i]; i++){
|
||||
if( zFormat[i]!='%' || zFormat[i+1]==0 ) continue;
|
||||
i++;
|
||||
switch( zFormat[i] ){
|
||||
case 'd': {
|
||||
(void)va_arg(ap, int);
|
||||
nByte += 20;
|
||||
break;
|
||||
}
|
||||
case 'z':
|
||||
case 's': {
|
||||
char *z2 = va_arg(ap, char*);
|
||||
if( z2==0 ) z2 = zNull;
|
||||
nByte += strlen(z2);
|
||||
break;
|
||||
}
|
||||
case 'T': {
|
||||
Token *p = va_arg(ap, Token*);
|
||||
nByte += p->n;
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
SrcList *p = va_arg(ap, SrcList*);
|
||||
int k = va_arg(ap, int);
|
||||
assert( p->nSrc>k && k>=0 );
|
||||
nByte += strlen(p->a[k].zName);
|
||||
if( p->a[k].zDatabase && p->a[k].zDatabase[0] ){
|
||||
nByte += strlen(p->a[k].zDatabase)+1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
nByte++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
z = sqliteMalloc( nByte );
|
||||
if( z==0 ) return;
|
||||
sqliteFree(pParse->zErrMsg);
|
||||
pParse->zErrMsg = z;
|
||||
va_start(ap, zFormat);
|
||||
for(i=j=0; zFormat[i]; i++){
|
||||
if( zFormat[i]!='%' || zFormat[i+1]==0 ) continue;
|
||||
if( i>j ){
|
||||
memcpy(z, &zFormat[j], i-j);
|
||||
z += i-j;
|
||||
}
|
||||
j = i+2;
|
||||
i++;
|
||||
switch( zFormat[i] ){
|
||||
case 'd': {
|
||||
int x = va_arg(ap, int);
|
||||
sprintf(z, "%d", x);
|
||||
z += strlen(z);
|
||||
break;
|
||||
}
|
||||
case 'z':
|
||||
case 's': {
|
||||
int len;
|
||||
char *z2 = va_arg(ap, char*);
|
||||
if( z2==0 ) z2 = zNull;
|
||||
len = strlen(z2);
|
||||
memcpy(z, z2, len);
|
||||
z += len;
|
||||
if( zFormat[i]=='z' && z2!=zNull ){
|
||||
sqliteFree(z2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'T': {
|
||||
Token *p = va_arg(ap, Token*);
|
||||
memcpy(z, p->z, p->n);
|
||||
z += p->n;
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
int len;
|
||||
SrcList *p = va_arg(ap, SrcList*);
|
||||
int k = va_arg(ap, int);
|
||||
assert( p->nSrc>k && k>=0 );
|
||||
if( p->a[k].zDatabase && p->a[k].zDatabase[0] ){
|
||||
len = strlen(p->a[k].zDatabase);
|
||||
memcpy(z, p->a[k].zDatabase, len);
|
||||
z += len;
|
||||
*(z++) = '.';
|
||||
}
|
||||
len = strlen(p->a[k].zName);
|
||||
memcpy(z, p->a[k].zName, len);
|
||||
z += len;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
*(z++) = zFormat[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pParse->zErrMsg = sqliteVMPrintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
if( i>j ){
|
||||
memcpy(z, &zFormat[j], i-j);
|
||||
z += i-j;
|
||||
}
|
||||
assert( (z - pParse->zErrMsg) < nByte );
|
||||
*z = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -662,7 +553,7 @@ int sqliteIsNumber(const char *z){
|
|||
** of "." depending on how locale is set. But that would cause problems
|
||||
** for SQL. So this routine always uses "." regardless of locale.
|
||||
*/
|
||||
double sqliteAtoF(const char *z){
|
||||
double sqliteAtoF(const char *z, const char **pzEnd){
|
||||
int sign = 1;
|
||||
LONGDOUBLE_TYPE v1 = 0.0;
|
||||
if( *z=='-' ){
|
||||
|
@ -710,6 +601,7 @@ double sqliteAtoF(const char *z){
|
|||
v1 *= scale;
|
||||
}
|
||||
}
|
||||
if( pzEnd ) *pzEnd = z;
|
||||
return sign<0 ? -v1 : v1;
|
||||
}
|
||||
|
||||
|
@ -759,8 +651,8 @@ int sqliteCompare(const char *atext, const char *btext){
|
|||
result = -1;
|
||||
}else{
|
||||
double rA, rB;
|
||||
rA = sqliteAtoF(atext);
|
||||
rB = sqliteAtoF(btext);
|
||||
rA = sqliteAtoF(atext, 0);
|
||||
rB = sqliteAtoF(btext, 0);
|
||||
if( rA<rB ){
|
||||
result = -1;
|
||||
}else if( rA>rB ){
|
||||
|
@ -852,8 +744,8 @@ int sqliteSortCompare(const char *a, const char *b){
|
|||
res = -1;
|
||||
break;
|
||||
}
|
||||
rA = sqliteAtoF(&a[1]);
|
||||
rB = sqliteAtoF(&b[1]);
|
||||
rA = sqliteAtoF(&a[1], 0);
|
||||
rB = sqliteAtoF(&b[1], 0);
|
||||
if( rA<rB ){
|
||||
res = -1;
|
||||
break;
|
||||
|
|
|
@ -108,7 +108,6 @@ static int execsql(char **pzErrMsg, sqlite *db, const char *zSql){
|
|||
*/
|
||||
static int vacuumCallback2(void *pArg, int argc, char **argv, char **NotUsed){
|
||||
vacuumStruct *p = (vacuumStruct*)pArg;
|
||||
int rc = 0;
|
||||
const char *zSep = "(";
|
||||
int i;
|
||||
|
||||
|
@ -127,8 +126,8 @@ static int vacuumCallback2(void *pArg, int argc, char **argv, char **NotUsed){
|
|||
}
|
||||
}
|
||||
appendText(&p->s2,")", 1);
|
||||
rc = execsql(p->pzErrMsg, p->dbNew, p->s2.z);
|
||||
return rc;
|
||||
p->rc = execsql(p->pzErrMsg, p->dbNew, p->s2.z);
|
||||
return p->rc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -160,6 +159,7 @@ static int vacuumCallback1(void *pArg, int argc, char **argv, char **NotUsed){
|
|||
sqlite_freemem(zErrMsg);
|
||||
}
|
||||
}
|
||||
if( rc!=SQLITE_ABORT ) p->rc = rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -170,7 +170,6 @@ static int vacuumCallback1(void *pArg, int argc, char **argv, char **NotUsed){
|
|||
*/
|
||||
static int vacuumCallback3(void *pArg, int argc, char **argv, char **NotUsed){
|
||||
vacuumStruct *p = (vacuumStruct*)pArg;
|
||||
int rc = 0;
|
||||
char zBuf[200];
|
||||
assert( argc==1 );
|
||||
if( argv==0 ) return 0;
|
||||
|
@ -178,21 +177,21 @@ static int vacuumCallback3(void *pArg, int argc, char **argv, char **NotUsed){
|
|||
assert( strlen(p->zPragma)<100 );
|
||||
assert( strlen(argv[0])<30 );
|
||||
sprintf(zBuf,"PRAGMA %s=%s;", p->zPragma, argv[0]);
|
||||
rc = execsql(p->pzErrMsg, p->dbNew, zBuf);
|
||||
return rc;
|
||||
p->rc = execsql(p->pzErrMsg, p->dbNew, zBuf);
|
||||
return p->rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate a random name of 20 character in length.
|
||||
*/
|
||||
static void randomName(char *zBuf){
|
||||
static const char zChars[] =
|
||||
static void randomName(unsigned char *zBuf){
|
||||
static const unsigned char zChars[] =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789";
|
||||
int i;
|
||||
sqliteRandomness(20, zBuf);
|
||||
for(i=0; i<20; i++){
|
||||
int n = sqliteRandomByte() % (sizeof(zChars)-1);
|
||||
zBuf[i] = zChars[n];
|
||||
zBuf[i] = zChars[ zBuf[i]%(sizeof(zChars)-1) ];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -240,6 +239,9 @@ int sqliteRunVacuum(char **pzErrMsg, sqlite *db){
|
|||
(char*)0);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if( db->flags & SQLITE_Interrupt ){
|
||||
return SQLITE_INTERRUPT;
|
||||
}
|
||||
memset(&sVac, 0, sizeof(sVac));
|
||||
|
||||
/* Get the full pathname of the database file and create two
|
||||
|
@ -257,7 +259,7 @@ int sqliteRunVacuum(char **pzErrMsg, sqlite *db){
|
|||
strcpy(zTemp, zFilename);
|
||||
for(i=0; i<10; i++){
|
||||
zTemp[nFilename] = '-';
|
||||
randomName(&zTemp[nFilename+1]);
|
||||
randomName((unsigned char*)&zTemp[nFilename+1]);
|
||||
if( !sqliteOsFileExists(zTemp) ) break;
|
||||
}
|
||||
if( i>=10 ){
|
||||
|
@ -273,8 +275,8 @@ int sqliteRunVacuum(char **pzErrMsg, sqlite *db){
|
|||
zTemp, " - ", zErrMsg, (char*)0);
|
||||
goto end_of_vacuum;
|
||||
}
|
||||
if( execsql(pzErrMsg, db, "BEGIN") ) goto end_of_vacuum;
|
||||
if( execsql(pzErrMsg, dbNew, "PRAGMA synchronous=off; BEGIN") ){
|
||||
if( (rc = execsql(pzErrMsg, db, "BEGIN"))!=0 ) goto end_of_vacuum;
|
||||
if( (rc = execsql(pzErrMsg, dbNew, "PRAGMA synchronous=off; BEGIN"))!=0 ){
|
||||
goto end_of_vacuum;
|
||||
}
|
||||
|
||||
|
@ -309,13 +311,17 @@ end_of_vacuum:
|
|||
zErrMsg, (char*)0);
|
||||
}
|
||||
sqlite_exec(db, "ROLLBACK", 0, 0, 0);
|
||||
if( (dbNew && (dbNew->flags & SQLITE_Interrupt))
|
||||
|| (db->flags & SQLITE_Interrupt) ){
|
||||
rc = SQLITE_INTERRUPT;
|
||||
}
|
||||
if( dbNew ) sqlite_close(dbNew);
|
||||
sqliteOsDelete(zTemp);
|
||||
sqliteFree(zTemp);
|
||||
sqliteFree(sVac.s1.z);
|
||||
sqliteFree(sVac.s2.z);
|
||||
if( zErrMsg ) sqlite_freemem(zErrMsg);
|
||||
if( rc==SQLITE_ABORT ) rc = SQLITE_ERROR;
|
||||
return rc;
|
||||
if( rc==SQLITE_ABORT && sVac.rc!=SQLITE_INTERRUPT ) sVac.rc = SQLITE_ERROR;
|
||||
return sVac.rc;
|
||||
#endif
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -34,7 +34,7 @@ typedef struct Vdbe Vdbe;
|
|||
** as an instance of the following structure:
|
||||
*/
|
||||
struct VdbeOp {
|
||||
int opcode; /* What operation to perform */
|
||||
u8 opcode; /* What operation to perform */
|
||||
int p1; /* First operand */
|
||||
int p2; /* Second parameter (often the jump destination) */
|
||||
char *p3; /* Third parameter */
|
||||
|
@ -46,6 +46,18 @@ struct VdbeOp {
|
|||
};
|
||||
typedef struct VdbeOp VdbeOp;
|
||||
|
||||
/*
|
||||
** A smaller version of VdbeOp used for the VdbeAddOpList() function because
|
||||
** it takes up less space.
|
||||
*/
|
||||
struct VdbeOpList {
|
||||
u8 opcode; /* What operation to perform */
|
||||
signed char p1; /* First operand */
|
||||
short int p2; /* Second parameter (often the jump destination) */
|
||||
char *p3; /* Third parameter */
|
||||
};
|
||||
typedef struct VdbeOpList VdbeOpList;
|
||||
|
||||
/*
|
||||
** Allowed values of VdbeOp.p3type
|
||||
*/
|
||||
|
@ -75,7 +87,9 @@ typedef struct VdbeOp VdbeOp;
|
|||
Vdbe *sqliteVdbeCreate(sqlite*);
|
||||
void sqliteVdbeCreateCallback(Vdbe*, int*);
|
||||
int sqliteVdbeAddOp(Vdbe*,int,int,int);
|
||||
int sqliteVdbeAddOpList(Vdbe*, int nOp, VdbeOp const *aOp);
|
||||
int sqliteVdbeOp3(Vdbe*,int,int,int,const char *zP3,int);
|
||||
int sqliteVdbeCode(Vdbe*,...);
|
||||
int sqliteVdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp);
|
||||
void sqliteVdbeChangeP1(Vdbe*, int addr, int P1);
|
||||
void sqliteVdbeChangeP2(Vdbe*, int addr, int P2);
|
||||
void sqliteVdbeChangeP3(Vdbe*, int addr, const char *zP1, int N);
|
||||
|
@ -84,7 +98,7 @@ int sqliteVdbeFindOp(Vdbe*, int, int);
|
|||
VdbeOp *sqliteVdbeGetOp(Vdbe*, int);
|
||||
int sqliteVdbeMakeLabel(Vdbe*);
|
||||
void sqliteVdbeDelete(Vdbe*);
|
||||
void sqliteVdbeMakeReady(Vdbe*,int,sqlite_callback,void*,int);
|
||||
void sqliteVdbeMakeReady(Vdbe*,int,int);
|
||||
int sqliteVdbeExec(Vdbe*);
|
||||
int sqliteVdbeList(Vdbe*);
|
||||
int sqliteVdbeFinalize(Vdbe*,char**);
|
||||
|
|
|
@ -106,47 +106,36 @@ struct Sorter {
|
|||
#define NBFS 32
|
||||
|
||||
/*
|
||||
** A single level of the stack is an instance of the following
|
||||
** structure. Except, string values are stored on a separate
|
||||
** list of of pointers to character. The reason for storing
|
||||
** strings separately is so that they can be easily passed
|
||||
** to the callback function.
|
||||
*/
|
||||
struct Stack {
|
||||
int i; /* Integer value */
|
||||
int n; /* Number of characters in string value, including '\0' */
|
||||
int flags; /* Some combination of STK_Null, STK_Str, STK_Dyn, etc. */
|
||||
double r; /* Real value */
|
||||
char z[NBFS]; /* Space for short strings */
|
||||
};
|
||||
typedef struct Stack Stack;
|
||||
|
||||
/*
|
||||
** Memory cells use the same structure as the stack except that space
|
||||
** for an arbitrary string is added.
|
||||
** A single level of the stack or a single memory cell
|
||||
** is an instance of the following structure.
|
||||
*/
|
||||
struct Mem {
|
||||
Stack s; /* All values of the memory cell besides string */
|
||||
char *z; /* String value for this memory cell */
|
||||
int i; /* Integer value */
|
||||
int n; /* Number of characters in string value, including '\0' */
|
||||
int flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
|
||||
double r; /* Real value */
|
||||
char *z; /* String value */
|
||||
char zShort[NBFS]; /* Space for short strings */
|
||||
};
|
||||
typedef struct Mem Mem;
|
||||
|
||||
/*
|
||||
** Allowed values for Stack.flags
|
||||
** Allowed values for Mem.flags
|
||||
*/
|
||||
#define STK_Null 0x0001 /* Value is NULL */
|
||||
#define STK_Str 0x0002 /* Value is a string */
|
||||
#define STK_Int 0x0004 /* Value is an integer */
|
||||
#define STK_Real 0x0008 /* Value is a real number */
|
||||
#define STK_Dyn 0x0010 /* Need to call sqliteFree() on zStack[] */
|
||||
#define STK_Static 0x0020 /* zStack[] points to a static string */
|
||||
#define STK_Ephem 0x0040 /* zStack[] points to an ephemeral string */
|
||||
#define MEM_Null 0x0001 /* Value is NULL */
|
||||
#define MEM_Str 0x0002 /* Value is a string */
|
||||
#define MEM_Int 0x0004 /* Value is an integer */
|
||||
#define MEM_Real 0x0008 /* Value is a real number */
|
||||
#define MEM_Dyn 0x0010 /* Need to call sqliteFree() on Mem.z */
|
||||
#define MEM_Static 0x0020 /* Mem.z points to a static string */
|
||||
#define MEM_Ephem 0x0040 /* Mem.z points to an ephemeral string */
|
||||
#define MEM_Short 0x0080 /* Mem.z points to Mem.zShort */
|
||||
|
||||
/* The following STK_ value appears only in AggElem.aMem.s.flag fields.
|
||||
/* The following MEM_ value appears only in AggElem.aMem.s.flag fields.
|
||||
** It indicates that the corresponding AggElem.aMem.z points to a
|
||||
** aggregate function context that needs to be finalized.
|
||||
*/
|
||||
#define STK_AggCtx 0x0040 /* zStack[] points to an agg function context */
|
||||
#define MEM_AggCtx 0x0100 /* Mem.z points to an agg function context */
|
||||
|
||||
/*
|
||||
** The "context" argument for a installable function. A pointer to an
|
||||
|
@ -159,12 +148,11 @@ typedef struct Mem Mem;
|
|||
** structure are known.
|
||||
**
|
||||
** This structure is defined inside of vdbe.c because it uses substructures
|
||||
** (Stack) which are only defined there.
|
||||
** (Mem) which are only defined there.
|
||||
*/
|
||||
struct sqlite_func {
|
||||
FuncDef *pFunc; /* Pointer to function information. MUST BE FIRST */
|
||||
Stack s; /* Small strings, ints, and double values go here */
|
||||
char *z; /* Space for holding dynamic string results */
|
||||
Mem s; /* The return value is stored here */
|
||||
void *pAgg; /* Aggregate context */
|
||||
u8 isError; /* Set to true for an error */
|
||||
u8 isStep; /* Current in the step function */
|
||||
|
@ -219,6 +207,19 @@ struct Keylist {
|
|||
int aKey[1]; /* One or more keys. Extra space allocated as needed */
|
||||
};
|
||||
|
||||
/*
|
||||
** A Context stores the last insert rowid, the last statement change count,
|
||||
** and the current statement change count (i.e. changes since last statement).
|
||||
** Elements of Context structure type make up the ContextStack, which is
|
||||
** updated by the ContextPush and ContextPop opcodes (used by triggers)
|
||||
*/
|
||||
typedef struct Context Context;
|
||||
struct Context {
|
||||
int lastRowid; /* Last insert rowid (from db->lastRowid) */
|
||||
int lsChange; /* Last statement change count (from db->lsChange) */
|
||||
int csChange; /* Current statement change count (from db->csChange) */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the virtual machine. This structure contains the complete
|
||||
** state of the virtual machine.
|
||||
|
@ -236,9 +237,9 @@ struct Vdbe {
|
|||
int nLabel; /* Number of labels used */
|
||||
int nLabelAlloc; /* Number of slots allocated in aLabel[] */
|
||||
int *aLabel; /* Space to hold the labels */
|
||||
int tos; /* Index of top of stack */
|
||||
Stack *aStack; /* The operand stack, except string values */
|
||||
char **zStack; /* Text or binary values of the stack */
|
||||
Mem *aStack; /* The operand stack, except string values */
|
||||
Mem *pTos; /* Top entry in the operand stack */
|
||||
char **zArgv; /* Text values used by the callback */
|
||||
char **azColName; /* Becomes the 4th parameter to callbacks */
|
||||
int nCursor; /* Number of slots in aCsr[] */
|
||||
Cursor *aCsr; /* One element of this array for each open cursor */
|
||||
|
@ -262,6 +263,8 @@ struct Vdbe {
|
|||
Keylist *pList; /* A list of ROWIDs */
|
||||
int keylistStackDepth; /* The size of the "keylist" stack */
|
||||
Keylist **keylistStack; /* The stack used by opcodes ListPush & ListPop */
|
||||
int contextStackDepth; /* The size of the "context" stack */
|
||||
Context *contextStack; /* Stack used by opcodes ContextPush & ContextPop*/
|
||||
int pc; /* The program counter */
|
||||
int rc; /* Value to return */
|
||||
unsigned uniqueCnt; /* Used by OP_MakeRecord when P2!=0 */
|
||||
|
@ -271,9 +274,7 @@ struct Vdbe {
|
|||
int returnStack[100]; /* Return address stack for OP_Gosub & OP_Return */
|
||||
int returnDepth; /* Next unused element in returnStack[] */
|
||||
int nResColumn; /* Number of columns in one row of the result set */
|
||||
char **azResColumn; /* Values for one row of result */
|
||||
int (*xCallback)(void*,int,char**,char**); /* Callback for SELECT results */
|
||||
void *pCbArg; /* First argument to xCallback() */
|
||||
char **azResColumn; /* Values for one row of result */
|
||||
int popStack; /* Pop the stack this much on entry to VdbeExec() */
|
||||
char *zErrMsg; /* Error message written here */
|
||||
u8 explain; /* True if EXPLAIN present on SQL command */
|
||||
|
@ -287,16 +288,6 @@ struct Vdbe {
|
|||
#define VDBE_MAGIC_HALT 0x519c2973 /* VDBE has completed execution */
|
||||
#define VDBE_MAGIC_DEAD 0xb606c3c8 /* The VDBE has been deallocated */
|
||||
|
||||
/*
|
||||
** Here is a macro to handle the common case of popping the stack
|
||||
** once. This macro only works from within the sqliteVdbeExec()
|
||||
** function.
|
||||
*/
|
||||
#define POPSTACK \
|
||||
assert(p->tos>=0); \
|
||||
if( aStack[p->tos].flags & STK_Dyn ) sqliteFree(zStack[p->tos]); \
|
||||
p->tos--;
|
||||
|
||||
/*
|
||||
** Function prototypes
|
||||
*/
|
||||
|
|
|
@ -73,6 +73,7 @@ void sqliteVdbeTrace(Vdbe *p, FILE *trace){
|
|||
*/
|
||||
int sqliteVdbeAddOp(Vdbe *p, int op, int p1, int p2){
|
||||
int i;
|
||||
VdbeOp *pOp;
|
||||
|
||||
i = p->nOp;
|
||||
p->nOp++;
|
||||
|
@ -89,20 +90,50 @@ int sqliteVdbeAddOp(Vdbe *p, int op, int p1, int p2){
|
|||
p->aOp = aNew;
|
||||
memset(&p->aOp[oldSize], 0, (p->nOpAlloc-oldSize)*sizeof(Op));
|
||||
}
|
||||
p->aOp[i].opcode = op;
|
||||
p->aOp[i].p1 = p1;
|
||||
pOp = &p->aOp[i];
|
||||
pOp->opcode = op;
|
||||
pOp->p1 = p1;
|
||||
if( p2<0 && (-1-p2)<p->nLabel && p->aLabel[-1-p2]>=0 ){
|
||||
p2 = p->aLabel[-1-p2];
|
||||
}
|
||||
p->aOp[i].p2 = p2;
|
||||
p->aOp[i].p3 = 0;
|
||||
p->aOp[i].p3type = P3_NOTUSED;
|
||||
pOp->p2 = p2;
|
||||
pOp->p3 = 0;
|
||||
pOp->p3type = P3_NOTUSED;
|
||||
#ifndef NDEBUG
|
||||
if( sqlite_vdbe_addop_trace ) sqliteVdbePrintOp(0, i, &p->aOp[i]);
|
||||
#endif
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add an opcode that includes the p3 value.
|
||||
*/
|
||||
int sqliteVdbeOp3(Vdbe *p, int op, int p1, int p2, const char *zP3, int p3type){
|
||||
int addr = sqliteVdbeAddOp(p, op, p1, p2);
|
||||
sqliteVdbeChangeP3(p, addr, zP3, p3type);
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add multiple opcodes. The list is terminated by an opcode of 0.
|
||||
*/
|
||||
int sqliteVdbeCode(Vdbe *p, ...){
|
||||
int addr;
|
||||
va_list ap;
|
||||
int opcode, p1, p2;
|
||||
va_start(ap, p);
|
||||
addr = p->nOp;
|
||||
while( (opcode = va_arg(ap,int))!=0 ){
|
||||
p1 = va_arg(ap,int);
|
||||
p2 = va_arg(ap,int);
|
||||
sqliteVdbeAddOp(p, opcode, p1, p2);
|
||||
}
|
||||
va_end(ap);
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Create a new symbolic label for an instruction that has yet to be
|
||||
** coded. The symbolic label is really just a negative number. The
|
||||
|
@ -167,7 +198,7 @@ int sqliteVdbeCurrentAddr(Vdbe *p){
|
|||
** Add a whole list of operations to the operation stack. Return the
|
||||
** address of the first operation added.
|
||||
*/
|
||||
int sqliteVdbeAddOpList(Vdbe *p, int nOp, VdbeOp const *aOp){
|
||||
int sqliteVdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
|
||||
int addr;
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
if( p->nOp + nOp >= p->nOpAlloc ){
|
||||
|
@ -185,11 +216,15 @@ int sqliteVdbeAddOpList(Vdbe *p, int nOp, VdbeOp const *aOp){
|
|||
addr = p->nOp;
|
||||
if( nOp>0 ){
|
||||
int i;
|
||||
for(i=0; i<nOp; i++){
|
||||
int p2 = aOp[i].p2;
|
||||
p->aOp[i+addr] = aOp[i];
|
||||
if( p2<0 ) p->aOp[i+addr].p2 = addr + ADDR(p2);
|
||||
p->aOp[i+addr].p3type = aOp[i].p3 ? P3_STATIC : P3_NOTUSED;
|
||||
VdbeOpList const *pIn = aOp;
|
||||
for(i=0; i<nOp; i++, pIn++){
|
||||
int p2 = pIn->p2;
|
||||
VdbeOp *pOut = &p->aOp[i+addr];
|
||||
pOut->opcode = pIn->opcode;
|
||||
pOut->p1 = pIn->p1;
|
||||
pOut->p2 = p2<0 ? addr + ADDR(p2) : p2;
|
||||
pOut->p3 = pIn->p3;
|
||||
pOut->p3type = pIn->p3 ? P3_STATIC : P3_NOTUSED;
|
||||
#ifndef NDEBUG
|
||||
if( sqlite_vdbe_addop_trace ){
|
||||
sqliteVdbePrintOp(0, i+addr, &p->aOp[i+addr]);
|
||||
|
@ -280,7 +315,11 @@ void sqliteVdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){
|
|||
void sqliteVdbeDequoteP3(Vdbe *p, int addr){
|
||||
Op *pOp;
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
if( p->aOp==0 || addr<0 || addr>=p->nOp ) return;
|
||||
if( p->aOp==0 ) return;
|
||||
if( addr<0 || addr>=p->nOp ){
|
||||
addr = p->nOp - 1;
|
||||
if( addr<0 ) return;
|
||||
}
|
||||
pOp = &p->aOp[addr];
|
||||
if( pOp->p3==0 || pOp->p3[0]==0 ) return;
|
||||
if( pOp->p3type==P3_POINTER ) return;
|
||||
|
@ -371,48 +410,48 @@ VdbeOp *sqliteVdbeGetOp(Vdbe *p, int addr){
|
|||
*/
|
||||
char *sqlite_set_result_string(sqlite_func *p, const char *zResult, int n){
|
||||
assert( !p->isStep );
|
||||
if( p->s.flags & STK_Dyn ){
|
||||
sqliteFree(p->z);
|
||||
if( p->s.flags & MEM_Dyn ){
|
||||
sqliteFree(p->s.z);
|
||||
}
|
||||
if( zResult==0 ){
|
||||
p->s.flags = STK_Null;
|
||||
p->s.flags = MEM_Null;
|
||||
n = 0;
|
||||
p->z = 0;
|
||||
p->s.z = 0;
|
||||
p->s.n = 0;
|
||||
}else{
|
||||
if( n<0 ) n = strlen(zResult);
|
||||
if( n<NBFS-1 ){
|
||||
memcpy(p->s.z, zResult, n);
|
||||
p->s.z[n] = 0;
|
||||
p->s.flags = STK_Str;
|
||||
p->z = p->s.z;
|
||||
memcpy(p->s.zShort, zResult, n);
|
||||
p->s.zShort[n] = 0;
|
||||
p->s.flags = MEM_Str | MEM_Short;
|
||||
p->s.z = p->s.zShort;
|
||||
}else{
|
||||
p->z = sqliteMallocRaw( n+1 );
|
||||
if( p->z ){
|
||||
memcpy(p->z, zResult, n);
|
||||
p->z[n] = 0;
|
||||
p->s.z = sqliteMallocRaw( n+1 );
|
||||
if( p->s.z ){
|
||||
memcpy(p->s.z, zResult, n);
|
||||
p->s.z[n] = 0;
|
||||
}
|
||||
p->s.flags = STK_Str | STK_Dyn;
|
||||
p->s.flags = MEM_Str | MEM_Dyn;
|
||||
}
|
||||
p->s.n = n+1;
|
||||
}
|
||||
return p->z;
|
||||
return p->s.z;
|
||||
}
|
||||
void sqlite_set_result_int(sqlite_func *p, int iResult){
|
||||
assert( !p->isStep );
|
||||
if( p->s.flags & STK_Dyn ){
|
||||
sqliteFree(p->z);
|
||||
if( p->s.flags & MEM_Dyn ){
|
||||
sqliteFree(p->s.z);
|
||||
}
|
||||
p->s.i = iResult;
|
||||
p->s.flags = STK_Int;
|
||||
p->s.flags = MEM_Int;
|
||||
}
|
||||
void sqlite_set_result_double(sqlite_func *p, double rResult){
|
||||
assert( !p->isStep );
|
||||
if( p->s.flags & STK_Dyn ){
|
||||
sqliteFree(p->z);
|
||||
if( p->s.flags & MEM_Dyn ){
|
||||
sqliteFree(p->s.z);
|
||||
}
|
||||
p->s.r = rResult;
|
||||
p->s.flags = STK_Real;
|
||||
p->s.flags = MEM_Real;
|
||||
}
|
||||
void sqlite_set_result_error(sqlite_func *p, const char *zMsg, int n){
|
||||
assert( !p->isStep );
|
||||
|
@ -442,7 +481,8 @@ void *sqlite_aggregate_context(sqlite_func *p, int nByte){
|
|||
assert( p && p->pFunc && p->pFunc->xStep );
|
||||
if( p->pAgg==0 ){
|
||||
if( nByte<=NBFS ){
|
||||
p->pAgg = (void*)p->z;
|
||||
p->pAgg = (void*)p->s.z;
|
||||
memset(p->pAgg, 0, nByte);
|
||||
}else{
|
||||
p->pAgg = sqliteMalloc( nByte );
|
||||
}
|
||||
|
@ -495,6 +535,7 @@ int sqliteVdbeList(
|
|||
){
|
||||
sqlite *db = p->db;
|
||||
int i;
|
||||
int rc = SQLITE_OK;
|
||||
static char *azColumnNames[] = {
|
||||
"addr", "opcode", "p1", "p2", "p3",
|
||||
"int", "text", "int", "int", "text",
|
||||
|
@ -504,48 +545,39 @@ int sqliteVdbeList(
|
|||
assert( p->popStack==0 );
|
||||
assert( p->explain );
|
||||
p->azColName = azColumnNames;
|
||||
p->azResColumn = p->zStack;
|
||||
for(i=0; i<5; i++) p->zStack[i] = p->aStack[i].z;
|
||||
p->rc = SQLITE_OK;
|
||||
for(i=p->pc; p->rc==SQLITE_OK && i<p->nOp; i++){
|
||||
if( db->flags & SQLITE_Interrupt ){
|
||||
db->flags &= ~SQLITE_Interrupt;
|
||||
if( db->magic!=SQLITE_MAGIC_BUSY ){
|
||||
p->rc = SQLITE_MISUSE;
|
||||
}else{
|
||||
p->rc = SQLITE_INTERRUPT;
|
||||
}
|
||||
sqliteSetString(&p->zErrMsg, sqlite_error_string(p->rc), (char*)0);
|
||||
break;
|
||||
}
|
||||
sprintf(p->zStack[0],"%d",i);
|
||||
sprintf(p->zStack[2],"%d", p->aOp[i].p1);
|
||||
sprintf(p->zStack[3],"%d", p->aOp[i].p2);
|
||||
if( p->aOp[i].p3type==P3_POINTER ){
|
||||
sprintf(p->aStack[4].z, "ptr(%#lx)", (long)p->aOp[i].p3);
|
||||
p->zStack[4] = p->aStack[4].z;
|
||||
p->azResColumn = p->zArgv;
|
||||
for(i=0; i<5; i++) p->zArgv[i] = p->aStack[i].zShort;
|
||||
i = p->pc;
|
||||
if( i>=p->nOp ){
|
||||
p->rc = SQLITE_OK;
|
||||
rc = SQLITE_DONE;
|
||||
}else if( db->flags & SQLITE_Interrupt ){
|
||||
db->flags &= ~SQLITE_Interrupt;
|
||||
if( db->magic!=SQLITE_MAGIC_BUSY ){
|
||||
p->rc = SQLITE_MISUSE;
|
||||
}else{
|
||||
p->zStack[4] = p->aOp[i].p3;
|
||||
p->rc = SQLITE_INTERRUPT;
|
||||
}
|
||||
p->zStack[1] = sqliteOpcodeNames[p->aOp[i].opcode];
|
||||
if( p->xCallback==0 ){
|
||||
p->pc = i+1;
|
||||
p->azResColumn = p->zStack;
|
||||
p->nResColumn = 5;
|
||||
return SQLITE_ROW;
|
||||
}
|
||||
if( sqliteSafetyOff(db) ){
|
||||
p->rc = SQLITE_MISUSE;
|
||||
break;
|
||||
}
|
||||
if( p->xCallback(p->pCbArg, 5, p->zStack, p->azColName) ){
|
||||
p->rc = SQLITE_ABORT;
|
||||
}
|
||||
if( sqliteSafetyOn(db) ){
|
||||
p->rc = SQLITE_MISUSE;
|
||||
rc = SQLITE_ERROR;
|
||||
sqliteSetString(&p->zErrMsg, sqlite_error_string(p->rc), (char*)0);
|
||||
}else{
|
||||
sprintf(p->zArgv[0],"%d",i);
|
||||
sprintf(p->zArgv[2],"%d", p->aOp[i].p1);
|
||||
sprintf(p->zArgv[3],"%d", p->aOp[i].p2);
|
||||
if( p->aOp[i].p3type==P3_POINTER ){
|
||||
sprintf(p->aStack[4].zShort, "ptr(%#lx)", (long)p->aOp[i].p3);
|
||||
p->zArgv[4] = p->aStack[4].zShort;
|
||||
}else{
|
||||
p->zArgv[4] = p->aOp[i].p3;
|
||||
}
|
||||
p->zArgv[1] = sqliteOpcodeNames[p->aOp[i].opcode];
|
||||
p->pc = i+1;
|
||||
p->azResColumn = p->zArgv;
|
||||
p->nResColumn = 5;
|
||||
p->rc = SQLITE_OK;
|
||||
rc = SQLITE_ROW;
|
||||
}
|
||||
return p->rc==SQLITE_OK ? SQLITE_DONE : SQLITE_ERROR;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -553,20 +585,10 @@ int sqliteVdbeList(
|
|||
** as allocating stack space and initializing the program counter.
|
||||
** After the VDBE has be prepped, it can be executed by one or more
|
||||
** calls to sqliteVdbeExec().
|
||||
**
|
||||
** The behavior of sqliteVdbeExec() is influenced by the parameters to
|
||||
** this routine. If xCallback is NULL, then sqliteVdbeExec() will return
|
||||
** with SQLITE_ROW whenever there is a row of the result set ready
|
||||
** to be delivered. p->azResColumn will point to the row and
|
||||
** p->nResColumn gives the number of columns in the row. If xCallback
|
||||
** is not NULL, then the xCallback() routine is invoked to process each
|
||||
** row in the result set.
|
||||
*/
|
||||
void sqliteVdbeMakeReady(
|
||||
Vdbe *p, /* The VDBE */
|
||||
int nVar, /* Number of '?' see in the SQL statement */
|
||||
sqlite_callback xCallback, /* Result callback */
|
||||
void *pCallbackArg, /* 1st argument to xCallback() */
|
||||
int isExplain /* True if the EXPLAIN keywords is present */
|
||||
){
|
||||
int n;
|
||||
|
@ -592,11 +614,11 @@ void sqliteVdbeMakeReady(
|
|||
assert( nVar>=0 );
|
||||
n = isExplain ? 10 : p->nOp;
|
||||
p->aStack = sqliteMalloc(
|
||||
n*(sizeof(p->aStack[0]) + 2*sizeof(char*)) /* aStack and zStack */
|
||||
+ p->nVar*(sizeof(char*)+sizeof(int)+1) /* azVar, anVar, abVar */
|
||||
n*(sizeof(p->aStack[0]) + 2*sizeof(char*)) /* aStack and zArgv */
|
||||
+ p->nVar*(sizeof(char*)+sizeof(int)+1) /* azVar, anVar, abVar */
|
||||
);
|
||||
p->zStack = (char**)&p->aStack[n];
|
||||
p->azColName = (char**)&p->zStack[n];
|
||||
p->zArgv = (char**)&p->aStack[n];
|
||||
p->azColName = (char**)&p->zArgv[n];
|
||||
p->azVar = (char**)&p->azColName[n];
|
||||
p->anVar = (int*)&p->azVar[p->nVar];
|
||||
p->abVar = (u8*)&p->anVar[p->nVar];
|
||||
|
@ -609,15 +631,13 @@ void sqliteVdbeMakeReady(
|
|||
p->trace = stdout;
|
||||
}
|
||||
#endif
|
||||
p->tos = -1;
|
||||
p->pTos = &p->aStack[-1];
|
||||
p->pc = 0;
|
||||
p->rc = SQLITE_OK;
|
||||
p->uniqueCnt = 0;
|
||||
p->returnDepth = 0;
|
||||
p->errorAction = OE_Abort;
|
||||
p->undoTransOnError = 0;
|
||||
p->xCallback = xCallback;
|
||||
p->pCbArg = pCallbackArg;
|
||||
p->popStack = 0;
|
||||
p->explain |= isExplain;
|
||||
p->magic = VDBE_MAGIC_RUN;
|
||||
|
@ -646,25 +666,6 @@ void sqliteVdbeSorterReset(Vdbe *p){
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Pop the stack N times. Free any memory associated with the
|
||||
** popped stack elements.
|
||||
*/
|
||||
void sqliteVdbePopStack(Vdbe *p, int N){
|
||||
assert( N>=0 );
|
||||
if( p->zStack==0 ) return;
|
||||
assert( p->aStack || sqlite_malloc_failed );
|
||||
if( p->aStack==0 ) return;
|
||||
while( N-- > 0 ){
|
||||
if( p->aStack[p->tos].flags & STK_Dyn ){
|
||||
sqliteFree(p->zStack[p->tos]);
|
||||
}
|
||||
p->aStack[p->tos].flags = 0;
|
||||
p->zStack[p->tos] = 0;
|
||||
p->tos--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset an Agg structure. Delete all its contents.
|
||||
**
|
||||
|
@ -682,20 +683,22 @@ void sqliteVdbeAggReset(Agg *pAgg){
|
|||
assert( pAgg->apFunc!=0 );
|
||||
for(i=0; i<pAgg->nMem; i++){
|
||||
Mem *pMem = &pElem->aMem[i];
|
||||
if( pAgg->apFunc[i] && (pMem->s.flags & STK_AggCtx)!=0 ){
|
||||
if( pAgg->apFunc[i] && (pMem->flags & MEM_AggCtx)!=0 ){
|
||||
sqlite_func ctx;
|
||||
ctx.pFunc = pAgg->apFunc[i];
|
||||
ctx.s.flags = STK_Null;
|
||||
ctx.z = 0;
|
||||
ctx.s.flags = MEM_Null;
|
||||
ctx.pAgg = pMem->z;
|
||||
ctx.cnt = pMem->s.i;
|
||||
ctx.cnt = pMem->i;
|
||||
ctx.isStep = 0;
|
||||
ctx.isError = 0;
|
||||
(*pAgg->apFunc[i]->xFinalize)(&ctx);
|
||||
if( pMem->z!=0 && pMem->z!=pMem->s.z ){
|
||||
if( pMem->z!=0 && pMem->z!=pMem->zShort ){
|
||||
sqliteFree(pMem->z);
|
||||
}
|
||||
}else if( pMem->s.flags & STK_Dyn ){
|
||||
if( ctx.s.flags & MEM_Dyn ){
|
||||
sqliteFree(ctx.s.z);
|
||||
}
|
||||
}else if( pMem->flags & MEM_Dyn ){
|
||||
sqliteFree(pMem->z);
|
||||
}
|
||||
}
|
||||
|
@ -757,11 +760,20 @@ static void closeAllCursors(Vdbe *p){
|
|||
*/
|
||||
static void Cleanup(Vdbe *p){
|
||||
int i;
|
||||
sqliteVdbePopStack(p, p->tos+1);
|
||||
if( p->aStack ){
|
||||
Mem *pTos = p->pTos;
|
||||
while( pTos>=p->aStack ){
|
||||
if( pTos->flags & MEM_Dyn ){
|
||||
sqliteFree(pTos->z);
|
||||
}
|
||||
pTos--;
|
||||
}
|
||||
p->pTos = pTos;
|
||||
}
|
||||
closeAllCursors(p);
|
||||
if( p->aMem ){
|
||||
for(i=0; i<p->nMem; i++){
|
||||
if( p->aMem[i].s.flags & STK_Dyn ){
|
||||
if( p->aMem[i].flags & MEM_Dyn ){
|
||||
sqliteFree(p->aMem[i].z);
|
||||
}
|
||||
}
|
||||
|
@ -806,6 +818,8 @@ static void Cleanup(Vdbe *p){
|
|||
p->keylistStackDepth = 0;
|
||||
p->keylistStack = 0;
|
||||
}
|
||||
sqliteFree(p->contextStack);
|
||||
p->contextStack = 0;
|
||||
sqliteFree(p->zErrMsg);
|
||||
p->zErrMsg = 0;
|
||||
}
|
||||
|
@ -832,6 +846,8 @@ int sqliteVdbeReset(Vdbe *p, char **pzErrMsg){
|
|||
sqliteFree(p->zErrMsg);
|
||||
}
|
||||
p->zErrMsg = 0;
|
||||
}else if( p->rc ){
|
||||
sqliteSetString(pzErrMsg, sqlite_error_string(p->rc), (char*)0);
|
||||
}
|
||||
Cleanup(p);
|
||||
if( p->rc!=SQLITE_OK ){
|
||||
|
@ -870,7 +886,7 @@ int sqliteVdbeReset(Vdbe *p, char **pzErrMsg){
|
|||
db->aDb[i].inTrans = 1;
|
||||
}
|
||||
}
|
||||
assert( p->tos<p->pc || sqlite_malloc_failed==1 );
|
||||
assert( p->pTos<&p->aStack[p->pc] || sqlite_malloc_failed==1 );
|
||||
#ifdef VDBE_PROFILE
|
||||
{
|
||||
FILE *out = fopen("vdbe_profile.out", "a");
|
||||
|
@ -915,6 +931,9 @@ int sqliteVdbeFinalize(Vdbe *p, char **pzErrMsg){
|
|||
if( db->want_to_close && db->pVdbe==0 ){
|
||||
sqlite_close(db);
|
||||
}
|
||||
if( rc==SQLITE_SCHEMA ){
|
||||
sqliteResetInternalSchema(db, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -380,11 +380,8 @@ WhereInfo *sqliteWhereBegin(
|
|||
memset(aExpr, 0, sizeof(aExpr));
|
||||
nExpr = exprSplit(ARRAYSIZE(aExpr), aExpr, pWhere);
|
||||
if( nExpr==ARRAYSIZE(aExpr) ){
|
||||
char zBuf[50];
|
||||
sprintf(zBuf, "%d", (int)ARRAYSIZE(aExpr)-1);
|
||||
sqliteSetString(&pParse->zErrMsg, "WHERE clause too complex - no more "
|
||||
"than ", zBuf, " terms allowed", (char*)0);
|
||||
pParse->nErr++;
|
||||
sqliteErrorMsg(pParse, "WHERE clause too complex - no more "
|
||||
"than %d terms allowed", (int)ARRAYSIZE(aExpr)-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -672,18 +669,17 @@ WhereInfo *sqliteWhereBegin(
|
|||
*/
|
||||
for(i=0; i<pTabList->nSrc; i++){
|
||||
Table *pTab;
|
||||
Index *pIx;
|
||||
|
||||
pTab = pTabList->a[i].pTab;
|
||||
if( pTab->isTransient || pTab->pSelect ) continue;
|
||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenRead, pTabList->a[i].iCursor, pTab->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||
sqliteVdbeOp3(v, OP_OpenRead, pTabList->a[i].iCursor, pTab->tnum,
|
||||
pTab->zName, P3_STATIC);
|
||||
sqliteCodeVerifySchema(pParse, pTab->iDb);
|
||||
if( pWInfo->a[i].pIdx!=0 ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pWInfo->a[i].pIdx->iDb, 0);
|
||||
sqliteVdbeAddOp(v, OP_OpenRead,
|
||||
pWInfo->a[i].iCur, pWInfo->a[i].pIdx->tnum);
|
||||
sqliteVdbeChangeP3(v, -1, pWInfo->a[i].pIdx->zName, P3_STATIC);
|
||||
if( (pIx = pWInfo->a[i].pIdx)!=0 ){
|
||||
sqliteVdbeAddOp(v, OP_Integer, pIx->iDb, 0);
|
||||
sqliteVdbeOp3(v, OP_OpenRead, pWInfo->a[i].iCur, pIx->tnum, pIx->zName,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -855,10 +851,8 @@ WhereInfo *sqliteWhereBegin(
|
|||
}else{
|
||||
sqliteExprCode(pParse, aExpr[k].p->pLeft);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_IsNumeric, 1, brk);
|
||||
if( aExpr[k].p->op==TK_LT || aExpr[k].p->op==TK_GT ){
|
||||
sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_ForceInt,
|
||||
aExpr[k].p->op==TK_LT || aExpr[k].p->op==TK_GT, brk);
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, iCur, brk);
|
||||
aExpr[k].p = 0;
|
||||
}else{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue