diff options
| author | Wez Furlong <wez@php.net> | 2003-06-04 22:40:00 +0000 | 
|---|---|---|
| committer | Wez Furlong <wez@php.net> | 2003-06-04 22:40:00 +0000 | 
| commit | 80e7f7001d39add9010ba78be636245410b79c24 (patch) | |
| tree | 23ae7f9f01ea9bb1add35b1ff85efa2421d05142 /ext/sqlite/libsqlite/src/update.c | |
| parent | 82a1818fdec3afe8e3a5cc8aa7171f4472ea1e4a (diff) | |
| download | php-git-80e7f7001d39add9010ba78be636245410b79c24.tar.gz | |
Update bundled library to version 2.8.2.
Make OnUpdateInt compatible with ZE2.
Fix the makefile fragment for non-gnu makes
Diffstat (limited to 'ext/sqlite/libsqlite/src/update.c')
| -rw-r--r-- | ext/sqlite/libsqlite/src/update.c | 358 | 
1 files changed, 195 insertions, 163 deletions
diff --git a/ext/sqlite/libsqlite/src/update.c b/ext/sqlite/libsqlite/src/update.c index b5825dd12e..95a49590dc 100644 --- a/ext/sqlite/libsqlite/src/update.c +++ b/ext/sqlite/libsqlite/src/update.c @@ -18,81 +18,76 @@  /*  ** Process an UPDATE statement. +** +**   UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL; +**          \_______/ \________/     \______/       \________________/ +*            onError   pTabList      pChanges             pWhere  */  void sqliteUpdate(    Parse *pParse,         /* The parser context */ -  Token *pTableName,     /* The table in which we should change things */ +  SrcList *pTabList,     /* The table in which we should change things */    ExprList *pChanges,    /* Things to be changed */    Expr *pWhere,          /* The WHERE clause.  May be null */    int onError            /* How to handle constraint errors */  ){    int i, j;              /* Loop counters */    Table *pTab;           /* The table to be updated */ -  SrcList *pTabList = 0; /* Fake FROM clause containing only pTab */    int addr;              /* VDBE instruction address of the start of the loop */    WhereInfo *pWInfo;     /* Information about the WHERE clause */    Vdbe *v;               /* The virtual database engine */    Index *pIdx;           /* For looping over indices */    int nIdx;              /* Number of indices that need updating */    int nIdxTotal;         /* Total number of indices */ -  int base;              /* Index of first available table cursor */ +  int iCur;              /* VDBE Cursor number of pTab */    sqlite *db;            /* The database structure */    Index **apIdx = 0;     /* An array of indices that need updating too */ -  char *aIdxUsed = 0;    /* aIdxUsed[i] if the i-th index is used */ +  char *aIdxUsed = 0;    /* aIdxUsed[i]==1 if the i-th index is used */    int *aXRef = 0;        /* aXRef[i] is the index in pChanges->a[] of the                           ** an expression for the i-th column of the table.                           ** aXRef[i]==-1 if the i-th column is not changed. */ -  int openOp;            /* Opcode used to open tables */    int chngRecno;         /* True if the record number is being changed */    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 */ +  AuthContext sContext;  /* The authorization context */ -  int row_triggers_exist = 0; +  int before_triggers;         /* True if there are any BEFORE triggers */ +  int after_triggers;          /* True if there are any AFTER triggers */ +  int row_triggers_exist = 0;  /* True if any row triggers exist */    int newIdx      = -1;  /* index of trigger "new" temp table       */    int oldIdx      = -1;  /* index of trigger "old" temp table       */ +  sContext.pParse = 0;    if( pParse->nErr || sqlite_malloc_failed ) goto update_cleanup;    db = pParse->db; +  assert( pTabList->nSrc==1 ); -  /* Check for the special case of a VIEW with one or more ON UPDATE triggers  -   * defined  -   */ -  { -    char *zTab = sqliteTableNameFromToken(pTableName); - -    if( zTab != 0 ){ -      pTab = sqliteFindTable(pParse->db, zTab); -      if( pTab ){ -        row_triggers_exist =  -          sqliteTriggersExist(pParse, pTab->pTrigger,  -              TK_UPDATE, TK_BEFORE, TK_ROW, pChanges) || -          sqliteTriggersExist(pParse, pTab->pTrigger,  -              TK_UPDATE, TK_AFTER, TK_ROW, pChanges); -      } -      sqliteFree(zTab); -      if( row_triggers_exist &&  pTab->pSelect ){ -        /* Just fire VIEW triggers */ -        sqliteViewTriggers(pParse, pTab, pWhere, onError, pChanges); -        return; -      } +  /* Locate the table which we want to update.  +  */ +  pTab = sqliteSrcListLookup(pParse, pTabList); +  if( pTab==0 ) goto update_cleanup; +  before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,  +            TK_UPDATE, TK_BEFORE, TK_ROW, pChanges); +  after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,  +            TK_UPDATE, TK_AFTER, TK_ROW, pChanges); +  row_triggers_exist = before_triggers || after_triggers; +  isView = pTab->pSelect!=0; +  if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){ +    goto update_cleanup; +  } +  if( isView ){ +    if( sqliteViewGetColumnNames(pParse, pTab) ){ +      goto update_cleanup;      }    } - -  /* Locate the table which we want to update.  This table has to be -  ** put in an SrcList structure because some of the subroutines we -  ** will be calling are designed to work with multiple tables and expect -  ** an SrcList* parameter instead of just a Table* parameter. -  */ -  pTabList = sqliteTableTokenToSrcList(pParse, pTableName); -  if( pTabList==0 ) goto update_cleanup; -  pTab = pTabList->a[0].pTab; -  assert( pTab->pSelect==0 );  /* This table is not a VIEW */    aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );    if( aXRef==0 ) goto update_cleanup;    for(i=0; i<pTab->nCol; i++) aXRef[i] = -1; -  /* If there are FOR EACH ROW triggers, allocate temp tables */ +  /* If there are FOR EACH ROW triggers, allocate cursors for the +  ** special OLD and NEW tables +  */    if( row_triggers_exist ){      newIdx = pParse->nTab++;      oldIdx = pParse->nTab++; @@ -103,28 +98,20 @@ void sqliteUpdate(    ** need to occur right after the database cursor.  So go ahead and    ** allocate enough space, just in case.    */ -  base = pParse->nTab++; +  pTabList->a[0].iCursor = iCur = pParse->nTab++;    for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){      pParse->nTab++;    } -  /* Resolve the column names in all the expressions in both the -  ** WHERE clause and in the new values.  Also find the column index +  /* Resolve the column names in all the expressions of the +  ** of the UPDATE statement.  Also find the column index    ** for each column to be updated in the pChanges array.  For each    ** column to be updated, make sure we have authorization to change    ** that column.    */ -  if( pWhere ){ -    if( sqliteExprResolveIds(pParse, base, pTabList, 0, pWhere) ){ -      goto update_cleanup; -    } -    if( sqliteExprCheck(pParse, pWhere, 0, 0) ){ -      goto update_cleanup; -    } -  }    chngRecno = 0;    for(i=0; i<pChanges->nExpr; i++){ -    if( sqliteExprResolveIds(pParse, base, pTabList, 0, pChanges->a[i].pExpr) ){ +    if( sqliteExprResolveIds(pParse, pTabList, 0, pChanges->a[i].pExpr) ){        goto update_cleanup;      }      if( sqliteExprCheck(pParse, pChanges->a[i].pExpr, 0, 0) ){ @@ -141,16 +128,14 @@ void sqliteUpdate(        }      }      if( j>=pTab->nCol ){ -      sqliteSetString(&pParse->zErrMsg, "no such column: ",  -         pChanges->a[i].zName, 0); -      pParse->nErr++; +      sqliteErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName);        goto update_cleanup;      }  #ifndef SQLITE_OMIT_AUTHORIZATION      {        int rc;        rc = sqliteAuthCheck(pParse, SQLITE_UPDATE, pTab->zName, -                           pTab->aCol[j].zName); +                           pTab->aCol[j].zName, db->aDb[pTab->iDb].zName);        if( rc==SQLITE_DENY ){          goto update_cleanup;        }else if( rc==SQLITE_IGNORE ){ @@ -196,15 +181,43 @@ void sqliteUpdate(      }    } +  /* Resolve the column names in all the expressions in the +  ** WHERE clause. +  */ +  if( pWhere ){ +    if( sqliteExprResolveIds(pParse, pTabList, 0, pWhere) ){ +      goto update_cleanup; +    } +    if( sqliteExprCheck(pParse, pWhere, 0, 0) ){ +      goto update_cleanup; +    } +  } + +  /* Start the view context +  */ +  if( isView ){ +    sqliteAuthContextPush(pParse, &sContext, pTab->zName); +  } +    /* Begin generating code.    */    v = sqliteGetVdbe(pParse);    if( v==0 ) goto update_cleanup; -  sqliteBeginWriteOperation(pParse, 1, !row_triggers_exist && pTab->isTemp); +  sqliteBeginWriteOperation(pParse, 1, pTab->iDb); + +  /* If we are trying to update a view, construct that view into +  ** a temporary table. +  */ +  if( isView ){ +    Select *pView; +    pView = sqliteSelectDup(pTab->pSelect); +    sqliteSelect(pParse, pView, SRT_TempTable, iCur, 0, 0, 0); +    sqliteSelectDelete(pView); +  }    /* Begin the database scan    */ -  pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1, 0); +  pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1, 0);    if( pWInfo==0 ) goto update_cleanup;    /* Remember the index of every item to be updated. @@ -222,136 +235,150 @@ void sqliteUpdate(    }    if( row_triggers_exist ){ -    int ii; - -    sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0); -    sqliteVdbeAddOp(v, OP_OpenTemp, newIdx, 0); +    /* Create pseudo-tables for NEW and OLD +    */ +    sqliteVdbeAddOp(v, OP_OpenPseudo, oldIdx, 0); +    sqliteVdbeAddOp(v, OP_OpenPseudo, newIdx, 0); +    /* 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_Dup, 0, 0); +    /* Open a cursor and make it point to the record that is +    ** being updated. +    */      sqliteVdbeAddOp(v, OP_Dup, 0, 0); -    sqliteVdbeAddOp(v, (pTab->isTemp?OP_OpenAux:OP_Open), base, pTab->tnum); -    sqliteVdbeAddOp(v, OP_MoveTo, base, 0); - -    sqliteVdbeAddOp(v, OP_Integer, 13, 0); -    for(ii = 0; ii < pTab->nCol; ii++){ -      if( ii == pTab->iPKey ){ -	sqliteVdbeAddOp(v, OP_Recno, base, 0); -      }else{ -	sqliteVdbeAddOp(v, OP_Column, base, ii); -      } +    if( !isView ){ +      sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); +      sqliteVdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);      } -    sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); +    sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0); + +    /* Generate the OLD table +    */ +    sqliteVdbeAddOp(v, OP_Recno, iCur, 0); +    sqliteVdbeAddOp(v, OP_RowData, iCur, 0);      sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0); -    sqliteVdbeAddOp(v, OP_Integer, 13, 0); -    for(ii = 0; ii < pTab->nCol; ii++){ -      if( aXRef[ii] < 0 ){ -        if( ii == pTab->iPKey ){ -          sqliteVdbeAddOp(v, OP_Recno, base, 0); -	}else{ -          sqliteVdbeAddOp(v, OP_Column, base, ii); -	} +    /* Generate the NEW table +    */ +    if( chngRecno ){ +      sqliteExprCode(pParse, pRecnoExpr); +    }else{ +      sqliteVdbeAddOp(v, OP_Recno, iCur, 0); +    } +    for(i=0; i<pTab->nCol; i++){ +      if( i==pTab->iPKey ){ +        sqliteVdbeAddOp(v, OP_String, 0, 0); +        continue; +      } +      j = aXRef[i]; +      if( j<0 ){ +        sqliteVdbeAddOp(v, OP_Column, iCur, i);        }else{ -        sqliteExprCode(pParse, pChanges->a[aXRef[ii]].pExpr); +        sqliteExprCode(pParse, pChanges->a[j].pExpr);        }      }      sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);      sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0); -    sqliteVdbeAddOp(v, OP_Close, base, 0); - -    sqliteVdbeAddOp(v, OP_Rewind, oldIdx, 0); -    sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0); +    if( !isView ){ +      sqliteVdbeAddOp(v, OP_Close, iCur, 0); +    } +    /* Fire the BEFORE and INSTEAD OF triggers +    */      if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab,             newIdx, oldIdx, onError, addr) ){        goto update_cleanup;      }    } -  /* Rewind the list of records that need to be updated and -  ** open every index that needs updating.  Note that if any -  ** index could potentially invoke a REPLACE conflict resolution  -  ** action, then we need to open all indices because we might need -  ** to be deleting some records. -  */ -  openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; -  sqliteVdbeAddOp(v, openOp, base, pTab->tnum); -  if( onError==OE_Replace ){ -    openAll = 1; -  }else{ -    openAll = 0; -    for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ -      if( pIdx->onError==OE_Replace ){ -        openAll = 1; -        break; +  if( !isView ){ +    /*  +    ** Open every index that needs updating.  Note that if any +    ** index could potentially invoke a REPLACE conflict resolution  +    ** action, then we need to open all indices because we might need +    ** to be deleting some records. +    */ +    sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); +    sqliteVdbeAddOp(v, OP_OpenWrite, iCur, pTab->tnum); +    if( onError==OE_Replace ){ +      openAll = 1; +    }else{ +      openAll = 0; +      for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ +        if( pIdx->onError==OE_Replace ){ +          openAll = 1; +          break; +        }        }      } -  } -  for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ -    if( openAll || aIdxUsed[i] ){ -      sqliteVdbeAddOp(v, openOp, base+i+1, pIdx->tnum); -      assert( pParse->nTab>base+i+1 ); +    for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ +      if( openAll || aIdxUsed[i] ){ +        sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0); +        sqliteVdbeAddOp(v, OP_OpenWrite, iCur+i+1, pIdx->tnum); +        assert( pParse->nTab>iCur+i+1 ); +      }      } -  } -  /* Loop over every record that needs updating.  We have to load -  ** the old data for each record to be updated because some columns -  ** might not change and we will need to copy the old value. -  ** Also, the old data is needed to delete the old index entires. -  ** So make the cursor point at the old record. -  */ -  if( !row_triggers_exist ){ -    sqliteVdbeAddOp(v, OP_ListRewind, 0, 0); -    addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0); -    sqliteVdbeAddOp(v, OP_Dup, 0, 0); -  } -  sqliteVdbeAddOp(v, OP_NotExists, base, addr); - -  /* If the record number will change, push the record number as it -  ** will be after the update. (The old record number is currently -  ** on top of the stack.) -  */ -  if( chngRecno ){ -    sqliteExprCode(pParse, pRecnoExpr); -    sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0); -  } +    /* Loop over every record that needs updating.  We have to load +    ** the old data for each record to be updated because some columns +    ** might not change and we will need to copy the old value. +    ** Also, the old data is needed to delete the old index entires. +    ** So make the cursor point at the old record. +    */ +    if( !row_triggers_exist ){ +      sqliteVdbeAddOp(v, OP_ListRewind, 0, 0); +      addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0); +      sqliteVdbeAddOp(v, OP_Dup, 0, 0); +    } +    sqliteVdbeAddOp(v, OP_NotExists, iCur, addr); -  /* Compute new data for this record.   -  */ -  for(i=0; i<pTab->nCol; i++){ -    if( i==pTab->iPKey ){ -      sqliteVdbeAddOp(v, OP_String, 0, 0); -      continue; +    /* If the record number will change, push the record number as it +    ** will be after the update. (The old record number is currently +    ** on top of the stack.) +    */ +    if( chngRecno ){ +      sqliteExprCode(pParse, pRecnoExpr); +      sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);      } -    j = aXRef[i]; -    if( j<0 ){ -      sqliteVdbeAddOp(v, OP_Column, base, i); -    }else{ -      sqliteExprCode(pParse, pChanges->a[j].pExpr); + +    /* Compute new data for this record.   +    */ +    for(i=0; i<pTab->nCol; i++){ +      if( i==pTab->iPKey ){ +        sqliteVdbeAddOp(v, OP_String, 0, 0); +        continue; +      } +      j = aXRef[i]; +      if( j<0 ){ +        sqliteVdbeAddOp(v, OP_Column, iCur, i); +      }else{ +        sqliteExprCode(pParse, pChanges->a[j].pExpr); +      }      } -  } -  /* Do constraint checks -  */ -  sqliteGenerateConstraintChecks(pParse, pTab, base, aIdxUsed, chngRecno, 1, -                                 onError, addr); +    /* Do constraint checks +    */ +    sqliteGenerateConstraintChecks(pParse, pTab, iCur, aIdxUsed, chngRecno, 1, +                                   onError, addr); -  /* Delete the old indices for the current record. -  */ -  sqliteGenerateRowIndexDelete(db, v, pTab, base, aIdxUsed); +    /* Delete the old indices for the current record. +    */ +    sqliteGenerateRowIndexDelete(db, v, pTab, iCur, aIdxUsed); -  /* If changing the record number, delete the old record. -  */ -  if( chngRecno ){ -    sqliteVdbeAddOp(v, OP_Delete, base, 0); -  } +    /* If changing the record number, delete the old record. +    */ +    if( chngRecno ){ +      sqliteVdbeAddOp(v, OP_Delete, iCur, 0); +    } -  /* Create the new index entries and the new record. -  */ -  sqliteCompleteInsertion(pParse, pTab, base, aIdxUsed, chngRecno, 1); +    /* Create the new index entries and the new record. +    */ +    sqliteCompleteInsertion(pParse, pTab, iCur, aIdxUsed, chngRecno, 1, -1); +  }    /* Increment the row counter     */ @@ -359,14 +386,18 @@ void sqliteUpdate(      sqliteVdbeAddOp(v, OP_AddImm, 1, 0);    } +  /* If there are triggers, close all the cursors after each iteration +  ** through the loop.  The fire the after triggers. +  */    if( row_triggers_exist ){ -    for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ -      if( openAll || aIdxUsed[i] ) -        sqliteVdbeAddOp(v, OP_Close, base+i+1, 0); +    if( !isView ){ +      for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ +        if( openAll || aIdxUsed[i] ) +          sqliteVdbeAddOp(v, OP_Close, iCur+i+1, 0); +      } +      sqliteVdbeAddOp(v, OP_Close, iCur, 0); +      pParse->nTab = iCur;      } -    sqliteVdbeAddOp(v, OP_Close, base, 0); -    pParse->nTab = base; -      if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab,             newIdx, oldIdx, onError, addr) ){        goto update_cleanup; @@ -384,11 +415,11 @@ void sqliteUpdate(    if( !row_triggers_exist ){      for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){        if( openAll || aIdxUsed[i] ){ -        sqliteVdbeAddOp(v, OP_Close, base+i+1, 0); +        sqliteVdbeAddOp(v, OP_Close, iCur+i+1, 0);        }      } -    sqliteVdbeAddOp(v, OP_Close, base, 0); -    pParse->nTab = base; +    sqliteVdbeAddOp(v, OP_Close, iCur, 0); +    pParse->nTab = iCur;    }else{      sqliteVdbeAddOp(v, OP_Close, newIdx, 0);      sqliteVdbeAddOp(v, OP_Close, oldIdx, 0); @@ -406,6 +437,7 @@ void sqliteUpdate(    }  update_cleanup: +  sqliteAuthContextPop(&sContext);    sqliteFree(apIdx);    sqliteFree(aXRef);    sqliteSrcListDelete(pTabList);  | 
