⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 trigger.c

📁 sqlite-3.4.1,嵌入式数据库.是一个功能强大的开源数据库,给学习和研发以及小型公司的发展带来了全所未有的好处.
💻 C
📖 第 1 页 / 共 2 页
字号:
    return 0;  }  pTriggerStep->op = TK_DELETE;  pTriggerStep->target  = *pTableName;  pTriggerStep->pWhere = pWhere;  pTriggerStep->orconf = OE_Default;  sqlitePersistTriggerStep(pTriggerStep);  return pTriggerStep;}/* ** Recursively delete a Trigger structure*/void sqlite3DeleteTrigger(Trigger *pTrigger){  if( pTrigger==0 ) return;  sqlite3DeleteTriggerStep(pTrigger->step_list);  sqliteFree(pTrigger->name);  sqliteFree(pTrigger->table);  sqlite3ExprDelete(pTrigger->pWhen);  sqlite3IdListDelete(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 and therefore identifies** the trigger by name.  The sqlite3DropTriggerPtr() routine does the** same job as this routine except it takes a pointer to the trigger** instead of the trigger name.**/void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){  Trigger *pTrigger = 0;  int i;  const char *zDb;  const char *zName;  int nName;  sqlite3 *db = pParse->db;  if( sqlite3MallocFailed() ) goto drop_trigger_cleanup;  if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){    goto drop_trigger_cleanup;  }  assert( pName->nSrc==1 );  zDb = pName->a[0].zDatabase;  zName = pName->a[0].zName;  nName = strlen(zName);  for(i=OMIT_TEMPDB; i<db->nDb; i++){    int j = (i<2) ? i^1 : i;  /* Search TEMP before MAIN */    if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue;    pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName, nName);    if( pTrigger ) break;  }  if( !pTrigger ){    if( !noErr ){      sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0);    }    goto drop_trigger_cleanup;  }  sqlite3DropTriggerPtr(pParse, pTrigger);drop_trigger_cleanup:  sqlite3SrcListDelete(pName);}/*** Return a pointer to the Table structure for the table that a trigger** is set on.*/static Table *tableOfTrigger(Trigger *pTrigger){  int n = strlen(pTrigger->table) + 1;  return sqlite3HashFind(&pTrigger->pTabSchema->tblHash, pTrigger->table, n);}/*** Drop a trigger given a pointer to that trigger. */void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){  Table   *pTable;  Vdbe *v;  sqlite3 *db = pParse->db;  int iDb;  iDb = sqlite3SchemaToIndex(pParse->db, pTrigger->pSchema);  assert( iDb>=0 && iDb<db->nDb );  pTable = tableOfTrigger(pTrigger);  assert( pTable );  assert( pTable->pSchema==pTrigger->pSchema || iDb==1 );#ifndef SQLITE_OMIT_AUTHORIZATION  {    int code = SQLITE_DROP_TRIGGER;    const char *zDb = db->aDb[iDb].zName;    const char *zTab = SCHEMA_TABLE(iDb);    if( iDb==1 ) code = SQLITE_DROP_TEMP_TRIGGER;    if( sqlite3AuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) ||      sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){      return;    }  }#endif  /* Generate code to destroy the database record of the trigger.  */  assert( pTable!=0 );  if( (v = sqlite3GetVdbe(pParse))!=0 ){    int base;    static const VdbeOpList dropTrigger[] = {      { OP_Rewind,     0, ADDR(9),  0},      { OP_String8,    0, 0,        0}, /* 1 */      { OP_Column,     0, 1,        0},      { OP_Ne,         0, ADDR(8),  0},      { OP_String8,    0, 0,        "trigger"},      { OP_Column,     0, 0,        0},      { OP_Ne,         0, ADDR(8),  0},      { OP_Delete,     0, 0,        0},      { OP_Next,       0, ADDR(1),  0}, /* 8 */    };    sqlite3BeginWriteOperation(pParse, 0, iDb);    sqlite3OpenMasterTable(pParse, iDb);    base = sqlite3VdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);    sqlite3VdbeChangeP3(v, base+1, pTrigger->name, 0);    sqlite3ChangeCookie(db, v, iDb);    sqlite3VdbeAddOp(v, OP_Close, 0, 0);    sqlite3VdbeOp3(v, OP_DropTrigger, iDb, 0, pTrigger->name, 0);  }}/*** Remove a trigger from the hash tables of the sqlite* pointer.*/void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){  Trigger *pTrigger;  int nName = strlen(zName);  pTrigger = sqlite3HashInsert(&(db->aDb[iDb].pSchema->trigHash),                               zName, nName, 0);  if( pTrigger ){    Table *pTable = tableOfTrigger(pTrigger);    assert( pTable!=0 );    if( pTable->pTrigger == pTrigger ){      pTable->pTrigger = pTrigger->pNext;    }else{      Trigger *cc = pTable->pTrigger;      while( cc ){         if( cc->pNext == pTrigger ){          cc->pNext = cc->pNext->pNext;          break;        }        cc = cc->pNext;      }      assert(cc);    }    sqlite3DeleteTrigger(pTrigger);    db->flags |= SQLITE_InternChanges;  }}/*** pEList is the SET clause of an UPDATE statement.  Each entry** in pEList is of the format <id>=<expr>.  If any of the entries** in pEList have an <id> which matches an identifier in pIdList,** then return TRUE.  If pIdList==NULL, then it is considered a** wildcard that matches anything.  Likewise if pEList==NULL then** it matches anything so always return true.  Return false only** if there is no match.*/static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){  int e;  if( !pIdList || !pEList ) return 1;  for(e=0; e<pEList->nExpr; e++){    if( sqlite3IdListIndex(pIdList, pEList->a[e].zName)>=0 ) return 1;  }  return 0; }/*** Return a bit vector to indicate what kind of triggers exist for operation** "op" on table pTab.  If pChanges is not NULL then it is a list of columns** that are being updated.  Triggers only match if the ON clause of the** trigger definition overlaps the set of columns being updated.**** The returned bit vector is some combination of TRIGGER_BEFORE and** TRIGGER_AFTER.*/int sqlite3TriggersExist(  Parse *pParse,          /* Used to check for recursive triggers */  Table *pTab,            /* The table the contains the triggers */  int op,                 /* one of TK_DELETE, TK_INSERT, TK_UPDATE */  ExprList *pChanges      /* Columns that change in an UPDATE statement */){  Trigger *pTrigger;  int mask = 0;  pTrigger = IsVirtual(pTab) ? 0 : pTab->pTrigger;  while( pTrigger ){    if( pTrigger->op==op && checkColumnOverLap(pTrigger->pColumns, pChanges) ){      mask |= pTrigger->tr_tm;    }    pTrigger = pTrigger->pNext;  }  return mask;}/*** 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 = sqlite3SchemaToIndex(pParse->db, pStep->pTrig->pSchema);  if( iDb==0 || iDb>=2 ){    assert( iDb<pParse->db->nDb );    sDb.z = (u8*)pParse->db->aDb[iDb].zName;    sDb.n = strlen((char*)sDb.z);    pSrc = sqlite3SrcListAppend(0, &sDb, &pStep->target);  } else {    pSrc = sqlite3SrcListAppend(0, &pStep->target, 0);  }  return pSrc;}/*** Generate VDBE code for zero or more statements inside the body of a** trigger.  */static int codeTriggerProgram(  Parse *pParse,            /* The parser context */  TriggerStep *pStepList,   /* List of statements inside the trigger body */  int orconfin              /* Conflict algorithm. (OE_Abort, etc) */  ){  TriggerStep * pTriggerStep = pStepList;  int orconf;  Vdbe *v = pParse->pVdbe;  assert( pTriggerStep!=0 );  assert( v!=0 );  sqlite3VdbeAddOp(v, OP_ContextPush, 0, 0);  VdbeComment((v, "# begin trigger %s", pStepList->pTrig->name));  while( pTriggerStep ){    orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;    pParse->trigStack->orconf = orconf;    switch( pTriggerStep->op ){      case TK_SELECT: {	Select *ss = sqlite3SelectDup(pTriggerStep->pSelect);        if( ss ){          sqlite3SelectResolve(pParse, ss, 0);          sqlite3Select(pParse, ss, SRT_Discard, 0, 0, 0, 0, 0);          sqlite3SelectDelete(ss);        }	break;      }      case TK_UPDATE: {        SrcList *pSrc;        pSrc = targetSrcList(pParse, pTriggerStep);        sqlite3VdbeAddOp(v, OP_ResetCount, 0, 0);        sqlite3Update(pParse, pSrc,		sqlite3ExprListDup(pTriggerStep->pExprList), 		sqlite3ExprDup(pTriggerStep->pWhere), orconf);        sqlite3VdbeAddOp(v, OP_ResetCount, 1, 0);        break;      }      case TK_INSERT: {        SrcList *pSrc;        pSrc = targetSrcList(pParse, pTriggerStep);        sqlite3VdbeAddOp(v, OP_ResetCount, 0, 0);        sqlite3Insert(pParse, pSrc,          sqlite3ExprListDup(pTriggerStep->pExprList),           sqlite3SelectDup(pTriggerStep->pSelect),           sqlite3IdListDup(pTriggerStep->pIdList), orconf);        sqlite3VdbeAddOp(v, OP_ResetCount, 1, 0);        break;      }      case TK_DELETE: {        SrcList *pSrc;        sqlite3VdbeAddOp(v, OP_ResetCount, 0, 0);        pSrc = targetSrcList(pParse, pTriggerStep);        sqlite3DeleteFrom(pParse, pSrc, sqlite3ExprDup(pTriggerStep->pWhere));        sqlite3VdbeAddOp(v, OP_ResetCount, 1, 0);        break;      }      default:        assert(0);    }     pTriggerStep = pTriggerStep->pNext;  }  sqlite3VdbeAddOp(v, OP_ContextPop, 0, 0);  VdbeComment((v, "# end trigger %s", pStepList->pTrig->name));  return 0;}/*** This is called to code FOR EACH ROW triggers.**** When the code that this function generates is executed, the following ** must be true:**** 1. No cursors may be open in the main database.  (But newIdx and oldIdx**    can be indices of cursors in temporary tables.  See below.)**** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then**    a temporary vdbe cursor (index newIdx) must be open and pointing at**    a row containing values to be substituted for new.* expressions in the**    trigger program(s).**** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then**    a temporary vdbe cursor (index oldIdx) must be open and pointing at**    a row containing values to be substituted for old.* expressions in the**    trigger program(s).***/int sqlite3CodeRowTrigger(  Parse *pParse,       /* Parse context */  int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */  ExprList *pChanges,  /* Changes list for any UPDATE OF triggers */  int tr_tm,           /* One of TRIGGER_BEFORE, TRIGGER_AFTER */  Table *pTab,         /* The table to code triggers from */  int newIdx,          /* The indice of the "new" row to access */  int oldIdx,          /* The indice of the "old" row to access */  int orconf,          /* ON CONFLICT policy */  int ignoreJump       /* Instruction to jump to for RAISE(IGNORE) */){  Trigger *p;  TriggerStack trigStackEntry;  assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);  assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER );  assert(newIdx != -1 || oldIdx != -1);  for(p=pTab->pTrigger; p; p=p->pNext){    int fire_this = 0;    /* Determine whether we should code this trigger */    if(       p->op==op &&       p->tr_tm==tr_tm &&       (p->pSchema==p->pTabSchema || p->pSchema==pParse->db->aDb[1].pSchema) &&      (op!=TK_UPDATE||!p->pColumns||checkColumnOverLap(p->pColumns,pChanges))    ){      TriggerStack *pS;      /* Pointer to trigger-stack entry */      for(pS=pParse->trigStack; pS && p!=pS->pTrigger; pS=pS->pNext){}      if( !pS ){        fire_this = 1;      }#if 0    /* Give no warning for recursive triggers.  Just do not do them */      else{        sqlite3ErrorMsg(pParse, "recursive triggers not supported (%s)",            p->name);        return SQLITE_ERROR;      }#endif    }     if( fire_this ){      int endTrigger;      Expr * whenExpr;      AuthContext sContext;      NameContext sNC;      memset(&sNC, 0, sizeof(sNC));      sNC.pParse = pParse;      /* Push an entry on to the trigger stack */      trigStackEntry.pTrigger = p;      trigStackEntry.newIdx = newIdx;      trigStackEntry.oldIdx = oldIdx;      trigStackEntry.pTab = pTab;      trigStackEntry.pNext = pParse->trigStack;      trigStackEntry.ignoreJump = ignoreJump;      pParse->trigStack = &trigStackEntry;      sqlite3AuthContextPush(pParse, &sContext, p->name);      /* code the WHEN clause */      endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe);      whenExpr = sqlite3ExprDup(p->pWhen);      if( sqlite3ExprResolveNames(&sNC, whenExpr) ){        pParse->trigStack = trigStackEntry.pNext;        sqlite3ExprDelete(whenExpr);        return 1;      }      sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, 1);      sqlite3ExprDelete(whenExpr);      codeTriggerProgram(pParse, p->step_list, orconf);       /* Pop the entry off the trigger stack */      pParse->trigStack = trigStackEntry.pNext;      sqlite3AuthContextPop(&sContext);      sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger);    }  }  return 0;}#endif /* !defined(SQLITE_OMIT_TRIGGER) */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -