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

📄 trigger.c

📁 sqlite数据库管理系统开放源码
💻 C
📖 第 1 页 / 共 2 页
字号:
 * 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){  Trigger *pTrigger;  int i;  const char *zDb;  const char *zName;  int nName;  sqlite *db = pParse->db;  if( sqlite_malloc_failed ) goto drop_trigger_cleanup;  assert( pName->nSrc==1 );  zDb = pName->a[0].zDatabase;  zName = pName->a[0].zName;  nName = strlen(zName);  for(i=0; i<db->nDb; i++){    int j = (i<2) ? i^1 : i;  /* Search TEMP before MAIN */    if( zDb && sqliteStrICmp(db->aDb[j].zName, zDb) ) continue;    pTrigger = sqliteHashFind(&(db->aDb[j].trigHash), zName, nName+1);    if( pTrigger ) break;  }  if( !pTrigger ){    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);    return;  }  pTable = sqliteFindTable(db, pTrigger->table,db->aDb[pTrigger->iTabDb].zName);  assert(pTable);  assert( pTable->iDb==pTrigger->iDb || pTrigger->iDb==1 );#ifndef SQLITE_OMIT_AUTHORIZATION  {    int code = SQLITE_DROP_TRIGGER;    const char *zDb = db->aDb[pTrigger->iDb].zName;    const char *zTab = SCHEMA_TABLE(pTrigger->iDb);    if( pTrigger->iDb ) code = SQLITE_DROP_TEMP_TRIGGER;    if( sqliteAuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) ||      sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){      return;    }  }#endif  /* Generate code to destroy the database record of the trigger.  */  if( pTable!=0 && !nested && (v = sqliteGetVdbe(pParse))!=0 ){    int base;    static VdbeOpList dropTrigger[] = {      { OP_Rewind,     0, ADDR(9),  0},      { OP_String,     0, 0,        0}, /* 1 */      { OP_Column,     0, 1,        0},      { OP_Ne,         0, ADDR(8),  0},      { OP_String,     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 */    };    sqliteBeginWriteOperation(pParse, 0, 0);    sqliteOpenMasterTable(v, pTrigger->iDb);    base = sqliteVdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);    sqliteVdbeChangeP3(v, base+1, pTrigger->name, 0);    if( pTrigger->iDb==0 ){      sqliteChangeCookie(db, v);    }    sqliteVdbeAddOp(v, OP_Close, 0, 0);    sqliteEndWriteOperation(pParse);  }  /*   * 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{      Trigger *cc = pTable->pTrigger;      while( cc ){         if( cc->pNext == pTrigger ){          cc->pNext = cc->pNext->pNext;          break;        }        cc = cc->pNext;      }      assert(cc);    }    sqliteHashInsert(&(db->aDb[pTrigger->iDb].trigHash), zName, nName+1, 0);    sqliteDeleteTrigger(pTrigger);  }}/*** 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( sqliteIdListIndex(pIdList, pEList->a[e].zName)>=0 ) return 1;  }  return 0; }/* A global variable that is TRUE if we should always set up temp tables for * for triggers, even if there are no triggers to code. This is used to test  * how much overhead the triggers algorithm is causing. * * This flag can be set or cleared using the "trigger_overhead_test" pragma. * The pragma is not documented since it is not really part of the interface * to SQLite, just the test procedure.*/int always_code_trigger_setup = 0;/* * Returns true if a trigger matching op, tr_tm and foreach that is NOT already * on the Parse objects trigger-stack (to prevent recursive trigger firing) is * found in the list specified as pTrigger. */int sqliteTriggersExist(  Parse *pParse,          /* Used to check for recursive triggers */  Trigger *pTrigger,      /* A list of triggers associated with a table */  int op,                 /* one of TK_DELETE, TK_INSERT, TK_UPDATE */  int tr_tm,              /* one of TK_BEFORE, TK_AFTER */  int foreach,            /* one of TK_ROW or TK_STATEMENT */  ExprList *pChanges      /* Columns that change in an UPDATE statement */){  Trigger * pTriggerCursor;  if( always_code_trigger_setup ){    return 1;  }  pTriggerCursor = pTrigger;  while( pTriggerCursor ){    if( pTriggerCursor->op == op && 	pTriggerCursor->tr_tm == tr_tm && 	pTriggerCursor->foreach == foreach &&	checkColumnOverLap(pTriggerCursor->pColumns, pChanges) ){      TriggerStack * ss;      ss = pParse->trigStack;      while( ss && ss->pTrigger != pTrigger ){	ss = ss->pNext;      }      if( !ss )return 1;    }    pTriggerCursor = pTriggerCursor->pNext;  }  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.  */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;  while( pTriggerStep ){    int saveNTab = pParse->nTab;     orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;    pParse->trigStack->orconf = orconf;    switch( pTriggerStep->op ){      case TK_SELECT: {	Select * ss = sqliteSelectDup(pTriggerStep->pSelect);		  	assert(ss);	assert(ss->pSrc);	sqliteSelect(pParse, ss, SRT_Discard, 0, 0, 0, 0);	sqliteSelectDelete(ss);	break;      }      case TK_UPDATE: {        SrcList *pSrc;        pSrc = targetSrcList(pParse, pTriggerStep);        sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);        sqliteUpdate(pParse, pSrc,		sqliteExprListDup(pTriggerStep->pExprList), 		sqliteExprDup(pTriggerStep->pWhere), orconf);        sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);        break;      }      case TK_INSERT: {        SrcList *pSrc;        pSrc = targetSrcList(pParse, pTriggerStep);        sqliteInsert(pParse, pSrc,          sqliteExprListDup(pTriggerStep->pExprList),           sqliteSelectDup(pTriggerStep->pSelect),           sqliteIdListDup(pTriggerStep->pIdList), orconf);        break;      }      case TK_DELETE: {        SrcList *pSrc;        sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);        pSrc = targetSrcList(pParse, pTriggerStep);        sqliteDeleteFrom(pParse, pSrc, sqliteExprDup(pTriggerStep->pWhere));        sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);        break;      }      default:        assert(0);    }     pParse->nTab = saveNTab;    pTriggerStep = pTriggerStep->pNext;  }  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 sqliteCodeRowTrigger(  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 TK_BEFORE, TK_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 * pTrigger;  TriggerStack * pTriggerStack;  assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);  assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER );  assert(newIdx != -1 || oldIdx != -1);  pTrigger = pTab->pTrigger;  while( pTrigger ){    int fire_this = 0;    /* determine whether we should code this trigger */    if( pTrigger->op == op && pTrigger->tr_tm == tr_tm &&         pTrigger->foreach == TK_ROW ){      fire_this = 1;      pTriggerStack = pParse->trigStack;      while( pTriggerStack ){        if( pTriggerStack->pTrigger == pTrigger ){	  fire_this = 0;	}        pTriggerStack = pTriggerStack->pNext;      }      if( op == TK_UPDATE && pTrigger->pColumns &&          !checkColumnOverLap(pTrigger->pColumns, pChanges) ){        fire_this = 0;      }    }    if( fire_this && (pTriggerStack = sqliteMalloc(sizeof(TriggerStack)))!=0 ){      int endTrigger;      SrcList dummyTablist;      Expr * whenExpr;      AuthContext sContext;      dummyTablist.nSrc = 0;      /* Push an entry on to the trigger stack */      pTriggerStack->pTrigger = pTrigger;      pTriggerStack->newIdx = newIdx;      pTriggerStack->oldIdx = oldIdx;      pTriggerStack->pTab = pTab;      pTriggerStack->pNext = pParse->trigStack;      pTriggerStack->ignoreJump = ignoreJump;      pParse->trigStack = pTriggerStack;      sqliteAuthContextPush(pParse, &sContext, pTrigger->name);      /* code the WHEN clause */      endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);      whenExpr = sqliteExprDup(pTrigger->pWhen);      if( sqliteExprResolveIds(pParse, &dummyTablist, 0, whenExpr) ){        pParse->trigStack = pParse->trigStack->pNext;        sqliteFree(pTriggerStack);        sqliteExprDelete(whenExpr);        return 1;      }      sqliteExprIfFalse(pParse, whenExpr, endTrigger, 1);      sqliteExprDelete(whenExpr);      sqliteVdbeAddOp(pParse->pVdbe, OP_ContextPush, 0, 0);      codeTriggerProgram(pParse, pTrigger->step_list, orconf);       sqliteVdbeAddOp(pParse->pVdbe, OP_ContextPop, 0, 0);      /* Pop the entry off the trigger stack */      pParse->trigStack = pParse->trigStack->pNext;      sqliteAuthContextPop(&sContext);      sqliteFree(pTriggerStack);      sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);    }    pTrigger = pTrigger->pNext;  }  return 0;}

⌨️ 快捷键说明

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