mirror of
https://github.com/php/php-src.git
synced 2025-08-18 15:08:55 +02:00
Update bundled library to 2.8.3 + patches from sqlite author to enable
authorization checks for the ATTACH database command.
This commit is contained in:
parent
6610183236
commit
30fc9e152f
26 changed files with 1056 additions and 907 deletions
|
@ -43,6 +43,32 @@ void sqliteAttach(Parse *pParse, Token *pFilename, Token *pDbname){
|
|||
pParse->rc = SQLITE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
zFile = 0;
|
||||
sqliteSetNString(&zFile, pFilename->z, pFilename->n, 0);
|
||||
if( zFile==0 ) return;
|
||||
sqliteDequote(zFile);
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqliteAuthCheck(pParse, SQLITE_ATTACH, zFile, 0, 0)!=SQLITE_OK ){
|
||||
sqliteFree(zFile);
|
||||
return;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_AUTHORIZATION */
|
||||
|
||||
zName = 0;
|
||||
sqliteSetNString(&zName, pDbname->z, pDbname->n, 0);
|
||||
if( zName==0 ) return;
|
||||
sqliteDequote(zName);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].zName && sqliteStrICmp(db->aDb[i].zName, zName)==0 ){
|
||||
sqliteErrorMsg(pParse, "database %z is already in use", zName);
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
sqliteFree(zFile);
|
||||
sqliteFree(zName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( db->aDb==db->aDbStatic ){
|
||||
aNew = sqliteMalloc( sizeof(db->aDb[0])*3 );
|
||||
if( aNew==0 ) return;
|
||||
|
@ -58,24 +84,7 @@ void sqliteAttach(Parse *pParse, Token *pFilename, Token *pDbname){
|
|||
sqliteHashInit(&aNew->idxHash, SQLITE_HASH_STRING, 0);
|
||||
sqliteHashInit(&aNew->trigHash, SQLITE_HASH_STRING, 0);
|
||||
sqliteHashInit(&aNew->aFKey, SQLITE_HASH_STRING, 1);
|
||||
|
||||
zName = 0;
|
||||
sqliteSetNString(&zName, pDbname->z, pDbname->n, 0);
|
||||
if( zName==0 ) return;
|
||||
sqliteDequote(zName);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].zName && sqliteStrICmp(db->aDb[i].zName, zName)==0 ){
|
||||
sqliteErrorMsg(pParse, "database %z is already in use", zName);
|
||||
db->nDb--;
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
aNew->zName = zName;
|
||||
zFile = 0;
|
||||
sqliteSetNString(&zFile, pFilename->z, pFilename->n, 0);
|
||||
if( zFile==0 ) return;
|
||||
sqliteDequote(zFile);
|
||||
rc = sqliteBtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt);
|
||||
if( rc ){
|
||||
sqliteErrorMsg(pParse, "unable to open database: %s", zFile);
|
||||
|
@ -117,6 +126,11 @@ void sqliteDetach(Parse *pParse, Token *pDbname){
|
|||
sqliteErrorMsg(pParse, "cannot detach database %T", pDbname);
|
||||
return;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
if( sqliteAuthCheck(pParse,SQLITE_DETACH,db->aDb[i].zName,0,0)!=SQLITE_OK ){
|
||||
return;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_AUTHORIZATION */
|
||||
sqliteBtreeClose(db->aDb[i].pBt);
|
||||
db->aDb[i].pBt = 0;
|
||||
sqliteFree(db->aDb[i].zName);
|
||||
|
@ -128,3 +142,138 @@ void sqliteDetach(Parse *pParse, Token *pDbname){
|
|||
sqliteResetInternalSchema(db, i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize a DbFixer structure. This routine must be called prior
|
||||
** to passing the structure to one of the sqliteFixAAAA() routines below.
|
||||
**
|
||||
** The return value indicates whether or not fixation is required. TRUE
|
||||
** means we do need to fix the database references, FALSE means we do not.
|
||||
*/
|
||||
int sqliteFixInit(
|
||||
DbFixer *pFix, /* The fixer to be initialized */
|
||||
Parse *pParse, /* Error messages will be written here */
|
||||
int iDb, /* This is the database that must must be used */
|
||||
const char *zType, /* "view", "trigger", or "index" */
|
||||
const Token *pName /* Name of the view, trigger, or index */
|
||||
){
|
||||
sqlite *db;
|
||||
|
||||
if( iDb<0 || iDb==1 ) return 0;
|
||||
db = pParse->db;
|
||||
assert( db->nDb>iDb );
|
||||
pFix->pParse = pParse;
|
||||
pFix->zDb = db->aDb[iDb].zName;
|
||||
pFix->zType = zType;
|
||||
pFix->pName = pName;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** The following set of routines walk through the parse tree and assign
|
||||
** a specific database to all table references where the database name
|
||||
** was left unspecified in the original SQL statement. The pFix structure
|
||||
** must have been initialized by a prior call to sqliteFixInit().
|
||||
**
|
||||
** These routines are used to make sure that an index, trigger, or
|
||||
** view in one database does not refer to objects in a different database.
|
||||
** (Exception: indices, triggers, and views in the TEMP database are
|
||||
** allowed to refer to anything.) If a reference is explicitly made
|
||||
** to an object in a different database, an error message is added to
|
||||
** pParse->zErrMsg and these routines return non-zero. If everything
|
||||
** checks out, these routines return 0.
|
||||
*/
|
||||
int sqliteFixSrcList(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
SrcList *pList /* The Source list to check and modify */
|
||||
){
|
||||
int i;
|
||||
const char *zDb;
|
||||
|
||||
if( pList==0 ) return 0;
|
||||
zDb = pFix->zDb;
|
||||
for(i=0; i<pList->nSrc; i++){
|
||||
if( pList->a[i].zDatabase==0 ){
|
||||
pList->a[i].zDatabase = sqliteStrDup(zDb);
|
||||
}else if( sqliteStrICmp(pList->a[i].zDatabase,zDb)!=0 ){
|
||||
sqliteErrorMsg(pFix->pParse,
|
||||
"%s %z cannot reference objects in database %s",
|
||||
pFix->zType, sqliteStrNDup(pFix->pName->z, pFix->pName->n),
|
||||
pList->a[i].zDatabase);
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixSelect(pFix, pList->a[i].pSelect) ) return 1;
|
||||
if( sqliteFixExpr(pFix, pList->a[i].pOn) ) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sqliteFixSelect(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
Select *pSelect /* The SELECT statement to be fixed to one database */
|
||||
){
|
||||
while( pSelect ){
|
||||
if( sqliteFixExprList(pFix, pSelect->pEList) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixSrcList(pFix, pSelect->pSrc) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixExpr(pFix, pSelect->pWhere) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixExpr(pFix, pSelect->pHaving) ){
|
||||
return 1;
|
||||
}
|
||||
pSelect = pSelect->pPrior;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sqliteFixExpr(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
Expr *pExpr /* The expression to be fixed to one database */
|
||||
){
|
||||
while( pExpr ){
|
||||
if( sqliteFixSelect(pFix, pExpr->pSelect) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixExprList(pFix, pExpr->pList) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixExpr(pFix, pExpr->pRight) ){
|
||||
return 1;
|
||||
}
|
||||
pExpr = pExpr->pLeft;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sqliteFixExprList(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
ExprList *pList /* The expression to be fixed to one database */
|
||||
){
|
||||
int i;
|
||||
if( pList==0 ) return 0;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
if( sqliteFixExpr(pFix, pList->a[i].pExpr) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sqliteFixTriggerStep(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
TriggerStep *pStep /* The trigger step be fixed to one database */
|
||||
){
|
||||
while( pStep ){
|
||||
if( sqliteFixSelect(pFix, pStep->pSelect) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixExpr(pFix, pStep->pWhere) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqliteFixExprList(pFix, pStep->pExprList) ){
|
||||
return 1;
|
||||
}
|
||||
pStep = pStep->pNext;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -2477,7 +2477,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){
|
|||
int minV = pgnoNew[i];
|
||||
int minI = i;
|
||||
for(j=i+1; j<k; j++){
|
||||
if( pgnoNew[j]<minV ){
|
||||
if( pgnoNew[j]<(unsigned)minV ){
|
||||
minI = j;
|
||||
minV = pgnoNew[j];
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ struct BtCursorOps {
|
|||
#define SQLITE_N_BTREE_META 10
|
||||
|
||||
int sqliteBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree);
|
||||
int sqliteRBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree);
|
||||
int sqliteRbtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree);
|
||||
|
||||
#define btOps(pBt) (*((BtOps **)(pBt)))
|
||||
#define btCOps(pCur) (*((BtCursorOps **)(pCur)))
|
||||
|
|
|
@ -580,8 +580,9 @@ int sqliteRbtreeOpen(
|
|||
const char *zFilename,
|
||||
int mode,
|
||||
int nPg,
|
||||
Rbtree **ppRbtree
|
||||
Btree **ppBtree
|
||||
){
|
||||
Rbtree **ppRbtree = (Rbtree**)ppBtree;
|
||||
*ppRbtree = (Rbtree *)sqliteMalloc(sizeof(Rbtree));
|
||||
sqliteHashInit(&(*ppRbtree)->tblHash, SQLITE_HASH_INT, 0);
|
||||
|
||||
|
|
|
@ -118,6 +118,12 @@ void sqliteExec(Parse *pParse){
|
|||
** of that table and (optionally) the name of the database
|
||||
** containing the table. Return NULL if not found.
|
||||
**
|
||||
** If zDatabase is 0, all databases are searched for the
|
||||
** table and the first matching table is returned. (No checking
|
||||
** for duplicate table names is done.) The search order is
|
||||
** TEMP first, then MAIN, then any auxiliary databases added
|
||||
** using the ATTACH command.
|
||||
**
|
||||
** See also sqliteLocateTable().
|
||||
*/
|
||||
Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){
|
||||
|
@ -137,38 +143,22 @@ Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){
|
|||
** a particular database table given the name
|
||||
** of that table and (optionally) the name of the database
|
||||
** containing the table. Return NULL if not found.
|
||||
** Also leave an error message in pParse->zErrMsg.
|
||||
**
|
||||
** If pParse->useDb is not negative, then the table must be
|
||||
** located in that database. If a different database is specified,
|
||||
** an error message is generated into pParse->zErrMsg.
|
||||
** The difference between this routine and sqliteFindTable()
|
||||
** is that this routine leaves an error message in pParse->zErrMsg
|
||||
** where sqliteFindTable() does not.
|
||||
*/
|
||||
Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){
|
||||
sqlite *db;
|
||||
const char *zUse;
|
||||
Table *p;
|
||||
db = pParse->db;
|
||||
if( pParse->useDb<0 ){
|
||||
p = sqliteFindTable(db, zName, zDbase);
|
||||
}else {
|
||||
assert( pParse->useDb<db->nDb );
|
||||
assert( db->aDb[pParse->useDb].pBt!=0 );
|
||||
zUse = db->aDb[pParse->useDb].zName;
|
||||
if( zDbase && pParse->useDb!=1 && sqliteStrICmp(zDbase, zUse)!=0 ){
|
||||
sqliteErrorMsg(pParse,"cannot use database %s in this context", zDbase);
|
||||
return 0;
|
||||
}
|
||||
p = sqliteFindTable(db, zName, zUse);
|
||||
if( p==0 && pParse->useDb==1 && zDbase==0 ){
|
||||
p = sqliteFindTable(db, zName, 0);
|
||||
}
|
||||
}
|
||||
|
||||
p = sqliteFindTable(pParse->db, zName, zDbase);
|
||||
if( p==0 ){
|
||||
if( zDbase ){
|
||||
sqliteErrorMsg(pParse, "no such table: %s.%s", zDbase, zName);
|
||||
}else if( (pParse->useDb==0 || pParse->useDb>=2)
|
||||
&& sqliteFindTable(db, zName, 0)!=0 ){
|
||||
}else if( sqliteFindTable(pParse->db, zName, 0)!=0 ){
|
||||
sqliteErrorMsg(pParse, "table \"%s\" is not in database \"%s\"",
|
||||
zName, zUse);
|
||||
zName, zDbase);
|
||||
}else{
|
||||
sqliteErrorMsg(pParse, "no such table: %s", zName);
|
||||
}
|
||||
|
@ -181,6 +171,12 @@ Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){
|
|||
** a particular index given the name of that index
|
||||
** and the name of the database that contains the index.
|
||||
** Return NULL if not found.
|
||||
**
|
||||
** If zDatabase is 0, all databases are searched for the
|
||||
** table and the first matching index is returned. (No checking
|
||||
** for duplicate index names is done.) The search order is
|
||||
** TEMP first, then MAIN, then any auxiliary databases added
|
||||
** using the ATTACH command.
|
||||
*/
|
||||
Index *sqliteFindIndex(sqlite *db, const char *zName, const char *zDb){
|
||||
Index *p = 0;
|
||||
|
@ -1046,6 +1042,7 @@ void sqliteCreateView(
|
|||
int n;
|
||||
const char *z;
|
||||
Token sEnd;
|
||||
DbFixer sFix;
|
||||
|
||||
sqliteStartTable(pParse, pBegin, pName, isTemp, 1);
|
||||
p = pParse->pNewTable;
|
||||
|
@ -1053,6 +1050,12 @@ void sqliteCreateView(
|
|||
sqliteSelectDelete(pSelect);
|
||||
return;
|
||||
}
|
||||
if( sqliteFixInit(&sFix, pParse, p->iDb, "view", pName)
|
||||
&& sqliteFixSelect(&sFix, pSelect)
|
||||
){
|
||||
sqliteSelectDelete(pSelect);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make a copy of the entire SELECT statement that defines the view.
|
||||
** This will force all the Expr.token.z values to be dynamically
|
||||
|
@ -1297,12 +1300,8 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){
|
|||
/* Drop all triggers associated with the table being dropped */
|
||||
pTrigger = pTable->pTrigger;
|
||||
while( pTrigger ){
|
||||
SrcList *pNm;
|
||||
assert( pTrigger->iDb==pTable->iDb || pTrigger->iDb==1 );
|
||||
pNm = sqliteSrcListAppend(0, 0, 0);
|
||||
pNm->a[0].zName = sqliteStrDup(pTrigger->name);
|
||||
pNm->a[0].zDatabase = sqliteStrDup(db->aDb[pTable->iDb].zName);
|
||||
sqliteDropTrigger(pParse, pNm, 1);
|
||||
sqliteDropTriggerPtr(pParse, pTrigger, 1);
|
||||
if( pParse->explain ){
|
||||
pTrigger = pTrigger->pNext;
|
||||
}else{
|
||||
|
@ -1538,10 +1537,17 @@ void sqliteCreateIndex(
|
|||
Index *pIndex; /* The index to be created */
|
||||
char *zName = 0;
|
||||
int i, j;
|
||||
Token nullId; /* Fake token for an empty ID list */
|
||||
Token nullId; /* Fake token for an empty ID list */
|
||||
DbFixer sFix; /* For assigning database names to pTable */
|
||||
sqlite *db = pParse->db;
|
||||
|
||||
if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index;
|
||||
if( !isTemp && pParse->initFlag
|
||||
&& sqliteFixInit(&sFix, pParse, pParse->iDb, "index", pName)
|
||||
&& sqliteFixSrcList(&sFix, pTable)
|
||||
){
|
||||
goto exit_create_index;
|
||||
}
|
||||
|
||||
/*
|
||||
** Find the table that is to be indexed. Return early if not found.
|
||||
|
|
|
@ -96,7 +96,8 @@ void sqliteCopy(
|
|||
sqliteVdbeAddOp(v, OP_FileColumn, i, 0);
|
||||
}
|
||||
}
|
||||
sqliteGenerateConstraintChecks(pParse, pTab, 0, 0, 0, 0, onError, addr);
|
||||
sqliteGenerateConstraintChecks(pParse, pTab, 0, 0, pTab->iPKey>=0,
|
||||
0, onError, addr);
|
||||
sqliteCompleteInsertion(pParse, pTab, 0, 0, 0, 0, -1);
|
||||
if( (db->flags & SQLITE_CountRows)!=0 ){
|
||||
sqliteVdbeAddOp(v, OP_AddImm, 1, 0); /* Increment row count */
|
||||
|
|
|
@ -191,6 +191,7 @@ SrcList *sqliteSrcListDup(SrcList *p){
|
|||
if( pNew==0 ) return 0;
|
||||
pNew->nSrc = 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;
|
||||
|
|
|
@ -293,10 +293,14 @@ void sqliteInsert(
|
|||
}
|
||||
}
|
||||
if( j>=pTab->nCol ){
|
||||
sqliteErrorMsg(pParse, "table %S has no column named %s",
|
||||
pTabList, 0, pColumn->a[i].zName);
|
||||
pParse->nErr++;
|
||||
goto insert_cleanup;
|
||||
if( sqliteIsRowid(pColumn->a[i].zName) ){
|
||||
keyColumn = i;
|
||||
}else{
|
||||
sqliteErrorMsg(pParse, "table %S has no column named %s",
|
||||
pTabList, 0, pColumn->a[i].zName);
|
||||
pParse->nErr++;
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -482,7 +486,8 @@ void sqliteInsert(
|
|||
/* Generate code to check constraints and generate index keys and
|
||||
** do the insertion.
|
||||
*/
|
||||
sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop);
|
||||
sqliteGenerateConstraintChecks(pParse, pTab, base, 0, keyColumn>=0,
|
||||
0, onError, endOfLoop);
|
||||
sqliteCompleteInsertion(pParse, pTab, base, 0,0,0,
|
||||
after_triggers ? newIdx : -1);
|
||||
}
|
||||
|
@ -660,7 +665,6 @@ void sqliteGenerateConstraintChecks(
|
|||
*/
|
||||
for(i=0; i<nCol; i++){
|
||||
if( i==pTab->iPKey ){
|
||||
/* Fix me: Make sure the INTEGER PRIMARY KEY is not NULL. */
|
||||
continue;
|
||||
}
|
||||
onError = pTab->aCol[i].notNull;
|
||||
|
@ -711,10 +715,8 @@ void sqliteGenerateConstraintChecks(
|
|||
/* If we have an INTEGER PRIMARY KEY, make sure the primary key
|
||||
** of the new record does not previously exist. Except, if this
|
||||
** is an UPDATE and the primary key is not changing, that is OK.
|
||||
** Also, if the conflict resolution policy is REPLACE, then we
|
||||
** can skip this test.
|
||||
*/
|
||||
if( (recnoChng || !isUpdate) && pTab->iPKey>=0 ){
|
||||
if( recnoChng ){
|
||||
onError = pTab->keyConf;
|
||||
if( overrideError!=OE_Default ){
|
||||
onError = overrideError;
|
||||
|
@ -723,37 +725,49 @@ void sqliteGenerateConstraintChecks(
|
|||
}else if( onError==OE_Default ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
if( onError!=OE_Replace ){
|
||||
if( isUpdate ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
|
||||
jumpInst1 = sqliteVdbeAddOp(v, OP_Eq, 0, 0);
|
||||
|
||||
if( isUpdate ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
|
||||
jumpInst1 = sqliteVdbeAddOp(v, OP_Eq, 0, 0);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol, 1);
|
||||
jumpInst2 = sqliteVdbeAddOp(v, OP_NotExists, base, 0);
|
||||
switch( onError ){
|
||||
default: {
|
||||
onError = OE_Abort;
|
||||
/* Fall thru into the next case */
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol, 1);
|
||||
jumpInst2 = sqliteVdbeAddOp(v, OP_NotExists, base, 0);
|
||||
switch( onError ){
|
||||
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);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
}
|
||||
case OE_Replace: {
|
||||
sqliteGenerateRowIndexDelete(pParse->db, v, pTab, base, 0);
|
||||
if( isUpdate ){
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+hasTwoRecnos, 1);
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
||||
}
|
||||
case OE_Ignore: {
|
||||
sqliteVdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest);
|
||||
break;
|
||||
}
|
||||
default: assert(0);
|
||||
seenReplace = 1;
|
||||
break;
|
||||
}
|
||||
contAddr = sqliteVdbeCurrentAddr(v);
|
||||
sqliteVdbeChangeP2(v, jumpInst2, contAddr);
|
||||
if( isUpdate ){
|
||||
sqliteVdbeChangeP2(v, jumpInst1, contAddr);
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
||||
case OE_Ignore: {
|
||||
assert( seenReplace==0 );
|
||||
sqliteVdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest);
|
||||
break;
|
||||
}
|
||||
}
|
||||
contAddr = sqliteVdbeCurrentAddr(v);
|
||||
sqliteVdbeChangeP2(v, jumpInst2, contAddr);
|
||||
if( isUpdate ){
|
||||
sqliteVdbeChangeP2(v, jumpInst1, contAddr);
|
||||
sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test all UNIQUE constraints by creating entries for each UNIQUE
|
||||
|
@ -788,6 +802,11 @@ void sqliteGenerateConstraintChecks(
|
|||
}else if( onError==OE_Default ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
if( seenReplace ){
|
||||
if( onError==OE_Ignore ) onError = OE_Replace;
|
||||
else if( onError==OE_Fail ) onError = OE_Abort;
|
||||
}
|
||||
|
||||
|
||||
/* Check to see if the new index entry will be unique */
|
||||
sqliteVdbeAddOp(v, OP_Dup, extra+nCol+1+hasTwoRecnos, 1);
|
||||
|
|
|
@ -80,7 +80,6 @@ int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){
|
|||
sParse.db = pData->db;
|
||||
sParse.initFlag = 1;
|
||||
sParse.iDb = atoi(argv[4]);
|
||||
sParse.useDb = -1;
|
||||
sParse.newTnum = atoi(argv[2]);
|
||||
sParse.useCallback = 1;
|
||||
sqliteRunParser(&sParse, argv[3], pData->pzErrMsg);
|
||||
|
@ -322,7 +321,7 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
|
|||
db->aDb[iDb].pBt = 0;
|
||||
return SQLITE_FORMAT;
|
||||
}
|
||||
sqliteBtreeSetCacheSize(db->aDb[iDb].pBt, size);
|
||||
sqliteBtreeSetCacheSize(db->aDb[iDb].pBt, db->cache_size);
|
||||
sqliteBtreeSetSafetyLevel(db->aDb[iDb].pBt, meta[4]==0 ? 2 : meta[4]);
|
||||
|
||||
/* Read the schema information out of the schema tables
|
||||
|
@ -332,7 +331,6 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){
|
|||
sParse.xCallback = sqliteInitCallback;
|
||||
sParse.pArg = (void*)&initData;
|
||||
sParse.initFlag = 1;
|
||||
sParse.useDb = -1;
|
||||
sParse.useCallback = 1;
|
||||
if( iDb==0 ){
|
||||
sqliteRunParser(&sParse,
|
||||
|
@ -442,6 +440,9 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
|
|||
}
|
||||
|
||||
/* Open the backend database driver */
|
||||
if( zFilename[0]==':' && strcmp(zFilename,":memory:")==0 ){
|
||||
db->temp_store = 2;
|
||||
}
|
||||
rc = sqliteBtreeFactory(db, zFilename, 0, MAX_PAGES, &db->aDb[0].pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
switch( rc ){
|
||||
|
@ -623,7 +624,6 @@ static int sqliteMain(
|
|||
sParse.db = db;
|
||||
sParse.xCallback = xCallback;
|
||||
sParse.pArg = pArg;
|
||||
sParse.useDb = -1;
|
||||
sParse.useCallback = ppVm==0;
|
||||
if( db->xTrace ) db->xTrace(db->pTraceArg, zSql);
|
||||
sqliteRunParser(&sParse, zSql, pzErrMsg);
|
||||
|
|
|
@ -992,6 +992,13 @@ int sqliteOsFileSize(OsFile *id, off_t *pSize){
|
|||
/*
|
||||
** Return true (non-zero) if we are running under WinNT, Win2K or WinXP.
|
||||
** Return false (zero) for Win95, Win98, or WinME.
|
||||
**
|
||||
** Here is an interesting observation: Win95, Win98, and WinME lack
|
||||
** the LockFileEx() API. But we can still statically link against that
|
||||
** API as long as we don't call it win running Win95/98/ME. A call to
|
||||
** this routine is used to determine if the host is Win95/98/ME or
|
||||
** WinNT/2K/XP so that we will know whether or not we can safely call
|
||||
** the LockFileEx() API.
|
||||
*/
|
||||
int isNT(void){
|
||||
static osType = 0; /* 0=unknown 1=win95 2=winNT */
|
||||
|
@ -1006,10 +1013,10 @@ int isNT(void){
|
|||
#endif
|
||||
|
||||
/*
|
||||
** Windows file locking notes: [the same/equivalent applies to MacOS]
|
||||
** Windows file locking notes: [similar issues apply to MacOS]
|
||||
**
|
||||
** We cannot use LockFileEx() or UnlockFileEx() because those functions
|
||||
** are not available under Win95/98/ME. So we use only LockFile() and
|
||||
** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
|
||||
** those functions are not available. So we use only LockFile() and
|
||||
** UnlockFile().
|
||||
**
|
||||
** LockFile() prevents not just writing but also reading by other processes.
|
||||
|
@ -1034,6 +1041,14 @@ int isNT(void){
|
|||
** another process jumping into the middle and messing us up. The same
|
||||
** argument applies to sqliteOsWriteLock().
|
||||
**
|
||||
** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
|
||||
** which means we can use reader/writer locks. When reader writer locks
|
||||
** are used, the lock is placed on the same range of bytes that is used
|
||||
** for probabilistic locking in Win95/98/ME. Hence, the locking scheme
|
||||
** will support two or more Win95 readers or two or more WinNT readers.
|
||||
** But a single Win95 reader will lock out all WinNT readers and a single
|
||||
** WinNT reader will lock out all other Win95 readers.
|
||||
**
|
||||
** Note: On MacOS we use the resource fork for locking.
|
||||
**
|
||||
** The following #defines specify the range of bytes used for locking.
|
||||
|
@ -1096,14 +1111,22 @@ int sqliteOsReadLock(OsFile *id){
|
|||
int lk = (sqliteRandomInteger() & 0x7ffffff)%N_LOCKBYTE+1;
|
||||
int res;
|
||||
int cnt = 100;
|
||||
int page = isNT() ? 0xffffffff : 0;
|
||||
while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, page, 1, 0))==0 ){
|
||||
while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){
|
||||
Sleep(1);
|
||||
}
|
||||
if( res ){
|
||||
UnlockFile(id->h, FIRST_LOCKBYTE+1, page, N_LOCKBYTE, 0);
|
||||
res = LockFile(id->h, FIRST_LOCKBYTE+lk, page, 1, 0);
|
||||
UnlockFile(id->h, FIRST_LOCKBYTE, page, 1, 0);
|
||||
UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0);
|
||||
if( isNT() ){
|
||||
OVERLAPPED ovlp;
|
||||
ovlp.Offset = FIRST_LOCKBYTE+1;
|
||||
ovlp.OffsetHigh = 0;
|
||||
ovlp.hEvent = 0;
|
||||
res = LockFileEx(id->h, LOCKFILE_FAIL_IMMEDIATELY,
|
||||
0, N_LOCKBYTE, 0, &ovlp);
|
||||
}else{
|
||||
res = LockFile(id->h, FIRST_LOCKBYTE+lk, 0, 1, 0);
|
||||
}
|
||||
UnlockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0);
|
||||
}
|
||||
if( res ){
|
||||
id->locked = lk;
|
||||
|
@ -1191,18 +1214,23 @@ int sqliteOsWriteLock(OsFile *id){
|
|||
}else{
|
||||
int res;
|
||||
int cnt = 100;
|
||||
int page = isNT() ? 0xffffffff : 0;
|
||||
while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, page, 1, 0))==0 ){
|
||||
while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){
|
||||
Sleep(1);
|
||||
}
|
||||
if( res ){
|
||||
if( id->locked==0
|
||||
|| UnlockFile(id->h, FIRST_LOCKBYTE + id->locked, page, 1, 0) ){
|
||||
res = LockFile(id->h, FIRST_LOCKBYTE+1, page, N_LOCKBYTE, 0);
|
||||
if( id->locked>0 ){
|
||||
if( isNT() ){
|
||||
UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0);
|
||||
}else{
|
||||
res = UnlockFile(id->h, FIRST_LOCKBYTE + id->locked, 0, 1, 0);
|
||||
}
|
||||
}
|
||||
if( res ){
|
||||
res = LockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0);
|
||||
}else{
|
||||
res = 0;
|
||||
}
|
||||
UnlockFile(id->h, FIRST_LOCKBYTE, page, 1, 0);
|
||||
UnlockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0);
|
||||
}
|
||||
if( res ){
|
||||
id->locked = -1;
|
||||
|
@ -1291,15 +1319,14 @@ int sqliteOsUnlock(OsFile *id){
|
|||
#endif
|
||||
#if OS_WIN
|
||||
int rc;
|
||||
int page = isNT() ? 0xffffffff : 0;
|
||||
if( id->locked==0 ){
|
||||
rc = SQLITE_OK;
|
||||
}else if( id->locked<0 ){
|
||||
UnlockFile(id->h, FIRST_LOCKBYTE+1, page, N_LOCKBYTE, 0);
|
||||
}else if( isNT() || id->locked<0 ){
|
||||
UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0);
|
||||
rc = SQLITE_OK;
|
||||
id->locked = 0;
|
||||
}else{
|
||||
UnlockFile(id->h, FIRST_LOCKBYTE+id->locked, page, 1, 0);
|
||||
UnlockFile(id->h, FIRST_LOCKBYTE+id->locked, 0, 1, 0);
|
||||
rc = SQLITE_OK;
|
||||
id->locked = 0;
|
||||
}
|
||||
|
|
|
@ -496,7 +496,7 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int format){
|
|||
if( pgRec.pgno==0 ){
|
||||
return SQLITE_DONE;
|
||||
}
|
||||
if( pgRec.pgno>pPager->dbSize ){
|
||||
if( pgRec.pgno>(unsigned)pPager->dbSize ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( format>=JOURNAL_FORMAT_3 ){
|
||||
|
@ -944,7 +944,7 @@ int sqlitepager_truncate(Pager *pPager, Pgno nPage){
|
|||
rc = pager_errcode(pPager);
|
||||
return rc;
|
||||
}
|
||||
if( nPage>=pPager->dbSize ){
|
||||
if( nPage>=(unsigned)pPager->dbSize ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
syncAllPages(pPager);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -848,7 +848,7 @@ expr(A) ::= RAISE(X) LP FAIL COMMA nm(Z) RP(Y). {
|
|||
|
||||
//////////////////////// DROP TRIGGER statement //////////////////////////////
|
||||
cmd ::= DROP TRIGGER nm(X) dbnm(D). {
|
||||
sqliteDropTrigger(pParse,sqliteSrcListAppend(0,&X,&D),0);
|
||||
sqliteDropTrigger(pParse,sqliteSrcListAppend(0,&X,&D));
|
||||
}
|
||||
|
||||
//////////////////////// ATTACH DATABASE file AS name /////////////////////////
|
||||
|
|
|
@ -421,6 +421,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
static VdbeOp indexListPreface[] = {
|
||||
{ OP_ColumnName, 0, 0, "seq"},
|
||||
{ OP_ColumnName, 1, 0, "name"},
|
||||
{ OP_ColumnName, 2, 0, "file"},
|
||||
};
|
||||
|
||||
sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface);
|
||||
|
@ -430,7 +431,10 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
|||
sqliteVdbeAddOp(v, OP_Integer, i, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, db->aDb[i].zName, P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 2, 0);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeChangeP3(v, -1, sqliteBtreeGetFilename(db->aDb[i].pBt),
|
||||
P3_STATIC);
|
||||
sqliteVdbeAddOp(v, OP_Callback, 3, 0);
|
||||
}
|
||||
}else
|
||||
/*
|
||||
|
|
|
@ -1657,6 +1657,7 @@ static int flattenSubquery(
|
|||
if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){
|
||||
sqliteDeleteTable(0, pSrc->a[iFrom].pTab);
|
||||
}
|
||||
sqliteFree(pSrc->a[iFrom].zDatabase);
|
||||
sqliteFree(pSrc->a[iFrom].zName);
|
||||
sqliteFree(pSrc->a[iFrom].zAlias);
|
||||
if( nSubSrc>1 ){
|
||||
|
|
|
@ -391,6 +391,7 @@ int sqlite_get_table_vprintf(
|
|||
va_list ap /* Arguments to the format string */
|
||||
);
|
||||
char *sqlite_mprintf(const char*,...);
|
||||
char *sqlite_vmprintf(const char*, va_list);
|
||||
|
||||
/*
|
||||
** Windows systems should call this routine to free memory that
|
||||
|
@ -554,6 +555,9 @@ int sqlite_set_authorizer(
|
|||
#define SQLITE_SELECT 21 /* NULL NULL */
|
||||
#define SQLITE_TRANSACTION 22 /* NULL NULL */
|
||||
#define SQLITE_UPDATE 23 /* Table Name Column Name */
|
||||
#define SQLITE_ATTACH 24 /* Filename NULL */
|
||||
#define SQLITE_DETACH 25 /* Database Name NULL */
|
||||
|
||||
|
||||
/*
|
||||
** The return value of the authorization function should be one of the
|
||||
|
|
|
@ -28,7 +28,7 @@ extern "C" {
|
|||
/*
|
||||
** The version of the SQLite library.
|
||||
*/
|
||||
#define SQLITE_VERSION "2.8.2"
|
||||
#define SQLITE_VERSION "2.8.3"
|
||||
|
||||
/*
|
||||
** The version string is also compiled into the library so that a program
|
||||
|
@ -42,7 +42,7 @@ extern const char sqlite_version[];
|
|||
** UTF-8 encoded data. The SQLITE_ISO8859 macro is defined if the
|
||||
** iso8859 encoded should be used.
|
||||
*/
|
||||
#define SQLITE_iso8859 1
|
||||
#define SQLITE_ISO8859 1
|
||||
|
||||
/*
|
||||
** The following constant holds one of two strings, "UTF-8" or "iso8859",
|
||||
|
@ -391,6 +391,7 @@ int sqlite_get_table_vprintf(
|
|||
va_list ap /* Arguments to the format string */
|
||||
);
|
||||
char *sqlite_mprintf(const char*,...);
|
||||
char *sqlite_vmprintf(const char*, va_list);
|
||||
|
||||
/*
|
||||
** Windows systems should call this routine to free memory that
|
||||
|
@ -554,6 +555,9 @@ int sqlite_set_authorizer(
|
|||
#define SQLITE_SELECT 21 /* NULL NULL */
|
||||
#define SQLITE_TRANSACTION 22 /* NULL NULL */
|
||||
#define SQLITE_UPDATE 23 /* Table Name Column Name */
|
||||
#define SQLITE_ATTACH 24 /* Filename NULL */
|
||||
#define SQLITE_DETACH 25 /* Database Name NULL */
|
||||
|
||||
|
||||
/*
|
||||
** The return value of the authorization function should be one of the
|
||||
|
|
|
@ -132,8 +132,13 @@ typedef unsigned INTPTR_TYPE uptr; /* Big enough to hold a pointer */
|
|||
** multi-megabyte records are OK. If your needs are different, you can
|
||||
** change this define and recompile to increase or decrease the record
|
||||
** size.
|
||||
**
|
||||
** The 16777198 is computed as follows: 238 bytes of payload on the
|
||||
** original pages plus 16448 overflow pages each holding 1020 bytes of
|
||||
** data.
|
||||
*/
|
||||
#define MAX_BYTES_PER_ROW 1048576
|
||||
/* #define MAX_BYTES_PER_ROW 16777198 */
|
||||
|
||||
/*
|
||||
** If memory allocation problems are found, recompile with
|
||||
|
@ -236,7 +241,7 @@ struct Db {
|
|||
Hash idxHash; /* All (named) indices indexed by name */
|
||||
Hash trigHash; /* All triggers indexed by name */
|
||||
Hash aFKey; /* Foreign keys indexed by to-table */
|
||||
u8 inTrans; /* True if a transaction is underway for this backend */
|
||||
u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */
|
||||
u16 flags; /* Flags associated with this database */
|
||||
};
|
||||
|
||||
|
@ -852,7 +857,6 @@ struct Parse {
|
|||
** 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 useDb; /* Restrict references to tables in this database */
|
||||
int newTnum; /* Table number to use when reparsing CREATE TABLEs */
|
||||
int nErr; /* Number of errors seen */
|
||||
int nTab; /* Number of previously allocated VDBE cursors */
|
||||
|
@ -893,12 +897,14 @@ struct Trigger {
|
|||
char *name; /* The name of the trigger */
|
||||
char *table; /* The table or view to which the trigger applies */
|
||||
u8 iDb; /* Database containing this trigger */
|
||||
u8 iTabDb; /* Database containing Trigger.table */
|
||||
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */
|
||||
u8 tr_tm; /* One of TK_BEFORE, TK_AFTER */
|
||||
Expr *pWhen; /* The WHEN clause of the expresion (may be NULL) */
|
||||
IdList *pColumns; /* If this is an UPDATE OF <column-list> trigger,
|
||||
the <column-list> is stored here */
|
||||
int foreach; /* One of TK_ROW or TK_STATEMENT */
|
||||
Token nameToken; /* Token containing zName. Use during parsing only */
|
||||
|
||||
TriggerStep *step_list; /* Link list of trigger program steps */
|
||||
Trigger *pNext; /* Next trigger associated with the table */
|
||||
|
@ -995,6 +1001,19 @@ struct TriggerStack {
|
|||
TriggerStack *pNext; /* Next trigger down on the trigger stack */
|
||||
};
|
||||
|
||||
/*
|
||||
** The following structure contains information used by the sqliteFix...
|
||||
** routines as they walk the parse tree to make database references
|
||||
** explicit.
|
||||
*/
|
||||
typedef struct DbFixer DbFixer;
|
||||
struct DbFixer {
|
||||
Parse *pParse; /* The parsing context. Error messages written here */
|
||||
const char *zDb; /* Make sure all objects are contained in this database */
|
||||
const char *zType; /* Type of the container - used for error messages */
|
||||
const Token *pName; /* Name of the container - used for error messages */
|
||||
};
|
||||
|
||||
/*
|
||||
* This global flag is set for performance testing of triggers. When it is set
|
||||
* SQLite will perform the overhead of building new and old trigger references
|
||||
|
@ -1135,7 +1154,8 @@ int sqliteSafetyCheck(sqlite*);
|
|||
void sqliteChangeCookie(sqlite*, Vdbe*);
|
||||
void sqliteBeginTrigger(Parse*, Token*,int,int,IdList*,SrcList*,int,Expr*,int);
|
||||
void sqliteFinishTrigger(Parse*, TriggerStep*, Token*);
|
||||
void sqliteDropTrigger(Parse*, SrcList*, int);
|
||||
void sqliteDropTrigger(Parse*, SrcList*);
|
||||
void sqliteDropTriggerPtr(Parse*, Trigger*, int);
|
||||
int sqliteTriggersExist(Parse* , Trigger* , int , int , int, ExprList*);
|
||||
int sqliteCodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int,
|
||||
int, int);
|
||||
|
@ -1164,3 +1184,9 @@ void sqliteAttach(Parse*, Token*, Token*);
|
|||
void sqliteDetach(Parse*, Token*);
|
||||
int sqliteBtreeFactory(const sqlite *db, const char *zFilename,
|
||||
int mode, int nPg, Btree **ppBtree);
|
||||
int sqliteFixInit(DbFixer*, Parse*, int, const char*, const Token*);
|
||||
int sqliteFixSrcList(DbFixer*, SrcList*);
|
||||
int sqliteFixSelect(DbFixer*, Select*);
|
||||
int sqliteFixExpr(DbFixer*, Expr*);
|
||||
int sqliteFixExprList(DbFixer*, ExprList*);
|
||||
int sqliteFixTriggerStep(DbFixer*, TriggerStep*);
|
||||
|
|
|
@ -54,6 +54,7 @@ void sqliteBeginTrigger(
|
|||
char *zName = 0; /* Name of the trigger */
|
||||
sqlite *db = pParse->db;
|
||||
int iDb; /* When database to store the trigger in */
|
||||
DbFixer sFix;
|
||||
|
||||
/* Check that:
|
||||
** 1. the trigger name does not already exist.
|
||||
|
@ -64,6 +65,12 @@ void sqliteBeginTrigger(
|
|||
*/
|
||||
if( sqlite_malloc_failed ) goto trigger_cleanup;
|
||||
assert( pTableName->nSrc==1 );
|
||||
if( pParse->initFlag
|
||||
&& sqliteFixInit(&sFix, pParse, pParse->iDb, "trigger", pName)
|
||||
&& sqliteFixSrcList(&sFix, pTableName)
|
||||
){
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
tab = sqliteSrcListLookup(pParse, pTableName);
|
||||
if( !tab ){
|
||||
goto trigger_cleanup;
|
||||
|
@ -127,11 +134,13 @@ void sqliteBeginTrigger(
|
|||
nt->table = sqliteStrDup(pTableName->a[0].zName);
|
||||
if( sqlite_malloc_failed ) goto trigger_cleanup;
|
||||
nt->iDb = iDb;
|
||||
nt->iTabDb = tab->iDb;
|
||||
nt->op = op;
|
||||
nt->tr_tm = tr_tm;
|
||||
nt->pWhen = sqliteExprDup(pWhen);
|
||||
nt->pColumns = sqliteIdListDup(pColumns);
|
||||
nt->foreach = foreach;
|
||||
sqliteTokenCopy(&nt->nameToken,pName);
|
||||
assert( pParse->pNewTrigger==0 );
|
||||
pParse->pNewTrigger = nt;
|
||||
|
||||
|
@ -151,8 +160,9 @@ void sqliteFinishTrigger(
|
|||
TriggerStep *pStepList, /* The triggered program */
|
||||
Token *pAll /* Token that describes the complete CREATE TRIGGER */
|
||||
){
|
||||
Trigger *nt; /* The trigger whose construction is finishing up */
|
||||
Trigger *nt = 0; /* The trigger whose construction is finishing up */
|
||||
sqlite *db = pParse->db; /* The database */
|
||||
DbFixer sFix;
|
||||
|
||||
if( pParse->nErr || pParse->pNewTrigger==0 ) goto triggerfinish_cleanup;
|
||||
nt = pParse->pNewTrigger;
|
||||
|
@ -162,6 +172,10 @@ void sqliteFinishTrigger(
|
|||
pStepList->pTrig = nt;
|
||||
pStepList = pStepList->pNext;
|
||||
}
|
||||
if( sqliteFixInit(&sFix, pParse, nt->iDb, "trigger", &nt->nameToken)
|
||||
&& sqliteFixTriggerStep(&sFix, nt->step_list) ){
|
||||
goto triggerfinish_cleanup;
|
||||
}
|
||||
|
||||
/* if we are not initializing, and this trigger is not on a TEMP table,
|
||||
** build the sqlite_master entry
|
||||
|
@ -184,7 +198,7 @@ void sqliteFinishTrigger(
|
|||
v = sqliteGetVdbe(pParse);
|
||||
if( v==0 ) goto triggerfinish_cleanup;
|
||||
sqliteBeginWriteOperation(pParse, 0, 0);
|
||||
sqliteOpenMasterTable(v, nt->iDb==1);
|
||||
sqliteOpenMasterTable(v, nt->iDb);
|
||||
addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
|
||||
sqliteVdbeChangeP3(v, addr+2, nt->name, 0);
|
||||
sqliteVdbeChangeP3(v, addr+3, nt->table, 0);
|
||||
|
@ -200,15 +214,15 @@ void sqliteFinishTrigger(
|
|||
Table *pTab;
|
||||
sqliteHashInsert(&db->aDb[nt->iDb].trigHash,
|
||||
nt->name, strlen(nt->name)+1, nt);
|
||||
pTab = sqliteLocateTable(pParse, nt->table, 0);
|
||||
pTab = sqliteLocateTable(pParse, nt->table, db->aDb[nt->iTabDb].zName);
|
||||
assert( pTab!=0 );
|
||||
nt->pNext = pTab->pTrigger;
|
||||
pTab->pTrigger = nt;
|
||||
}else{
|
||||
sqliteDeleteTrigger(nt);
|
||||
nt = 0;
|
||||
}
|
||||
|
||||
triggerfinish_cleanup:
|
||||
sqliteDeleteTrigger(nt);
|
||||
sqliteDeleteTrigger(pParse->pNewTrigger);
|
||||
pParse->pNewTrigger = 0;
|
||||
sqliteDeleteTriggerStep(pStepList);
|
||||
|
@ -353,24 +367,25 @@ void sqliteDeleteTrigger(Trigger *pTrigger){
|
|||
sqliteFree(pTrigger->table);
|
||||
sqliteExprDelete(pTrigger->pWhen);
|
||||
sqliteIdListDelete(pTrigger->pColumns);
|
||||
if( pTrigger->nameToken.dyn ) sqliteFree((char*)pTrigger->nameToken.z);
|
||||
sqliteFree(pTrigger);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called to drop a trigger from the database schema.
|
||||
*
|
||||
* This may be called directly from the parser, or from within
|
||||
* sqliteDropTable(). In the latter case the "nested" argument is true.
|
||||
* This may be called directly from the parser and therefore identifies
|
||||
* the trigger by name. The sqliteDropTriggerPtr() routine does the
|
||||
* same job as this routine except it take a spointer to the trigger
|
||||
* instead of the trigger name.
|
||||
*
|
||||
* Note that this function does not delete the trigger entirely. Instead it
|
||||
* removes it from the internal schema and places it in the trigDrop hash
|
||||
* table. This is so that the trigger can be restored into the database schema
|
||||
* if the transaction is rolled back.
|
||||
*/
|
||||
void sqliteDropTrigger(Parse *pParse, SrcList *pName, int nested){
|
||||
void sqliteDropTrigger(Parse *pParse, SrcList *pName){
|
||||
Trigger *pTrigger;
|
||||
Table *pTable;
|
||||
Vdbe *v;
|
||||
int i;
|
||||
const char *zDb;
|
||||
const char *zName;
|
||||
|
@ -392,13 +407,29 @@ void sqliteDropTrigger(Parse *pParse, SrcList *pName, int nested){
|
|||
sqliteErrorMsg(pParse, "no such trigger: %S", pName, 0);
|
||||
goto drop_trigger_cleanup;
|
||||
}
|
||||
sqliteDropTriggerPtr(pParse, pTrigger, 0);
|
||||
|
||||
drop_trigger_cleanup:
|
||||
sqliteSrcListDelete(pName);
|
||||
}
|
||||
|
||||
/*
|
||||
** Drop a trigger given a pointer to that trigger. If nested is false,
|
||||
** then also generate code to remove the trigger from the SQLITE_MASTER
|
||||
** table.
|
||||
*/
|
||||
void sqliteDropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
|
||||
Table *pTable;
|
||||
Vdbe *v;
|
||||
sqlite *db = pParse->db;
|
||||
|
||||
assert( pTrigger->iDb<db->nDb );
|
||||
if( pTrigger->iDb>=2 ){
|
||||
sqliteErrorMsg(pParse, "triggers may not be removed from "
|
||||
"auxiliary database %s", db->aDb[pTrigger->iDb].zName);
|
||||
goto drop_trigger_cleanup;
|
||||
return;
|
||||
}
|
||||
pTable = sqliteFindTable(db, pTrigger->table, db->aDb[pTrigger->iDb].zName);
|
||||
pTable = sqliteFindTable(db, pTrigger->table,db->aDb[pTrigger->iTabDb].zName);
|
||||
assert(pTable);
|
||||
assert( pTable->iDb==pTrigger->iDb || pTrigger->iDb==1 );
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
|
@ -409,7 +440,7 @@ void sqliteDropTrigger(Parse *pParse, SrcList *pName, int nested){
|
|||
if( pTrigger->iDb ) code = SQLITE_DROP_TEMP_TRIGGER;
|
||||
if( sqliteAuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) ||
|
||||
sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
|
||||
goto drop_trigger_cleanup;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -432,7 +463,7 @@ void sqliteDropTrigger(Parse *pParse, SrcList *pName, int nested){
|
|||
sqliteBeginWriteOperation(pParse, 0, 0);
|
||||
sqliteOpenMasterTable(v, pTrigger->iDb);
|
||||
base = sqliteVdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger);
|
||||
sqliteVdbeChangeP3(v, base+1, zName, 0);
|
||||
sqliteVdbeChangeP3(v, base+1, pTrigger->name, 0);
|
||||
if( pTrigger->iDb==0 ){
|
||||
sqliteChangeCookie(db, v);
|
||||
}
|
||||
|
@ -444,6 +475,8 @@ void sqliteDropTrigger(Parse *pParse, SrcList *pName, int nested){
|
|||
* If this is not an "explain", then delete the trigger structure.
|
||||
*/
|
||||
if( !pParse->explain ){
|
||||
const char *zName = pTrigger->name;
|
||||
int nName = strlen(zName);
|
||||
if( pTable->pTrigger == pTrigger ){
|
||||
pTable->pTrigger = pTrigger->pNext;
|
||||
}else{
|
||||
|
@ -460,9 +493,6 @@ void sqliteDropTrigger(Parse *pParse, SrcList *pName, int nested){
|
|||
sqliteHashInsert(&(db->aDb[pTrigger->iDb].trigHash), zName, nName+1, 0);
|
||||
sqliteDeleteTrigger(pTrigger);
|
||||
}
|
||||
|
||||
drop_trigger_cleanup:
|
||||
sqliteSrcListDelete(pName);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -531,6 +561,36 @@ int sqliteTriggersExist(
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert the pStep->target token into a SrcList and return a pointer
|
||||
** to that SrcList.
|
||||
**
|
||||
** This routine adds a specific database name, if needed, to the target when
|
||||
** forming the SrcList. This prevents a trigger in one database from
|
||||
** referring to a target in another database. An exception is when the
|
||||
** trigger is in TEMP in which case it can refer to any other database it
|
||||
** wants.
|
||||
*/
|
||||
static SrcList *targetSrcList(
|
||||
Parse *pParse, /* The parsing context */
|
||||
TriggerStep *pStep /* The trigger containing the target token */
|
||||
){
|
||||
Token sDb; /* Dummy database name token */
|
||||
int iDb; /* Index of the database to use */
|
||||
SrcList *pSrc; /* SrcList to be returned */
|
||||
|
||||
iDb = pStep->pTrig->iDb;
|
||||
if( iDb==0 || iDb>=2 ){
|
||||
assert( iDb<pParse->db->nDb );
|
||||
sDb.z = pParse->db->aDb[iDb].zName;
|
||||
sDb.n = strlen(sDb.z);
|
||||
pSrc = sqliteSrcListAppend(0, &sDb, &pStep->target);
|
||||
} else {
|
||||
pSrc = sqliteSrcListAppend(0, &pStep->target, 0);
|
||||
}
|
||||
return pSrc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate VDBE code for zero or more statements inside the body of a
|
||||
** trigger.
|
||||
|
@ -545,11 +605,9 @@ static int codeTriggerProgram(
|
|||
|
||||
while( pTriggerStep ){
|
||||
int saveNTab = pParse->nTab;
|
||||
int saveUseDb = pParse->useDb;
|
||||
|
||||
orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
|
||||
pParse->trigStack->orconf = orconf;
|
||||
pParse->useDb = pTriggerStep->pTrig->iDb;
|
||||
if( pParse->useDb==1 ) pParse->useDb = -1;
|
||||
switch( pTriggerStep->op ){
|
||||
case TK_SELECT: {
|
||||
Select * ss = sqliteSelectDup(pTriggerStep->pSelect);
|
||||
|
@ -561,7 +619,7 @@ static int codeTriggerProgram(
|
|||
}
|
||||
case TK_UPDATE: {
|
||||
SrcList *pSrc;
|
||||
pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
|
||||
pSrc = targetSrcList(pParse, pTriggerStep);
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
|
||||
sqliteUpdate(pParse, pSrc,
|
||||
sqliteExprListDup(pTriggerStep->pExprList),
|
||||
|
@ -571,7 +629,7 @@ static int codeTriggerProgram(
|
|||
}
|
||||
case TK_INSERT: {
|
||||
SrcList *pSrc;
|
||||
pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
|
||||
pSrc = targetSrcList(pParse, pTriggerStep);
|
||||
sqliteInsert(pParse, pSrc,
|
||||
sqliteExprListDup(pTriggerStep->pExprList),
|
||||
sqliteSelectDup(pTriggerStep->pSelect),
|
||||
|
@ -581,7 +639,7 @@ static int codeTriggerProgram(
|
|||
case TK_DELETE: {
|
||||
SrcList *pSrc;
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
|
||||
pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
|
||||
pSrc = targetSrcList(pParse, pTriggerStep);
|
||||
sqliteDeleteFrom(pParse, pSrc, sqliteExprDup(pTriggerStep->pWhere));
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
|
||||
break;
|
||||
|
@ -590,7 +648,6 @@ static int codeTriggerProgram(
|
|||
assert(0);
|
||||
}
|
||||
pParse->nTab = saveNTab;
|
||||
pParse->useDb = saveUseDb;
|
||||
pTriggerStep = pTriggerStep->pNext;
|
||||
}
|
||||
|
||||
|
|
|
@ -128,8 +128,13 @@ void sqliteUpdate(
|
|||
}
|
||||
}
|
||||
if( j>=pTab->nCol ){
|
||||
sqliteErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName);
|
||||
goto update_cleanup;
|
||||
if( sqliteIsRowid(pChanges->a[i].zName) ){
|
||||
chngRecno = 1;
|
||||
pRecnoExpr = pChanges->a[i].pExpr;
|
||||
}else{
|
||||
sqliteErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName);
|
||||
goto update_cleanup;
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
|
|
|
@ -317,7 +317,8 @@ char *sqliteStrNDup(const char *z, int n){
|
|||
** Create a string from the 2nd and subsequent arguments (up to the
|
||||
** first NULL argument), store the string in memory obtained from
|
||||
** sqliteMalloc() and make the pointer indicated by the 1st argument
|
||||
** point to that string.
|
||||
** point to that string. The 1st argument must either be NULL or
|
||||
** point to memory obtained from sqliteMalloc().
|
||||
*/
|
||||
void sqliteSetString(char **pz, const char *zFirst, ...){
|
||||
va_list ap;
|
||||
|
@ -355,7 +356,9 @@ void sqliteSetString(char **pz, const char *zFirst, ...){
|
|||
/*
|
||||
** Works like sqliteSetString, but each string is now followed by
|
||||
** a length integer which specifies how much of the source string
|
||||
** to copy (in bytes). -1 means use the whole string.
|
||||
** to copy (in bytes). -1 means use the whole string. The 1st
|
||||
** argument must either be NULL or point to memory obtained from
|
||||
** sqliteMalloc().
|
||||
*/
|
||||
void sqliteSetNString(char **pz, ...){
|
||||
va_list ap;
|
||||
|
@ -610,188 +613,6 @@ int sqliteStrNICmp(const char *zLeft, const char *zRight, int N){
|
|||
return N<0 ? 0 : *a - *b;
|
||||
}
|
||||
|
||||
#if 0 /* NOT USED */
|
||||
/*
|
||||
** The sortStrCmp() function below is used to order elements according
|
||||
** to the ORDER BY clause of a SELECT. The sort order is a little different
|
||||
** from what one might expect. This note attempts to describe what is
|
||||
** going on.
|
||||
**
|
||||
** We want the main string comparision function used for sorting to
|
||||
** sort both numbers and alphanumeric words into the correct sequence.
|
||||
** The same routine should do both without prior knowledge of which
|
||||
** type of text the input represents. It should even work for strings
|
||||
** which are a mixture of text and numbers. (It does not work for
|
||||
** numeric substrings in exponential notation, however.)
|
||||
**
|
||||
** To accomplish this, we keep track of a state number while scanning
|
||||
** the two strings. The states are as follows:
|
||||
**
|
||||
** 1 Beginning of word
|
||||
** 2 Arbitrary text
|
||||
** 3 Integer
|
||||
** 4 Negative integer
|
||||
** 5 Real number
|
||||
** 6 Negative real
|
||||
**
|
||||
** The scan begins in state 1, beginning of word. Transitions to other
|
||||
** states are determined by characters seen, as shown in the following
|
||||
** chart:
|
||||
**
|
||||
** Current State Character Seen New State
|
||||
** -------------------- -------------- -------------------
|
||||
** 0 Beginning of word "-" 3 Negative integer
|
||||
** digit 2 Integer
|
||||
** space 0 Beginning of word
|
||||
** otherwise 1 Arbitrary text
|
||||
**
|
||||
** 1 Arbitrary text space 0 Beginning of word
|
||||
** digit 2 Integer
|
||||
** otherwise 1 Arbitrary text
|
||||
**
|
||||
** 2 Integer space 0 Beginning of word
|
||||
** "." 4 Real number
|
||||
** digit 2 Integer
|
||||
** otherwise 1 Arbitrary text
|
||||
**
|
||||
** 3 Negative integer space 0 Beginning of word
|
||||
** "." 5 Negative Real num
|
||||
** digit 3 Negative integer
|
||||
** otherwise 1 Arbitrary text
|
||||
**
|
||||
** 4 Real number space 0 Beginning of word
|
||||
** digit 4 Real number
|
||||
** otherwise 1 Arbitrary text
|
||||
**
|
||||
** 5 Negative real num space 0 Beginning of word
|
||||
** digit 5 Negative real num
|
||||
** otherwise 1 Arbitrary text
|
||||
**
|
||||
** To implement this state machine, we first classify each character
|
||||
** into on of the following categories:
|
||||
**
|
||||
** 0 Text
|
||||
** 1 Space
|
||||
** 2 Digit
|
||||
** 3 "-"
|
||||
** 4 "."
|
||||
**
|
||||
** Given an arbitrary character, the array charClass[] maps that character
|
||||
** into one of the atove categories.
|
||||
*/
|
||||
static const unsigned char charClass[] = {
|
||||
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
|
||||
/* 0x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,
|
||||
/* 1x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 2x */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0,
|
||||
/* 3x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0,
|
||||
/* 4x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 5x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 6x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 7x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 8x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 9x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* Ax */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* Bx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* Cx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* Dx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* Ex */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* Fx */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
#define N_CHAR_CLASS 5
|
||||
|
||||
/*
|
||||
** Given the current state number (0 thru 5), this array figures
|
||||
** the new state number given the character class.
|
||||
*/
|
||||
static const unsigned char stateMachine[] = {
|
||||
/* Text, Space, Digit, "-", "." */
|
||||
1, 0, 2, 3, 1, /* State 0: Beginning of word */
|
||||
1, 0, 2, 1, 1, /* State 1: Arbitrary text */
|
||||
1, 0, 2, 1, 4, /* State 2: Integer */
|
||||
1, 0, 3, 1, 5, /* State 3: Negative integer */
|
||||
1, 0, 4, 1, 1, /* State 4: Real number */
|
||||
1, 0, 5, 1, 1, /* State 5: Negative real num */
|
||||
};
|
||||
|
||||
/* This routine does a comparison of two strings. Case is used only
|
||||
** if useCase!=0. Numeric substrings compare in numerical order for the
|
||||
** most part but this routine does not understand exponential notation.
|
||||
*/
|
||||
static int sortStrCmp(const char *atext, const char *btext, int useCase){
|
||||
register unsigned char *a, *b, *map, ca, cb;
|
||||
int result;
|
||||
register int cclass = 0;
|
||||
|
||||
a = (unsigned char *)atext;
|
||||
b = (unsigned char *)btext;
|
||||
if( useCase ){
|
||||
do{
|
||||
if( (ca= *a++)!=(cb= *b++) ) break;
|
||||
cclass = stateMachine[cclass*N_CHAR_CLASS + charClass[ca]];
|
||||
}while( ca!=0 );
|
||||
}else{
|
||||
map = UpperToLower;
|
||||
do{
|
||||
if( (ca=map[*a++])!=(cb=map[*b++]) ) break;
|
||||
cclass = stateMachine[cclass*N_CHAR_CLASS + charClass[ca]];
|
||||
}while( ca!=0 );
|
||||
if( ca>='[' && ca<='`' ) cb = b[-1];
|
||||
if( cb>='[' && cb<='`' ) ca = a[-1];
|
||||
}
|
||||
switch( cclass ){
|
||||
case 0:
|
||||
case 1: {
|
||||
if( isdigit(ca) && isdigit(cb) ){
|
||||
cclass = 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch( cclass ){
|
||||
case 2:
|
||||
case 3: {
|
||||
if( isdigit(ca) ){
|
||||
if( isdigit(cb) ){
|
||||
int acnt, bcnt;
|
||||
acnt = bcnt = 0;
|
||||
while( isdigit(*a++) ) acnt++;
|
||||
while( isdigit(*b++) ) bcnt++;
|
||||
result = acnt - bcnt;
|
||||
if( result==0 ) result = ca-cb;
|
||||
}else{
|
||||
result = 1;
|
||||
}
|
||||
}else if( isdigit(cb) ){
|
||||
result = -1;
|
||||
}else if( ca=='.' ){
|
||||
result = 1;
|
||||
}else if( cb=='.' ){
|
||||
result = -1;
|
||||
}else{
|
||||
result = ca - cb;
|
||||
cclass = 2;
|
||||
}
|
||||
if( cclass==3 ) result = -result;
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
case 1:
|
||||
case 4: {
|
||||
result = ca - cb;
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
result = cb - ca;
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif /* NOT USED */
|
||||
|
||||
/*
|
||||
** Return TRUE if z is a pure numeric string. Return FALSE if the
|
||||
** string contains any character which is not part of a number.
|
||||
|
|
|
@ -2287,7 +2287,7 @@ case OP_MustBeInt: {
|
|||
/* Do nothing */
|
||||
}else if( aStack[tos].flags & STK_Real ){
|
||||
int i = aStack[tos].r;
|
||||
double r = i;
|
||||
double r = (double)i;
|
||||
if( r!=aStack[tos].r ){
|
||||
goto mismatch;
|
||||
}
|
||||
|
@ -3168,8 +3168,9 @@ case OP_IncrKey: {
|
|||
*/
|
||||
case OP_Checkpoint: {
|
||||
int i = pOp->p1;
|
||||
if( i>=0 && i<db->nDb && db->aDb[i].pBt ){
|
||||
if( i>=0 && i<db->nDb && db->aDb[i].pBt && db->aDb[i].inTrans==1 ){
|
||||
rc = sqliteBtreeBeginCkpt(db->aDb[i].pBt);
|
||||
if( rc==SQLITE_OK ) db->aDb[i].inTrans = 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -3382,6 +3383,11 @@ case OP_VerifyCookie: {
|
|||
** Open a read/write cursor named P1 on the table or index whose root
|
||||
** page is P2. If P2==0 then take the root page number from the stack.
|
||||
**
|
||||
** The P3 value is the name of the table or index being opened.
|
||||
** The P3 value is not actually used by this opcode and may be
|
||||
** omitted. But the code generator usually inserts the index or
|
||||
** table name into P3 to make the code easier to read.
|
||||
**
|
||||
** This instruction works just like OpenRead except that it opens the cursor
|
||||
** in read/write mode. For a given table, there can be one or more read-only
|
||||
** cursors or a single read/write cursor but not both.
|
||||
|
@ -3890,7 +3896,7 @@ case OP_NewRecno: {
|
|||
|
||||
/* Opcode: PutIntKey P1 P2 *
|
||||
**
|
||||
** Write an entry into the database file P1. A new entry is
|
||||
** Write an entry into the table of cursor P1. A new entry is
|
||||
** created if it doesn't already exist or the data for an existing
|
||||
** entry is overwritten. The data is the value on the top of the
|
||||
** stack. The key is the next value down on the stack. The key must
|
||||
|
@ -3902,7 +3908,7 @@ case OP_NewRecno: {
|
|||
*/
|
||||
/* Opcode: PutStrKey P1 * *
|
||||
**
|
||||
** Write an entry into the database file P1. A new entry is
|
||||
** Write an entry into the table of cursor P1. A new entry is
|
||||
** created if it doesn't already exist or the data for an existing
|
||||
** entry is overwritten. The data is the value on the top of the
|
||||
** stack. The key is the next value down on the stack. The key must
|
||||
|
@ -4384,13 +4390,13 @@ case OP_Next: {
|
|||
|
||||
/* Opcode: IdxPut P1 P2 P3
|
||||
**
|
||||
** The top of the stack hold an SQL index key made using the
|
||||
** The top of the stack holds a SQL index key made using the
|
||||
** MakeIdxKey instruction. This opcode writes that key into the
|
||||
** index P1. Data for the entry is nil.
|
||||
**
|
||||
** If P2==1, then the key must be unique. If the key is not unique,
|
||||
** the program aborts with a SQLITE_CONSTRAINT error and the database
|
||||
** is rolled back. If P3 is not null, then it because part of the
|
||||
** is rolled back. If P3 is not null, then it becomes part of the
|
||||
** error message returned with the SQLITE_CONSTRAINT.
|
||||
*/
|
||||
case OP_IdxPut: {
|
||||
|
@ -4690,7 +4696,8 @@ case OP_ListWrite: {
|
|||
|
||||
/* Opcode: ListRewind * * *
|
||||
**
|
||||
** Rewind the temporary buffer back to the beginning.
|
||||
** Rewind the temporary buffer back to the beginning. This is
|
||||
** now a no-op.
|
||||
*/
|
||||
case OP_ListRewind: {
|
||||
/* This is now a no-op */
|
||||
|
@ -5820,8 +5827,9 @@ int sqliteVdbeFinalize(Vdbe *p, char **pzErrMsg){
|
|||
sqliteRollbackInternalChanges(db);
|
||||
}
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt ){
|
||||
if( db->aDb[i].pBt && db->aDb[i].inTrans==2 ){
|
||||
sqliteBtreeCommitCkpt(db->aDb[i].pBt);
|
||||
db->aDb[i].inTrans = 1;
|
||||
}
|
||||
}
|
||||
assert( p->tos<p->pc || sqlite_malloc_failed==1 );
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue