Exemplo n.º 1
0
/*
** This routine is call to handle SQL of the following forms:
**
**    insert into TABLE (IDLIST) values(EXPRLIST)
**    insert into TABLE (IDLIST) select
**
** The IDLIST following the table name is always optional.  If omitted,
** then a list of all columns for the table is substituted.  The IDLIST
** appears in the pColumn parameter.  pColumn is NULL if IDLIST is omitted.
**
** The pList parameter holds EXPRLIST in the first form of the INSERT
** statement above, and pSelect is NULL.  For the second form, pList is
** NULL and pSelect is a pointer to the select statement used to generate
** data for the insert.
**
** The code generated follows one of three templates.  For a simple
** select with data coming from a VALUES clause, the code executes
** once straight down through.  The template looks like this:
**
**         open write cursor to <table> and its indices
**         puts VALUES clause expressions onto the stack
**         write the resulting record into <table>
**         cleanup
**
** If the statement is of the form
**
**   INSERT INTO <table> SELECT ...
**
** And the SELECT clause does not read from <table> at any time, then
** the generated code follows this template:
**
**         goto B
**      A: setup for the SELECT
**         loop over the tables in the SELECT
**           gosub C
**         end loop
**         cleanup after the SELECT
**         goto D
**      B: open write cursor to <table> and its indices
**         goto A
**      C: insert the select result into <table>
**         return
**      D: cleanup
**
** The third template is used if the insert statement takes its
** values from a SELECT but the data is being inserted into a table
** that is also read as part of the SELECT.  In the third form,
** we have to use a intermediate table to store the results of
** the select.  The template is like this:
**
**         goto B
**      A: setup for the SELECT
**         loop over the tables in the SELECT
**           gosub C
**         end loop
**         cleanup after the SELECT
**         goto D
**      C: insert the select result into the intermediate table
**         return
**      B: open a cursor to an intermediate table
**         goto A
**      D: open write cursor to <table> and its indices
**         loop over the intermediate table
**           transfer values form intermediate table into <table>
**         end the loop
**         cleanup
*/
void sqlite3Insert(
  Parse *pParse,        /* Parser context */
  SrcList *pTabList,    /* Name of table into which we are inserting */
  ExprList *pList,      /* List of values to be inserted */
  Select *pSelect,      /* A SELECT statement to use as the data source */
  IdList *pColumn,      /* Column names corresponding to IDLIST. */
  int onError           /* How to handle constraint errors */
){
  Table *pTab;          /* The table to insert into */
  char *zTab;           /* Name of the table into which we are inserting */
  const char *zDb;      /* Name of the database holding this table */
  int i, j, idx;        /* Loop counters */
  Vdbe *v;              /* Generate code into this virtual machine */
  Index *pIdx;          /* For looping over indices of the table */
  int nColumn;          /* Number of columns in the data */
  int base = 0;         /* VDBE Cursor number for pTab */
  int iCont=0,iBreak=0; /* Beginning and end of the loop over srcTab */
  sqlite3 *db;          /* The main database structure */
  int keyColumn = -1;   /* Column that is the INTEGER PRIMARY KEY */
  int endOfLoop;        /* Label for the end of the insertion loop */
  int useTempTable;     /* Store SELECT results in intermediate table */
  int srcTab = 0;       /* Data comes from this temporary cursor if >=0 */
  int iSelectLoop = 0;  /* Address of code that implements the SELECT */
  int iCleanup = 0;     /* Address of the cleanup code */
  int iInsertBlock = 0; /* Address of the subroutine used to insert data */
  int iCntMem = 0;      /* Memory cell used for the row counter */
  int isView;           /* True if attempting to insert into a view */

  int row_triggers_exist = 0; /* True if there are FOR EACH ROW triggers */
  int before_triggers;        /* True if there are BEFORE triggers */
  int after_triggers;         /* True if there are AFTER triggers */
  int newIdx = -1;            /* Cursor for the NEW table */

  if( pParse->nErr || sqlite3_malloc_failed ) goto insert_cleanup;
  db = pParse->db;

  /* Locate the table into which we will be inserting new information.
  */
  assert( pTabList->nSrc==1 );
  zTab = pTabList->a[0].zName;
  if( zTab==0 ) goto insert_cleanup;
  pTab = sqlite3SrcListLookup(pParse, pTabList);
  if( pTab==0 ){
    goto insert_cleanup;
  }
  assert( pTab->iDb<db->nDb );
  zDb = db->aDb[pTab->iDb].zName;
  if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
    goto insert_cleanup;
  }

  /* Ensure that:
  *  (a) the table is not read-only, 
  *  (b) that if it is a view then ON INSERT triggers exist
  */
  before_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger, TK_INSERT, 
                                       TK_BEFORE, TK_ROW, 0);
  after_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger, TK_INSERT,
                                       TK_AFTER, TK_ROW, 0);
  row_triggers_exist = before_triggers || after_triggers;
  isView = pTab->pSelect!=0;
  if( sqlite3IsReadOnly(pParse, pTab, before_triggers) ){
    goto insert_cleanup;
  }
  if( pTab==0 ) goto insert_cleanup;

  /* If pTab is really a view, make sure it has been initialized.
  */
  if( isView && sqlite3ViewGetColumnNames(pParse, pTab) ){
    goto insert_cleanup;
  }

  /* Ensure all required collation sequences are available. */
  for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
    if( sqlite3CheckIndexCollSeq(pParse, pIdx) ){
      goto insert_cleanup;
    }
  }

  /* Allocate a VDBE
  */
  v = sqlite3GetVdbe(pParse);
  if( v==0 ) goto insert_cleanup;
  sqlite3VdbeCountChanges(v);
  sqlite3BeginWriteOperation(pParse, pSelect || row_triggers_exist, pTab->iDb);

  /* if there are row triggers, allocate a temp table for new.* references. */
  if( row_triggers_exist ){
    newIdx = pParse->nTab++;
  }

  /* Figure out how many columns of data are supplied.  If the data
  ** is coming from a SELECT statement, then this step also generates
  ** all the code to implement the SELECT statement and invoke a subroutine
  ** to process each row of the result. (Template 2.) If the SELECT
  ** statement uses the the table that is being inserted into, then the
  ** subroutine is also coded here.  That subroutine stores the SELECT
  ** results in a temporary table. (Template 3.)
  */
  if( pSelect ){
    /* Data is coming from a SELECT.  Generate code to implement that SELECT
    */
    int rc, iInitCode;
    iInitCode = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
    iSelectLoop = sqlite3VdbeCurrentAddr(v);
    iInsertBlock = sqlite3VdbeMakeLabel(v);
    rc = sqlite3Select(pParse, pSelect, SRT_Subroutine, iInsertBlock, 0,0,0,0);
    if( rc || pParse->nErr || sqlite3_malloc_failed ) goto insert_cleanup;
    iCleanup = sqlite3VdbeMakeLabel(v);
    sqlite3VdbeAddOp(v, OP_Goto, 0, iCleanup);
    assert( pSelect->pEList );
    nColumn = pSelect->pEList->nExpr;

    /* Set useTempTable to TRUE if the result of the SELECT statement
    ** should be written into a temporary table.  Set to FALSE if each
    ** row of the SELECT can be written directly into the result table.
    **
    ** A temp table must be used if the table being updated is also one
    ** of the tables being read by the SELECT statement.  Also use a 
    ** temp table in the case of row triggers.
    */
    if( row_triggers_exist ){
      useTempTable = 1;
    }else{
      int addr = 0;
      useTempTable = 0;
      while( useTempTable==0 ){
        VdbeOp *pOp;
        addr = sqlite3VdbeFindOp(v, addr, OP_OpenRead, pTab->tnum);
        if( addr==0 ) break;
        pOp = sqlite3VdbeGetOp(v, addr-2);
        if( pOp->opcode==OP_Integer && pOp->p1==pTab->iDb ){
          useTempTable = 1;
        }
      }
    }

    if( useTempTable ){
      /* Generate the subroutine that SELECT calls to process each row of
      ** the result.  Store the result in a temporary table
      */
      srcTab = pParse->nTab++;
      sqlite3VdbeResolveLabel(v, iInsertBlock);
      sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
      sqlite3TableAffinityStr(v, pTab);
      sqlite3VdbeAddOp(v, OP_NewRecno, srcTab, 0);
      sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
      sqlite3VdbeAddOp(v, OP_PutIntKey, srcTab, 0);
      sqlite3VdbeAddOp(v, OP_Return, 0, 0);

      /* The following code runs first because the GOTO at the very top
      ** of the program jumps to it.  Create the temporary table, then jump
      ** back up and execute the SELECT code above.
      */
      sqlite3VdbeChangeP2(v, iInitCode, sqlite3VdbeCurrentAddr(v));
      sqlite3VdbeAddOp(v, OP_OpenTemp, srcTab, 0);
      sqlite3VdbeAddOp(v, OP_SetNumColumns, srcTab, nColumn);
      sqlite3VdbeAddOp(v, OP_Goto, 0, iSelectLoop);
      sqlite3VdbeResolveLabel(v, iCleanup);
    }else{
      sqlite3VdbeChangeP2(v, iInitCode, sqlite3VdbeCurrentAddr(v));
    }
  }else{
    /* This is the case if the data for the INSERT is coming from a VALUES
    ** clause
    */
    SrcList dummy;
    assert( pList!=0 );
    srcTab = -1;
    useTempTable = 0;
    assert( pList );
    nColumn = pList->nExpr;
    dummy.nSrc = 0;
    for(i=0; i<nColumn; i++){
      if( sqlite3ExprResolveAndCheck(pParse,&dummy,0,pList->a[i].pExpr,0,0) ){
        goto insert_cleanup;
      }
    }
  }

  /* Make sure the number of columns in the source data matches the number
  ** of columns to be inserted into the table.
  */
  if( pColumn==0 && nColumn!=pTab->nCol ){
    sqlite3ErrorMsg(pParse, 
       "table %S has %d columns but %d values were supplied",
       pTabList, 0, pTab->nCol, nColumn);
    goto insert_cleanup;
  }
  if( pColumn!=0 && nColumn!=pColumn->nId ){
    sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId);
    goto insert_cleanup;
  }

  /* If the INSERT statement included an IDLIST term, then make sure
  ** all elements of the IDLIST really are columns of the table and 
  ** remember the column indices.
  **
  ** If the table has an INTEGER PRIMARY KEY column and that column
  ** is named in the IDLIST, then record in the keyColumn variable
  ** the index into IDLIST of the primary key column.  keyColumn is
  ** the index of the primary key as it appears in IDLIST, not as
  ** is appears in the original table.  (The index of the primary
  ** key in the original table is pTab->iPKey.)
  */
  if( pColumn ){
    for(i=0; i<pColumn->nId; i++){
      pColumn->a[i].idx = -1;
    }
    for(i=0; i<pColumn->nId; i++){
      for(j=0; j<pTab->nCol; j++){
        if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
          pColumn->a[i].idx = j;
          if( j==pTab->iPKey ){
            keyColumn = i;
          }
          break;
        }
      }
      if( j>=pTab->nCol ){
        if( sqlite3IsRowid(pColumn->a[i].zName) ){
          keyColumn = i;
        }else{
          sqlite3ErrorMsg(pParse, "table %S has no column named %s",
              pTabList, 0, pColumn->a[i].zName);
          pParse->nErr++;
          goto insert_cleanup;
        }
      }
    }
  }

  /* If there is no IDLIST term but the table has an integer primary
  ** key, the set the keyColumn variable to the primary key column index
  ** in the original table definition.
  */
  if( pColumn==0 ){
    keyColumn = pTab->iPKey;
  }

  /* Open the temp table for FOR EACH ROW triggers
  */
  if( row_triggers_exist ){
    sqlite3VdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
    sqlite3VdbeAddOp(v, OP_SetNumColumns, newIdx, pTab->nCol);
  }
    
  /* Initialize the count of rows to be inserted
  */
  if( db->flags & SQLITE_CountRows ){
    iCntMem = pParse->nMem++;
    sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
    sqlite3VdbeAddOp(v, OP_MemStore, iCntMem, 1);
  }

  /* Open tables and indices if there are no row triggers */
  if( !row_triggers_exist ){
    base = pParse->nTab;
    sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite);
  }

  /* If the data source is a temporary table, then we have to create
  ** a loop because there might be multiple rows of data.  If the data
  ** source is a subroutine call from the SELECT statement, then we need
  ** to launch the SELECT statement processing.
  */
  if( useTempTable ){
    iBreak = sqlite3VdbeMakeLabel(v);
    sqlite3VdbeAddOp(v, OP_Rewind, srcTab, iBreak);
    iCont = sqlite3VdbeCurrentAddr(v);
  }else if( pSelect ){
    sqlite3VdbeAddOp(v, OP_Goto, 0, iSelectLoop);
    sqlite3VdbeResolveLabel(v, iInsertBlock);
  }

  /* Run the BEFORE and INSTEAD OF triggers, if there are any
  */
  endOfLoop = sqlite3VdbeMakeLabel(v);
  if( before_triggers ){

    /* build the NEW.* reference row.  Note that if there is an INTEGER
    ** PRIMARY KEY into which a NULL is being inserted, that NULL will be
    ** translated into a unique ID for the row.  But on a BEFORE trigger,
    ** we do not know what the unique ID will be (because the insert has
    ** not happened yet) so we substitute a rowid of -1
    */
    if( keyColumn<0 ){
      sqlite3VdbeAddOp(v, OP_Integer, -1, 0);
    }else if( useTempTable ){
      sqlite3VdbeAddOp(v, OP_Column, srcTab, keyColumn);
    }else if( pSelect ){
      sqlite3VdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1);
    }else{
      sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr);
      sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3);
      sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
      sqlite3VdbeAddOp(v, OP_Integer, -1, 0);
      sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0);
    }

    /* Create the new column data
    */
    for(i=0; i<pTab->nCol; i++){
      if( pColumn==0 ){
        j = i;
      }else{
        for(j=0; j<pColumn->nId; j++){
          if( pColumn->a[j].idx==i ) break;
        }
      }
      if( pColumn && j>=pColumn->nId ){
        sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[i].zDflt, P3_STATIC);
      }else if( useTempTable ){
        sqlite3VdbeAddOp(v, OP_Column, srcTab, j); 
      }else if( pSelect ){
        sqlite3VdbeAddOp(v, OP_Dup, nColumn-j-1, 1);
      }else{
        sqlite3ExprCode(pParse, pList->a[j].pExpr);
      }
    }
    sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);

    /* If this is an INSERT on a view with an INSTEAD OF INSERT trigger,
    ** do not attempt any conversions before assembling the record.
    ** If this is a real table, attempt conversions as required by the
    ** table column affinities.
    */
    if( !isView ){
      sqlite3TableAffinityStr(v, pTab);
    }
    sqlite3VdbeAddOp(v, OP_PutIntKey, newIdx, 0);

    /* Fire BEFORE or INSTEAD OF triggers */
    if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab, 
        newIdx, -1, onError, endOfLoop) ){
      goto insert_cleanup;
    }
  }

  /* If any triggers exists, the opening of tables and indices is deferred
  ** until now.
  */
  if( row_triggers_exist && !isView ){
    base = pParse->nTab;
    sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite);
  }

  /* Push the record number for the new entry onto the stack.  The
  ** record number is a randomly generate integer created by NewRecno
  ** except when the table has an INTEGER PRIMARY KEY column, in which
  ** case the record number is the same as that column. 
  */
  if( !isView ){
    if( keyColumn>=0 ){
      if( useTempTable ){
        sqlite3VdbeAddOp(v, OP_Column, srcTab, keyColumn);
      }else if( pSelect ){
        sqlite3VdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1);
      }else{
        sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr);
      }
      /* If the PRIMARY KEY expression is NULL, then use OP_NewRecno
      ** to generate a unique primary key value.
      */
      sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3);
      sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
      sqlite3VdbeAddOp(v, OP_NewRecno, base, 0);
      sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0);
    }else{
      sqlite3VdbeAddOp(v, OP_NewRecno, base, 0);
    }

    /* Push onto the stack, data for all columns of the new entry, beginning
    ** with the first column.
    */
    for(i=0; i<pTab->nCol; i++){
      if( i==pTab->iPKey ){
        /* The value of the INTEGER PRIMARY KEY column is always a NULL.
        ** Whenever this column is read, the record number will be substituted
        ** in its place.  So will fill this column with a NULL to avoid
        ** taking up data space with information that will never be used. */
        sqlite3VdbeAddOp(v, OP_String8, 0, 0);
        continue;
      }
      if( pColumn==0 ){
        j = i;
      }else{
        for(j=0; j<pColumn->nId; j++){
          if( pColumn->a[j].idx==i ) break;
        }
      }
      if( pColumn && j>=pColumn->nId ){
        sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[i].zDflt, P3_STATIC);
      }else if( useTempTable ){
        sqlite3VdbeAddOp(v, OP_Column, srcTab, j); 
      }else if( pSelect ){
        sqlite3VdbeAddOp(v, OP_Dup, i+nColumn-j, 1);
      }else{
        sqlite3ExprCode(pParse, pList->a[j].pExpr);
      }
    }

    /* Generate code to check constraints and generate index keys and
    ** do the insertion.
    */
    sqlite3GenerateConstraintChecks(pParse, pTab, base, 0, keyColumn>=0,
                                   0, onError, endOfLoop);
    sqlite3CompleteInsertion(pParse, pTab, base, 0,0,0,
                            after_triggers ? newIdx : -1);
  }

  /* Update the count of rows that are inserted
  */
  if( (db->flags & SQLITE_CountRows)!=0 ){
    sqlite3VdbeAddOp(v, OP_MemIncr, iCntMem, 0);
  }

  if( row_triggers_exist ){
    /* Close all tables opened */
    if( !isView ){
      sqlite3VdbeAddOp(v, OP_Close, base, 0);
      for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
        sqlite3VdbeAddOp(v, OP_Close, idx+base, 0);
      }
    }

    /* Code AFTER triggers */
    if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TK_AFTER, pTab, newIdx, -1, 
          onError, endOfLoop) ){
      goto insert_cleanup;
    }
  }

  /* The bottom of the loop, if the data source is a SELECT statement
  */
  sqlite3VdbeResolveLabel(v, endOfLoop);
  if( useTempTable ){
    sqlite3VdbeAddOp(v, OP_Next, srcTab, iCont);
    sqlite3VdbeResolveLabel(v, iBreak);
    sqlite3VdbeAddOp(v, OP_Close, srcTab, 0);
  }else if( pSelect ){
    sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0);
    sqlite3VdbeAddOp(v, OP_Return, 0, 0);
    sqlite3VdbeResolveLabel(v, iCleanup);
  }

  if( !row_triggers_exist ){
    /* Close all tables opened */
    sqlite3VdbeAddOp(v, OP_Close, base, 0);
    for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
      sqlite3VdbeAddOp(v, OP_Close, idx+base, 0);
    }
  }

  sqlite3EndWriteOperation(pParse);

  /*
  ** Return the number of rows inserted.
  */
  if( db->flags & SQLITE_CountRows ){
    sqlite3VdbeAddOp(v, OP_MemLoad, iCntMem, 0);
    sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
    sqlite3VdbeSetNumCols(v, 1);
    sqlite3VdbeSetColName(v, 0, "rows inserted", P3_STATIC);
  }

insert_cleanup:
  sqlite3SrcListDelete(pTabList);
  if( pList ) sqlite3ExprListDelete(pList);
  if( pSelect ) sqlite3SelectDelete(pSelect);
  sqlite3IdListDelete(pColumn);
}
Exemplo n.º 2
0
/*
** This routine is called after all of the trigger actions have been parsed
** in order to complete the process of building the trigger.
*/
void sqlite3FinishTrigger(
  Parse *pParse,          /* Parser context */
  TriggerStep *pStepList, /* The triggered program */
  Token *pAll             /* Token that describes the complete CREATE TRIGGER */
){
  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;
  pParse->pNewTrigger = 0;
  nt->step_list = pStepList;
  while( pStepList ){
    pStepList->pTrig = nt;
    pStepList = pStepList->pNext;
  }
  if( sqlite3FixInit(&sFix, pParse, nt->iDb, "trigger", &nt->nameToken) 
          && sqlite3FixTriggerStep(&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
  */
  if( !db->init.busy ){
    static VdbeOpList insertTrig[] = {
      { OP_NewRecno,   0, 0,  0          },
      { OP_String8,     0, 0,  "trigger"  },
      { OP_String8,     0, 0,  0          },  /* 2: trigger name */
      { OP_String8,     0, 0,  0          },  /* 3: table name */
      { OP_Integer,    0, 0,  0          },
      { OP_String8,     0, 0,  "CREATE TRIGGER "},
      { OP_String8,     0, 0,  0          },  /* 6: SQL */
      { OP_Concat8,     2, 0,  0          }, 
      { OP_MakeRecord, 5, 0,  "tttit"    },
      { OP_PutIntKey,  0, 0,  0          },
    };
    int addr;
    Vdbe *v;

    /* Make an entry in the sqlite_master table */
    v = sqlite3GetVdbe(pParse);
    if( v==0 ) goto triggerfinish_cleanup;
    sqlite3BeginWriteOperation(pParse, 0, nt->iDb);
    sqlite3OpenMasterTable(v, nt->iDb);
    addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
    sqlite3VdbeChangeP3(v, addr+2, nt->name, 0); 
    sqlite3VdbeChangeP3(v, addr+3, nt->table, 0); 
    sqlite3VdbeChangeP3(v, addr+6, pAll->z, pAll->n);
    if( nt->iDb!=0 ){
      sqlite3ChangeCookie(db, v, nt->iDb);
    }
    sqlite3VdbeAddOp(v, OP_Close, 0, 0);
    sqlite3EndWriteOperation(pParse);
  }

  if( !pParse->explain ){
    Table *pTab;
    sqlite3HashInsert(&db->aDb[nt->iDb].trigHash, 
                     nt->name, strlen(nt->name)+1, nt);
    pTab = sqlite3LocateTable(pParse, nt->table, db->aDb[nt->iTabDb].zName);
    assert( pTab!=0 );
    nt->pNext = pTab->pTrigger;
    pTab->pTrigger = nt;
    nt = 0;
  }

triggerfinish_cleanup:
  sqlite3DeleteTrigger(nt);
  sqlite3DeleteTrigger(pParse->pNewTrigger);
  pParse->pNewTrigger = 0;
  sqlite3DeleteTriggerStep(pStepList);
}
Exemplo n.º 3
0
/*
** Process a DELETE FROM statement.
*/
void sqlite3DeleteFrom(
    Parse *pParse,         /* The parser context */
    SrcList *pTabList,     /* The table from which we should delete things */
    Expr *pWhere           /* The WHERE clause.  May be null */
) {
    Vdbe *v;               /* The virtual database engine */
    Table *pTab;           /* The table from which records will be deleted */
    const char *zDb;       /* Name of database holding pTab */
    int end, addr = 0;     /* A couple addresses of generated code */
    int i;                 /* Loop counter */
    WhereInfo *pWInfo;     /* Information about the WHERE clause */
    Index *pIdx;           /* For looping over indices of the table */
    int iCur;              /* VDBE Cursor number for pTab */
    sqlite *db;            /* Main database structure */
    int isView;            /* True if attempting to delete from a view */
    AuthContext sContext;  /* Authorization context */

    int row_triggers_exist = 0;  /* True if any triggers exist */
    int before_triggers;         /* True if there are BEFORE triggers */
    int after_triggers;          /* True if there are AFTER triggers */
    int oldIdx = -1;             /* Cursor for the OLD table of AFTER triggers */

    sContext.pParse = 0;
    if( pParse->nErr || sqlite3_malloc_failed ) {
        pTabList = 0;
        goto delete_from_cleanup;
    }
    db = pParse->db;
    assert( pTabList->nSrc==1 );

    /* Locate the table which we want to delete.  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.
    */
    pTab = sqlite3SrcListLookup(pParse, pTabList);
    if( pTab==0 )  goto delete_from_cleanup;
    before_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger,
                                           TK_DELETE, TK_BEFORE, TK_ROW, 0);
    after_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger,
                                          TK_DELETE, TK_AFTER, TK_ROW, 0);
    row_triggers_exist = before_triggers || after_triggers;
    isView = pTab->pSelect!=0;
    if( sqlite3IsReadOnly(pParse, pTab, before_triggers) ) {
        goto delete_from_cleanup;
    }
    assert( pTab->iDb<db->nDb );
    zDb = db->aDb[pTab->iDb].zName;
    if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ) {
        goto delete_from_cleanup;
    }

    /* If pTab is really a view, make sure it has been initialized.
    */
    if( isView && sqlite3ViewGetColumnNames(pParse, pTab) ) {
        goto delete_from_cleanup;
    }

    /* Allocate a cursor used to store the old.* data for a trigger.
    */
    if( row_triggers_exist ) {
        oldIdx = pParse->nTab++;
    }

    /* Resolve the column names in all the expressions.
    */
    assert( pTabList->nSrc==1 );
    iCur = pTabList->a[0].iCursor = pParse->nTab++;
    if( pWhere ) {
        if( sqlite3ExprResolveIds(pParse, pTabList, 0, pWhere) ) {
            goto delete_from_cleanup;
        }
        if( sqlite3ExprCheck(pParse, pWhere, 0, 0) ) {
            goto delete_from_cleanup;
        }
    }

    /* Start the view context
    */
    if( isView ) {
        sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
    }

    /* Begin generating code.
    */
    v = sqlite3GetVdbe(pParse);
    if( v==0 ) {
        goto delete_from_cleanup;
    }
    sqlite3VdbeCountChanges(v);
    sqlite3BeginWriteOperation(pParse, row_triggers_exist, pTab->iDb);

    /* If we are trying to delete from a view, construct that view into
    ** a temporary table.
    */
    if( isView ) {
        Select *pView = sqlite3SelectDup(pTab->pSelect);
        sqlite3Select(pParse, pView, SRT_TempTable, iCur, 0, 0, 0, 0);
        sqlite3SelectDelete(pView);
    }

    /* Initialize the counter of the number of rows deleted, if
    ** we are counting rows.
    */
    if( db->flags & SQLITE_CountRows ) {
        sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
    }

    /* Special case: A DELETE without a WHERE clause deletes everything.
    ** It is easier just to erase the whole table.  Note, however, that
    ** this means that the row change count will be incorrect.
    */
    if( pWhere==0 && !row_triggers_exist ) {
        if( db->flags & SQLITE_CountRows ) {
            /* If counting rows deleted, just count the total number of
            ** entries in the table. */
            int endOfLoop = sqlite3VdbeMakeLabel(v);
            int addr;
            if( !isView ) {
                sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
                sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);
                sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
            }
            sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2);
            addr = sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
            sqlite3VdbeAddOp(v, OP_Next, iCur, addr);
            sqlite3VdbeResolveLabel(v, endOfLoop);
            sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
        }
        if( !isView ) {
            sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb);
            for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext) {
                sqlite3VdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb);
            }
        }
    }

    /* The usual case: There is a WHERE clause so we have to scan through
    ** the table and pick which records to delete.
    */
    else {
        /* Ensure all required collation sequences are available. */
        for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext) {
            if( sqlite3CheckIndexCollSeq(pParse, pIdx) ) {
                goto delete_from_cleanup;
            }
        }

        /* Begin the database scan
        */
        pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0);
        if( pWInfo==0 ) goto delete_from_cleanup;

        /* Remember the key of every item to be deleted.
        */
        sqlite3VdbeAddOp(v, OP_ListWrite, 0, 0);
        if( db->flags & SQLITE_CountRows ) {
            sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
        }

        /* End the database scan loop.
        */
        sqlite3WhereEnd(pWInfo);

        /* Open the pseudo-table used to store OLD if there are triggers.
        */
        if( row_triggers_exist ) {
            sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
            sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
        }

        /* Delete every item whose key was written to the list during the
        ** database scan.  We have to delete items after the scan is complete
        ** because deleting an item can change the scan order.
        */
        sqlite3VdbeAddOp(v, OP_ListRewind, 0, 0);
        end = sqlite3VdbeMakeLabel(v);

        /* This is the beginning of the delete loop when there are
        ** row triggers.
        */
        if( row_triggers_exist ) {
            addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, end);
            sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
            if( !isView ) {
                sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
                sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);
                sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
            }
            sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
            sqlite3VdbeAddOp(v, OP_Recno, iCur, 0);
            sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
            sqlite3VdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
            if( !isView ) {
                sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
            }

            sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1,
                                  oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
                                  addr);
        }

        if( !isView ) {
            /* Open cursors for the table we are deleting from and all its
            ** indices.  If there are row triggers, this happens inside the
            ** OP_ListRead loop because the cursor have to all be closed
            ** before the trigger fires.  If there are no row triggers, the
            ** cursors are opened only once on the outside the loop.
            */
            pParse->nTab = iCur + 1;
            sqlite3OpenTableAndIndices(pParse, pTab, iCur);

            /* This is the beginning of the delete loop when there are no
            ** row triggers */
            if( !row_triggers_exist ) {
                addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, end);
            }

            /* Delete the row */
            sqlite3GenerateRowDelete(db, v, pTab, iCur, 1);
        }

        /* If there are row triggers, close all cursors then invoke
        ** the AFTER triggers
        */
        if( row_triggers_exist ) {
            if( !isView ) {
                for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext) {
                    sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
                }
                sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
            }
            sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1,
                                  oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
                                  addr);
        }

        /* End of the delete loop */
        sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
        sqlite3VdbeResolveLabel(v, end);
        sqlite3VdbeAddOp(v, OP_ListReset, 0, 0);

        /* Close the cursors after the loop if there are no row triggers */
        if( !row_triggers_exist ) {
            for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext) {
                sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
            }
            sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
            pParse->nTab = iCur;
        }
    }
    sqlite3EndWriteOperation(pParse);

    /*
    ** Return the number of rows that were deleted.
    */
    if( db->flags & SQLITE_CountRows ) {
        sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
        sqlite3VdbeSetNumCols(v, 1);
        sqlite3VdbeSetColName(v, 0, "rows deleted", P3_STATIC);
    }

delete_from_cleanup:
    sqlite3AuthContextPop(&sContext);
    sqlite3SrcListDelete(pTabList);
    sqlite3ExprDelete(pWhere);
    return;
}