static Select sqlite3SelectNew( Parse pParse, /* Parsing context */ ExprList pEList, /* which columns to include in the result */ SrcList pSrc, /* the FROM clause -- which tables to scan */ Expr pWhere, /* the WHERE clause */ ExprList pGroupBy, /* the GROUP BY clause */ Expr pHaving, /* the HAVING clause */ ExprList pOrderBy, /* the ORDER BY clause */ int isDistinct, /* true if the DISTINCT keyword is present */ Expr pLimit, /* LIMIT value. NULL means not used */ Expr pOffset /* OFFSET value. NULL means no offset */ ) { Select pNew; // Select standin; sqlite3 db = pParse.db; pNew = new Select();//sqlite3DbMallocZero(db, sizeof(*pNew) ); Debug.Assert( //db.mallocFailed != 0 || null == pOffset || pLimit != null ); /* OFFSET implies LIMIT */ //if( pNew==null ){ // pNew = standin; // memset(pNew, 0, sizeof(*pNew)); //} if ( pEList == null ) { pEList = sqlite3ExprListAppend( pParse, null, sqlite3Expr( db, TK_ALL, null ) ); } pNew.pEList = pEList; pNew.pSrc = pSrc; pNew.pWhere = pWhere; pNew.pGroupBy = pGroupBy; pNew.pHaving = pHaving; pNew.pOrderBy = pOrderBy; pNew.selFlags = (u16)( isDistinct != 0 ? SF_Distinct : 0 ); pNew.op = TK_SELECT; pNew.pLimit = pLimit; pNew.pOffset = pOffset; Debug.Assert( pOffset == null || pLimit != null ); pNew.addrOpenEphm[0] = -1; pNew.addrOpenEphm[1] = -1; pNew.addrOpenEphm[2] = -1; //if ( db.mallocFailed != 0 ) //{ // clearSelect( db, pNew ); // //if ( pNew != standin ) sqlite3DbFree( db, ref pNew ); // pNew = null; //} return pNew; }
private void AuthRead(Expr Expr, Schema Schema, SrcList TabList) { //sqlite3 *db = pParse->db; //Table *pTab = 0; /* The table being read */ //const char *zCol; /* Name of the column of the table */ //int iSrc; /* Index in pTabList->a[] of table being read */ //int iDb; /* The index of the database the expression refers to */ //int iCol; /* Index of column in table */ //if( db->xAuth==0 ) return; //iDb = sqlite3SchemaToIndex(pParse->db, pSchema); //if( iDb<0 ){ // /* An attempt to read a column out of a subquery or other // ** temporary table. */ // return; //} //assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER ); //if( pExpr->op==TK_TRIGGER ){ // pTab = pParse->pTriggerTab; //}else{ // assert( pTabList ); // for(iSrc=0; ALWAYS(iSrc<pTabList->nSrc); iSrc++){ // if( pExpr->iTable==pTabList->a[iSrc].iCursor ){ // pTab = pTabList->a[iSrc].pTab; // break; // } // } //} //iCol = pExpr->iColumn; //if( NEVER(pTab==0) ) return; //if( iCol>=0 ){ // assert( iCol<pTab->nCol ); // zCol = pTab->aCol[iCol].zName; //}else if( pTab->iPKey>=0 ){ // assert( pTab->iPKey<pTab->nCol ); // zCol = pTab->aCol[pTab->iPKey].zName; //}else{ // zCol = "ROWID"; //} //assert( iDb>=0 && iDb<db->nDb ); //if( SQLITE_IGNORE==sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){ // pExpr->op = TK_NULL; //} }
/* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** in order to generate code for DELETE FROM statements. ************************************************************************* ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart ** C#-SQLite is an independent reimplementation of the SQLite software library ** ** SQLITE_SOURCE_ID: 2010-08-23 18:52:01 42537b60566f288167f1b5864a5435986838e3a3 ** ************************************************************************* */ //#include "sqliteInt.h" /* ** Look up every table that is named in pSrc. If any table is not found, ** add an error message to pParse.zErrMsg and return NULL. If all tables ** are found, return a pointer to the last table. */ static Table sqlite3SrcListLookup( Parse pParse, SrcList pSrc ) { SrcList_item pItem = pSrc.a[0]; Table pTab; Debug.Assert( pItem != null && pSrc.nSrc == 1 ); pTab = sqlite3LocateTable( pParse, 0, pItem.zName, pItem.zDatabase ); sqlite3DeleteTable( pParse.db, ref pItem.pTab ); pItem.pTab = pTab; if ( pTab != null ) { pTab.nRef++; } if ( sqlite3IndexedByLookup( pParse, pItem ) != 0 ) { pTab = null; } return pTab; }
public static Select New(Parse parse, ExprList list, SrcList src, Expr where_, ExprList groupBy, Expr having, ExprList orderBy, SF selFlags, Expr limit, Expr offset) { Context ctx = parse.Ctx; Select newSelect = new Select(); Debug.Assert(ctx.MallocFailed || offset == null || limit != null); // OFFSET implies LIMIT // Select standin; if (newSelect == null) { Debug.Assert(ctx.MallocFailed); //newSelect = standin; //_memset(newSelect, 0, sizeof(newSelect)); } if (list == null) list = Expr.ListAppend(parse, null, Expr.Expr_(ctx, TK.ALL, null)); newSelect.EList = list; if (src == null) src = new SrcList(); newSelect.Src = src; newSelect.Where = where_; newSelect.GroupBy = groupBy; newSelect.Having = having; newSelect.OrderBy = orderBy; newSelect.SelFlags = selFlags; newSelect.OP = TK.SELECT; newSelect.Limit = limit; newSelect.Offset = offset; Debug.Assert(offset == null || limit != null); newSelect.AddrOpenEphms[0] = (OP) - 1; newSelect.AddrOpenEphms[1] = (OP) - 1; newSelect.AddrOpenEphms[2] = (OP) - 1; if (ctx.MallocFailed) { ClearSelect(ctx, newSelect); //if (newSelect != standin) C._tagfree(ctx, ref newSelect); newSelect = null; } else Debug.Assert(newSelect.Src != null || parse.Errs > 0); //Debug.Assert(newSelect != standin); return newSelect; }
static int sqlite3Select( Parse pParse, /* The parser context */ Select p, /* The SELECT statement being coded. */ ref SelectDest pDest /* What to do with the query results */ ) { int i, j; /* Loop counters */ WhereInfo pWInfo; /* Return from sqlite3WhereBegin() */ Vdbe v; /* The virtual machine under construction */ bool isAgg; /* True for select lists like "count()" */ ExprList pEList = new ExprList(); /* List of columns to extract. */ SrcList pTabList = new SrcList(); /* List of tables to select from */ Expr pWhere; /* The WHERE clause. May be NULL */ ExprList pOrderBy; /* The ORDER BY clause. May be NULL */ ExprList pGroupBy; /* The GROUP BY clause. May be NULL */ Expr pHaving; /* The HAVING clause. May be NULL */ bool isDistinct; /* True if the DISTINCT keyword is present */ int distinct; /* Table to use for the distinct set */ int rc = 1; /* Value to return from this function */ int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */ AggInfo sAggInfo; /* Information used by aggregate queries */ int iEnd; /* Address of the end of the query */ sqlite3 db; /* The database connection */ #if !SQLITE_OMIT_EXPLAIN int iRestoreSelectId = pParse.iSelectId; pParse.iSelectId = pParse.iNextSelectId++; #endif db = pParse.db; if ( p == null /*|| db.mallocFailed != 0 */ || pParse.nErr != 0 ) { return 1; } #if !SQLITE_OMIT_AUTHORIZATION if (sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0)) return 1; #endif sAggInfo = new AggInfo();// memset(sAggInfo, 0, sAggInfo).Length; if ( pDest.eDest <= SRT_Discard ) //IgnorableOrderby(pDest)) { Debug.Assert( pDest.eDest == SRT_Exists || pDest.eDest == SRT_Union || pDest.eDest == SRT_Except || pDest.eDest == SRT_Discard ); /* If ORDER BY makes no difference in the output then neither does ** DISTINCT so it can be removed too. */ sqlite3ExprListDelete( db, ref p.pOrderBy ); p.pOrderBy = null; p.selFlags = (u16)( p.selFlags & ~SF_Distinct ); } sqlite3SelectPrep( pParse, p, null ); pOrderBy = p.pOrderBy; pTabList = p.pSrc; pEList = p.pEList; if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ ) { goto select_end; } isAgg = ( p.selFlags & SF_Aggregate ) != 0; Debug.Assert( pEList != null ); /* Begin generating code. */ v = sqlite3GetVdbe( pParse ); if ( v == null ) goto select_end; /* If writing to memory or generating a set ** only a single column may be output. */ #if !SQLITE_OMIT_SUBQUERY if ( checkForMultiColumnSelectError( pParse, pDest, pEList.nExpr ) ) { goto select_end; } #endif /* Generate code for all sub-queries in the FROM clause */ #if !SQLITE_OMIT_SUBQUERY || !SQLITE_OMIT_VIEW for ( i = 0; p.pPrior == null && i < pTabList.nSrc; i++ ) { SrcList_item pItem = pTabList.a[i]; SelectDest dest = new SelectDest(); Select pSub = pItem.pSelect; bool isAggSub; if ( pSub == null || pItem.isPopulated != 0 ) continue; /* Increment Parse.nHeight by the height of the largest expression ** tree refered to by this, the parent select. The child select ** may contain expression trees of at most ** (SQLITE_MAX_EXPR_DEPTH-Parse.nHeight) height. This is a bit ** more conservative than necessary, but much easier than enforcing ** an exact limit. */ pParse.nHeight += sqlite3SelectExprHeight( p ); /* Check to see if the subquery can be absorbed into the parent. */ isAggSub = ( pSub.selFlags & SF_Aggregate ) != 0; if ( flattenSubquery( pParse, p, i, isAgg, isAggSub ) != 0 ) { if ( isAggSub ) { isAgg = true; p.selFlags |= SF_Aggregate; } i = -1; } else { sqlite3SelectDestInit( dest, SRT_EphemTab, pItem.iCursor ); Debug.Assert( 0 == pItem.isPopulated ); explainSetInteger( ref pItem.iSelectId, (int)pParse.iNextSelectId ); sqlite3Select( pParse, pSub, ref dest ); pItem.isPopulated = 1; pItem.pTab.nRowEst = (uint)pSub.nSelectRow; } //if ( /* pParse.nErr != 0 || */ db.mallocFailed != 0 ) //{ // goto select_end; //} pParse.nHeight -= sqlite3SelectExprHeight( p ); pTabList = p.pSrc; if ( !( pDest.eDest <= SRT_Discard ) )// if( null==IgnorableOrderby(pDest) ) { pOrderBy = p.pOrderBy; } } pEList = p.pEList; #endif pWhere = p.pWhere; pGroupBy = p.pGroupBy; pHaving = p.pHaving; isDistinct = ( p.selFlags & SF_Distinct ) != 0; #if !SQLITE_OMIT_COMPOUND_SELECT /* If there is are a sequence of queries, do the earlier ones first. */ if ( p.pPrior != null ) { if ( p.pRightmost == null ) { Select pLoop, pRight = null; int cnt = 0; int mxSelect; for ( pLoop = p; pLoop != null; pLoop = pLoop.pPrior, cnt++ ) { pLoop.pRightmost = p; pLoop.pNext = pRight; pRight = pLoop; } mxSelect = db.aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; if ( mxSelect != 0 && cnt > mxSelect ) { sqlite3ErrorMsg( pParse, "too many terms in compound SELECT" ); goto select_end; } } rc = multiSelect( pParse, p, pDest ); explainSetInteger( ref pParse.iSelectId, iRestoreSelectId ); return rc; } #endif /* If possible, rewrite the query to use GROUP BY instead of DISTINCT. ** GROUP BY might use an index, DISTINCT never does. */ Debug.Assert( p.pGroupBy == null || ( p.selFlags & SF_Aggregate ) != 0 ); if ( ( p.selFlags & ( SF_Distinct | SF_Aggregate ) ) == SF_Distinct ) { p.pGroupBy = sqlite3ExprListDup( db, p.pEList, 0 ); pGroupBy = p.pGroupBy; p.selFlags = (u16)( p.selFlags & ~SF_Distinct ); } /* If there is both a GROUP BY and an ORDER BY clause and they are ** identical, then disable the ORDER BY clause since the GROUP BY ** will cause elements to come out in the correct order. This is ** an optimization - the correct answer should result regardless. ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER ** to disable this optimization for testing purposes. */ if ( sqlite3ExprListCompare( p.pGroupBy, pOrderBy ) == 0 && ( db.flags & SQLITE_GroupByOrder ) == 0 ) { pOrderBy = null; } /* If there is an ORDER BY clause, then this sorting ** index might end up being unused if the data can be ** extracted in pre-sorted order. If that is the case, then the ** OP_OpenEphemeral instruction will be changed to an OP_Noop once ** we figure out that the sorting index is not needed. The addrSortIndex ** variable is used to facilitate that change. */ if ( pOrderBy != null ) { KeyInfo pKeyInfo; pKeyInfo = keyInfoFromExprList( pParse, pOrderBy ); pOrderBy.iECursor = pParse.nTab++; p.addrOpenEphm[2] = addrSortIndex = sqlite3VdbeAddOp4( v, OP_OpenEphemeral, pOrderBy.iECursor, pOrderBy.nExpr + 2, 0, pKeyInfo, P4_KEYINFO_HANDOFF ); } else { addrSortIndex = -1; } /* If the output is destined for a temporary table, open that table. */ if ( pDest.eDest == SRT_EphemTab ) { sqlite3VdbeAddOp2( v, OP_OpenEphemeral, pDest.iParm, pEList.nExpr ); } /* Set the limiter. */ iEnd = sqlite3VdbeMakeLabel( v ); p.nSelectRow = (double)LARGEST_INT64; computeLimitRegisters( pParse, p, iEnd ); /* Open a virtual index to use for the distinct set. */ if ( ( p.selFlags & SF_Distinct ) != 0 ) { KeyInfo pKeyInfo; Debug.Assert( isAgg || pGroupBy != null ); distinct = pParse.nTab++; pKeyInfo = keyInfoFromExprList( pParse, p.pEList ); sqlite3VdbeAddOp4( v, OP_OpenEphemeral, distinct, 0, 0, pKeyInfo, P4_KEYINFO_HANDOFF ); sqlite3VdbeChangeP5( v, BTREE_UNORDERED ); } else { distinct = -1; } /* Aggregate and non-aggregate queries are handled differently */ if ( !isAgg && pGroupBy == null ) { /* This case is for non-aggregate queries ** Begin the database scan */ pWInfo = sqlite3WhereBegin( pParse, pTabList, pWhere, ref pOrderBy, 0 ); if ( pWInfo == null ) goto select_end; if ( pWInfo.nRowOut < p.nSelectRow ) p.nSelectRow = pWInfo.nRowOut; /* If sorting index that was created by a prior OP_OpenEphemeral ** instruction ended up not being needed, then change the OP_OpenEphemeral ** into an OP_Noop. */ if ( addrSortIndex >= 0 && pOrderBy == null ) { sqlite3VdbeChangeToNoop( v, addrSortIndex, 1 ); p.addrOpenEphm[2] = -1; } /* Use the standard inner loop */ Debug.Assert( !isDistinct ); selectInnerLoop( pParse, p, pEList, 0, 0, pOrderBy, -1, pDest, pWInfo.iContinue, pWInfo.iBreak ); /* End the database scan loop. */ sqlite3WhereEnd( pWInfo ); } else { /* This is the processing for aggregate queries */ NameContext sNC; /* Name context for processing aggregate information */ int iAMem; /* First Mem address for storing current GROUP BY */ int iBMem; /* First Mem address for previous GROUP BY */ int iUseFlag; /* Mem address holding flag indicating that at least ** one row of the input to the aggregator has been ** processed */ int iAbortFlag; /* Mem address which causes query abort if positive */ int groupBySort; /* Rows come from source in GR BY' clause thanROUP BY order */ int addrEnd; /* End of processing for this SELECT */ /* Remove any and all aliases between the result set and the ** GROUP BY clause. */ if ( pGroupBy != null ) { int k; /* Loop counter */ ExprList_item pItem; /* For looping over expression in a list */ for ( k = p.pEList.nExpr; k > 0; k-- )//, pItem++) { pItem = p.pEList.a[p.pEList.nExpr - k]; pItem.iAlias = 0; } for ( k = pGroupBy.nExpr; k > 0; k-- )//, pItem++ ) { pItem = pGroupBy.a[pGroupBy.nExpr - k]; pItem.iAlias = 0; } if ( p.nSelectRow > (double)100 ) p.nSelectRow = (double)100; } else { p.nSelectRow = (double)1; } /* Create a label to jump to when we want to abort the query */ addrEnd = sqlite3VdbeMakeLabel( v ); /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the ** SELECT statement. */ sNC = new NameContext(); // memset(sNC, 0, sNC).Length; sNC.pParse = pParse; sNC.pSrcList = pTabList; sNC.pAggInfo = sAggInfo; sAggInfo.nSortingColumn = pGroupBy != null ? pGroupBy.nExpr + 1 : 0; sAggInfo.pGroupBy = pGroupBy; sqlite3ExprAnalyzeAggList( sNC, pEList ); sqlite3ExprAnalyzeAggList( sNC, pOrderBy ); if ( pHaving != null ) { sqlite3ExprAnalyzeAggregates( sNC, ref pHaving ); } sAggInfo.nAccumulator = sAggInfo.nColumn; for ( i = 0; i < sAggInfo.nFunc; i++ ) { Debug.Assert( !ExprHasProperty( sAggInfo.aFunc[i].pExpr, EP_xIsSelect ) ); sqlite3ExprAnalyzeAggList( sNC, sAggInfo.aFunc[i].pExpr.x.pList ); } // if ( db.mallocFailed != 0 ) goto select_end; /* Processing for aggregates with GROUP BY is very different and ** much more complex than aggregates without a GROUP BY. */ if ( pGroupBy != null ) { KeyInfo pKeyInfo; /* Keying information for the group by clause */ int j1; /* A-vs-B comparision jump */ int addrOutputRow; /* Start of subroutine that outputs a result row */ int regOutputRow; /* Return address register for output subroutine */ int addrSetAbort; /* Set the abort flag and return */ int addrTopOfLoop; /* Top of the input loop */ int addrSortingIdx; /* The OP_OpenEphemeral for the sorting index */ int addrReset; /* Subroutine for resetting the accumulator */ int regReset; /* Return address register for reset subroutine */ /* If there is a GROUP BY clause we might need a sorting index to ** implement it. Allocate that sorting index now. If it turns out ** that we do not need it after all, the OpenEphemeral instruction ** will be converted into a Noop. */ sAggInfo.sortingIdx = pParse.nTab++; pKeyInfo = keyInfoFromExprList( pParse, pGroupBy ); addrSortingIdx = sqlite3VdbeAddOp4( v, OP_OpenEphemeral, sAggInfo.sortingIdx, sAggInfo.nSortingColumn, 0, pKeyInfo, P4_KEYINFO_HANDOFF ); /* Initialize memory locations used by GROUP BY aggregate processing */ iUseFlag = ++pParse.nMem; iAbortFlag = ++pParse.nMem; regOutputRow = ++pParse.nMem; addrOutputRow = sqlite3VdbeMakeLabel( v ); regReset = ++pParse.nMem; addrReset = sqlite3VdbeMakeLabel( v ); iAMem = pParse.nMem + 1; pParse.nMem += pGroupBy.nExpr; iBMem = pParse.nMem + 1; pParse.nMem += pGroupBy.nExpr; sqlite3VdbeAddOp2( v, OP_Integer, 0, iAbortFlag ); #if SQLITE_DEBUG VdbeComment( v, "clear abort flag" ); #endif sqlite3VdbeAddOp2( v, OP_Integer, 0, iUseFlag ); #if SQLITE_DEBUG VdbeComment( v, "indicate accumulator empty" ); #endif /* Begin a loop that will extract all source rows in GROUP BY order. ** This might involve two separate loops with an OP_Sort in between, or ** it might be a single loop that uses an index to extract information ** in the right order to begin with. */ sqlite3VdbeAddOp2( v, OP_Gosub, regReset, addrReset ); pWInfo = sqlite3WhereBegin( pParse, pTabList, pWhere, ref pGroupBy, 0 ); if ( pWInfo == null ) goto select_end; if ( pGroupBy == null ) { /* The optimizer is able to deliver rows in group by order so ** we do not have to sort. The OP_OpenEphemeral table will be ** cancelled later because we still need to use the pKeyInfo */ pGroupBy = p.pGroupBy; groupBySort = 0; } else { /* Rows are coming out in undetermined order. We have to push ** each row into a sorting index, terminate the first loop, ** then loop over the sorting index in order to get the output ** in sorted order */ int regBase; int regRecord; int nCol; int nGroupBy; explainTempTable( pParse, isDistinct && 0 == ( p.selFlags & SF_Distinct ) ? "DISTINCT" : "GROUP BY" ); groupBySort = 1; nGroupBy = pGroupBy.nExpr; nCol = nGroupBy + 1; j = nGroupBy + 1; for ( i = 0; i < sAggInfo.nColumn; i++ ) { if ( sAggInfo.aCol[i].iSorterColumn >= j ) { nCol++; j++; } } regBase = sqlite3GetTempRange( pParse, nCol ); sqlite3ExprCacheClear( pParse ); sqlite3ExprCodeExprList( pParse, pGroupBy, regBase, false ); sqlite3VdbeAddOp2( v, OP_Sequence, sAggInfo.sortingIdx, regBase + nGroupBy ); j = nGroupBy + 1; for ( i = 0; i < sAggInfo.nColumn; i++ ) { AggInfo_col pCol = sAggInfo.aCol[i]; if ( pCol.iSorterColumn >= j ) { int r1 = j + regBase; int r2; r2 = sqlite3ExprCodeGetColumn( pParse, pCol.pTab, pCol.iColumn, pCol.iTable, r1 ); if ( r1 != r2 ) { sqlite3VdbeAddOp2( v, OP_SCopy, r2, r1 ); } j++; } } regRecord = sqlite3GetTempReg( pParse ); sqlite3VdbeAddOp3( v, OP_MakeRecord, regBase, nCol, regRecord ); sqlite3VdbeAddOp2( v, OP_IdxInsert, sAggInfo.sortingIdx, regRecord ); sqlite3ReleaseTempReg( pParse, regRecord ); sqlite3ReleaseTempRange( pParse, regBase, nCol ); sqlite3WhereEnd( pWInfo ); sqlite3VdbeAddOp2( v, OP_Sort, sAggInfo.sortingIdx, addrEnd ); #if SQLITE_DEBUG VdbeComment( v, "GROUP BY sort" ); #endif sAggInfo.useSortingIdx = 1; sqlite3ExprCacheClear( pParse ); } /* Evaluate the current GROUP BY terms and store in b0, b1, b2... ** (b0 is memory location iBMem+0, b1 is iBMem+1, and so forth) ** Then compare the current GROUP BY terms against the GROUP BY terms ** from the previous row currently stored in a0, a1, a2... */ addrTopOfLoop = sqlite3VdbeCurrentAddr( v ); sqlite3ExprCacheClear( pParse ); for ( j = 0; j < pGroupBy.nExpr; j++ ) { if ( groupBySort != 0 ) { sqlite3VdbeAddOp3( v, OP_Column, sAggInfo.sortingIdx, j, iBMem + j ); } else { sAggInfo.directMode = 1; sqlite3ExprCode( pParse, pGroupBy.a[j].pExpr, iBMem + j ); } } sqlite3VdbeAddOp4( v, OP_Compare, iAMem, iBMem, pGroupBy.nExpr, pKeyInfo, P4_KEYINFO ); j1 = sqlite3VdbeCurrentAddr( v ); sqlite3VdbeAddOp3( v, OP_Jump, j1 + 1, 0, j1 + 1 ); /* Generate code that runs whenever the GROUP BY changes. ** Changes in the GROUP BY are detected by the previous code ** block. If there were no changes, this block is skipped. ** ** This code copies current group by terms in b0,b1,b2,... ** over to a0,a1,a2. It then calls the output subroutine ** and resets the aggregate accumulator registers in preparation ** for the next GROUP BY batch. */ sqlite3ExprCodeMove( pParse, iBMem, iAMem, pGroupBy.nExpr ); sqlite3VdbeAddOp2( v, OP_Gosub, regOutputRow, addrOutputRow ); #if SQLITE_DEBUG VdbeComment( v, "output one row" ); #endif sqlite3VdbeAddOp2( v, OP_IfPos, iAbortFlag, addrEnd ); #if SQLITE_DEBUG VdbeComment( v, "check abort flag" ); #endif sqlite3VdbeAddOp2( v, OP_Gosub, regReset, addrReset ); #if SQLITE_DEBUG VdbeComment( v, "reset accumulator" ); #endif /* Update the aggregate accumulators based on the content of ** the current row */ sqlite3VdbeJumpHere( v, j1 ); updateAccumulator( pParse, sAggInfo ); sqlite3VdbeAddOp2( v, OP_Integer, 1, iUseFlag ); #if SQLITE_DEBUG VdbeComment( v, "indicate data in accumulator" ); #endif /* End of the loop */ if ( groupBySort != 0 ) { sqlite3VdbeAddOp2( v, OP_Next, sAggInfo.sortingIdx, addrTopOfLoop ); } else { sqlite3WhereEnd( pWInfo ); sqlite3VdbeChangeToNoop( v, addrSortingIdx, 1 ); } /* Output the final row of result */ sqlite3VdbeAddOp2( v, OP_Gosub, regOutputRow, addrOutputRow ); #if SQLITE_DEBUG VdbeComment( v, "output final row" ); #endif /* Jump over the subroutines */ sqlite3VdbeAddOp2( v, OP_Goto, 0, addrEnd ); /* Generate a subroutine that outputs a single row of the result ** set. This subroutine first looks at the iUseFlag. If iUseFlag ** is less than or equal to zero, the subroutine is a no-op. If ** the processing calls for the query to abort, this subroutine ** increments the iAbortFlag memory location before returning in ** order to signal the caller to abort. */ addrSetAbort = sqlite3VdbeCurrentAddr( v ); sqlite3VdbeAddOp2( v, OP_Integer, 1, iAbortFlag ); VdbeComment( v, "set abort flag" ); sqlite3VdbeAddOp1( v, OP_Return, regOutputRow ); sqlite3VdbeResolveLabel( v, addrOutputRow ); addrOutputRow = sqlite3VdbeCurrentAddr( v ); sqlite3VdbeAddOp2( v, OP_IfPos, iUseFlag, addrOutputRow + 2 ); VdbeComment( v, "Groupby result generator entry point" ); sqlite3VdbeAddOp1( v, OP_Return, regOutputRow ); finalizeAggFunctions( pParse, sAggInfo ); sqlite3ExprIfFalse( pParse, pHaving, addrOutputRow + 1, SQLITE_JUMPIFNULL ); selectInnerLoop( pParse, p, p.pEList, 0, 0, pOrderBy, distinct, pDest, addrOutputRow + 1, addrSetAbort ); sqlite3VdbeAddOp1( v, OP_Return, regOutputRow ); VdbeComment( v, "end groupby result generator" ); /* Generate a subroutine that will reset the group-by accumulator */ sqlite3VdbeResolveLabel( v, addrReset ); resetAccumulator( pParse, sAggInfo ); sqlite3VdbeAddOp1( v, OP_Return, regReset ); } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ else { ExprList pDel = null; #if !SQLITE_OMIT_BTREECOUNT Table pTab; if ( ( pTab = isSimpleCount( p, sAggInfo ) ) != null ) { /* If isSimpleCount() returns a pointer to a Table structure, then ** the SQL statement is of the form: ** ** SELECT count() FROM <tbl> ** ** where the Table structure returned represents table <tbl>. ** ** This statement is so common that it is optimized specially. The ** OP_Count instruction is executed either on the intkey table that ** contains the data for table <tbl> or on one of its indexes. It ** is better to execute the op on an index, as indexes are almost ** always spread across less pages than their corresponding tables. */ int iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema ); int iCsr = pParse.nTab++; /* Cursor to scan b-tree */ Index pIdx; /* Iterator variable */ KeyInfo pKeyInfo = null; /* Keyinfo for scanned index */ Index pBest = null; /* Best index found so far */ int iRoot = pTab.tnum; /* Root page of scanned b-tree */ sqlite3CodeVerifySchema( pParse, iDb ); sqlite3TableLock( pParse, iDb, pTab.tnum, 0, pTab.zName ); /* Search for the index that has the least amount of columns. If ** there is such an index, and it has less columns than the table ** does, then we can assume that it consumes less space on disk and ** will therefore be cheaper to scan to determine the query result. ** In this case set iRoot to the root page number of the index b-tree ** and pKeyInfo to the KeyInfo structure required to navigate the ** index. ** ** (2011-04-15) Do not do a full scan of an unordered index. ** ** In practice the KeyInfo structure will not be used. It is only ** passed to keep OP_OpenRead happy. */ for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext ) { if ( pIdx.bUnordered == 0 && ( null == pBest || pIdx.nColumn < pBest.nColumn ) ) { pBest = pIdx; } } if ( pBest != null && pBest.nColumn < pTab.nCol ) { iRoot = pBest.tnum; pKeyInfo = sqlite3IndexKeyinfo( pParse, pBest ); } /* Open a read-only cursor, execute the OP_Count, close the cursor. */ sqlite3VdbeAddOp3( v, OP_OpenRead, iCsr, iRoot, iDb ); if ( pKeyInfo != null ) { sqlite3VdbeChangeP4( v, -1, pKeyInfo, P4_KEYINFO_HANDOFF ); } sqlite3VdbeAddOp2( v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem ); sqlite3VdbeAddOp1( v, OP_Close, iCsr ); explainSimpleCount( pParse, pTab, pBest ); } else #endif //* SQLITE_OMIT_BTREECOUNT */ { /* Check if the query is of one of the following forms: ** ** SELECT min(x) FROM ... ** SELECT max(x) FROM ... ** ** If it is, then ask the code in where.c to attempt to sort results ** as if there was an "ORDER ON x" or "ORDER ON x DESC" clause. ** If where.c is able to produce results sorted in this order, then ** add vdbe code to break out of the processing loop after the ** first iteration (since the first iteration of the loop is ** guaranteed to operate on the row with the minimum or maximum ** value of x, the only row required). ** ** A special flag must be passed to sqlite3WhereBegin() to slightly ** modify behavior as follows: ** ** + If the query is a "SELECT min(x)", then the loop coded by ** where.c should not iterate over any values with a NULL value ** for x. ** ** + The optimizer code in where.c (the thing that decides which ** index or indices to use) should place a different priority on ** satisfying the 'ORDER BY' clause than it does in other cases. ** Refer to code and comments in where.c for details. */ ExprList pMinMax = null; int flag = minMaxQuery( p ); if ( flag != 0 ) { Debug.Assert( !ExprHasProperty( p.pEList.a[0].pExpr, EP_xIsSelect ) ); pMinMax = sqlite3ExprListDup( db, p.pEList.a[0].pExpr.x.pList, 0 ); pDel = pMinMax; if ( pMinMax != null )///* && 0 == db.mallocFailed */ ) { pMinMax.a[0].sortOrder = (u8)( flag != WHERE_ORDERBY_MIN ? 1 : 0 ); pMinMax.a[0].pExpr.op = TK_COLUMN; } } /* This case runs if the aggregate has no GROUP BY clause. The ** processing is much simpler since there is only a single row ** of output. */ resetAccumulator( pParse, sAggInfo ); pWInfo = sqlite3WhereBegin( pParse, pTabList, pWhere, ref pMinMax, (byte)flag ); if ( pWInfo == null ) { sqlite3ExprListDelete( db, ref pDel ); goto select_end; } updateAccumulator( pParse, sAggInfo ); if ( pMinMax == null && flag != 0 ) { sqlite3VdbeAddOp2( v, OP_Goto, 0, pWInfo.iBreak ); #if SQLITE_DEBUG VdbeComment( v, "%s() by index", ( flag == WHERE_ORDERBY_MIN ? "min" : "max" ) ); #endif } sqlite3WhereEnd( pWInfo ); finalizeAggFunctions( pParse, sAggInfo ); } pOrderBy = null; sqlite3ExprIfFalse( pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL ); selectInnerLoop( pParse, p, p.pEList, 0, 0, null, -1, pDest, addrEnd, addrEnd ); sqlite3ExprListDelete( db, ref pDel ); } sqlite3VdbeResolveLabel( v, addrEnd ); } /* endif aggregate query */ if ( distinct >= 0 ) { explainTempTable( pParse, "DISTINCT" ); } /* If there is an ORDER BY clause, then we need to sort the results ** and send them to the callback one by one. */ if ( pOrderBy != null ) { explainTempTable( pParse, "ORDER BY" ); generateSortTail( pParse, p, v, pEList.nExpr, pDest ); } /* Jump here to skip this query */ sqlite3VdbeResolveLabel( v, iEnd ); /* The SELECT was successfully coded. Set the return code to 0 ** to indicate no errors. */ rc = 0; /* Control jumps to here if an error is encountered above, or upon ** successful coding of the SELECT. */ select_end: explainSetInteger( ref pParse.iSelectId, iRestoreSelectId ); /* Identify column names if results of the SELECT are to be output. */ if ( rc == SQLITE_OK && pDest.eDest == SRT_Output ) { generateColumnNames( pParse, pTabList, pEList ); } sqlite3DbFree( db, ref sAggInfo.aCol ); sqlite3DbFree( db, ref sAggInfo.aFunc ); return rc; }
/* Make sure "isView" and other macros defined above are undefined. Otherwise ** thely may interfere with compilation of other functions in this file ** (or in another file, if this file becomes part of the amalgamation). */ //#ifdef isView // #undef isView //#endif //#ifdef pTrigger // #undef pTrigger //#endif #if !SQLITE_OMIT_VIRTUALTABLE /* ** Generate code for an UPDATE of a virtual table. ** ** The strategy is that we create an ephemerial table that contains ** for each row to be changed: ** ** (A) The original rowid of that row. ** (B) The revised rowid for the row. (note1) ** (C) The content of every column in the row. ** ** Then we loop over this ephemeral table and for each row in ** the ephermeral table call VUpdate. ** ** When finished, drop the ephemeral table. ** ** (note1) Actually, if we know in advance that (A) is always the same ** as (B) we only store (A), then duplicate (A) when pulling ** it out of the ephemeral table before calling VUpdate. */ static void updateVirtualTable( Parse pParse, /* The parsing context */ SrcList pSrc, /* The virtual table to be modified */ Table pTab, /* The virtual table */ ExprList pChanges, /* The columns to change in the UPDATE statement */ Expr pRowid, /* Expression used to recompute the rowid */ int aXRef, /* Mapping from columns of pTab to entries in pChanges */ Expr pWhere /* WHERE clause of the UPDATE statement */ ) { Vdbe v = pParse.pVdbe; /* Virtual machine under construction */ ExprList pEList = 0; /* The result set of the SELECT statement */ Select pSelect = 0; /* The SELECT statement */ Expr pExpr; /* Temporary expression */ int ephemTab; /* Table holding the result of the SELECT */ int i; /* Loop counter */ int addr; /* Address of top of loop */ int iReg; /* First register in set passed to OP_VUpdate */ sqlite3 db = pParse.db; /* Database connection */ const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); SelectDest dest; /* Construct the SELECT statement that will find the new values for ** all updated rows. */ pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, "_rowid_")); if (pRowid) { pEList = sqlite3ExprListAppend(pParse, pEList, sqlite3ExprDup(db, pRowid, 0), 0); } Debug.Assert(pTab.iPKey < 0); for (i = 0; i < pTab.nCol; i++) { if (aXRef[i] >= 0) { pExpr = sqlite3ExprDup(db, pChanges.a[aXRef[i]].pExpr, 0); } else { pExpr = sqlite3Expr(db, TK_ID, pTab.aCol[i].zName); } pEList = sqlite3ExprListAppend(pParse, pEList, pExpr); } pSelect = sqlite3SelectNew(pParse, pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0); /* Create the ephemeral table into which the update results will ** be stored. */ Debug.Assert(v); ephemTab = pParse.nTab++; sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab.nCol + 1 + (pRowid != 0)); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); /* fill the ephemeral table */ sqlite3SelectDestInit(dest, SRT_Table, ephemTab); sqlite3Select(pParse, pSelect, ref dest); /* Generate code to scan the ephemeral table and call VUpdate. */ iReg = ++pParse.nMem; pParse.nMem += pTab.nCol + 1; addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg); sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid 1 : 0), iReg + 1); for (i = 0; i < pTab.nCol; i++) { sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i + 1 + (pRowid != 0), iReg + 2 + i); } sqlite3VtabMakeWritable(pParse, pTab); sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab.nCol + 2, iReg, pVTab, P4_VTAB); sqlite3MayAbort(pParse); sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr + 1); sqlite3VdbeJumpHere(v, addr); sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0); /* Cleanup */ sqlite3SelectDelete(pSelect); }
public static Select New(Parse parse, int dummy1, SrcList src, int dummy2, int dummy3, int dummy4, int dummy5, SF selFlags, int dummy6, int dummy7) { return New(parse, null, src, null, null, null, null, selFlags, null, null); }
/* ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" ** command. */ static void sqlite3AlterRenameTable( Parse pParse, /* Parser context. */ SrcList pSrc, /* The table to rename. */ Token pName /* The new table name. */ ) { int iDb; /* Database that contains the table */ string zDb; /* Name of database iDb */ Table pTab; /* Table being renamed */ string zName = null; /* NULL-terminated version of pName */ sqlite3 db = pParse.db; /* Database connection */ int nTabName; /* Number of UTF-8 characters in zTabName */ string zTabName; /* Original name of the table */ Vdbe v; #if !SQLITE_OMIT_TRIGGER string zWhere = ""; /* Where clause to locate temp triggers */ #endif VTable pVTab = null; /* Non-zero if this is a v-tab with an xRename() */ int savedDbFlags; /* Saved value of db->flags */ savedDbFlags = db.flags; //if ( NEVER( db.mallocFailed != 0 ) ) goto exit_rename_table; Debug.Assert(pSrc.nSrc == 1); Debug.Assert(sqlite3BtreeHoldsAllMutexes(pParse.db)); pTab = sqlite3LocateTable(pParse, 0, pSrc.a[0].zName, pSrc.a[0].zDatabase); if (pTab == null) { goto exit_rename_table; } iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema); zDb = db.aDb[iDb].zName; db.flags |= SQLITE_PreferBuiltin; /* Get a NULL terminated version of the new table name. */ zName = sqlite3NameFromToken(db, pName); if (zName == null) { goto exit_rename_table; } /* Check that a table or index named 'zName' does not already exist ** in database iDb. If so, this is an error. */ if (sqlite3FindTable(db, zName, zDb) != null || sqlite3FindIndex(db, zName, zDb) != null) { sqlite3ErrorMsg(pParse, "there is already another table or index with this name: %s", zName); goto exit_rename_table; } /* Make sure it is not a system table being altered, or a reserved name ** that the table is being renamed to. */ if (SQLITE_OK != isSystemTable(pParse, pTab.zName)) { goto exit_rename_table; } if (SQLITE_OK != sqlite3CheckObjectName(pParse, zName)) { goto exit_rename_table; } #if !SQLITE_OMIT_VIEW if (pTab.pSelect != null) { sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab.zName); goto exit_rename_table; } #endif #if !SQLITE_OMIT_AUTHORIZATION /* Invoke the authorization callback. */ if (sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab.zName, 0)) { goto exit_rename_table; } #endif if (sqlite3ViewGetColumnNames(pParse, pTab) != 0) { goto exit_rename_table; } #if !SQLITE_OMIT_VIRTUALTABLE if (IsVirtual(pTab)) { pVTab = sqlite3GetVTable(db, pTab); if (pVTab.pVtab.pModule.xRename == null) { pVTab = null; } } #endif /* Begin a transaction and code the VerifyCookie for database iDb. ** Then modify the schema cookie (since the ALTER TABLE modifies the ** schema). Open a statement transaction if the table is a virtual ** table. */ v = sqlite3GetVdbe(pParse); if (v == null) { goto exit_rename_table; } sqlite3BeginWriteOperation(pParse, pVTab != null ? 1 : 0, iDb); sqlite3ChangeCookie(pParse, iDb); /* If this is a virtual table, invoke the xRename() function if ** one is defined. The xRename() callback will modify the names ** of any resources used by the v-table implementation (including other ** SQLite tables) that are identified by the name of the virtual table. */ #if !SQLITE_OMIT_VIRTUALTABLE if (pVTab != null) { int i = ++pParse.nMem; sqlite3VdbeAddOp4(v, OP_String8, 0, i, 0, zName, 0); sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0, pVTab, P4_VTAB); sqlite3MayAbort(pParse); } #endif /* figure out how many UTF-8 characters are in zName */ zTabName = pTab.zName; nTabName = sqlite3Utf8CharLen(zTabName, -1); #if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER) if ((db.flags & SQLITE_ForeignKeys) != 0) { /* If foreign-key support is enabled, rewrite the CREATE TABLE ** statements corresponding to all child tables of foreign key constraints ** for which the renamed table is the parent table. */ if ((zWhere = whereForeignKeys(pParse, pTab)) != null) { sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " + "sql = sqlite_rename_parent(sql, %Q, %Q) " + "WHERE %s;", zDb, SCHEMA_TABLE(iDb), zTabName, zName, zWhere); sqlite3DbFree(db, ref zWhere); } } #endif /* Modify the sqlite_master table to use the new table name. */ sqlite3NestedParse(pParse, "UPDATE %Q.%s SET " + #if SQLITE_OMIT_TRIGGER "sql = sqlite_rename_table(sql, %Q), " + #else "sql = CASE " + "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" + "ELSE sqlite_rename_table(sql, %Q) END, " + #endif "tbl_name = %Q, " + "name = CASE " + "WHEN type='table' THEN %Q " + "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " + "'sqlite_autoindex_' || %Q || substr(name,%d+18) " + "ELSE name END " + "WHERE tbl_name=%Q AND " + "(type='table' OR type='index' OR type='trigger');", zDb, SCHEMA_TABLE(iDb), zName, zName, zName, #if !SQLITE_OMIT_TRIGGER zName, #endif zName, nTabName, zTabName ); #if !SQLITE_OMIT_AUTOINCREMENT /* If the sqlite_sequence table exists in this database, then update ** it with the new table name. */ if (sqlite3FindTable(db, "sqlite_sequence", zDb) != null) { sqlite3NestedParse(pParse, "UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q", zDb, zName, pTab.zName ); } #endif #if !SQLITE_OMIT_TRIGGER /* If there are TEMP triggers on this table, modify the sqlite_temp_master ** table. Don't do this if the table being ALTERed is itself located in ** the temp database. */ if ((zWhere = whereTempTriggers(pParse, pTab)) != "") { sqlite3NestedParse(pParse, "UPDATE sqlite_temp_master SET " + "sql = sqlite_rename_trigger(sql, %Q), " + "tbl_name = %Q " + "WHERE %s;", zName, zName, zWhere); sqlite3DbFree(db, ref zWhere); } #endif #if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER) if ((db.flags & SQLITE_ForeignKeys) != 0) { FKey p; for (p = sqlite3FkReferences(pTab); p != null; p = p.pNextTo) { Table pFrom = p.pFrom; if (pFrom != pTab) { reloadTableSchema(pParse, p.pFrom, pFrom.zName); } } } #endif /* Drop and reload the internal table schema. */ reloadTableSchema(pParse, pTab, zName); exit_rename_table: sqlite3SrcListDelete(db, ref pSrc); sqlite3DbFree(db, ref zName); db.flags = savedDbFlags; }
/* ** Generate an expression tree to implement the WHERE, ORDER BY, ** and LIMIT/OFFSET portion of DELETE and UPDATE statements. ** ** DELETE FROM table_wxyz WHERE a<5 ORDER BY a LIMIT 1; ** \__________________________/ ** pLimitWhere (pInClause) */ Expr sqlite3LimitWhere( Parse pParse, /* The parser context */ SrcList pSrc, /* the FROM clause -- which tables to scan */ Expr pWhere, /* The WHERE clause. May be null */ ExprList pOrderBy, /* The ORDER BY clause. May be null */ Expr pLimit, /* The LIMIT clause. May be null */ Expr pOffset, /* The OFFSET clause. May be null */ char zStmtType /* Either DELETE or UPDATE. For error messages. */ ) { Expr pWhereRowid = null; /* WHERE rowid .. */ Expr pInClause = null; /* WHERE rowid IN ( select ) */ Expr pSelectRowid = null; /* SELECT rowid ... */ ExprList pEList = null; /* Expression list contaning only pSelectRowid */ SrcList pSelectSrc = null; /* SELECT rowid FROM x ... (dup of pSrc) */ Select pSelect = null; /* Complete SELECT tree */ /* Check that there isn't an ORDER BY without a LIMIT clause. */ if( pOrderBy!=null && (pLimit == null) ) { sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType); pParse.parseError = 1; goto limit_where_cleanup_2; } /* We only need to generate a select expression if there ** is a limit/offset term to enforce. */ if ( pLimit == null ) { /* if pLimit is null, pOffset will always be null as well. */ Debug.Assert( pOffset == null ); return pWhere; } /* Generate a select expression tree to enforce the limit/offset ** term for the DELETE or UPDATE statement. For example: ** DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 ** becomes: ** DELETE FROM table_a WHERE rowid IN ( ** SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 ** ); */ pSelectRowid = sqlite3PExpr( pParse, TK_ROW, null, null, null ); if( pSelectRowid == null ) goto limit_where_cleanup_2; pEList = sqlite3ExprListAppend( pParse, null, pSelectRowid); if( pEList == null ) goto limit_where_cleanup_2; /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree ** and the SELECT subtree. */ pSelectSrc = sqlite3SrcListDup(pParse.db, pSrc,0); if( pSelectSrc == null ) { sqlite3ExprListDelete(pParse.db, pEList); goto limit_where_cleanup_2; } /* generate the SELECT expression tree. */ pSelect = sqlite3SelectNew( pParse, pEList, pSelectSrc, pWhere, null, null, pOrderBy, 0, pLimit, pOffset ); if( pSelect == null ) return null; /* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */ pWhereRowid = sqlite3PExpr( pParse, TK_ROW, null, null, null ); if( pWhereRowid == null ) goto limit_where_cleanup_1; pInClause = sqlite3PExpr( pParse, TK_IN, pWhereRowid, null, null ); if( pInClause == null ) goto limit_where_cleanup_1; pInClause->x.pSelect = pSelect; pInClause->flags |= EP_xIsSelect; sqlite3ExprSetHeight(pParse, pInClause); return pInClause; /* something went wrong. clean up anything allocated. */ limit_where_cleanup_1: sqlite3SelectDelete(pParse.db, pSelect); return null; limit_where_cleanup_2: sqlite3ExprDelete(pParse.db, ref pWhere); sqlite3ExprListDelete(pParse.db, pOrderBy); sqlite3ExprDelete(pParse.db, ref pLimit); sqlite3ExprDelete(pParse.db, ref pOffset); return null; }
static bool TableAndColumnIndex(SrcList src, int n, string colName, ref int tableOut, ref int colIdOut) { for (int i = 0; i < n; i++) { int colId = ColumnIndex(src.Ids[i].Table, colName); // Index of column matching zCol if (colId >= 0) { tableOut = i; colIdOut = colId; return true; } } return false; }
public Expr LimitWhere(Parse parse, SrcList src, Expr where_, ExprList orderBy, Expr limit, Expr offset, char stmtType) { // Check that there isn't an ORDER BY without a LIMIT clause. if (orderBy != null && (limit == null)) { parse.ErrorMsg("ORDER BY without LIMIT on %s", stmtType); goto limit_where_cleanup_2; } // We only need to generate a select expression if there is a limit/offset term to enforce. if (limit == null) { Debug.Assert(offset == null); // if pLimit is null, pOffset will always be null as well. return where_; } // Generate a select expression tree to enforce the limit/offset term for the DELETE or UPDATE statement. For example: // DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 // becomes: // DELETE FROM table_a WHERE rowid IN ( // SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 // ); Expr selectRowid = Expr.PExpr_(parse, TK.ROW, null, null, null); // SELECT rowid ... if (selectRowid == null) goto limit_where_cleanup_2; ExprList elist = ExprList.Append(parse, null, selectRowid); // Expression list contaning only pSelectRowid if (elist == null) goto limit_where_cleanup_2; // duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree and the SELECT subtree. SrcList selectSrc = SrcList.Dup(parse.Ctx, src, 0); // SELECT rowid FROM x ... (dup of pSrc) if (selectSrc == null) { ExprList.Delete(parse.Ctx, elist); goto limit_where_cleanup_2; } // generate the SELECT expression tree. Select select = Select.New(parse, elist, selectSrc, where_, null, null, orderBy, 0, limit, offset); // Complete SELECT tree if (select == null) return null; // now generate the new WHERE rowid IN clause for the DELETE/UDPATE Expr whereRowid = Expr.PExpr_(parse, TK.ROW, null, null, null); // WHERE rowid .. if (whereRowid == null) goto limit_where_cleanup_1; Expr inClause = Expr.PExpr_(parse, TK.IN, whereRowid, null, null); // WHERE rowid IN ( select ) if (inClause == null) goto limit_where_cleanup_1; inClause.x.Select = select; inClause.Flags |= EP.xIsSelect; Expr.SetHeight(parse, inClause); return inClause; // something went wrong. clean up anything allocated. limit_where_cleanup_1: Select.Delete(parse.Ctx, select); return null; limit_where_cleanup_2: Expr.Delete(parse.Ctx, ref where_); ExprList.Delete(parse.Ctx, orderBy); Expr.Delete(parse.Ctx, ref limit); Expr.Delete(parse.Ctx, ref offset); return null; }
/* ** This is called by the parser when it sees a CREATE TRIGGER statement ** up to the point of the BEGIN before the trigger actions. A Trigger ** structure is generated based on the information available and stored ** in pParse.pNewTrigger. After the trigger actions have been parsed, the ** sqlite3FinishTrigger() function is called to complete the trigger ** construction process. */ static void sqlite3BeginTrigger( Parse pParse, /* The parse context of the CREATE TRIGGER statement */ Token pName1, /* The name of the trigger */ Token pName2, /* The name of the trigger */ int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */ int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */ IdList pColumns, /* column list if this is an UPDATE OF trigger */ SrcList pTableName, /* The name of the table/view the trigger applies to */ Expr pWhen, /* WHEN clause */ int isTemp, /* True if the TEMPORARY keyword is present */ int noErr /* Suppress errors if the trigger already exists */ ) { Trigger pTrigger = null; /* The new trigger */ Table pTab; /* Table that the trigger fires off of */ string zName = null; /* Name of the trigger */ sqlite3 db = pParse.db; /* The database connection */ int iDb; /* The database to store the trigger in */ Token pName = null; /* The unqualified db name */ DbFixer sFix = new DbFixer(); /* State vector for the DB fixer */ int iTabDb; /* Index of the database holding pTab */ Debug.Assert(pName1 != null); /* pName1.z might be NULL, but not pName1 itself */ Debug.Assert(pName2 != null); Debug.Assert(op == TK_INSERT || op == TK_UPDATE || op == TK_DELETE); Debug.Assert(op > 0 && op < 0xff); if (isTemp != 0) { /* If TEMP was specified, then the trigger name may not be qualified. */ if (pName2.n > 0) { sqlite3ErrorMsg(pParse, "temporary trigger may not have qualified name"); goto trigger_cleanup; } iDb = 1; pName = pName1; } else { /* Figure out the db that the the trigger will be created in */ iDb = sqlite3TwoPartName(pParse, pName1, pName2, ref pName); if (iDb < 0) { goto trigger_cleanup; } } /* If the trigger name was unqualified, and the table is a temp table, ** then set iDb to 1 to create the trigger in the temporary database. ** If sqlite3SrcListLookup() returns 0, indicating the table does not ** exist, the error is caught by the block below. */ if (pTableName == null /*|| db.mallocFailed != 0 */) { goto trigger_cleanup; } pTab = sqlite3SrcListLookup(pParse, pTableName); if (pName2.n == 0 && pTab != null && pTab.pSchema == db.aDb[1].pSchema) { iDb = 1; } /* Ensure the table name matches database name and that the table exists */ // if ( db.mallocFailed != 0 ) goto trigger_cleanup; Debug.Assert(pTableName.nSrc == 1); if (sqlite3FixInit(sFix, pParse, iDb, "trigger", pName) != 0 && sqlite3FixSrcList(sFix, pTableName) != 0) { goto trigger_cleanup; } pTab = sqlite3SrcListLookup(pParse, pTableName); if (pTab == null) { /* The table does not exist. */ if (db.init.iDb == 1) { /* Ticket #3810. ** Normally, whenever a table is dropped, all associated triggers are ** dropped too. But if a TEMP trigger is created on a non-TEMP table ** and the table is dropped by a different database connection, the ** trigger is not visible to the database connection that does the ** drop so the trigger cannot be dropped. This results in an ** "orphaned trigger" - a trigger whose associated table is missing. */ db.init.orphanTrigger = 1; } goto trigger_cleanup; } if (IsVirtual(pTab)) { sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); goto trigger_cleanup; } /* Check that the trigger name is not reserved and that no trigger of the ** specified name exists */ zName = sqlite3NameFromToken(db, pName); if (zName == null || SQLITE_OK != sqlite3CheckObjectName(pParse, zName)) { goto trigger_cleanup; } if (sqlite3HashFind((db.aDb[iDb].pSchema.trigHash), zName, sqlite3Strlen30(zName)) != null) { if (noErr == 0) { sqlite3ErrorMsg(pParse, "trigger %T already exists", pName); } goto trigger_cleanup; } /* Do not create a trigger on a system table */ if (sqlite3StrNICmp(pTab.zName, "sqlite_", 7) == 0) { sqlite3ErrorMsg(pParse, "cannot create trigger on system table"); pParse.nErr++; goto trigger_cleanup; } /* INSTEAD of triggers are only for views and views only support INSTEAD ** of triggers. */ if (pTab.pSelect != null && tr_tm != TK_INSTEAD) { sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", (tr_tm == TK_BEFORE) ? "BEFORE" : "AFTER", pTableName, 0); goto trigger_cleanup; } if (pTab.pSelect == null && tr_tm == TK_INSTEAD) { sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" + " trigger on table: %S", pTableName, 0); goto trigger_cleanup; } iTabDb = sqlite3SchemaToIndex(db, pTab.pSchema); #if !SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_CREATE_TRIGGER; string zDb = db.aDb[iTabDb].zName; string zDbTrig = isTemp ? db.aDb[1].zName : zDb; if (iTabDb == 1 || isTemp) { code = SQLITE_CREATE_TEMP_TRIGGER; } if (sqlite3AuthCheck(pParse, code, zName, pTab.zName, zDbTrig)) { goto trigger_cleanup; } if (sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iTabDb), 0, zDb)) { goto trigger_cleanup; } } #endif /* INSTEAD OF triggers can only appear on views and BEFORE triggers ** cannot appear on views. So we might as well translate every ** INSTEAD OF trigger into a BEFORE trigger. It simplifies code ** elsewhere. */ if (tr_tm == TK_INSTEAD) { tr_tm = TK_BEFORE; } /* Build the Trigger object */ pTrigger = new Trigger();// (Trigger*)sqlite3DbMallocZero( db, sizeof(Trigger )) if (pTrigger == null) { goto trigger_cleanup; } pTrigger.name = zName; pTrigger.table = pTableName.a[0].zName;// sqlite3DbStrDup( db, pTableName.a[0].zName ); pTrigger.pSchema = db.aDb[iDb].pSchema; pTrigger.pTabSchema = pTab.pSchema; pTrigger.op = (u8)op; pTrigger.tr_tm = tr_tm == TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; pTrigger.pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); pTrigger.pColumns = sqlite3IdListDup(db, pColumns); Debug.Assert(pParse.pNewTrigger == null); pParse.pNewTrigger = pTrigger; trigger_cleanup: //sqlite3DbFree( db, ref zName ); sqlite3SrcListDelete(db, ref pTableName); sqlite3IdListDelete(db, ref pColumns); sqlite3ExprDelete(db, ref pWhen); if (pParse.pNewTrigger == null) { sqlite3DeleteTrigger(db, ref pTrigger); } else { Debug.Assert(pParse.pNewTrigger == pTrigger); } }
/* ** Generate code for a DELETE FROM statement. ** ** DELETE FROM table_wxyz WHERE a<5 AND b NOT NULL; ** \________/ \________________/ ** pTabList pWhere */ static 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 */ string 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 VdbeCursor number for pTab */ sqlite3 db; /* Main database structure */ AuthContext sContext; /* Authorization context */ NameContext sNC; /* Name context to resolve expressions in */ int iDb; /* Database number */ int memCnt = -1; /* Memory cell used for change counting */ int rcauth; /* Value returned by authorization callback */ #if !SQLITE_OMIT_TRIGGER bool isView; /* True if attempting to delete from a view */ Trigger pTrigger; /* List of table triggers, if required */ #endif sContext = new AuthContext(); //memset(&sContext, 0, sizeof(sContext)); db = pParse.db; if (pParse.nErr != 0 /*|| db.mallocFailed != 0 */) { goto delete_from_cleanup; } Debug.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 == null) { goto delete_from_cleanup; } /* Figure out if we have any triggers and if the table being ** deleted from is a view */ #if !SQLITE_OMIT_TRIGGER int iDummy = 0; pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, null, ref iDummy); isView = pTab.pSelect != null; #else const Trigger pTrigger = null; bool isView = false; #endif #if SQLITE_OMIT_VIEW //# undef isView isView = false; #endif /* If pTab is really a view, make sure it has been initialized. */ if (sqlite3ViewGetColumnNames(pParse, pTab) != 0) { goto delete_from_cleanup; } if (sqlite3IsReadOnly(pParse, pTab, (pTrigger != null ? 1 : 0))) { goto delete_from_cleanup; } iDb = sqlite3SchemaToIndex(db, pTab.pSchema); Debug.Assert(iDb < db.nDb); zDb = db.aDb[iDb].zName; #if !SQLITE_OMIT_AUTHORIZATION rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb); #else rcauth = SQLITE_OK; #endif Debug.Assert(rcauth == SQLITE_OK || rcauth == SQLITE_DENY || rcauth == SQLITE_IGNORE); if (rcauth == SQLITE_DENY) { goto delete_from_cleanup; } Debug.Assert(!isView || pTrigger != null); /* Assign cursor number to the table and all its indices. */ Debug.Assert(pTabList.nSrc == 1); iCur = pTabList.a[0].iCursor = pParse.nTab++; for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext) { pParse.nTab++; } #if !SQLITE_OMIT_AUTHORIZATION /* Start the view context */ if (isView) { sqlite3AuthContextPush(pParse, sContext, pTab.zName); } #endif /* Begin generating code. */ v = sqlite3GetVdbe(pParse); if (v == null) { goto delete_from_cleanup; } if (pParse.nested == 0) { sqlite3VdbeCountChanges(v); } sqlite3BeginWriteOperation(pParse, 1, iDb); /* If we are trying to delete from a view, realize that view into ** a ephemeral table. */ #if !(SQLITE_OMIT_VIEW) && !(SQLITE_OMIT_TRIGGER) if (isView) { sqlite3MaterializeView(pParse, pTab, pWhere, iCur); } #endif /* Resolve the column names in the WHERE clause. */ sNC = new NameContext();// memset( &sNC, 0, sizeof( sNC ) ); sNC.pParse = pParse; sNC.pSrcList = pTabList; if (sqlite3ResolveExprNames(sNC, ref pWhere) != 0) { goto delete_from_cleanup; } /* Initialize the counter of the number of rows deleted, if ** we are counting rows. */ if ((db.flags & SQLITE_CountRows) != 0) { memCnt = ++pParse.nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt); } #if !SQLITE_OMIT_TRUNCATE_OPTIMIZATION /* Special case: A DELETE without a WHERE clause deletes everything. ** It is easier just to erase the whole table. Prior to version 3.6.5, ** this optimization caused the row change count (the value returned by ** API function sqlite3_count_changes) to be set incorrectly. */ if (rcauth == SQLITE_OK && pWhere == null && null == pTrigger && !IsVirtual(pTab) && 0 == sqlite3FkRequired(pParse, pTab, null, 0) ) { Debug.Assert(!isView); sqlite3VdbeAddOp4(v, OP_Clear, pTab.tnum, iDb, memCnt, pTab.zName, P4_STATIC); for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext) { Debug.Assert(pIdx.pSchema == pTab.pSchema); sqlite3VdbeAddOp2(v, OP_Clear, pIdx.tnum, iDb); } } else #endif //* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ /* The usual case: There is a WHERE clause so we have to scan through ** the table and pick which records to delete. */ { int iRowSet = ++pParse.nMem; /* Register for rowset of rows to delete */ int iRowid = ++pParse.nMem; /* Used for storing rowid values. */ int regRowid; /* Actual register containing rowids */ /* Collect rowids of every row to be deleted. */ sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); ExprList elDummy = null; pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, ref elDummy, WHERE_DUPLICATES_OK); if (pWInfo == null) { goto delete_from_cleanup; } regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid); sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid); if ((db.flags & SQLITE_CountRows) != 0) { sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); } sqlite3WhereEnd(pWInfo); /* 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. */ end = sqlite3VdbeMakeLabel(v); /* Unless this is a view, open cursors for the table we are ** deleting from and all its indices. If this is a view, then the ** only effect this statement has is to fire the INSTEAD OF ** triggers. */ if (!isView) { sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite); } addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid); /* Delete the row */ #if !SQLITE_OMIT_VIRTUALTABLE if (IsVirtual(pTab)) { const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); sqlite3VtabMakeWritable(pParse, pTab); sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB); sqlite3MayAbort(pParse); } else #endif { int count = (pParse.nested == 0) ? 1 : 0; /* True to count changes */ sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default); } /* End of the delete loop */ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); sqlite3VdbeResolveLabel(v, end); /* Close the cursors open on the table and its indexes. */ if (!isView && !IsVirtual(pTab)) { for (i = 1, pIdx = pTab.pIndex; pIdx != null; i++, pIdx = pIdx.pNext) { sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx.tnum); } sqlite3VdbeAddOp1(v, OP_Close, iCur); } } /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ if (pParse.nested == 0 && pParse.pTriggerTab == null) { sqlite3AutoincrementEnd(pParse); } /* Return the number of rows that were deleted. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. */ if ((db.flags & SQLITE_CountRows) != 0 && 0 == pParse.nested && null == pParse.pTriggerTab) { sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC); } delete_from_cleanup: #if !SQLITE_OMIT_AUTHORIZATION sqlite3AuthContextPop(sContext); #endif sqlite3SrcListDelete(db, ref pTabList); sqlite3ExprDelete(db, ref pWhere); return; }
public static void DeleteFrom(Parse parse, SrcList tabList, Expr where_) { AuthContext sContext = new AuthContext(); // Authorization context Context ctx = parse.Ctx; // Main database structure if (parse.Errs != 0 || ctx.MallocFailed) goto delete_from_cleanup; Debug.Assert(tabList.Srcs == 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. Table table = SrcList.Lookup(parse, tabList); // The table from which records will be deleted if (table == null) goto delete_from_cleanup; // Figure out if we have any triggers and if the table being deleted from is a view #if !OMIT_TRIGGER int dummy; Trigger trigger = Triggers.Exist(parse, table, TK.DELETE, null, out dummy); // List of table triggers, if required #if OMIT_VIEW const bool isView = false; #else bool isView = (table.Select != null); // True if attempting to delete from a view #endif #else const Trigger trigger = null; bool isView = false; #endif // If pTab is really a view, make sure it has been initialized. if (sqlite3ViewGetColumnNames(parse, table) != null || IsReadOnly(parse, table, (trigger != null))) goto delete_from_cleanup; int db = sqlite3SchemaToIndex(ctx, table.Schema); // Database number Debug.Assert(db < ctx.DBs.length); string dbName = ctx.DBs[db].Name; // Name of database holding pTab ARC rcauth = Auth.Check(parse, AUTH.DELETE, table.Name, 0, dbName); // Value returned by authorization callback Debug.Assert(rcauth == ARC.OK || rcauth == ARC.DENY || rcauth == ARC.IGNORE); if (rcauth == ARC.DENY) goto delete_from_cleanup; Debug.Assert(!isView || trigger != null); // Assign cursor number to the table and all its indices. Debug.Assert(tabList.Srcs == 1); int curId = tabList.Ids[0].Cursor = parse.Tabs++; // VDBE VdbeCursor number for pTab Index idx; // For looping over indices of the table for (idx = table.Index; idx != null; idx = idx.Next) parse.Tabs++; // Start the view context if (isView) Auth.ContextPush(parse, sContext, table.Name); // Begin generating code. Vdbe v = parse.GetVdbe(); // The virtual database engine if (v == null) goto delete_from_cleanup; if (parse.Nested == 0) v.CountChanges(); parse.BeginWriteOperation(1, db); // If we are trying to delete from a view, realize that view into a ephemeral table. #if !OMIT_VIEW && !OMIT_TRIGGER if (isView) MaterializeView(parse, table, where_, curId); #endif // Resolve the column names in the WHERE clause. NameContext sNC = new NameContext(); // Name context to resolve expressions in sNC.Parse = parse; sNC.SrcList = tabList; if (sqlite3ResolveExprNames(sNC, ref where_) != 0) goto delete_from_cleanup; // Initialize the counter of the number of rows deleted, if we are counting rows. int memCnt = -1; // Memory cell used for change counting if ((ctx.Flags & Context.FLAG.CountRows) != 0) { memCnt = ++parse.Mems; v.AddOp2(OP.Integer, 0, memCnt); } #if !OMIT_TRUNCATE_OPTIMIZATION // Special case: A DELETE without a WHERE clause deletes everything. It is easier just to erase the whole table. Prior to version 3.6.5, // this optimization caused the row change count (the value returned by API function sqlite3_count_changes) to be set incorrectly. if (rcauth == ARC.OK && where_ == null && trigger == null && !IsVirtual(table) && !FKey.FkRequired(parse, table, null, 0)) { Debug.Assert(!isView); v.AddOp4(OP.Clear, table.Id, db, memCnt, table.Name, Vdbe.P4T.STATIC); for (idx = table.Index; idx != null; idx = idx.Next) { Debug.Assert(idx.Schema == table.Schema); v.AddOp2(OP.Clear, idx.Id, db); } } else #endif // The usual case: There is a WHERE clause so we have to scan through the table and pick which records to delete. { int rowSet = ++parse.Mems; // Register for rowset of rows to delete int rowid = ++parse.Mems; // Used for storing rowid values. // Collect rowids of every row to be deleted. v.AddOp2(OP.Null, 0, rowSet); ExprList dummy = null; WhereInfo winfo = Where.Begin(parse, tabList, where_, ref dummy, WHERE_DUPLICATES_OK, 0); // Information about the WHERE clause if (winfo == null) goto delete_from_cleanup; int regRowid = Expr.CodeGetColumn(parse, table, -1, curId, rowid); // Actual register containing rowids v.AddOp2(OP.RowSetAdd, rowSet, regRowid); if ((ctx.Flags & Context.FLAG.CountRows) != 0) v.AddOp2(OP.AddImm, memCnt, 1); Where.End(winfo); // 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. int end = v.MakeLabel(); // Unless this is a view, open cursors for the table we are deleting from and all its indices. If this is a view, then the // only effect this statement has is to fire the INSTEAD OF triggers. if (!isView) sqlite3OpenTableAndIndices(parse, table, curId, OP.OpenWrite); int addr = v.AddOp3(OP.RowSetRead, rowSet, end, rowid); // Delete the row #if !OMIT_VIRTUALTABLE if (IsVirtual(table)) { VTable vtable = VTable.GetVTable(ctx, table); VTable.MakeWritable(parse, table); v.AddOp4(OP.VUpdate, 0, 1, rowid, vtable, Vdbe.P4T.VTAB); v.ChangeP5(OE.Abort); sqlite3MayAbort(parse); } else #endif { int count = (parse.Nested == 0; // True to count changes GenerateRowDelete(parse, table, curId, rowid, count, trigger, OE.Default); } // End of the delete loop v.AddOp2(OP.Goto, 0, addr); v.ResolveLabel(end); // Close the cursors open on the table and its indexes. if (!isView && !IsVirtual(table)) { for (int i = 1, idx = table.Index; idx != null; i++, idx = idx.Next) v.AddOp2(OP.Close, curId + i, idx.Id); v.AddOp1(OP.Close, curId); } } // Update the sqlite_sequence table by storing the content of the maximum rowid counter values recorded while inserting into // autoincrement tables. if (parse.Nested == 0 && parse.TriggerTab == null) sqlite3AutoincrementEnd(parse); // Return the number of rows that were deleted. If this routine is generating code because of a call to sqlite3NestedParse(), do not // invoke the callback function. if ((ctx.Flags & Context.FLAG.CountRows) != 0 && parse.Nested == 0 && parse.TriggerTab == null) { v.AddOp2(OP.ResultRow, memCnt, 1); v.SetNumCols(1); v.SetColName(0, COLNAME_NAME, "rows deleted", SQLITE_STATIC); } delete_from_cleanup: Auth.ContextPop(sContext); SrcList.Delete(ctx, ref tabList); Expr.Delete(ctx, ref where_); return; }
/* ** Generate an expression tree to implement the WHERE, ORDER BY, ** and LIMIT/OFFSET portion of DELETE and UPDATE statements. ** ** DELETE FROM table_wxyz WHERE a<5 ORDER BY a LIMIT 1; ** \__________________________/ ** pLimitWhere (pInClause) */ Expr sqlite3LimitWhere( Parse pParse, /* The parser context */ SrcList pSrc, /* the FROM clause -- which tables to scan */ Expr pWhere, /* The WHERE clause. May be null */ ExprList pOrderBy, /* The ORDER BY clause. May be null */ Expr pLimit, /* The LIMIT clause. May be null */ Expr pOffset, /* The OFFSET clause. May be null */ char zStmtType /* Either DELETE or UPDATE. For error messages. */ ) { Expr pWhereRowid = null; /* WHERE rowid .. */ Expr pInClause = null; /* WHERE rowid IN ( select ) */ Expr pSelectRowid = null; /* SELECT rowid ... */ ExprList pEList = null; /* Expression list contaning only pSelectRowid */ SrcList pSelectSrc = null; /* SELECT rowid FROM x ... (dup of pSrc) */ Select pSelect = null; /* Complete SELECT tree */ /* Check that there isn't an ORDER BY without a LIMIT clause. */ if (pOrderBy != null && (pLimit == null)) { sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType); pParse.parseError = 1; goto limit_where_cleanup_2; } /* We only need to generate a select expression if there ** is a limit/offset term to enforce. */ if (pLimit == null) { /* if pLimit is null, pOffset will always be null as well. */ Debug.Assert(pOffset == null); return(pWhere); } /* Generate a select expression tree to enforce the limit/offset ** term for the DELETE or UPDATE statement. For example: ** DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 ** becomes: ** DELETE FROM table_a WHERE rowid IN ( ** SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 ** ); */ pSelectRowid = sqlite3PExpr(pParse, TK_ROW, null, null, null); if (pSelectRowid == null) { goto limit_where_cleanup_2; } pEList = sqlite3ExprListAppend(pParse, null, pSelectRowid); if (pEList == null) { goto limit_where_cleanup_2; } /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree ** and the SELECT subtree. */ pSelectSrc = sqlite3SrcListDup(pParse.db, pSrc, 0); if (pSelectSrc == null) { sqlite3ExprListDelete(pParse.db, pEList); goto limit_where_cleanup_2; } /* generate the SELECT expression tree. */ pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, null, null, pOrderBy, 0, pLimit, pOffset); if (pSelect == null) { return(null); } /* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */ pWhereRowid = sqlite3PExpr(pParse, TK_ROW, null, null, null); if (pWhereRowid == null) { goto limit_where_cleanup_1; } pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, null, null); if (pInClause == null) { goto limit_where_cleanup_1; } pInClause->x.pSelect = pSelect; pInClause->flags |= EP_xIsSelect; sqlite3ExprSetHeight(pParse, pInClause); return(pInClause); /* something went wrong. clean up anything allocated. */ limit_where_cleanup_1: sqlite3SelectDelete(pParse.db, pSelect); return(null); limit_where_cleanup_2: sqlite3ExprDelete(pParse.db, ref pWhere); sqlite3ExprListDelete(pParse.db, pOrderBy); sqlite3ExprDelete(pParse.db, ref pLimit); sqlite3ExprDelete(pParse.db, ref pOffset); return(null); }
static SrcList va_arg(object[] ap, SrcList sysType) { return((SrcList)ap[vaNEXT++]); }
/* ** This function is called by the parser after the table-name in ** an "ALTER TABLE <table-name> ADD" statement is parsed. Argument ** pSrc is the full-name of the table being altered. ** ** This routine makes a (partial) copy of the Table structure ** for the table being altered and sets Parse.pNewTable to point ** to it. Routines called by the parser as the column definition ** is parsed (i.e. sqlite3AddColumn()) add the new Column data to ** the copy. The copy of the Table structure is deleted by tokenize.c ** after parsing is finished. ** ** Routine sqlite3AlterFinishAddColumn() will be called to complete ** coding the "ALTER TABLE ... ADD" statement. */ static void sqlite3AlterBeginAddColumn(Parse pParse, SrcList pSrc) { Table pNew; Table pTab; Vdbe v; int iDb; int i; int nAlloc; sqlite3 db = pParse.db; /* Look up the table being altered. */ Debug.Assert(pParse.pNewTable == null); Debug.Assert(sqlite3BtreeHoldsAllMutexes(db)); // if ( db.mallocFailed != 0 ) goto exit_begin_add_column; pTab = sqlite3LocateTable(pParse, 0, pSrc.a[0].zName, pSrc.a[0].zDatabase); if (pTab == null) { goto exit_begin_add_column; } if (IsVirtual(pTab)) { sqlite3ErrorMsg(pParse, "virtual tables may not be altered"); goto exit_begin_add_column; } /* Make sure this is not an attempt to ALTER a view. */ if (pTab.pSelect != null) { sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); goto exit_begin_add_column; } if (SQLITE_OK != isSystemTable(pParse, pTab.zName)) { goto exit_begin_add_column; } Debug.Assert(pTab.addColOffset > 0); iDb = sqlite3SchemaToIndex(db, pTab.pSchema); /* Put a copy of the Table struct in Parse.pNewTable for the ** sqlite3AddColumn() function and friends to modify. But modify ** the name by adding an "sqlite_altertab_" prefix. By adding this ** prefix, we insure that the name will not collide with an existing ** table because user table are not allowed to have the "sqlite_" ** prefix on their name. */ pNew = new Table();// (Table*)sqlite3DbMallocZero( db, sizeof(Table)) if (pNew == null) { goto exit_begin_add_column; } pParse.pNewTable = pNew; pNew.nRef = 1; pNew.nCol = pTab.nCol; Debug.Assert(pNew.nCol > 0); nAlloc = (((pNew.nCol - 1) / 8) * 8) + 8; Debug.Assert(nAlloc >= pNew.nCol && nAlloc % 8 == 0 && nAlloc - pNew.nCol < 8); pNew.aCol = new Column[nAlloc];// (Column*)sqlite3DbMallocZero( db, sizeof(Column) * nAlloc ); pNew.zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab.zName); if (pNew.aCol == null || pNew.zName == null) { // db.mallocFailed = 1; goto exit_begin_add_column; } // memcpy( pNew.aCol, pTab.aCol, sizeof(Column) * pNew.nCol ); for (i = 0; i < pNew.nCol; i++) { Column pCol = pTab.aCol[i].Copy(); // sqlite3DbStrDup( db, pCol.zName ); pCol.zColl = null; pCol.zType = null; pCol.pDflt = null; pCol.zDflt = null; pNew.aCol[i] = pCol; } pNew.pSchema = db.aDb[iDb].pSchema; pNew.addColOffset = pTab.addColOffset; pNew.nRef = 1; /* Begin a transaction and increment the schema cookie. */ sqlite3BeginWriteOperation(pParse, 0, iDb); v = sqlite3GetVdbe(pParse); if (v == null) { goto exit_begin_add_column; } sqlite3ChangeCookie(pParse, iDb); exit_begin_add_column: sqlite3SrcListDelete(db, ref pSrc); return; }
/* ** 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. **/ static void sqlite3DropTrigger( Parse pParse, SrcList pName, int noErr ) { Trigger pTrigger = null; int i; string zDb; string zName; int nName; sqlite3 db = pParse.db; // if ( db.mallocFailed != 0 ) goto drop_trigger_cleanup; if ( SQLITE_OK != sqlite3ReadSchema( pParse ) ) { goto drop_trigger_cleanup; } Debug.Assert( pName.nSrc == 1 ); zDb = pName.a[0].zDatabase; zName = pName.a[0].zName; nName = sqlite3Strlen30( zName ); Debug.Assert( zDb != null || sqlite3BtreeHoldsAllMutexes( db ) ); for ( i = OMIT_TEMPDB; i < db.nDb; i++ ) { int j = ( i < 2 ) ? i ^ 1 : i; /* Search TEMP before MAIN */ if ( zDb != null && !db.aDb[j].zName.Equals( zDb ,StringComparison.InvariantCultureIgnoreCase ) ) continue; Debug.Assert( sqlite3SchemaMutexHeld( db, j, null ) ); pTrigger = sqlite3HashFind( ( db.aDb[j].pSchema.trigHash ), zName, nName, (Trigger)null ); if ( pTrigger != null ) break; } if ( pTrigger == null ) { if ( noErr == 0 ) { sqlite3ErrorMsg( pParse, "no such trigger: %S", pName, 0 ); } else { sqlite3CodeVerifyNamedSchema( pParse, zDb ); } pParse.checkSchema = 1; goto drop_trigger_cleanup; } sqlite3DropTriggerPtr( pParse, pTrigger ); drop_trigger_cleanup: sqlite3SrcListDelete( db, ref pName ); }
/* ** 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 */ private static void sqlite3Update( Parse pParse, /* The parser context */ 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 */ int addr = 0; /* 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 iCur; /* VDBE Cursor number of pTab */ sqlite3 db; /* The database structure */ int[] aRegIdx = null; /* One register assigned to each index to be updated */ int[] aXRef = null; /* 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. */ bool chngRowid; /* True if the record number is being changed */ Expr pRowidExpr = null; /* Expression defining the new record number */ bool openAll = false; /* True if all indices need to be opened */ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ int iDb; /* Database containing the table being updated */ bool okOnePass; /* True for one-pass algorithm without the FIFO */ bool hasFK; /* True if foreign key processing is required */ #if !SQLITE_OMIT_TRIGGER bool isView; /* True when updating a view (INSTEAD OF trigger) */ Trigger pTrigger; /* List of triggers on pTab, if required */ int tmask = 0; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ #endif int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */ /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ int regOldRowid; /* The old rowid */ int regNewRowid; /* The new rowid */ int regNew; int regOld = 0; int regRowSet = 0; /* Rowset of rows to be updated */ sContext = new AuthContext(); //memset( &sContext, 0, sizeof( sContext ) ); db = pParse.db; if (pParse.nErr != 0 /*|| db.mallocFailed != 0 */) { goto update_cleanup; } Debug.Assert(pTabList.nSrc == 1); /* Locate the table which we want to update. */ pTab = sqlite3SrcListLookup(pParse, pTabList); if (pTab == null) { goto update_cleanup; } iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema); /* Figure out if we have any triggers and if the table being ** updated is a view. */ #if !SQLITE_OMIT_TRIGGER pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, out tmask); isView = pTab.pSelect != null; Debug.Assert(pTrigger != null || tmask == 0); #else const Trigger pTrigger = null; //# define pTrigger 0 const int tmask = 0; //# define tmask 0 #endif #if SQLITE_OMIT_TRIGGER || SQLITE_OMIT_VIEW // # undef isView const bool isView = false; //# define isView 0 #endif if (sqlite3ViewGetColumnNames(pParse, pTab) != 0) { goto update_cleanup; } if (sqlite3IsReadOnly(pParse, pTab, tmask)) { goto update_cleanup; } aXRef = new int[pTab.nCol]; // sqlite3DbMallocRaw(db, sizeof(int) * pTab.nCol); //if ( aXRef == null ) goto update_cleanup; for (i = 0; i < pTab.nCol; i++) { aXRef[i] = -1; } /* Allocate a cursors for the main database table and for all indices. ** The index cursors might not be used, but if they are used they ** need to occur right after the database cursor. So go ahead and ** allocate enough space, just in case. */ pTabList.a[0].iCursor = iCur = pParse.nTab++; for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext) { pParse.nTab++; } /* Initialize the name-context */ sNC = new NameContext(); // memset(&sNC, 0, sNC).Length; sNC.pParse = pParse; sNC.pSrcList = pTabList; /* 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. */ chngRowid = false; for (i = 0; i < pChanges.nExpr; i++) { if (sqlite3ResolveExprNames(sNC, ref pChanges.a[i].pExpr) != 0) { goto update_cleanup; } for (j = 0; j < pTab.nCol; j++) { if (pTab.aCol[j].zName.Equals(pChanges.a[i].zName, StringComparison.OrdinalIgnoreCase)) { if (j == pTab.iPKey) { chngRowid = true; pRowidExpr = pChanges.a[i].pExpr; } aXRef[j] = i; break; } } if (j >= pTab.nCol) { if (sqlite3IsRowid(pChanges.a[i].zName)) { chngRowid = true; pRowidExpr = pChanges.a[i].pExpr; } else { sqlite3ErrorMsg(pParse, "no such column: %s", pChanges.a[i].zName); pParse.checkSchema = 1; goto update_cleanup; } } #if !SQLITE_OMIT_AUTHORIZATION { int rc; rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab.zName, pTab.aCol[j].zName, db.aDb[iDb].zName); if (rc == SQLITE_DENY) { goto update_cleanup; } else if (rc == SQLITE_IGNORE) { aXRef[j] = -1; } } #endif } hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid ? 1 : 0) != 0; /* Allocate memory for the array aRegIdx[]. There is one entry in the ** array for each index associated with table being updated. Fill in ** the value with a register number for indices that are to be used ** and with zero for unused indices. */ for (nIdx = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, nIdx++) { } if (nIdx > 0) { aRegIdx = new int[nIdx]; // sqlite3DbMallocRaw(db, Index*.Length * nIdx); if (aRegIdx == null) { goto update_cleanup; } } for (j = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, j++) { int reg; if (hasFK || chngRowid) { reg = ++pParse.nMem; } else { reg = 0; for (i = 0; i < pIdx.nColumn; i++) { if (aXRef[pIdx.aiColumn[i]] >= 0) { reg = ++pParse.nMem; break; } } } aRegIdx[j] = reg; } /* Begin generating code. */ v = sqlite3GetVdbe(pParse); if (v == null) { goto update_cleanup; } if (pParse.nested == 0) { sqlite3VdbeCountChanges(v); } sqlite3BeginWriteOperation(pParse, 1, iDb); #if !SQLITE_OMIT_VIRTUALTABLE /* Virtual tables must be handled separately */ if (IsVirtual(pTab)) { updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, pWhere, onError); pWhere = null; pTabList = null; goto update_cleanup; } #endif /* Allocate required registers. */ regOldRowid = regNewRowid = ++pParse.nMem; if (pTrigger != null || hasFK) { regOld = pParse.nMem + 1; pParse.nMem += pTab.nCol; } if (chngRowid || pTrigger != null || hasFK) { regNewRowid = ++pParse.nMem; } regNew = pParse.nMem + 1; pParse.nMem += pTab.nCol; /* Start the view context. */ if (isView) { sqlite3AuthContextPush(pParse, sContext, pTab.zName); } /* If we are trying to update a view, realize that view into ** a ephemeral table. */ #if !(SQLITE_OMIT_VIEW) && !(SQLITE_OMIT_TRIGGER) if (isView) { sqlite3MaterializeView(pParse, pTab, pWhere, iCur); } #endif /* Resolve the column names in all the expressions in the ** WHERE clause. */ if (sqlite3ResolveExprNames(sNC, ref pWhere) != 0) { goto update_cleanup; } /* Begin the database scan */ sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid); ExprList NullOrderby = null; pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, ref NullOrderby, WHERE_ONEPASS_DESIRED); if (pWInfo == null) { goto update_cleanup; } okOnePass = pWInfo.okOnePass != 0; /* Remember the rowid of every item to be updated. */ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid); if (!okOnePass) { regRowSet = ++pParse.nMem; sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); } /* End the database scan loop. */ sqlite3WhereEnd(pWInfo); /* Initialize the count of updated rows */ if ((db.flags & SQLITE_CountRows) != 0 && null == pParse.pTriggerTab) { regRowCount = ++pParse.nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); } 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. */ if (!okOnePass) { sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); } if (onError == OE_Replace) { openAll = true; } else { openAll = false; for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext) { if (pIdx.onError == OE_Replace) { openAll = true; break; } } } for (i = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, i++) { if (openAll || aRegIdx[i] > 0) { KeyInfo pKey = sqlite3IndexKeyinfo(pParse, pIdx); sqlite3VdbeAddOp4(v, OP_OpenWrite, iCur + i + 1, pIdx.tnum, iDb, pKey, P4_KEYINFO_HANDOFF); Debug.Assert(pParse.nTab > iCur + i + 1); } } } /* Top of the update loop */ if (okOnePass) { int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid); addr = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, a1); } else { addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid); } /* Make cursor iCur point to the record that is being updated. If ** this record does not exist for some reason (deleted by a trigger, ** for example, then jump to the next iteration of the RowSet loop. */ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); /* If the record number will change, set register regNewRowid to ** contain the new value. If the record number is not being modified, ** then regNewRowid is the same register as regOldRowid, which is ** already populated. */ Debug.Assert(chngRowid || pTrigger != null || hasFK || regOldRowid == regNewRowid); if (chngRowid) { sqlite3ExprCode(pParse, pRowidExpr, regNewRowid); sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); } /* If there are triggers on this table, populate an array of registers ** with the required old.* column data. */ if (hasFK || pTrigger != null) { u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0); oldmask |= sqlite3TriggerColmask(pParse, pTrigger, pChanges, 0, TRIGGER_BEFORE | TRIGGER_AFTER, pTab, onError ); for (i = 0; i < pTab.nCol; i++) { if (aXRef[i] < 0 || oldmask == 0xffffffff || (i < 32 && 0 != (oldmask & (1 << i)))) { sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld + i); } else { sqlite3VdbeAddOp2(v, OP_Null, 0, regOld + i); } } if (chngRowid == false) { sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); } } /* Populate the array of registers beginning at regNew with the new ** row data. This array is used to check constaints, create the new ** table and index records, and as the values for any new.* references ** made by triggers. ** ** If there are one or more BEFORE triggers, then do not populate the ** registers associated with columns that are (a) not modified by ** this UPDATE statement and (b) not accessed by new.* references. The ** values for registers not modified by the UPDATE must be reloaded from ** the database after the BEFORE triggers are fired anyway (as the trigger ** may have modified them). So not loading those that are not going to ** be used eliminates some redundant opcodes. */ newmask = (int)sqlite3TriggerColmask( pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError ); for (i = 0; i < pTab.nCol; i++) { if (i == pTab.iPKey) { sqlite3VdbeAddOp2(v, OP_Null, 0, regNew + i); } else { j = aXRef[i]; if (j >= 0) { sqlite3ExprCode(pParse, pChanges.a[j].pExpr, regNew + i); } else if (0 == (tmask & TRIGGER_BEFORE) || i > 31 || (newmask & (1 << i)) != 0) { /* This branch loads the value of a column that will not be changed ** into a register. This is done if there are no BEFORE triggers, or ** if there are one or more BEFORE triggers that use this value via ** a new.* reference in a trigger program. */ testcase(i == 31); testcase(i == 32); sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew + i); sqlite3ColumnDefault(v, pTab, i, regNew + i); } } } /* Fire any BEFORE UPDATE triggers. This happens before constraints are ** verified. One could argue that this is wrong. */ if ((tmask & TRIGGER_BEFORE) != 0) { sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab.nCol); sqlite3TableAffinityStr(v, pTab); sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab, regOldRowid, onError, addr); /* The row-trigger may have deleted the row being updated. In this ** case, jump to the next row. No updates or AFTER triggers are ** required. This behaviour - what happens when the row being updated ** is deleted or renamed by a BEFORE trigger - is left undefined in the ** documentation. */ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); /* If it did not delete it, the row-trigger may still have modified ** some of the columns of the row being updated. Load the values for ** all columns not modified by the update statement into their ** registers in case this has happened. */ for (i = 0; i < pTab.nCol; i++) { if (aXRef[i] < 0 && i != pTab.iPKey) { sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew + i); sqlite3ColumnDefault(v, pTab, i, regNew + i); } } } if (!isView) { int j1; /* Address of jump instruction */ /* Do constraint checks. */ int iDummy; sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid, aRegIdx, (chngRowid ? regOldRowid : 0), true, onError, addr, out iDummy); /* Do FK constraint checks. */ if (hasFK) { sqlite3FkCheck(pParse, pTab, regOldRowid, 0); } /* Delete the index entries associated with the current record. */ j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid); sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); /* If changing the record number, delete the old record. */ if (hasFK || chngRowid) { sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0); } sqlite3VdbeJumpHere(v, j1); if (hasFK) { sqlite3FkCheck(pParse, pTab, 0, regNewRowid); } /* Insert the new index entries and the new record. */ sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, true, false, false); /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to ** handle rows (possibly in other tables) that refer via a foreign key ** to the row just updated. */ if (hasFK) { sqlite3FkActions(pParse, pTab, pChanges, regOldRowid); } } /* Increment the row counter */ if ((db.flags & SQLITE_CountRows) != 0 && null == pParse.pTriggerTab) { sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); } sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab, regOldRowid, onError, addr); /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. */ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); sqlite3VdbeJumpHere(v, addr); /* Close all tables */ for (i = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, i++) { if (openAll || aRegIdx[i] > 0) { sqlite3VdbeAddOp2(v, OP_Close, iCur + i + 1, 0); } } sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ if (pParse.nested == 0 && pParse.pTriggerTab == null) { sqlite3AutoincrementEnd(pParse); } /* ** Return the number of rows that were changed. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. */ if ((db.flags & SQLITE_CountRows) != 0 && null == pParse.pTriggerTab && 0 == pParse.nested) { sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC); } update_cleanup: #if !SQLITE_OMIT_AUTHORIZATION sqlite3AuthContextPop(sContext); #endif sqlite3DbFree(db, ref aRegIdx); sqlite3DbFree(db, ref aXRef); sqlite3SrcListDelete(db, ref pTabList); sqlite3ExprListDelete(db, ref pChanges); sqlite3ExprDelete(db, ref pWhere); return; }
/* ** 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 */ static void sqlite3Update( Parse pParse, /* The parser context */ 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 */ int addr = 0; /* 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 iCur; /* VDBE Cursor number of pTab */ sqlite3 db; /* The database structure */ int[] aRegIdx = null; /* One register assigned to each index to be updated */ int[] aXRef = null; /* 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. */ bool chngRowid; /* True if the record number is being changed */ Expr pRowidExpr = null; /* Expression defining the new record number */ bool openAll = false; /* True if all indices need to be opened */ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ int iDb; /* Database containing the table being updated */ int j1; /* Addresses of jump instructions */ u8 okOnePass; /* True for one-pass algorithm without the FIFO */ #if !SQLITE_OMIT_TRIGGER bool isView = false; /* Trying to update a view */ Trigger pTrigger; /* List of triggers on pTab, if required */ #endif int iBeginAfterTrigger = 0; /* Address of after trigger program */ int iEndAfterTrigger = 0; /* Exit of after trigger program */ int iBeginBeforeTrigger = 0; /* Address of before trigger program */ int iEndBeforeTrigger = 0; /* Exit of before trigger program */ u32 old_col_mask = 0; /* Mask of OLD.* columns in use */ u32 new_col_mask = 0; /* Mask of NEW.* columns in use */ int newIdx = -1; /* index of trigger "new" temp table */ int oldIdx = -1; /* index of trigger "old" temp table */ /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ int regOldRowid; /* The old rowid */ int regNewRowid; /* The new rowid */ int regData; /* New data for the row */ int regRowSet = 0; /* Rowset of rows to be updated */ sContext = new AuthContext(); //memset( &sContext, 0, sizeof( sContext ) ); db = pParse.db; if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ ) { goto update_cleanup; } Debug.Assert( pTabList.nSrc == 1 ); /* Locate the table which we want to update. */ pTab = sqlite3SrcListLookup( pParse, pTabList ); if ( pTab == null ) goto update_cleanup; iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema ); /* Figure out if we have any triggers and if the table being ** updated is a view */ #if !SQLITE_OMIT_TRIGGER int iDummy = 0; pTrigger = sqlite3TriggersExist( pParse, pTab, TK_UPDATE, pChanges, ref iDummy ); isView = pTab.pSelect != null; #else const Trigger pTrigger = null; #if !SQLITE_OMIT_VIEW const bool isView = false; #endif #endif #if SQLITE_OMIT_VIEW // # undef isView const bool isView = false; #endif if ( sqlite3ViewGetColumnNames( pParse, pTab ) != 0 ) { goto update_cleanup; } if ( sqlite3IsReadOnly( pParse, pTab, ( pTrigger != null ? 1 : 0 ) ) ) { goto update_cleanup; } aXRef = new int[pTab.nCol];// sqlite3DbMallocRaw(db, sizeof(int) * pTab.nCol); //if ( aXRef == null ) goto update_cleanup; for ( i = 0 ; i < pTab.nCol ; i++ ) aXRef[i] = -1; /* If there are FOR EACH ROW triggers, allocate cursors for the ** special OLD and NEW tables */ if ( pTrigger != null ) { newIdx = pParse.nTab++; oldIdx = pParse.nTab++; } /* Allocate a cursors for the main database table and for all indices. ** The index cursors might not be used, but if they are used they ** need to occur right after the database cursor. So go ahead and ** allocate enough space, just in case. */ pTabList.a[0].iCursor = iCur = pParse.nTab++; for ( pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext ) { pParse.nTab++; } /* Initialize the name-context */ sNC = new NameContext();// memset(&sNC, 0, sNC).Length; sNC.pParse = pParse; sNC.pSrcList = pTabList; /* 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. */ chngRowid = false; for ( i = 0 ; i < pChanges.nExpr ; i++ ) { if ( sqlite3ResolveExprNames( sNC, ref pChanges.a[i].pExpr ) != 0 ) { goto update_cleanup; } for ( j = 0 ; j < pTab.nCol ; j++ ) { if ( sqlite3StrICmp( pTab.aCol[j].zName, pChanges.a[i].zName ) == 0 ) { if ( j == pTab.iPKey ) { chngRowid = true; pRowidExpr = pChanges.a[i].pExpr; } aXRef[j] = i; break; } } if ( j >= pTab.nCol ) { if ( sqlite3IsRowid( pChanges.a[i].zName ) ) { chngRowid = true; pRowidExpr = pChanges.a[i].pExpr; } else { sqlite3ErrorMsg( pParse, "no such column: %s", pChanges.a[i].zName ); goto update_cleanup; } } #if !SQLITE_OMIT_AUTHORIZATION { int rc; rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab.zName, pTab.aCol[j].zName, db.aDb[iDb].zName); if( rc==SQLITE_DENY ){ goto update_cleanup; }else if( rc==SQLITE_IGNORE ){ aXRef[j] = -1; } } #endif } /* Allocate memory for the array aRegIdx[]. There is one entry in the ** array for each index associated with table being updated. Fill in ** the value with a register number for indices that are to be used ** and with zero for unused indices. */ for ( nIdx = 0, pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext, nIdx++ ) { } if ( nIdx > 0 ) { aRegIdx = new int[nIdx]; // sqlite3DbMallocRaw(db, Index*.Length * nIdx); if ( aRegIdx == null ) goto update_cleanup; } for ( j = 0, pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext, j++ ) { int reg; if ( chngRowid ) { reg = ++pParse.nMem; } else { reg = 0; for ( i = 0 ; i < pIdx.nColumn ; i++ ) { if ( aXRef[pIdx.aiColumn[i]] >= 0 ) { reg = ++pParse.nMem; break; } } } aRegIdx[j] = reg; } /* Allocate a block of register used to store the change record ** sent to sqlite3GenerateConstraintChecks(). There are either ** one or two registers for holding the rowid. One rowid register ** is used if chngRowid is false and two are used if chngRowid is ** true. Following these are pTab.nCol register holding column ** data. */ regOldRowid = regNewRowid = pParse.nMem + 1; pParse.nMem += pTab.nCol + 1; if ( chngRowid ) { regNewRowid++; pParse.nMem++; } regData = regNewRowid + 1; /* Begin generating code. */ v = sqlite3GetVdbe( pParse ); if ( v == null ) goto update_cleanup; if ( pParse.nested == 0 ) sqlite3VdbeCountChanges( v ); sqlite3BeginWriteOperation( pParse, 1, iDb ); #if !SQLITE_OMIT_VIRTUALTABLE /* Virtual tables must be handled separately */ if ( IsVirtual( pTab ) ) { updateVirtualTable( pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, pWhere ); pWhere = null; pTabList = null; goto update_cleanup; } #endif /* Start the view context */ #if !SQLITE_OMIT_AUTHORIZATION if( isView ){ sqlite3AuthContextPush(pParse, sContext, pTab.zName); } #endif /* Generate the code for triggers. */ if ( pTrigger != null ) { int iGoto; /* Create pseudo-tables for NEW and OLD */ sqlite3VdbeAddOp3( v, OP_OpenPseudo, oldIdx, 0, pTab.nCol ); sqlite3VdbeAddOp3( v, OP_OpenPseudo, newIdx, 0, pTab.nCol ); iGoto = sqlite3VdbeAddOp2( v, OP_Goto, 0, 0 ); addr = sqlite3VdbeMakeLabel( v ); iBeginBeforeTrigger = sqlite3VdbeCurrentAddr( v ); if ( sqlite3CodeRowTrigger( pParse, pTrigger, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab, newIdx, oldIdx, onError, addr, ref old_col_mask, ref new_col_mask ) != 0 ) { goto update_cleanup; } iEndBeforeTrigger = sqlite3VdbeAddOp2( v, OP_Goto, 0, 0 ); iBeginAfterTrigger = sqlite3VdbeCurrentAddr( v ); #if !SQLITE_OMIT_TRIGGER if ( sqlite3CodeRowTrigger( pParse, pTrigger, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab, newIdx, oldIdx, onError, addr, ref old_col_mask, ref new_col_mask ) != 0 ) { goto update_cleanup; } #endif iEndAfterTrigger = sqlite3VdbeAddOp2( v, OP_Goto, 0, 0 ); sqlite3VdbeJumpHere( v, iGoto ); } /* If we are trying to update a view, realize that view into ** a ephemeral table. */ #if !(SQLITE_OMIT_VIEW) && !(SQLITE_OMIT_TRIGGER) if ( isView ) { sqlite3MaterializeView( pParse, pTab, pWhere, iCur ); } #endif /* Resolve the column names in all the expressions in the ** WHERE clause. */ if ( sqlite3ResolveExprNames( sNC, ref pWhere ) != 0 ) { goto update_cleanup; } /* Begin the database scan */ sqlite3VdbeAddOp2( v, OP_Null, 0, regOldRowid ); ExprList NullOrderby = null; pWInfo = sqlite3WhereBegin( pParse, pTabList, pWhere, ref NullOrderby, WHERE_ONEPASS_DESIRED ); if ( pWInfo == null ) goto update_cleanup; okOnePass = pWInfo.okOnePass; /* Remember the rowid of every item to be updated. */ sqlite3VdbeAddOp2( v, OP_Rowid, iCur, regOldRowid ); if ( 0 == okOnePass ) { regRowSet = ++pParse.nMem; sqlite3VdbeAddOp2( v, OP_RowSetAdd, regRowSet, regOldRowid ); } /* End the database scan loop. */ sqlite3WhereEnd( pWInfo ); /* Initialize the count of updated rows */ if ( ( db.flags & SQLITE_CountRows ) != 0 && pParse.trigStack == null ) { regRowCount = ++pParse.nMem; sqlite3VdbeAddOp2( v, OP_Integer, 0, regRowCount ); } 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. */ if ( 0 == okOnePass ) sqlite3OpenTable( pParse, iCur, iDb, pTab, OP_OpenWrite ); if ( onError == OE_Replace ) { openAll = true; } else { openAll = false; for ( pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext ) { if ( pIdx.onError == OE_Replace ) { openAll = true; break; } } } for ( i = 0, pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext, i++ ) { if ( openAll || aRegIdx[i] > 0 ) { KeyInfo pKey = sqlite3IndexKeyinfo( pParse, pIdx ); sqlite3VdbeAddOp4( v, OP_OpenWrite, iCur + i + 1, pIdx.tnum, iDb, pKey, P4_KEYINFO_HANDOFF ); Debug.Assert( pParse.nTab > iCur + i + 1 ); } } } /* Jump back to this point if a trigger encounters an IGNORE constraint. */ if ( pTrigger != null ) { sqlite3VdbeResolveLabel( v, addr ); } /* Top of the update loop */ if ( okOnePass != 0 ) { int a1 = sqlite3VdbeAddOp1( v, OP_NotNull, regOldRowid ); addr = sqlite3VdbeAddOp0( v, OP_Goto ); sqlite3VdbeJumpHere( v, a1 ); } else { addr = sqlite3VdbeAddOp3( v, OP_RowSetRead, regRowSet, 0, regOldRowid ); } if ( pTrigger != null ) { int regRowid; int regRow; int regCols; /* Make cursor iCur point to the record that is being updated. */ sqlite3VdbeAddOp3( v, OP_NotExists, iCur, addr, regOldRowid ); /* Generate the OLD table */ regRowid = sqlite3GetTempReg( pParse ); regRow = sqlite3GetTempReg( pParse ); sqlite3VdbeAddOp2( v, OP_Rowid, iCur, regRowid ); if ( old_col_mask == 0 ) { sqlite3VdbeAddOp2( v, OP_Null, 0, regRow ); } else { sqlite3VdbeAddOp2( v, OP_RowData, iCur, regRow ); } sqlite3VdbeAddOp3( v, OP_Insert, oldIdx, regRow, regRowid ); /* Generate the NEW table */ if ( chngRowid ) { sqlite3ExprCodeAndCache( pParse, pRowidExpr, regRowid ); sqlite3VdbeAddOp1( v, OP_MustBeInt, regRowid ); } else { sqlite3VdbeAddOp2( v, OP_Rowid, iCur, regRowid ); } regCols = sqlite3GetTempRange( pParse, pTab.nCol ); for ( i = 0 ; i < pTab.nCol ; i++ ) { if ( i == pTab.iPKey ) { sqlite3VdbeAddOp2( v, OP_Null, 0, regCols + i ); continue; } j = aXRef[i]; if ( ( i < 32 && ( new_col_mask & ( (u32)1 << i ) ) != 0 ) || new_col_mask == 0xffffffff ) { if ( j < 0 ) { sqlite3VdbeAddOp3( v, OP_Column, iCur, i, regCols + i ); sqlite3ColumnDefault( v, pTab, i, -1 ); } else { sqlite3ExprCodeAndCache( pParse, pChanges.a[j].pExpr, regCols + i ); } } else { sqlite3VdbeAddOp2( v, OP_Null, 0, regCols + i ); } } sqlite3VdbeAddOp3( v, OP_MakeRecord, regCols, pTab.nCol, regRow ); if ( !isView ) { sqlite3TableAffinityStr( v, pTab ); sqlite3ExprCacheAffinityChange( pParse, regCols, pTab.nCol ); } sqlite3ReleaseTempRange( pParse, regCols, pTab.nCol ); /* if( pParse.nErr ) goto update_cleanup; */ sqlite3VdbeAddOp3( v, OP_Insert, newIdx, regRow, regRowid ); sqlite3ReleaseTempReg( pParse, regRowid ); sqlite3ReleaseTempReg( pParse, regRow ); sqlite3VdbeAddOp2( v, OP_Goto, 0, iBeginBeforeTrigger ); sqlite3VdbeJumpHere( v, iEndBeforeTrigger ); } if ( !isView ) { /* 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 entries. ** So make the cursor point at the old record. */ sqlite3VdbeAddOp3( v, OP_NotExists, iCur, addr, regOldRowid ); /* 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 ( chngRowid ) { sqlite3ExprCode( pParse, pRowidExpr, regNewRowid ); sqlite3VdbeAddOp1( v, OP_MustBeInt, regNewRowid ); } /* Compute new data for this record. */ for ( i = 0 ; i < pTab.nCol ; i++ ) { if ( i == pTab.iPKey ) { sqlite3VdbeAddOp2( v, OP_Null, 0, regData + i ); continue; } j = aXRef[i]; if ( j < 0 ) { sqlite3VdbeAddOp3( v, OP_Column, iCur, i, regData + i ); sqlite3ColumnDefault( v, pTab, i, regData + i ); } else { sqlite3ExprCode( pParse, pChanges.a[j].pExpr, regData + i ); } } /* Do constraint checks */ iDummy = 0; sqlite3GenerateConstraintChecks( pParse, pTab, iCur, regNewRowid, aRegIdx, chngRowid, true, onError, addr, ref iDummy ); /* Delete the old indices for the current record. */ j1 = sqlite3VdbeAddOp3( v, OP_NotExists, iCur, 0, regOldRowid ); sqlite3GenerateRowIndexDelete( pParse, pTab, iCur, aRegIdx ); /* If changing the record number, delete the old record. */ if ( chngRowid ) { sqlite3VdbeAddOp2( v, OP_Delete, iCur, 0 ); } sqlite3VdbeJumpHere( v, j1 ); /* Create the new index entries and the new record. */ sqlite3CompleteInsertion( pParse, pTab, iCur, regNewRowid, aRegIdx, true, -1, false, false ); } /* Increment the row counter */ if ( ( db.flags & SQLITE_CountRows ) != 0 && pParse.trigStack == null ) { sqlite3VdbeAddOp2( v, OP_AddImm, regRowCount, 1 ); } /* If there are triggers, close all the cursors after each iteration ** through the loop. The fire the after triggers. */ if ( pTrigger != null ) { sqlite3VdbeAddOp2( v, OP_Goto, 0, iBeginAfterTrigger ); sqlite3VdbeJumpHere( v, iEndAfterTrigger ); } /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. */ sqlite3VdbeAddOp2( v, OP_Goto, 0, addr ); sqlite3VdbeJumpHere( v, addr ); /* Close all tables */ for ( i = 0, pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext, i++ ) { if ( openAll || aRegIdx[i] > 0 ) { sqlite3VdbeAddOp2( v, OP_Close, iCur + i + 1, 0 ); } } sqlite3VdbeAddOp2( v, OP_Close, iCur, 0 ); if ( pTrigger != null ) { sqlite3VdbeAddOp2( v, OP_Close, newIdx, 0 ); sqlite3VdbeAddOp2( v, OP_Close, oldIdx, 0 ); } /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ if ( pParse.nested == 0 && pParse.trigStack == null ) { sqlite3AutoincrementEnd( pParse ); } /* ** Return the number of rows that were changed. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. */ if ( ( db.flags & SQLITE_CountRows ) != 0 && pParse.trigStack == null && pParse.nested == 0 ) { sqlite3VdbeAddOp2( v, OP_ResultRow, regRowCount, 1 ); sqlite3VdbeSetNumCols( v, 1 ); sqlite3VdbeSetColName( v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC ); } update_cleanup: #if !SQLITE_OMIT_AUTHORIZATION sqlite3AuthContextPop(sContext); #endif //sqlite3DbFree( db, ref aRegIdx ); //sqlite3DbFree( db, ref aXRef ); sqlite3SrcListDelete( db, ref pTabList ); sqlite3ExprListDelete( db, ref pChanges ); sqlite3ExprDelete( db, ref pWhere ); return; }
static WRC LookupName(Parse parse, string dbName, string tableName, string colName, NameContext nc, Expr expr) { int cnt = 0; // Number of matching column names int cntTab = 0; // Number of matching table names int subquerys = 0; // How many levels of subquery Context ctx = parse.Ctx; // The database connection SrcList.SrcListItem item; // Use for looping over pSrcList items SrcList.SrcListItem match = null; // The matching pSrcList item NameContext topNC = nc; // First namecontext in the list Schema schema = null; // Schema of the expression bool isTrigger = false; int i, j; Debug.Assert(nc != null); // the name context cannot be NULL. Debug.Assert(colName != null); // The Z in X.Y.Z cannot be NULL Debug.Assert(!E.ExprHasAnyProperty(expr, EP.TokenOnly | EP.Reduced)); // Initialize the node to no-match expr.TableId = -1; expr.Table = null; E.ExprSetIrreducible(expr); // Translate the schema name in zDb into a pointer to the corresponding schema. If not found, pSchema will remain NULL and nothing will match // resulting in an appropriate error message toward the end of this routine if (dbName != null) { for (i = 0; i < ctx.DBs.length; i++) { Debug.Assert(ctx.DBs[i].Name != null); if (string.Compare(ctx.DBs[i].Name, dbName) == 0) { schema = ctx.DBs[i].Schema; break; } } } // Start at the inner-most context and move outward until a match is found while (nc != null && cnt == 0) { ExprList list; SrcList srcList = nc.SrcList; if (srcList != null) { for (i = 0; i < srcList.Srcs; i++) { item = srcList.Ids[i]; Table table = item.Table; Debug.Assert(table != null && table.Name != null); Debug.Assert(table.Cols.length > 0); if (item.Select != null && (item.Select.SelFlags & SF.NestedFrom) != 0) { bool hit = false; list = item.Select.EList; for (j = 0; j < list.Exprs; j++) { if (Walker.MatchSpanName(list.Ids[j].Span, colName, tableName, dbName)) { cnt++; cntTab = 2; match = item; expr.ColumnId = j; hit = true; } } if (hit || table == null) { continue; } } if (dbName != null && table.Schema != schema) { continue; } if (tableName != null) { string tableName2 = (item.Alias != null ? item.Alias : table.Name); Debug.Assert(tableName2 != null); if (!string.Equals(tableName2, tableName, StringComparison.OrdinalIgnoreCase)) { continue; } } if (cntTab++ == 0) { match = item; } Column col; for (j = 0; j < table.Cols.length; j++) { col = table.Cols[j]; if (string.Equals(col.Name, colName, StringComparison.InvariantCultureIgnoreCase)) { // If there has been exactly one prior match and this match is for the right-hand table of a NATURAL JOIN or is in a // USING clause, then skip this match. if (cnt == 1) { if ((item.Jointype & JT.NATURAL) != 0) { continue; } if (NameInUsingClause(item.Using, colName)) { continue; } } cnt++; match = item; // Substitute the rowid (column -1) for the INTEGER PRIMARY KEY expr.ColumnId = (j == table.PKey ? -1 : (short)j); break; } } } if (match != null) { expr.TableId = match.Cursor; expr.Table = match.Table; schema = expr.Table.Schema; } } #if !OMIT_TRIGGER // If we have not already resolved the name, then maybe it is a new.* or old.* trigger argument reference if (dbName == null && tableName != null && cnt == 0 && parse.TriggerTab != null) { TK op = parse.TriggerOp; Table table = null; Debug.Assert(op == TK.DELETE || op == TK.UPDATE || op == TK.INSERT); if (op != TK.DELETE && string.Equals("new", tableName, StringComparison.InvariantCultureIgnoreCase)) { expr.TableId = 1; table = parse.TriggerTab; } else if (op != TK.INSERT && string.Equals("old", tableName, StringComparison.InvariantCultureIgnoreCase)) { expr.TableId = 0; table = parse.TriggerTab; } if (table != null) { int colId; schema = table.Schema; cntTab++; for (colId = 0; colId < table.Cols.length; colId++) { Column col = table.Cols[colId]; if (string.Equals(col.Name, colName, StringComparison.InvariantCultureIgnoreCase)) { if (colId == table.PKey) { colId = -1; } break; } } if (colId >= table.Cols.length && Expr.IsRowid(colName)) { colId = -1; // IMP: R-44911-55124 } if (colId < table.Cols.length) { cnt++; if (colId < 0) { expr.Aff = AFF.INTEGER; } else if (expr.TableId == 0) { C.ASSERTCOVERAGE(colId == 31); C.ASSERTCOVERAGE(colId == 32); parse.Oldmask |= (colId >= 32 ? 0xffffffff : (((uint)1) << colId)); } else { C.ASSERTCOVERAGE(colId == 31); C.ASSERTCOVERAGE(colId == 32); parse.Newmask |= (colId >= 32 ? 0xffffffff : (((uint)1) << colId)); } expr.ColumnId = (short)colId; expr.Table = table; isTrigger = true; } } } #endif // Perhaps the name is a reference to the ROWID if (cnt == 0 && cntTab == 1 && Expr.IsRowid(colName)) { cnt = 1; expr.ColumnId = -1; // IMP: R-44911-55124 expr.Aff = AFF.INTEGER; } // If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z might refer to an result-set alias. This happens, for example, when // we are resolving names in the WHERE clause of the following command: // // SELECT a+b AS x FROM table WHERE x<10; // // In cases like this, replace pExpr with a copy of the expression that forms the result set entry ("a+b" in the example) and return immediately. // Note that the expression in the result set should have already been resolved by the time the WHERE clause is resolved. if (cnt == 0 && (list = nc.EList) != null && tableName == null) { for (j = 0; j < list.Exprs; j++) { string asName = list.Ids[j].Name; if (asName != null && string.Equals(asName, colName, StringComparison.InvariantCultureIgnoreCase)) { Debug.Assert(expr.Left == null && expr.Right == null); Debug.Assert(expr.x.List == null); Debug.Assert(expr.x.Select == null); Expr orig = list.Ids[j].Expr; if ((nc.NCFlags & NC.AllowAgg) == 0 && E.ExprHasProperty(orig, EP.Agg)) { parse.ErrorMsg("misuse of aliased aggregate %s", asName); return(WRC.Abort); } ResolveAlias(parse, list, j, expr, "", subquerys); cnt = 1; match = null; Debug.Assert(tableName == null && dbName == null); goto lookupname_end; } } } // Advance to the next name context. The loop will exit when either we have a match (cnt>0) or when we run out of name contexts. if (cnt == 0) { nc = nc.Next; subquerys++; } } // If X and Y are NULL (in other words if only the column name Z is supplied) and the value of Z is enclosed in double-quotes, then // Z is a string literal if it doesn't match any column names. In that case, we need to return right away and not make any changes to // pExpr. // // Because no reference was made to outer contexts, the pNC->nRef fields are not changed in any context. if (cnt == 0 && tableName == null && E.ExprHasProperty(expr, EP.DblQuoted)) { expr.OP = TK.STRING; expr.Table = null; return(WRC.Prune); } // cnt==0 means there was not match. cnt>1 means there were two or more matches. Either way, we have an error. if (cnt != 1) { string err = (cnt == 0 ? "no such column" : "ambiguous column name"); if (dbName != null) { parse.ErrorMsg("%s: %s.%s.%s", err, dbName, tableName, colName); } else if (tableName != null) { parse.ErrorMsg("%s: %s.%s", err, tableName, colName); } else { parse.ErrorMsg("%s: %s", err, colName); } parse.CheckSchema = 1; topNC.Errs++; } // If a column from a table in pSrcList is referenced, then record this fact in the pSrcList.a[].colUsed bitmask. Column 0 causes // bit 0 to be set. Column 1 sets bit 1. And so forth. If the column number is greater than the number of bits in the bitmask // then set the high-order bit of the bitmask. if (expr.ColumnId >= 0 && match != null) { int n = expr.ColumnId; C.ASSERTCOVERAGE(n == BMS - 1); if (n >= BMS) { n = BMS - 1; } Debug.Assert(match.Cursor == expr.TableId); match.ColUsed |= ((Bitmask)1) << n; } // Clean up and return Expr.Delete(ctx, ref expr.Left); expr.Left = null; Expr.Delete(ctx, ref expr.Right); expr.Right = null; expr.OP = (isTrigger ? TK.TRIGGER : TK.COLUMN); lookupname_end: if (cnt == 1) { Debug.Assert(nc != null); Auth.Read(parse, expr, schema, nc.SrcList); // Increment the nRef value on all name contexts from TopNC up to the point where the name matched. for (; ;) { Debug.Assert(topNC != null); topNC.Refs++; if (topNC == nc) { break; } topNC = topNC.Next; } return(WRC.Prune); } return(WRC.Abort); }
/* ** Generate code for a DELETE FROM statement. ** ** DELETE FROM table_wxyz WHERE a<5 AND b NOT NULL; ** \________/ \________________/ ** pTabList pWhere */ static 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 */ string 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 VdbeCursor number for pTab */ sqlite3 db; /* Main database structure */ AuthContext sContext; /* Authorization context */ NameContext sNC; /* Name context to resolve expressions in */ int iDb; /* Database number */ int memCnt = -1; /* Memory cell used for change counting */ int rcauth; /* Value returned by authorization callback */ #if !SQLITE_OMIT_TRIGGER bool isView; /* True if attempting to delete from a view */ Trigger pTrigger; /* List of table triggers, if required */ #endif sContext = new AuthContext();//memset(&sContext, 0, sizeof(sContext)); db = pParse.db; if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ ) { goto delete_from_cleanup; } Debug.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 == null ) goto delete_from_cleanup; /* Figure out if we have any triggers and if the table being ** deleted from is a view */ #if !SQLITE_OMIT_TRIGGER int iDummy = 0; pTrigger = sqlite3TriggersExist( pParse, pTab, TK_DELETE, null, ref iDummy ); isView = pTab.pSelect != null; #else const Trigger pTrigger = null; bool isView = false; #endif #if SQLITE_OMIT_VIEW //# undef isView isView = false; #endif /* If pTab is really a view, make sure it has been initialized. */ if ( sqlite3ViewGetColumnNames( pParse, pTab ) != 0 ) { goto delete_from_cleanup; } if ( sqlite3IsReadOnly( pParse, pTab, ( pTrigger != null ? 1 : 0 ) ) ) { goto delete_from_cleanup; } iDb = sqlite3SchemaToIndex( db, pTab.pSchema ); Debug.Assert( iDb < db.nDb ); zDb = db.aDb[iDb].zName; #if !SQLITE_OMIT_AUTHORIZATION rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb); #else rcauth = SQLITE_OK; #endif Debug.Assert( rcauth == SQLITE_OK || rcauth == SQLITE_DENY || rcauth == SQLITE_IGNORE ); if ( rcauth == SQLITE_DENY ) { goto delete_from_cleanup; } Debug.Assert( !isView || pTrigger != null ); /* Assign cursor number to the table and all its indices. */ Debug.Assert( pTabList.nSrc == 1 ); iCur = pTabList.a[0].iCursor = pParse.nTab++; for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext ) { pParse.nTab++; } #if !SQLITE_OMIT_AUTHORIZATION /* Start the view context */ if( isView ){ sqlite3AuthContextPush(pParse, sContext, pTab.zName); } #endif /* Begin generating code. */ v = sqlite3GetVdbe( pParse ); if ( v == null ) { goto delete_from_cleanup; } if ( pParse.nested == 0 ) sqlite3VdbeCountChanges( v ); sqlite3BeginWriteOperation( pParse, 1, iDb ); /* If we are trying to delete from a view, realize that view into ** a ephemeral table. */ #if !(SQLITE_OMIT_VIEW) && !(SQLITE_OMIT_TRIGGER) if ( isView ) { sqlite3MaterializeView( pParse, pTab, pWhere, iCur ); } #endif /* Resolve the column names in the WHERE clause. */ sNC = new NameContext();// memset( &sNC, 0, sizeof( sNC ) ); sNC.pParse = pParse; sNC.pSrcList = pTabList; if ( sqlite3ResolveExprNames( sNC, ref pWhere ) != 0 ) { goto delete_from_cleanup; } /* Initialize the counter of the number of rows deleted, if ** we are counting rows. */ if ( ( db.flags & SQLITE_CountRows ) != 0 ) { memCnt = ++pParse.nMem; sqlite3VdbeAddOp2( v, OP_Integer, 0, memCnt ); } #if !SQLITE_OMIT_TRUNCATE_OPTIMIZATION /* Special case: A DELETE without a WHERE clause deletes everything. ** It is easier just to erase the whole table. Prior to version 3.6.5, ** this optimization caused the row change count (the value returned by ** API function sqlite3_count_changes) to be set incorrectly. */ if ( rcauth == SQLITE_OK && pWhere == null && null == pTrigger && !IsVirtual( pTab ) && 0 == sqlite3FkRequired( pParse, pTab, null, 0 ) ) { Debug.Assert( !isView ); sqlite3VdbeAddOp4( v, OP_Clear, pTab.tnum, iDb, memCnt, pTab.zName, P4_STATIC ); for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext ) { Debug.Assert( pIdx.pSchema == pTab.pSchema ); sqlite3VdbeAddOp2( v, OP_Clear, pIdx.tnum, iDb ); } } else #endif //* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */ /* The usual case: There is a WHERE clause so we have to scan through ** the table and pick which records to delete. */ { int iRowSet = ++pParse.nMem; /* Register for rowset of rows to delete */ int iRowid = ++pParse.nMem; /* Used for storing rowid values. */ int regRowid; /* Actual register containing rowids */ /* Collect rowids of every row to be deleted. */ sqlite3VdbeAddOp2( v, OP_Null, 0, iRowSet ); ExprList elDummy = null; pWInfo = sqlite3WhereBegin( pParse, pTabList, pWhere, ref elDummy, WHERE_DUPLICATES_OK ); if ( pWInfo == null ) goto delete_from_cleanup; regRowid = sqlite3ExprCodeGetColumn( pParse, pTab, -1, iCur, iRowid ); sqlite3VdbeAddOp2( v, OP_RowSetAdd, iRowSet, regRowid ); if ( ( db.flags & SQLITE_CountRows ) != 0 ) { sqlite3VdbeAddOp2( v, OP_AddImm, memCnt, 1 ); } sqlite3WhereEnd( pWInfo ); /* 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. */ end = sqlite3VdbeMakeLabel( v ); /* Unless this is a view, open cursors for the table we are ** deleting from and all its indices. If this is a view, then the ** only effect this statement has is to fire the INSTEAD OF ** triggers. */ if ( !isView ) { sqlite3OpenTableAndIndices( pParse, pTab, iCur, OP_OpenWrite ); } addr = sqlite3VdbeAddOp3( v, OP_RowSetRead, iRowSet, end, iRowid ); /* Delete the row */ #if !SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); sqlite3VtabMakeWritable(pParse, pTab); sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB); sqlite3MayAbort(pParse); }else #endif { int count = ( pParse.nested == 0 ) ? 1 : 0; /* True to count changes */ sqlite3GenerateRowDelete( pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default ); } /* End of the delete loop */ sqlite3VdbeAddOp2( v, OP_Goto, 0, addr ); sqlite3VdbeResolveLabel( v, end ); /* Close the cursors open on the table and its indexes. */ if ( !isView && !IsVirtual( pTab ) ) { for ( i = 1, pIdx = pTab.pIndex; pIdx != null; i++, pIdx = pIdx.pNext ) { sqlite3VdbeAddOp2( v, OP_Close, iCur + i, pIdx.tnum ); } sqlite3VdbeAddOp1( v, OP_Close, iCur ); } } /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ if ( pParse.nested == 0 && pParse.pTriggerTab == null ) { sqlite3AutoincrementEnd( pParse ); } /* Return the number of rows that were deleted. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. */ if ( ( db.flags & SQLITE_CountRows ) != 0 && 0 == pParse.nested && null == pParse.pTriggerTab ) { sqlite3VdbeAddOp2( v, OP_ResultRow, memCnt, 1 ); sqlite3VdbeSetNumCols( v, 1 ); sqlite3VdbeSetColName( v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC ); } delete_from_cleanup: #if !SQLITE_OMIT_AUTHORIZATION sqlite3AuthContextPop(sContext); #endif sqlite3SrcListDelete( db, ref pTabList ); sqlite3ExprDelete( db, ref pWhere ); return; }
static void GenerateColumnTypes(Parse parse, SrcList tabList, ExprList list) { #if !OMIT_DECLTYPE Vdbe v = parse.V; NameContext sNC = new NameContext(); sNC.SrcList = tabList; sNC.Parse = parse; for (int i = 0; i < list.Exprs; i++) { Expr p = list.Ids[i].Expr; string typeName; #if ENABLE_COLUMN_METADATA string origDbName = null; string origTableName = null; string origColumnName = null; typeName = ColumnType(sNC, p, ref origDbName, ref origTableName, ref origColumnName); // The vdbe must make its own copy of the column-type and other column specific strings, in case the schema is reset before this // virtual machine is deleted. v.SetColName(i, COLNAME_DATABASE, origDbName, C.DESTRUCTOR_TRANSIENT); v.SetColName(i, COLNAME_TABLE, origTableName, C.DESTRUCTOR_TRANSIENT); v.SetColName(i, COLNAME_COLUMN, origColumnName, C.DESTRUCTOR_TRANSIENT); #else string dummy1 = null; typeName = ColumnType(sNC, p, ref dummy1, ref dummy1, ref dummy1); #endif v.SetColName(i, COLNAME_DECLTYPE, typeName, C.DESTRUCTOR_TRANSIENT); } #endif }
/* ** If cursors, triggers, views and subqueries are all omitted from ** the build, then none of the following routines, except for ** sqlite3SelectDup(), can be called. sqlite3SelectDup() is sometimes ** called with a NULL argument. */ #if !SQLITE_OMIT_VIEW || !SQLITE_OMIT_TRIGGER || !SQLITE_OMIT_SUBQUERY static SrcList sqlite3SrcListDup( sqlite3 db, SrcList p, int flags ) { SrcList pNew; int i; int nByte; if ( p == null ) return null; //nByte = sizeof(*p) + (p.nSrc>0 ? sizeof(p.a[0]) * (p.nSrc-1) : 0); pNew = new SrcList();//sqlite3DbMallocRaw(db, nByte ); if ( p.nSrc > 0 ) pNew.a = new SrcList_item[p.nSrc]; if ( pNew == null ) return null; pNew.nSrc = pNew.nAlloc = p.nSrc; for ( i = 0; i < p.nSrc; i++ ) { pNew.a[i] = new SrcList_item(); SrcList_item pNewItem = pNew.a[i]; SrcList_item pOldItem = p.a[i]; Table pTab; pNewItem.zDatabase = pOldItem.zDatabase;// sqlite3DbStrDup(db, pOldItem.zDatabase); pNewItem.zName = pOldItem.zName;// sqlite3DbStrDup(db, pOldItem.zName); pNewItem.zAlias = pOldItem.zAlias;// sqlite3DbStrDup(db, pOldItem.zAlias); pNewItem.jointype = pOldItem.jointype; pNewItem.iCursor = pOldItem.iCursor; pNewItem.isPopulated = pOldItem.isPopulated; pNewItem.zIndex = pOldItem.zIndex;// sqlite3DbStrDup( db, pOldItem.zIndex ); pNewItem.notIndexed = pOldItem.notIndexed; pNewItem.pIndex = pOldItem.pIndex; pTab = pNewItem.pTab = pOldItem.pTab; if ( pTab != null ) { pTab.nRef++; } pNewItem.pSelect = sqlite3SelectDup( db, pOldItem.pSelect, flags ); pNewItem.pOn = sqlite3ExprDup( db, pOldItem.pOn, flags ); pNewItem.pUsing = sqlite3IdListDup( db, pOldItem.pUsing ); pNewItem.colUsed = pOldItem.colUsed; } return pNew; }
/* ** Generate code that will tell the VDBE the declaration types of columns ** in the result set. */ static void generateColumnTypes( Parse pParse, /* Parser context */ SrcList pTabList, /* List of tables */ ExprList pEList /* Expressions defining the result set */ ) { #if !SQLITE_OMIT_DECLTYPE Vdbe v = pParse.pVdbe; int i; NameContext sNC = new NameContext(); sNC.pSrcList = pTabList; sNC.pParse = pParse; for ( i = 0; i < pEList.nExpr; i++ ) { Expr p = pEList.a[i].pExpr; string zType; #if SQLITE_ENABLE_COLUMN_METADATA string zOrigDb = null; string zOrigTab = null; string zOrigCol = null; zType = columnType( sNC, p, ref zOrigDb, ref zOrigTab, ref zOrigCol ); /* The vdbe must make its own copy of the column-type and other ** column specific strings, in case the schema is reset before this ** virtual machine is deleted. */ sqlite3VdbeSetColName( v, i, COLNAME_DATABASE, zOrigDb, SQLITE_TRANSIENT ); sqlite3VdbeSetColName( v, i, COLNAME_TABLE, zOrigTab, SQLITE_TRANSIENT ); sqlite3VdbeSetColName( v, i, COLNAME_COLUMN, zOrigCol, SQLITE_TRANSIENT ); #else string sDummy = null; zType = columnType( sNC, p, ref sDummy, ref sDummy, ref sDummy ); #endif sqlite3VdbeSetColName( v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT ); } #endif //* SQLITE_OMIT_DECLTYPE */ }
static void AddWhereTerm(Parse parse, SrcList src, int leftId, int colLeftId, int rightId, int colRightId, bool isOuterJoin, ref Expr where_) { Context ctx = parse.Ctx; Debug.Assert(leftId < rightId); Debug.Assert(src.Srcs > rightId); Debug.Assert(src.Ids[leftId].Table != null); Debug.Assert(src.Ids[rightId].Table != null); Expr e1 = Walker.CreateColumnExpr(ctx, src, leftId, colLeftId); Expr e2 = Walker.CreateColumnExpr(ctx, src, rightId, colRightId); Expr eq = Expr.PExpr_(parse, TK.EQ, e1, e2, null); if (eq != null && isOuterJoin) { E.ExprSetProperty(eq, EP.FromJoin); Debug.Assert(!E.ExprHasAnyProperty(eq, EP.TokenOnly | EP.Reduced)); E.ExprSetIrreducible(eq); eq.RightJoinTable = (short)e2.TableId; } where_ = Expr.And(ctx, where_, eq); }
/* ** Search the first N tables in pSrc, from left to right, looking for a ** table that has a column named zCol. ** ** When found, set *piTab and *piCol to the table index and column index ** of the matching column and return TRUE. ** ** If not found, return FALSE. */ static int tableAndColumnIndex( SrcList pSrc, /* Array of tables to search */ int N, /* Number of tables in pSrc.a[] to search */ string zCol, /* Name of the column we are looking for */ ref int piTab, /* Write index of pSrc.a[] here */ ref int piCol /* Write index of pSrc.a[*piTab].pTab.aCol[] here */ ) { int i; /* For looping over tables in pSrc */ int iCol; /* Index of column matching zCol */ Debug.Assert( ( piTab == 0 ) == ( piCol == 0 ) ); /* Both or neither are NULL */ for ( i = 0; i < N; i++ ) { iCol = columnIndex( pSrc.a[i].pTab, zCol ); if ( iCol >= 0 ) { //if( piTab ) { piTab = i; piCol = iCol; } return 1; } } return 0; }
/* ** This function is called to generate code that runs when table pTab is ** being dropped from the database. The SrcList passed as the second argument ** to this function contains a single entry guaranteed to resolve to ** table pTab. ** ** Normally, no code is required. However, if either ** ** (a) The table is the parent table of a FK constraint, or ** (b) The table is the child table of a deferred FK constraint and it is ** determined at runtime that there are outstanding deferred FK ** constraint violations in the database, ** ** then the equivalent of "DELETE FROM <tbl>" is executed before dropping ** the table from the database. Triggers are disabled while running this ** DELETE, but foreign key actions are not. */ static void sqlite3FkDropTable( Parse pParse, SrcList pName, Table pTab ) { sqlite3 db = pParse.db; if ( ( db.flags & SQLITE_ForeignKeys ) != 0 && !IsVirtual( pTab ) && null == pTab.pSelect ) { int iSkip = 0; Vdbe v = sqlite3GetVdbe( pParse ); Debug.Assert( v != null ); /* VDBE has already been allocated */ if ( sqlite3FkReferences( pTab ) == null ) { /* Search for a deferred foreign key constraint for which this table ** is the child table. If one cannot be found, return without ** generating any VDBE code. If one can be found, then jump over ** the entire DELETE if there are no outstanding deferred constraints ** when this statement is run. */ FKey p; for ( p = pTab.pFKey; p != null; p = p.pNextFrom ) { if ( p.isDeferred != 0 ) break; } if ( null == p ) return; iSkip = sqlite3VdbeMakeLabel( v ); sqlite3VdbeAddOp2( v, OP_FkIfZero, 1, iSkip ); } pParse.disableTriggers = 1; sqlite3DeleteFrom( pParse, sqlite3SrcListDup( db, pName, 0 ), null ); pParse.disableTriggers = 0; /* If the DELETE has generated immediate foreign key constraint ** violations, halt the VDBE and return an error at this point, before ** any modifications to the schema are made. This is because statement ** transactions are not able to rollback schema changes. */ sqlite3VdbeAddOp2( v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr( v ) + 2 ); sqlite3HaltConstraint( pParse, OE_Abort, "foreign key constraint failed", P4_STATIC ); if ( iSkip != 0 ) { sqlite3VdbeResolveLabel( v, iSkip ); } } }
static WRC ResolveExprStep(Walker walker, Expr expr) { NameContext nc = walker.u.NC; Debug.Assert(nc != null); Parse parse = nc.Parse; Debug.Assert(parse == walker.Parse); if (E.ExprHasAnyProperty(expr, EP.Resolved)) { return(WRC.Prune); } E.ExprSetProperty(expr, EP.Resolved); #if !NDEBUG if (nc.SrcList != null && nc.SrcList.Allocs > 0) { SrcList srcList = nc.SrcList; for (int i = 0; i < nc.SrcList.Srcs; i++) { Debug.Assert(srcList.Ids[i].Cursor >= 0 && srcList.Ids[i].Cursor < parse.Tabs); } } #endif switch (expr.OP) { #if ENABLE_UPDATE_DELETE_LIMIT && !OMIT_SUBQUERY // The special operator TK_ROW means use the rowid for the first column in the FROM clause. This is used by the LIMIT and ORDER BY // clause processing on UPDATE and DELETE statements. case TK.ROW: { SrcList srcList = nc.SrcList; Debug.Assert(srcList != null && srcList.Srcs == 1); SrcList.SrcListItem item = srcList.Ids[0]; expr.OP = TK.COLUMN; expr.Table = item.Table; expr.TableId = item.Cursor; expr.ColumnId = -1; expr.Aff = AFF.INTEGER; break; } #endif case TK.ID: // A lone identifier is the name of a column. { return(LookupName(parse, null, null, expr.u.Token, nc, expr)); } case TK.DOT: // A table name and column name: ID.ID Or a database, table and column: ID.ID.ID { string columnName; string tableName; string dbName; // if (srcList == nullptr) break; Expr right = expr.Right; if (right.OP == TK.ID) { dbName = null; tableName = expr.Left.u.Token; columnName = right.u.Token; } else { Debug.Assert(right.OP == TK.DOT); dbName = expr.Left.u.Token; tableName = right.Left.u.Token; columnName = right.Right.u.Token; } return(LookupName(parse, dbName, tableName, columnName, nc, expr)); } case TK.CONST_FUNC: case TK.FUNCTION: // Resolve function names { ExprList list = expr.x.List; // The argument list int n = (list != null ? list.Exprs : 0); // Number of arguments bool noSuchFunc = false; // True if no such function exists bool wrongNumArgs = false; // True if wrong number of arguments bool isAgg = false; // True if is an aggregate function TEXTENCODE encode = E.CTXENCODE(parse.Ctx); // The database encoding C.ASSERTCOVERAGE(expr.OP == TK.CONST_FUNC); Debug.Assert(!E.ExprHasProperty(expr, EP.xIsSelect)); string id = expr.u.Token; // The function name. int idLength = id.Length; // Number of characters in function name FuncDef def = Callback.FindFunction(parse.Ctx, id, idLength, n, encode, false); // Information about the function if (def == null) { def = Callback.FindFunction(parse.Ctx, id, idLength, -2, encode, false); if (def == null) { noSuchFunc = true; } else { wrongNumArgs = true; } } else { isAgg = (def.Func == null); } #if !OMIT_AUTHORIZATION if (def != null) { ARC auth = Auth.Check(parse, AUTH.FUNCTION, null, def.Name, null); // Authorization to use the function if (auth != ARC.OK) { if (auth == ARC.DENY) { parse.ErrorMsg("not authorized to use function: %s", def.Name); nc.Errs++; } expr.OP = TK.NULL; return(WRC.Prune); } } #endif if (isAgg && (nc.NCFlags & NC.AllowAgg) == 0) { parse.ErrorMsg("misuse of aggregate function %.*s()", idLength, id); nc.Errs++; isAgg = false; } else if (noSuchFunc && !ctx.Init.Busy) { parse.ErrorMsg("no such function: %.*s", idLength, id); nc.Errs++; } else if (wrongNumArgs) { parse.ErrorMsg("wrong number of arguments to function %.*s()", idLength, id); nc.Errs++; } if (isAgg) { nc.NCFlags &= ~NC.AllowAgg; } walker.WalkExprList(list); if (isAgg) { NameContext nc2 = nc; expr.OP = TK.AGG_FUNCTION; expr.OP2 = 0; while (nc2 != null && !expr.FunctionUsesThisSrc(nc2.SrcList)) { expr.OP2++; nc2 = nc2.Next; } if (nc2 != null) { nc2.NCFlags |= NC.HasAgg; } nc.NCFlags |= NC.AllowAgg; } // FIX ME: Compute pExpr->affinity based on the expected return type of the function return(WRC.Prune); } #if !OMIT_SUBQUERY case TK.SELECT: case TK.EXISTS: { C.ASSERTCOVERAGE(expr.OP == TK.EXISTS); goto case TK.IN; } #endif case TK.IN: { C.ASSERTCOVERAGE(expr.OP == TK.IN); if (E.ExprHasProperty(expr, EP.xIsSelect)) { int refs = nc.Refs; #if !OMIT_CHECK if ((nc.NCFlags & NC.IsCheck) != 0) { parse.ErrorMsg("subqueries prohibited in CHECK constraints"); } #endif walker.WalkSelect(expr.x.Select); Debug.Assert(nc.Refs >= refs); if (refs != nc.Refs) { E.ExprSetProperty(expr, EP.VarSelect); } } break; } #if !OMIT_CHECK case TK.VARIABLE: { if ((nc.NCFlags & NC.IsCheck) != 0) { parse.ErrorMsg("parameters prohibited in CHECK constraints"); } break; } #endif } return(parse.Errs != 0 || parse.Ctx.MallocFailed ? WRC.Abort : WRC.Continue); }
static void GenerateColumnNames(Parse parse, SrcList tabList, ExprList list) { #if !OMIT_EXPLAIN // If this is an EXPLAIN, skip this step if (parse.Explain != 0) return; #endif Vdbe v = parse.V; Context ctx = parse.Ctx; if (parse.ColNamesSet != 0 || C._NEVER(v == null) || ctx.MallocFailed) return; parse.ColNamesSet = 1; bool fullNames = ((ctx.Flags & Context.FLAG.FullColNames) != 0); bool shortNames = ((ctx.Flags & Context.FLAG.ShortColNames) != 0); v.SetNumCols(list.Exprs); for (int i = 0; i < list.Exprs; i++) { Expr p = list.Ids[i].Expr; if (C._NEVER(p == null)) continue; if (list.Ids[i].Name != null) { string name = list.Ids[i].Name; v.SetColName(i, COLNAME_NAME, name, C.DESTRUCTOR_TRANSIENT); } else if ((p.OP == TK.COLUMN || p.OP == TK.AGG_COLUMN) && tabList != null) { int colId = p.ColumnIdx; int j; for (j = 0; C._ALWAYS(j < tabList.Srcs); j++) if (tabList.Ids[j].Cursor == p.TableId) break; Debug.Assert(j < tabList.Srcs); Table table = tabList.Ids[j].Table; if (colId < 0) colId = table.PKey; Debug.Assert(colId == -1 || (colId >= 0 && colId < table.Cols.length)); string colName = (colId < 0 ? "rowid" : table.Cols[colId].Name); if (!shortNames && !fullNames) v.SetColName(i, COLNAME_NAME, list.Ids[i].Span, C.DESTRUCTOR_DYNAMIC); else if (fullNames) { string name = C._mtagprintf(ctx, "%s.%s", table.Name, colName); v.SetColName(i, COLNAME_NAME, name, C.DESTRUCTOR_DYNAMIC); } else v.SetColName(i, COLNAME_NAME, colName, C.DESTRUCTOR_TRANSIENT); } else v.SetColName(i, COLNAME_NAME, list.Ids[i].Span, C.DESTRUCTOR_TRANSIENT); } GenerateColumnTypes(parse, tabList, list); }
static void sqlite3Insert( Parse pParse, SrcList pTabList, int null_3, Select pSelect, IdList pColumn, int onError ) { sqlite3Insert( pParse, pTabList, null, pSelect, pColumn, onError ); }
/* ** Generate code that will tell the VDBE the names of columns ** in the result set. This information is used to provide the ** azCol[] values in the callback. */ static void generateColumnNames( Parse pParse, /* Parser context */ SrcList pTabList, /* List of tables */ ExprList pEList /* Expressions defining the result set */ ) { Vdbe v = pParse.pVdbe; int i, j; sqlite3 db = pParse.db; bool fullNames; bool shortNames; #if !SQLITE_OMIT_EXPLAIN /* If this is an EXPLAIN, skip this step */ if ( pParse.explain != 0 ) { return; } #endif if ( pParse.colNamesSet != 0 || NEVER( v == null ) /*|| db.mallocFailed != 0 */ ) return; pParse.colNamesSet = 1; fullNames = ( db.flags & SQLITE_FullColNames ) != 0; shortNames = ( db.flags & SQLITE_ShortColNames ) != 0; sqlite3VdbeSetNumCols( v, pEList.nExpr ); for ( i = 0; i < pEList.nExpr; i++ ) { Expr p; p = pEList.a[i].pExpr; if ( NEVER( p == null ) ) continue; if ( pEList.a[i].zName != null ) { string zName = pEList.a[i].zName; sqlite3VdbeSetColName( v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT ); } else if ( ( p.op == TK_COLUMN || p.op == TK_AGG_COLUMN ) && pTabList != null ) { Table pTab; string zCol; int iCol = p.iColumn; for ( j = 0; ALWAYS( j < pTabList.nSrc ); j++ ) { if ( pTabList.a[j].iCursor == p.iTable ) break; } Debug.Assert( j < pTabList.nSrc ); pTab = pTabList.a[j].pTab; if ( iCol < 0 ) iCol = pTab.iPKey; Debug.Assert( iCol == -1 || ( iCol >= 0 && iCol < pTab.nCol ) ); if ( iCol < 0 ) { zCol = "rowid"; } else { zCol = pTab.aCol[iCol].zName; } if ( !shortNames && !fullNames ) { sqlite3VdbeSetColName( v, i, COLNAME_NAME, pEList.a[i].zSpan, SQLITE_DYNAMIC );//sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC); } else if ( fullNames ) { string zName; zName = sqlite3MPrintf( db, "%s.%s", pTab.zName, zCol ); sqlite3VdbeSetColName( v, i, COLNAME_NAME, zName, SQLITE_DYNAMIC ); } else { sqlite3VdbeSetColName( v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT ); } } else { sqlite3VdbeSetColName( v, i, COLNAME_NAME, pEList.a[i].zSpan, SQLITE_DYNAMIC );//sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC); } } generateColumnTypes( pParse, pTabList, pEList ); }
static void sqlite3Insert( Parse pParse, SrcList pTabList, ExprList pList, int null_4, IdList pColumn, int onError ) { sqlite3Insert( pParse, pTabList, pList, null, pColumn, onError ); }
/* ** This function is used to add terms implied by JOIN syntax to the ** WHERE clause expression of a SELECT statement. The new term, which ** is ANDed with the existing WHERE clause, is of the form: ** ** (vtab1.col1 = tab2.col2) ** ** where tab1 is the iSrc'th table in SrcList pSrc and tab2 is the ** (iSrc+1)'th. Column col1 is column iColLeft of tab1, and col2 is ** column iColRight of tab2. */ static void addWhereTerm( Parse pParse, /* Parsing context */ SrcList pSrc, /* List of tables in FROM clause */ int iLeft, /* Index of first table to join in pSrc */ int iColLeft, /* Index of column in first table */ int iRight, /* Index of second table in pSrc */ int iColRight, /* Index of column in second table */ int isOuterJoin, /* True if this is an OUTER join */ ref Expr ppWhere /* IN/OUT: The WHERE clause to add to */ ) { sqlite3 db = pParse.db; Expr pE1; Expr pE2; Expr pEq; Debug.Assert( iLeft < iRight ); Debug.Assert( pSrc.nSrc > iRight ); Debug.Assert( pSrc.a[iLeft].pTab != null ); Debug.Assert( pSrc.a[iRight].pTab != null ); pE1 = sqlite3CreateColumnExpr( db, pSrc, iLeft, iColLeft ); pE2 = sqlite3CreateColumnExpr( db, pSrc, iRight, iColRight ); pEq = sqlite3PExpr( pParse, TK_EQ, pE1, pE2, 0 ); if ( pEq != null && isOuterJoin != 0 ) { ExprSetProperty( pEq, EP_FromJoin ); Debug.Assert( !ExprHasAnyProperty( pEq, EP_TokenOnly | EP_Reduced ) ); ExprSetIrreducible( pEq ); pEq.iRightJoinTable = (i16)pE2.iTable; } ppWhere = sqlite3ExprAnd( db, ppWhere, pEq ); }
static 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 */ ) { sqlite3 db; /* The main database structure */ Table pTab; /* The table to insert into. aka TABLE */ string zTab; /* Name of the table into which we are inserting */ string zDb; /* Name of the database holding this table */ int i = 0; int j = 0; int idx = 0; /* 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 nHidden = 0; /* Number of hidden columns if TABLE is virtual */ int baseCur = 0; /* VDBE VdbeCursor number for pTab */ int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ int endOfLoop = 0; /* Label for the end of the insertion loop */ bool useTempTable = false; /* Store SELECT results in intermediate table */ int srcTab = 0; /* Data comes from this temporary cursor if >=0 */ int addrInsTop = 0; /* Jump to label "D" */ int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */ int addrSelect = 0; /* Address of coroutine that implements the SELECT */ SelectDest dest; /* Destination for SELECT on rhs of INSERT */ int iDb; /* Index of database holding TABLE */ Db pDb; /* The database containing table being inserted into */ bool appendFlag = false; /* True if the insert is likely to be an append */ /* Register allocations */ int regFromSelect = 0; /* Base register for data coming from SELECT */ int regAutoinc = 0; /* Register holding the AUTOINCREMENT counter */ int regRowCount = 0; /* Memory cell used for the row counter */ int regIns; /* Block of regs holding rowid+data being inserted */ int regRowid; /* registers holding insert rowid */ int regData; /* register holding first column to insert */ int regEof = 0; /* Register recording end of SELECT data */ int[] aRegIdx = null; /* One register allocated to each index */ #if !SQLITE_OMIT_TRIGGER bool isView = false; /* True if attempting to insert into a view */ Trigger pTrigger; /* List of triggers on pTab, if required */ int tmask = 0; /* Mask of trigger times */ #endif db = pParse.db; dest = new SelectDest();// memset( &dest, 0, sizeof( dest ) ); if ( pParse.nErr != 0 /*|| db.mallocFailed != 0 */ ) { goto insert_cleanup; } /* Locate the table into which we will be inserting new information. */ Debug.Assert( pTabList.nSrc == 1 ); zTab = pTabList.a[0].zName; if ( NEVER( zTab == null ) ) goto insert_cleanup; pTab = sqlite3SrcListLookup( pParse, pTabList ); if ( pTab == null ) { goto insert_cleanup; } iDb = sqlite3SchemaToIndex( db, pTab.pSchema ); Debug.Assert( iDb < db.nDb ); pDb = db.aDb[iDb]; zDb = pDb.zName; #if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab.zName, 0, zDb) ){ goto insert_cleanup; } #endif /* Figure out if we have any triggers and if the table being ** inserted into is a view */ #if !SQLITE_OMIT_TRIGGER pTrigger = sqlite3TriggersExist( pParse, pTab, TK_INSERT, null, out tmask ); isView = pTab.pSelect != null; #else Trigger pTrigger = null; //# define pTrigger 0 int tmask = 0; //# define tmask 0 bool isView = false; #endif #if SQLITE_OMIT_VIEW //# undef isView isView = false; #endif #if !SQLITE_OMIT_TRIGGER Debug.Assert( ( pTrigger != null && tmask != 0 ) || ( pTrigger == null && tmask == 0 ) ); #endif #if !SQLITE_OMIT_VIEW /* If pTab is really a view, make sure it has been initialized. ** ViewGetColumnNames() is a no-op if pTab is not a view (or virtual ** module table). */ if ( sqlite3ViewGetColumnNames( pParse, pTab ) != -0 ) { goto insert_cleanup; } #endif /* Ensure that: * (a) the table is not read-only, * (b) that if it is a view then ON INSERT triggers exist */ if ( sqlite3IsReadOnly( pParse, pTab, tmask ) ) { goto insert_cleanup; } /* Allocate a VDBE */ v = sqlite3GetVdbe( pParse ); if ( v == null ) goto insert_cleanup; if ( pParse.nested == 0 ) sqlite3VdbeCountChanges( v ); sqlite3BeginWriteOperation( pParse, ( pSelect != null || pTrigger != null ) ? 1 : 0, iDb ); #if !SQLITE_OMIT_XFER_OPT /* If the statement is of the form ** ** INSERT INTO <table1> SELECT * FROM <table2>; ** ** Then special optimizations can be applied that make the transfer ** very fast and which reduce fragmentation of indices. ** ** This is the 2nd template. */ if ( pColumn == null && xferOptimization( pParse, pTab, pSelect, onError, iDb ) != 0 ) { Debug.Assert( null == pTrigger ); Debug.Assert( pList == null ); goto insert_end; } #endif // * SQLITE_OMIT_XFER_OPT */ /* If this is an AUTOINCREMENT table, look up the sequence number in the ** sqlite_sequence table and store it in memory cell regAutoinc. */ regAutoinc = autoIncBegin( pParse, iDb, pTab ); /* Figure out how many columns of data are supplied. If the data ** is coming from a SELECT statement, then generate a co-routine that ** produces a single row of the SELECT on each invocation. The ** co-routine is the common header to the 3rd and 4th templates. */ if ( pSelect != null ) { /* Data is coming from a SELECT. Generate code to implement that SELECT ** as a co-routine. The code is common to both the 3rd and 4th ** templates: ** ** EOF <- 0 ** X <- A ** goto B ** A: setup for the SELECT ** loop over the tables in the SELECT ** load value into register R..R+n ** yield X ** end loop ** cleanup after the SELECT ** EOF <- 1 ** yield X ** halt-error ** ** On each invocation of the co-routine, it puts a single row of the ** SELECT result into registers dest.iMem...dest.iMem+dest.nMem-1. ** (These output registers are allocated by sqlite3Select().) When ** the SELECT completes, it sets the EOF flag stored in regEof. */ int rc = 0, j1; regEof = ++pParse.nMem; sqlite3VdbeAddOp2( v, OP_Integer, 0, regEof ); /* EOF <- 0 */ #if SQLITE_DEBUG VdbeComment( v, "SELECT eof flag" ); #endif sqlite3SelectDestInit( dest, SRT_Coroutine, ++pParse.nMem ); addrSelect = sqlite3VdbeCurrentAddr( v ) + 2; sqlite3VdbeAddOp2( v, OP_Integer, addrSelect - 1, dest.iParm ); j1 = sqlite3VdbeAddOp2( v, OP_Goto, 0, 0 ); #if SQLITE_DEBUG VdbeComment( v, "Jump over SELECT coroutine" ); #endif /* Resolve the expressions in the SELECT statement and execute it. */ rc = sqlite3Select( pParse, pSelect, ref dest ); Debug.Assert( pParse.nErr == 0 || rc != 0 ); if ( rc != 0 || NEVER( pParse.nErr != 0 ) /*|| db.mallocFailed != 0 */ ) { goto insert_cleanup; } sqlite3VdbeAddOp2( v, OP_Integer, 1, regEof ); /* EOF <- 1 */ sqlite3VdbeAddOp1( v, OP_Yield, dest.iParm ); /* yield X */ sqlite3VdbeAddOp2( v, OP_Halt, SQLITE_INTERNAL, OE_Abort ); #if SQLITE_DEBUG VdbeComment( v, "End of SELECT coroutine" ); #endif sqlite3VdbeJumpHere( v, j1 ); /* label B: */ regFromSelect = dest.iMem; Debug.Assert( pSelect.pEList != null ); nColumn = pSelect.pEList.nExpr; Debug.Assert( dest.nMem == nColumn ); /* Set useTempTable to TRUE if the result of the SELECT statement ** should be written into a temporary table (template 4). Set to ** FALSE if each* row of the SELECT can be written directly into ** the destination table (template 3). ** ** 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 ( pTrigger != null || readsTable( pParse, addrSelect, iDb, pTab ) ) { useTempTable = true; } if ( useTempTable ) { /* Invoke the coroutine to extract information from the SELECT ** and add it to a transient table srcTab. The code generated ** here is from the 4th template: ** ** B: open temp table ** L: yield X ** if EOF goto M ** insert row from R..R+n into temp table ** goto L ** M: ... */ int regRec; /* Register to hold packed record */ int regTempRowid; /* Register to hold temp table ROWID */ int addrTop; /* Label "L" */ int addrIf; /* Address of jump to M */ srcTab = pParse.nTab++; regRec = sqlite3GetTempReg( pParse ); regTempRowid = sqlite3GetTempReg( pParse ); sqlite3VdbeAddOp2( v, OP_OpenEphemeral, srcTab, nColumn ); addrTop = sqlite3VdbeAddOp1( v, OP_Yield, dest.iParm ); addrIf = sqlite3VdbeAddOp1( v, OP_If, regEof ); sqlite3VdbeAddOp3( v, OP_MakeRecord, regFromSelect, nColumn, regRec ); sqlite3VdbeAddOp2( v, OP_NewRowid, srcTab, regTempRowid ); sqlite3VdbeAddOp3( v, OP_Insert, srcTab, regRec, regTempRowid ); sqlite3VdbeAddOp2( v, OP_Goto, 0, addrTop ); sqlite3VdbeJumpHere( v, addrIf ); sqlite3ReleaseTempReg( pParse, regRec ); sqlite3ReleaseTempReg( pParse, regTempRowid ); } } else { /* This is the case if the data for the INSERT is coming from a VALUES ** clause */ NameContext sNC; sNC = new NameContext();// memset( &sNC, 0, sNC ).Length; sNC.pParse = pParse; srcTab = -1; Debug.Assert( !useTempTable ); nColumn = pList != null ? pList.nExpr : 0; for ( i = 0; i < nColumn; i++ ) { if ( sqlite3ResolveExprNames( sNC, ref pList.a[i].pExpr ) != 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 ( IsVirtual( pTab ) ) { for ( i = 0; i < pTab.nCol; i++ ) { nHidden += ( IsHiddenColumn( pTab.aCol[i] ) ? 1 : 0 ); } } if ( pColumn == null && nColumn != 0 && nColumn != ( pTab.nCol - nHidden ) ) { sqlite3ErrorMsg( pParse, "table %S has %d columns but %d values were supplied", pTabList, 0, pTab.nCol - nHidden, nColumn ); goto insert_cleanup; } if ( pColumn != null && 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 != null ) { 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 ( pColumn.a[i].zName.Equals( pTab.aCol[j].zName ,StringComparison.InvariantCultureIgnoreCase ) ) { 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.checkSchema = 1; 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 == null && nColumn > 0 ) { keyColumn = pTab.iPKey; } /* Initialize the count of rows to be inserted */ if ( ( db.flags & SQLITE_CountRows ) != 0 ) { regRowCount = ++pParse.nMem; sqlite3VdbeAddOp2( v, OP_Integer, 0, regRowCount ); } /* If this is not a view, open the table and and all indices */ if ( !isView ) { int nIdx; baseCur = pParse.nTab; nIdx = sqlite3OpenTableAndIndices( pParse, pTab, baseCur, OP_OpenWrite ); aRegIdx = new int[nIdx + 1];// sqlite3DbMallocRaw( db, sizeof( int ) * ( nIdx + 1 ) ); if ( aRegIdx == null ) { goto insert_cleanup; } for ( i = 0; i < nIdx; i++ ) { aRegIdx[i] = ++pParse.nMem; } } /* This is the top of the main insertion loop */ if ( useTempTable ) { /* This block codes the top of loop only. The complete loop is the ** following pseudocode (template 4): ** ** rewind temp table ** C: loop over rows of intermediate table ** transfer values form intermediate table into <table> ** end loop ** D: ... */ addrInsTop = sqlite3VdbeAddOp1( v, OP_Rewind, srcTab ); addrCont = sqlite3VdbeCurrentAddr( v ); } else if ( pSelect != null ) { /* This block codes the top of loop only. The complete loop is the ** following pseudocode (template 3): ** ** C: yield X ** if EOF goto D ** insert the select result into <table> from R..R+n ** goto C ** D: ... */ addrCont = sqlite3VdbeAddOp1( v, OP_Yield, dest.iParm ); addrInsTop = sqlite3VdbeAddOp1( v, OP_If, regEof ); } /* Allocate registers for holding the rowid of the new row, ** the content of the new row, and the assemblied row record. */ regRowid = regIns = pParse.nMem + 1; pParse.nMem += pTab.nCol + 1; if ( IsVirtual( pTab ) ) { regRowid++; pParse.nMem++; } regData = regRowid + 1; /* Run the BEFORE and INSTEAD OF triggers, if there are any */ endOfLoop = sqlite3VdbeMakeLabel( v ); #if !SQLITE_OMIT_TRIGGER if ( ( tmask & TRIGGER_BEFORE ) != 0 ) { int regCols = sqlite3GetTempRange( pParse, pTab.nCol + 1 ); /* 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 ) { sqlite3VdbeAddOp2( v, OP_Integer, -1, regCols ); } else { int j1; if ( useTempTable ) { sqlite3VdbeAddOp3( v, OP_Column, srcTab, keyColumn, regCols ); } else { Debug.Assert( pSelect == null ); /* Otherwise useTempTable is true */ sqlite3ExprCode( pParse, pList.a[keyColumn].pExpr, regCols ); } j1 = sqlite3VdbeAddOp1( v, OP_NotNull, regCols ); sqlite3VdbeAddOp2( v, OP_Integer, -1, regCols ); sqlite3VdbeJumpHere( v, j1 ); sqlite3VdbeAddOp1( v, OP_MustBeInt, regCols ); } /* Cannot have triggers on a virtual table. If it were possible, ** this block would have to account for hidden column. */ Debug.Assert( !IsVirtual( pTab ) ); /* Create the new column data */ for ( i = 0; i < pTab.nCol; i++ ) { if ( pColumn == null ) { j = i; } else { for ( j = 0; j < pColumn.nId; j++ ) { if ( pColumn.a[j].idx == i ) break; } } if ( ( !useTempTable && null == pList ) || ( pColumn != null && j >= pColumn.nId ) ) { sqlite3ExprCode( pParse, pTab.aCol[i].pDflt, regCols + i + 1 ); } else if ( useTempTable ) { sqlite3VdbeAddOp3( v, OP_Column, srcTab, j, regCols + i + 1 ); } else { Debug.Assert( pSelect == null ); /* Otherwise useTempTable is true */ sqlite3ExprCodeAndCache( pParse, pList.a[j].pExpr, regCols + i + 1 ); } } /* 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 ) { sqlite3VdbeAddOp2( v, OP_Affinity, regCols + 1, pTab.nCol ); sqlite3TableAffinityStr( v, pTab ); } /* Fire BEFORE or INSTEAD OF triggers */ sqlite3CodeRowTrigger( pParse, pTrigger, TK_INSERT, null, TRIGGER_BEFORE, pTab, regCols - pTab.nCol - 1, onError, endOfLoop ); sqlite3ReleaseTempRange( pParse, regCols, pTab.nCol + 1 ); } #endif /* Push the record number for the new entry onto the stack. The ** record number is a randomly generate integer created by NewRowid ** 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 ( IsVirtual( pTab ) ) { /* The row that the VUpdate opcode will delete: none */ sqlite3VdbeAddOp2( v, OP_Null, 0, regIns ); } if ( keyColumn >= 0 ) { if ( useTempTable ) { sqlite3VdbeAddOp3( v, OP_Column, srcTab, keyColumn, regRowid ); } else if ( pSelect != null ) { sqlite3VdbeAddOp2( v, OP_SCopy, regFromSelect + keyColumn, regRowid ); } else { VdbeOp pOp; sqlite3ExprCode( pParse, pList.a[keyColumn].pExpr, regRowid ); pOp = sqlite3VdbeGetOp( v, -1 ); if ( ALWAYS( pOp != null ) && pOp.opcode == OP_Null && !IsVirtual( pTab ) ) { appendFlag = true; pOp.opcode = OP_NewRowid; pOp.p1 = baseCur; pOp.p2 = regRowid; pOp.p3 = regAutoinc; } } /* If the PRIMARY KEY expression is NULL, then use OP_NewRowid ** to generate a unique primary key value. */ if ( !appendFlag ) { int j1; if ( !IsVirtual( pTab ) ) { j1 = sqlite3VdbeAddOp1( v, OP_NotNull, regRowid ); sqlite3VdbeAddOp3( v, OP_NewRowid, baseCur, regRowid, regAutoinc ); sqlite3VdbeJumpHere( v, j1 ); } else { j1 = sqlite3VdbeCurrentAddr( v ); sqlite3VdbeAddOp2( v, OP_IsNull, regRowid, j1 + 2 ); } sqlite3VdbeAddOp1( v, OP_MustBeInt, regRowid ); } } else if ( IsVirtual( pTab ) ) { sqlite3VdbeAddOp2( v, OP_Null, 0, regRowid ); } else { sqlite3VdbeAddOp3( v, OP_NewRowid, baseCur, regRowid, regAutoinc ); appendFlag = true; } autoIncStep( pParse, regAutoinc, regRowid ); /* Push onto the stack, data for all columns of the new entry, beginning ** with the first column. */ nHidden = 0; for ( i = 0; i < pTab.nCol; i++ ) { int iRegStore = regRowid + 1 + 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. */ sqlite3VdbeAddOp2( v, OP_Null, 0, iRegStore ); continue; } if ( pColumn == null ) { if ( IsHiddenColumn( pTab.aCol[i] ) ) { Debug.Assert( IsVirtual( pTab ) ); j = -1; nHidden++; } else { j = i - nHidden; } } else { for ( j = 0; j < pColumn.nId; j++ ) { if ( pColumn.a[j].idx == i ) break; } } if ( j < 0 || nColumn == 0 || ( pColumn != null && j >= pColumn.nId ) ) { sqlite3ExprCode( pParse, pTab.aCol[i].pDflt, iRegStore ); } else if ( useTempTable ) { sqlite3VdbeAddOp3( v, OP_Column, srcTab, j, iRegStore ); } else if ( pSelect != null ) { sqlite3VdbeAddOp2( v, OP_SCopy, regFromSelect + j, iRegStore ); } else { sqlite3ExprCode( pParse, pList.a[j].pExpr, iRegStore ); } } /* Generate code to check constraints and generate index keys and ** do the insertion. */ #if !SQLITE_OMIT_VIRTUALTABLE if ( IsVirtual( pTab ) ) { VTable pVTab = sqlite3GetVTable( db, pTab ); sqlite3VtabMakeWritable( pParse, pTab ); sqlite3VdbeAddOp4( v, OP_VUpdate, 1, pTab.nCol + 2, regIns, pVTab, P4_VTAB ); sqlite3VdbeChangeP5( v, (byte)( onError == OE_Default ? OE_Abort : onError ) ); sqlite3MayAbort( pParse ); } else #endif { int isReplace = 0; /* Set to true if constraints may cause a replace */ sqlite3GenerateConstraintChecks( pParse, pTab, baseCur, regIns, aRegIdx, keyColumn >= 0 ? 1 : 0, false, onError, endOfLoop, out isReplace ); sqlite3FkCheck( pParse, pTab, 0, regIns ); sqlite3CompleteInsertion( pParse, pTab, baseCur, regIns, aRegIdx, false, appendFlag, isReplace == 0 ); } } /* Update the count of rows that are inserted */ if ( ( db.flags & SQLITE_CountRows ) != 0 ) { sqlite3VdbeAddOp2( v, OP_AddImm, regRowCount, 1 ); } #if !SQLITE_OMIT_TRIGGER if ( pTrigger != null ) { /* Code AFTER triggers */ sqlite3CodeRowTrigger( pParse, pTrigger, TK_INSERT, null, TRIGGER_AFTER, pTab, regData - 2 - pTab.nCol, onError, endOfLoop ); } #endif /* The bottom of the main insertion loop, if the data source ** is a SELECT statement. */ sqlite3VdbeResolveLabel( v, endOfLoop ); if ( useTempTable ) { sqlite3VdbeAddOp2( v, OP_Next, srcTab, addrCont ); sqlite3VdbeJumpHere( v, addrInsTop ); sqlite3VdbeAddOp1( v, OP_Close, srcTab ); } else if ( pSelect != null ) { sqlite3VdbeAddOp2( v, OP_Goto, 0, addrCont ); sqlite3VdbeJumpHere( v, addrInsTop ); } if ( !IsVirtual( pTab ) && !isView ) { /* Close all tables opened */ sqlite3VdbeAddOp1( v, OP_Close, baseCur ); for ( idx = 1, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, idx++ ) { sqlite3VdbeAddOp1( v, OP_Close, idx + baseCur ); } } insert_end: /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ if ( pParse.nested == 0 && pParse.pTriggerTab == null ) { sqlite3AutoincrementEnd( pParse ); } /* ** Return the number of rows inserted. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. */ if ( ( db.flags & SQLITE_CountRows ) != 0 && 0 == pParse.nested && null == pParse.pTriggerTab ) { sqlite3VdbeAddOp2( v, OP_ResultRow, regRowCount, 1 ); sqlite3VdbeSetNumCols( v, 1 ); sqlite3VdbeSetColName( v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC ); } insert_cleanup: sqlite3SrcListDelete( db, ref pTabList ); sqlite3ExprListDelete( db, ref pList ); sqlite3SelectDelete( db, ref pSelect ); sqlite3IdListDelete( db, ref pColumn ); sqlite3DbFree( db, ref aRegIdx ); }
/* ** Allocate a new Select structure and return a pointer to that ** structure. */ // OVERLOADS, so I don't need to rewrite parse.c static Select sqlite3SelectNew( Parse pParse, int null_2, SrcList pSrc, int null_4, int null_5, int null_6, int null_7, int isDistinct, int null_9, int null_10 ) { return sqlite3SelectNew( pParse, null, pSrc, null, null, null, null, isDistinct, null, null ); }
/* ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" ** command. */ static void sqlite3AlterRenameTable( Parse pParse, /* Parser context. */ SrcList pSrc, /* The table to rename. */ Token pName /* The new table name. */ ) { int iDb; /* Database that contains the table */ string zDb; /* Name of database iDb */ Table pTab; /* Table being renamed */ string zName = null; /* NULL-terminated version of pName */ sqlite3 db = pParse.db; /* Database connection */ int nTabName; /* Number of UTF-8 characters in zTabName */ string zTabName; /* Original name of the table */ Vdbe v; #if !SQLITE_OMIT_TRIGGER string zWhere = ""; /* Where clause to locate temp triggers */ #endif VTable pVTab = null; /* Non-zero if this is a v-tab with an xRename() */ int savedDbFlags; /* Saved value of db->flags */ savedDbFlags = db.flags; //if ( NEVER( db.mallocFailed != 0 ) ) goto exit_rename_table; Debug.Assert( pSrc.nSrc == 1 ); Debug.Assert( sqlite3BtreeHoldsAllMutexes( pParse.db ) ); pTab = sqlite3LocateTable( pParse, 0, pSrc.a[0].zName, pSrc.a[0].zDatabase ); if ( pTab == null ) goto exit_rename_table; iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema ); zDb = db.aDb[iDb].zName; db.flags |= SQLITE_PreferBuiltin; /* Get a NULL terminated version of the new table name. */ zName = sqlite3NameFromToken( db, pName ); if ( zName == null ) goto exit_rename_table; /* Check that a table or index named 'zName' does not already exist ** in database iDb. If so, this is an error. */ if ( sqlite3FindTable( db, zName, zDb ) != null || sqlite3FindIndex( db, zName, zDb ) != null ) { sqlite3ErrorMsg( pParse, "there is already another table or index with this name: %s", zName ); goto exit_rename_table; } /* Make sure it is not a system table being altered, or a reserved name ** that the table is being renamed to. */ if ( SQLITE_OK!=isSystemTable(pParse, pTab.zName) ) { goto exit_rename_table; } if ( SQLITE_OK != sqlite3CheckObjectName( pParse, zName ) ) { goto exit_rename_table; } #if !SQLITE_OMIT_VIEW if ( pTab.pSelect != null ) { sqlite3ErrorMsg( pParse, "view %s may not be altered", pTab.zName ); goto exit_rename_table; } #endif #if !SQLITE_OMIT_AUTHORIZATION /* Invoke the authorization callback. */ if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab.zName, 0) ){ goto exit_rename_table; } #endif if ( sqlite3ViewGetColumnNames( pParse, pTab ) != 0 ) { goto exit_rename_table; } #if !SQLITE_OMIT_VIRTUALTABLE if ( IsVirtual( pTab ) ) { pVTab = sqlite3GetVTable( db, pTab ); if ( pVTab.pVtab.pModule.xRename == null ) { pVTab = null; } } #endif /* Begin a transaction and code the VerifyCookie for database iDb. ** Then modify the schema cookie (since the ALTER TABLE modifies the ** schema). Open a statement transaction if the table is a virtual ** table. */ v = sqlite3GetVdbe( pParse ); if ( v == null ) { goto exit_rename_table; } sqlite3BeginWriteOperation( pParse, pVTab != null ? 1 : 0, iDb ); sqlite3ChangeCookie( pParse, iDb ); /* If this is a virtual table, invoke the xRename() function if ** one is defined. The xRename() callback will modify the names ** of any resources used by the v-table implementation (including other ** SQLite tables) that are identified by the name of the virtual table. */ #if !SQLITE_OMIT_VIRTUALTABLE if ( pVTab !=null) { int i = ++pParse.nMem; sqlite3VdbeAddOp4( v, OP_String8, 0, i, 0, zName, 0 ); sqlite3VdbeAddOp4( v, OP_VRename, i, 0, 0, pVTab, P4_VTAB ); sqlite3MayAbort(pParse); } #endif /* figure out how many UTF-8 characters are in zName */ zTabName = pTab.zName; nTabName = sqlite3Utf8CharLen( zTabName, -1 ); #if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER) if ( ( db.flags & SQLITE_ForeignKeys ) != 0 ) { /* If foreign-key support is enabled, rewrite the CREATE TABLE ** statements corresponding to all child tables of foreign key constraints ** for which the renamed table is the parent table. */ if ( ( zWhere = whereForeignKeys( pParse, pTab ) ) != null ) { sqlite3NestedParse( pParse, "UPDATE \"%w\".%s SET " + "sql = sqlite_rename_parent(sql, %Q, %Q) " + "WHERE %s;", zDb, SCHEMA_TABLE( iDb ), zTabName, zName, zWhere ); sqlite3DbFree( db, ref zWhere ); } } #endif /* Modify the sqlite_master table to use the new table name. */ sqlite3NestedParse( pParse, "UPDATE %Q.%s SET " + #if SQLITE_OMIT_TRIGGER "sql = sqlite_rename_table(sql, %Q), " + #else "sql = CASE " + "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" + "ELSE sqlite_rename_table(sql, %Q) END, " + #endif "tbl_name = %Q, " + "name = CASE " + "WHEN type='table' THEN %Q " + "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " + "'sqlite_autoindex_' || %Q || substr(name,%d+18) " + "ELSE name END " + "WHERE tbl_name=%Q AND " + "(type='table' OR type='index' OR type='trigger');", zDb, SCHEMA_TABLE( iDb ), zName, zName, zName, #if !SQLITE_OMIT_TRIGGER zName, #endif zName, nTabName, zTabName ); #if !SQLITE_OMIT_AUTOINCREMENT /* If the sqlite_sequence table exists in this database, then update ** it with the new table name. */ if ( sqlite3FindTable( db, "sqlite_sequence", zDb ) != null ) { sqlite3NestedParse( pParse, "UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q", zDb, zName, pTab.zName ); } #endif #if !SQLITE_OMIT_TRIGGER /* If there are TEMP triggers on this table, modify the sqlite_temp_master ** table. Don't do this if the table being ALTERed is itself located in ** the temp database. */ if ( ( zWhere = whereTempTriggers( pParse, pTab ) ) != "" ) { sqlite3NestedParse( pParse, "UPDATE sqlite_temp_master SET " + "sql = sqlite_rename_trigger(sql, %Q), " + "tbl_name = %Q " + "WHERE %s;", zName, zName, zWhere ); sqlite3DbFree( db, ref zWhere ); } #endif #if !(SQLITE_OMIT_FOREIGN_KEY) && !(SQLITE_OMIT_TRIGGER) if ( ( db.flags & SQLITE_ForeignKeys ) != 0 ) { FKey p; for ( p = sqlite3FkReferences( pTab ); p != null; p = p.pNextTo ) { Table pFrom = p.pFrom; if ( pFrom != pTab ) { reloadTableSchema( pParse, p.pFrom, pFrom.zName ); } } } #endif /* Drop and reload the internal table schema. */ reloadTableSchema( pParse, pTab, zName ); exit_rename_table: sqlite3SrcListDelete( db, ref pSrc ); sqlite3DbFree( db, ref zName ); db.flags = savedDbFlags; }
/* ** This is called by the parser when it sees a CREATE TRIGGER statement ** up to the point of the BEGIN before the trigger actions. A Trigger ** structure is generated based on the information available and stored ** in pParse.pNewTrigger. After the trigger actions have been parsed, the ** sqlite3FinishTrigger() function is called to complete the trigger ** construction process. */ static void sqlite3BeginTrigger( Parse pParse, /* The parse context of the CREATE TRIGGER statement */ Token pName1, /* The name of the trigger */ Token pName2, /* The name of the trigger */ int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */ int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */ IdList pColumns, /* column list if this is an UPDATE OF trigger */ SrcList pTableName,/* The name of the table/view the trigger applies to */ Expr pWhen, /* WHEN clause */ int isTemp, /* True if the TEMPORARY keyword is present */ int noErr /* Suppress errors if the trigger already exists */ ) { Trigger pTrigger = null; /* The new trigger */ Table pTab; /* Table that the trigger fires off of */ string zName = null; /* Name of the trigger */ sqlite3 db = pParse.db; /* The database connection */ int iDb; /* The database to store the trigger in */ Token pName = null; /* The unqualified db name */ DbFixer sFix = new DbFixer(); /* State vector for the DB fixer */ int iTabDb; /* Index of the database holding pTab */ Debug.Assert( pName1 != null ); /* pName1.z might be NULL, but not pName1 itself */ Debug.Assert( pName2 != null ); Debug.Assert( op == TK_INSERT || op == TK_UPDATE || op == TK_DELETE ); Debug.Assert( op > 0 && op < 0xff ); if ( isTemp != 0 ) { /* If TEMP was specified, then the trigger name may not be qualified. */ if ( pName2.n > 0 ) { sqlite3ErrorMsg( pParse, "temporary trigger may not have qualified name" ); goto trigger_cleanup; } iDb = 1; pName = pName1; } else { /* Figure out the db that the the trigger will be created in */ iDb = sqlite3TwoPartName( pParse, pName1, pName2, ref pName ); if ( iDb < 0 ) { goto trigger_cleanup; } } if ( null == pTableName ) //|| db.mallocFailed { goto trigger_cleanup; } /* A long-standing parser bug is that this syntax was allowed: ** ** CREATE TRIGGER attached.demo AFTER INSERT ON attached.tab .... ** ^^^^^^^^ ** ** To maintain backwards compatibility, ignore the database ** name on pTableName if we are reparsing our of SQLITE_MASTER. */ if ( db.init.busy != 0 && iDb != 1 ) { //sqlite3DbFree( db, pTableName.a[0].zDatabase ); pTableName.a[0].zDatabase = null; } /* If the trigger name was unqualified, and the table is a temp table, ** then set iDb to 1 to create the trigger in the temporary database. ** If sqlite3SrcListLookup() returns 0, indicating the table does not ** exist, the error is caught by the block below. */ if ( pTableName == null /*|| db.mallocFailed != 0 */ ) { goto trigger_cleanup; } pTab = sqlite3SrcListLookup( pParse, pTableName ); if ( db.init.busy == 0 && pName2.n == 0 && pTab != null && pTab.pSchema == db.aDb[1].pSchema ) { iDb = 1; } /* Ensure the table name matches database name and that the table exists */ // if ( db.mallocFailed != 0 ) goto trigger_cleanup; Debug.Assert( pTableName.nSrc == 1 ); if ( sqlite3FixInit( sFix, pParse, iDb, "trigger", pName ) != 0 && sqlite3FixSrcList( sFix, pTableName ) != 0 ) { goto trigger_cleanup; } pTab = sqlite3SrcListLookup( pParse, pTableName ); if ( pTab == null ) { /* The table does not exist. */ if ( db.init.iDb == 1 ) { /* Ticket #3810. ** Normally, whenever a table is dropped, all associated triggers are ** dropped too. But if a TEMP trigger is created on a non-TEMP table ** and the table is dropped by a different database connection, the ** trigger is not visible to the database connection that does the ** drop so the trigger cannot be dropped. This results in an ** "orphaned trigger" - a trigger whose associated table is missing. */ db.init.orphanTrigger = 1; } goto trigger_cleanup; } if ( IsVirtual( pTab ) ) { sqlite3ErrorMsg( pParse, "cannot create triggers on virtual tables" ); goto trigger_cleanup; } /* Check that the trigger name is not reserved and that no trigger of the ** specified name exists */ zName = sqlite3NameFromToken( db, pName ); if ( zName == null || SQLITE_OK != sqlite3CheckObjectName( pParse, zName ) ) { goto trigger_cleanup; } Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) ); if ( sqlite3HashFind( ( db.aDb[iDb].pSchema.trigHash ), zName, sqlite3Strlen30( zName ), (Trigger)null ) != null ) { if ( noErr == 0 ) { sqlite3ErrorMsg( pParse, "trigger %T already exists", pName ); } else { Debug.Assert( 0==db.init.busy ); sqlite3CodeVerifySchema( pParse, iDb ); } goto trigger_cleanup; } /* Do not create a trigger on a system table */ if ( pTab.zName.StartsWith( "sqlite_", System.StringComparison.InvariantCultureIgnoreCase ) ) { sqlite3ErrorMsg( pParse, "cannot create trigger on system table" ); pParse.nErr++; goto trigger_cleanup; } /* INSTEAD of triggers are only for views and views only support INSTEAD ** of triggers. */ if ( pTab.pSelect != null && tr_tm != TK_INSTEAD ) { sqlite3ErrorMsg( pParse, "cannot create %s trigger on view: %S", ( tr_tm == TK_BEFORE ) ? "BEFORE" : "AFTER", pTableName, 0 ); goto trigger_cleanup; } if ( pTab.pSelect == null && tr_tm == TK_INSTEAD ) { sqlite3ErrorMsg( pParse, "cannot create INSTEAD OF" + " trigger on table: %S", pTableName, 0 ); goto trigger_cleanup; } iTabDb = sqlite3SchemaToIndex( db, pTab.pSchema ); #if !SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_CREATE_TRIGGER; string zDb = db.aDb[iTabDb].zName; string zDbTrig = isTemp ? db.aDb[1].zName : zDb; if( iTabDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER; if( sqlite3AuthCheck(pParse, code, zName, pTab.zName, zDbTrig) ){ goto trigger_cleanup; } if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iTabDb),0,zDb)){ goto trigger_cleanup; } } #endif /* INSTEAD OF triggers can only appear on views and BEFORE triggers ** cannot appear on views. So we might as well translate every ** INSTEAD OF trigger into a BEFORE trigger. It simplifies code ** elsewhere. */ if ( tr_tm == TK_INSTEAD ) { tr_tm = TK_BEFORE; } /* Build the Trigger object */ pTrigger = new Trigger();// (Trigger*)sqlite3DbMallocZero( db, sizeof(Trigger )) if ( pTrigger == null ) goto trigger_cleanup; pTrigger.zName = zName; pTrigger.table = pTableName.a[0].zName;// sqlite3DbStrDup( db, pTableName.a[0].zName ); pTrigger.pSchema = db.aDb[iDb].pSchema; pTrigger.pTabSchema = pTab.pSchema; pTrigger.op = (u8)op; pTrigger.tr_tm = tr_tm == TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; pTrigger.pWhen = sqlite3ExprDup( db, pWhen, EXPRDUP_REDUCE ); pTrigger.pColumns = sqlite3IdListDup( db, pColumns ); Debug.Assert( pParse.pNewTrigger == null ); pParse.pNewTrigger = pTrigger; trigger_cleanup: sqlite3DbFree( db, ref zName ); sqlite3SrcListDelete( db, ref pTableName ); sqlite3IdListDelete( db, ref pColumns ); sqlite3ExprDelete( db, ref pWhen ); if ( pParse.pNewTrigger == null ) { sqlite3DeleteTrigger( db, ref pTrigger ); } else { Debug.Assert( pParse.pNewTrigger == pTrigger ); } }
/* ** This function is called by the parser after the table-name in ** an "ALTER TABLE <table-name> ADD" statement is parsed. Argument ** pSrc is the full-name of the table being altered. ** ** This routine makes a (partial) copy of the Table structure ** for the table being altered and sets Parse.pNewTable to point ** to it. Routines called by the parser as the column definition ** is parsed (i.e. sqlite3AddColumn()) add the new Column data to ** the copy. The copy of the Table structure is deleted by tokenize.c ** after parsing is finished. ** ** Routine sqlite3AlterFinishAddColumn() will be called to complete ** coding the "ALTER TABLE ... ADD" statement. */ static void sqlite3AlterBeginAddColumn( Parse pParse, SrcList pSrc ) { Table pNew; Table pTab; Vdbe v; int iDb; int i; int nAlloc; sqlite3 db = pParse.db; /* Look up the table being altered. */ Debug.Assert( pParse.pNewTable == null ); Debug.Assert( sqlite3BtreeHoldsAllMutexes( db ) ); // if ( db.mallocFailed != 0 ) goto exit_begin_add_column; pTab = sqlite3LocateTable( pParse, 0, pSrc.a[0].zName, pSrc.a[0].zDatabase ); if ( pTab == null ) goto exit_begin_add_column; if ( IsVirtual( pTab ) ) { sqlite3ErrorMsg( pParse, "virtual tables may not be altered" ); goto exit_begin_add_column; } /* Make sure this is not an attempt to ALTER a view. */ if ( pTab.pSelect != null ) { sqlite3ErrorMsg( pParse, "Cannot add a column to a view" ); goto exit_begin_add_column; } if ( SQLITE_OK != isSystemTable( pParse, pTab.zName ) ) { goto exit_begin_add_column; } Debug.Assert( pTab.addColOffset > 0 ); iDb = sqlite3SchemaToIndex( db, pTab.pSchema ); /* Put a copy of the Table struct in Parse.pNewTable for the ** sqlite3AddColumn() function and friends to modify. But modify ** the name by adding an "sqlite_altertab_" prefix. By adding this ** prefix, we insure that the name will not collide with an existing ** table because user table are not allowed to have the "sqlite_" ** prefix on their name. */ pNew = new Table();// (Table*)sqlite3DbMallocZero( db, sizeof(Table)) if ( pNew == null ) goto exit_begin_add_column; pParse.pNewTable = pNew; pNew.nRef = 1; pNew.nCol = pTab.nCol; Debug.Assert( pNew.nCol > 0 ); nAlloc = ( ( ( pNew.nCol - 1 ) / 8 ) * 8 ) + 8; Debug.Assert( nAlloc >= pNew.nCol && nAlloc % 8 == 0 && nAlloc - pNew.nCol < 8 ); pNew.aCol = new Column[nAlloc];// (Column*)sqlite3DbMallocZero( db, sizeof(Column) * nAlloc ); pNew.zName = sqlite3MPrintf( db, "sqlite_altertab_%s", pTab.zName ); if ( pNew.aCol == null || pNew.zName == null ) { // db.mallocFailed = 1; goto exit_begin_add_column; } // memcpy( pNew.aCol, pTab.aCol, sizeof(Column) * pNew.nCol ); for ( i = 0; i < pNew.nCol; i++ ) { Column pCol = pTab.aCol[i].Copy(); // sqlite3DbStrDup( db, pCol.zName ); pCol.zColl = null; pCol.zType = null; pCol.pDflt = null; pCol.zDflt = null; pNew.aCol[i] = pCol; } pNew.pSchema = db.aDb[iDb].pSchema; pNew.addColOffset = pTab.addColOffset; pNew.nRef = 1; /* Begin a transaction and increment the schema cookie. */ sqlite3BeginWriteOperation( pParse, 0, iDb ); v = sqlite3GetVdbe( pParse ); if ( v == null ) goto exit_begin_add_column; sqlite3ChangeCookie( pParse, iDb ); exit_begin_add_column: sqlite3SrcListDelete( db, ref pSrc ); return; }
/* ** 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 */ static void sqlite3Update( Parse pParse, /* The parser context */ 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 */ int addr = 0; /* 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 iCur; /* VDBE Cursor number of pTab */ sqlite3 db; /* The database structure */ int[] aRegIdx = null; /* One register assigned to each index to be updated */ int[] aXRef = null; /* 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. */ bool chngRowid; /* True if the record number is being changed */ Expr pRowidExpr = null; /* Expression defining the new record number */ bool openAll = false; /* True if all indices need to be opened */ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ int iDb; /* Database containing the table being updated */ bool okOnePass; /* True for one-pass algorithm without the FIFO */ bool hasFK; /* True if foreign key processing is required */ #if !SQLITE_OMIT_TRIGGER bool isView; /* True when updating a view (INSTEAD OF trigger) */ Trigger pTrigger; /* List of triggers on pTab, if required */ int tmask = 0; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ #endif int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */ /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ int regOldRowid; /* The old rowid */ int regNewRowid; /* The new rowid */ int regNew; int regOld = 0; int regRowSet = 0; /* Rowset of rows to be updated */ sContext = new AuthContext(); //memset( &sContext, 0, sizeof( sContext ) ); db = pParse.db; if (pParse.nErr != 0 /*|| db.mallocFailed != 0 */ ) { goto update_cleanup; } Debug.Assert(pTabList.nSrc == 1); /* Locate the table which we want to update. */ pTab = sqlite3SrcListLookup(pParse, pTabList); if (pTab == null) goto update_cleanup; iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema); /* Figure out if we have any triggers and if the table being ** updated is a view. */ #if !SQLITE_OMIT_TRIGGER pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, out tmask); isView = pTab.pSelect != null; Debug.Assert(pTrigger != null || tmask == 0); #else const Trigger pTrigger = null;//# define pTrigger 0 const int tmask = 0; //# define tmask 0 #endif #if SQLITE_OMIT_TRIGGER || SQLITE_OMIT_VIEW // # undef isView const bool isView = false; //# define isView 0 #endif if (sqlite3ViewGetColumnNames(pParse, pTab) != 0) { goto update_cleanup; } if (sqlite3IsReadOnly(pParse, pTab, tmask)) { goto update_cleanup; } aXRef = new int[pTab.nCol];// sqlite3DbMallocRaw(db, sizeof(int) * pTab.nCol); //if ( aXRef == null ) goto update_cleanup; for (i = 0; i < pTab.nCol; i++) aXRef[i] = -1; /* Allocate a cursors for the main database table and for all indices. ** The index cursors might not be used, but if they are used they ** need to occur right after the database cursor. So go ahead and ** allocate enough space, just in case. */ pTabList.a[0].iCursor = iCur = pParse.nTab++; for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext) { pParse.nTab++; } /* Initialize the name-context */ sNC = new NameContext();// memset(&sNC, 0, sNC).Length; sNC.pParse = pParse; sNC.pSrcList = pTabList; /* 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. */ chngRowid = false; for (i = 0; i < pChanges.nExpr; i++) { if (sqlite3ResolveExprNames(sNC, ref pChanges.a[i].pExpr) != 0) { goto update_cleanup; } for (j = 0; j < pTab.nCol; j++) { if (pTab.aCol[j].zName.Equals(pChanges.a[i].zName, StringComparison.InvariantCultureIgnoreCase)) { if (j == pTab.iPKey) { chngRowid = true; pRowidExpr = pChanges.a[i].pExpr; } aXRef[j] = i; break; } } if (j >= pTab.nCol) { if (sqlite3IsRowid(pChanges.a[i].zName)) { chngRowid = true; pRowidExpr = pChanges.a[i].pExpr; } else { sqlite3ErrorMsg(pParse, "no such column: %s", pChanges.a[i].zName); pParse.checkSchema = 1; goto update_cleanup; } } #if !SQLITE_OMIT_AUTHORIZATION { int rc; rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab.zName, pTab.aCol[j].zName, db.aDb[iDb].zName); if( rc==SQLITE_DENY ){ goto update_cleanup; }else if( rc==SQLITE_IGNORE ){ aXRef[j] = -1; } } #endif } hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid ? 1 : 0) != 0; /* Allocate memory for the array aRegIdx[]. There is one entry in the ** array for each index associated with table being updated. Fill in ** the value with a register number for indices that are to be used ** and with zero for unused indices. */ for (nIdx = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, nIdx++) { } if (nIdx > 0) { aRegIdx = new int[nIdx]; // sqlite3DbMallocRaw(db, Index*.Length * nIdx); if (aRegIdx == null) goto update_cleanup; } for (j = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, j++) { int reg; if (hasFK || chngRowid) { reg = ++pParse.nMem; } else { reg = 0; for (i = 0; i < pIdx.nColumn; i++) { if (aXRef[pIdx.aiColumn[i]] >= 0) { reg = ++pParse.nMem; break; } } } aRegIdx[j] = reg; } /* Begin generating code. */ v = sqlite3GetVdbe(pParse); if (v == null) goto update_cleanup; if (pParse.nested == 0) sqlite3VdbeCountChanges(v); sqlite3BeginWriteOperation(pParse, 1, iDb); #if !SQLITE_OMIT_VIRTUALTABLE /* Virtual tables must be handled separately */ if (IsVirtual(pTab)) { updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, pWhere, onError); pWhere = null; pTabList = null; goto update_cleanup; } #endif /* Allocate required registers. */ regOldRowid = regNewRowid = ++pParse.nMem; if (pTrigger != null || hasFK) { regOld = pParse.nMem + 1; pParse.nMem += pTab.nCol; } if (chngRowid || pTrigger != null || hasFK) { regNewRowid = ++pParse.nMem; } regNew = pParse.nMem + 1; pParse.nMem += pTab.nCol; /* Start the view context. */ if (isView) { sqlite3AuthContextPush(pParse, sContext, pTab.zName); } /* If we are trying to update a view, realize that view into ** a ephemeral table. */ #if !(SQLITE_OMIT_VIEW) && !(SQLITE_OMIT_TRIGGER) if (isView) { sqlite3MaterializeView(pParse, pTab, pWhere, iCur); } #endif /* Resolve the column names in all the expressions in the ** WHERE clause. */ if (sqlite3ResolveExprNames(sNC, ref pWhere) != 0) { goto update_cleanup; } /* Begin the database scan */ sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid); ExprList NullOrderby = null; pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, ref NullOrderby, WHERE_ONEPASS_DESIRED); if (pWInfo == null) goto update_cleanup; okOnePass = pWInfo.okOnePass != 0; /* Remember the rowid of every item to be updated. */ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid); if (!okOnePass) { regRowSet = ++pParse.nMem; sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid); } /* End the database scan loop. */ sqlite3WhereEnd(pWInfo); /* Initialize the count of updated rows */ if ((db.flags & SQLITE_CountRows) != 0 && null == pParse.pTriggerTab) { regRowCount = ++pParse.nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); } 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. */ if (!okOnePass) sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); if (onError == OE_Replace) { openAll = true; } else { openAll = false; for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext) { if (pIdx.onError == OE_Replace) { openAll = true; break; } } } for (i = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, i++) { if (openAll || aRegIdx[i] > 0) { KeyInfo pKey = sqlite3IndexKeyinfo(pParse, pIdx); sqlite3VdbeAddOp4(v, OP_OpenWrite, iCur + i + 1, pIdx.tnum, iDb, pKey, P4_KEYINFO_HANDOFF); Debug.Assert(pParse.nTab > iCur + i + 1); } } } /* Top of the update loop */ if (okOnePass) { int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid); addr = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, a1); } else { addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid); } /* Make cursor iCur point to the record that is being updated. If ** this record does not exist for some reason (deleted by a trigger, ** for example, then jump to the next iteration of the RowSet loop. */ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); /* If the record number will change, set register regNewRowid to ** contain the new value. If the record number is not being modified, ** then regNewRowid is the same register as regOldRowid, which is ** already populated. */ Debug.Assert(chngRowid || pTrigger != null || hasFK || regOldRowid == regNewRowid); if (chngRowid) { sqlite3ExprCode(pParse, pRowidExpr, regNewRowid); sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); } /* If there are triggers on this table, populate an array of registers ** with the required old.* column data. */ if (hasFK || pTrigger != null) { u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0); oldmask |= sqlite3TriggerColmask(pParse, pTrigger, pChanges, 0, TRIGGER_BEFORE | TRIGGER_AFTER, pTab, onError ); for (i = 0; i < pTab.nCol; i++) { if (aXRef[i] < 0 || oldmask == 0xffffffff || (i < 32 && 0 != (oldmask & (1 << i)))) { sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld + i); } else { sqlite3VdbeAddOp2(v, OP_Null, 0, regOld + i); } } if (chngRowid == false) { sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); } } /* Populate the array of registers beginning at regNew with the new ** row data. This array is used to check constaints, create the new ** table and index records, and as the values for any new.* references ** made by triggers. ** ** If there are one or more BEFORE triggers, then do not populate the ** registers associated with columns that are (a) not modified by ** this UPDATE statement and (b) not accessed by new.* references. The ** values for registers not modified by the UPDATE must be reloaded from ** the database after the BEFORE triggers are fired anyway (as the trigger ** may have modified them). So not loading those that are not going to ** be used eliminates some redundant opcodes. */ newmask = (int)sqlite3TriggerColmask( pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError ); for (i = 0; i < pTab.nCol; i++) { if (i == pTab.iPKey) { sqlite3VdbeAddOp2(v, OP_Null, 0, regNew + i); } else { j = aXRef[i]; if (j >= 0) { sqlite3ExprCode(pParse, pChanges.a[j].pExpr, regNew + i); } else if (0 == (tmask & TRIGGER_BEFORE) || i > 31 || (newmask & (1 << i)) != 0) { /* This branch loads the value of a column that will not be changed ** into a register. This is done if there are no BEFORE triggers, or ** if there are one or more BEFORE triggers that use this value via ** a new.* reference in a trigger program. */ testcase(i == 31); testcase(i == 32); sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew + i); sqlite3ColumnDefault(v, pTab, i, regNew + i); } } } /* Fire any BEFORE UPDATE triggers. This happens before constraints are ** verified. One could argue that this is wrong. */ if ((tmask & TRIGGER_BEFORE) != 0) { sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab.nCol); sqlite3TableAffinityStr(v, pTab); sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab, regOldRowid, onError, addr); /* The row-trigger may have deleted the row being updated. In this ** case, jump to the next row. No updates or AFTER triggers are ** required. This behaviour - what happens when the row being updated ** is deleted or renamed by a BEFORE trigger - is left undefined in the ** documentation. */ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); /* If it did not delete it, the row-trigger may still have modified ** some of the columns of the row being updated. Load the values for ** all columns not modified by the update statement into their ** registers in case this has happened. */ for (i = 0; i < pTab.nCol; i++) { if (aXRef[i] < 0 && i != pTab.iPKey) { sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew + i); sqlite3ColumnDefault(v, pTab, i, regNew + i); } } } if (!isView) { int j1; /* Address of jump instruction */ /* Do constraint checks. */ int iDummy; sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid, aRegIdx, (chngRowid ? regOldRowid : 0), true, onError, addr, out iDummy); /* Do FK constraint checks. */ if (hasFK) { sqlite3FkCheck(pParse, pTab, regOldRowid, 0); } /* Delete the index entries associated with the current record. */ j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid); sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); /* If changing the record number, delete the old record. */ if (hasFK || chngRowid) { sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0); } sqlite3VdbeJumpHere(v, j1); if (hasFK) { sqlite3FkCheck(pParse, pTab, 0, regNewRowid); } /* Insert the new index entries and the new record. */ sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, true, false, false); /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to ** handle rows (possibly in other tables) that refer via a foreign key ** to the row just updated. */ if (hasFK) { sqlite3FkActions(pParse, pTab, pChanges, regOldRowid); } } /* Increment the row counter */ if ((db.flags & SQLITE_CountRows) != 0 && null == pParse.pTriggerTab) { sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); } sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, TRIGGER_AFTER, pTab, regOldRowid, onError, addr); /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. */ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); sqlite3VdbeJumpHere(v, addr); /* Close all tables */ for (i = 0, pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext, i++) { if (openAll || aRegIdx[i] > 0) { sqlite3VdbeAddOp2(v, OP_Close, iCur + i + 1, 0); } } sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ if (pParse.nested == 0 && pParse.pTriggerTab == null) { sqlite3AutoincrementEnd(pParse); } /* ** Return the number of rows that were changed. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. */ if ((db.flags & SQLITE_CountRows) != 0 && null == pParse.pTriggerTab && 0 == pParse.nested) { sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC); } update_cleanup: #if !SQLITE_OMIT_AUTHORIZATION sqlite3AuthContextPop(sContext); #endif sqlite3DbFree(db, ref aRegIdx); sqlite3DbFree(db, ref aXRef); sqlite3SrcListDelete(db, ref pTabList); sqlite3ExprListDelete(db, ref pChanges); sqlite3ExprDelete(db, ref pWhere); return; }
/* ** Allocate and return a pointer to an expression to load the column iCol ** from datasource iSrc in SrcList pSrc. */ static Expr sqlite3CreateColumnExpr( sqlite3 db, SrcList pSrc, int iSrc, int iCol ) { Expr p = sqlite3ExprAlloc( db, TK_COLUMN, null, 0 ); if ( p != null ) { SrcList_item pItem = pSrc.a[iSrc]; p.pTab = pItem.pTab; p.iTable = pItem.iCursor; if ( p.pTab.iPKey == iCol ) { p.iColumn = -1; } else { p.iColumn = (ynVar)iCol; testcase( iCol == BMS ); testcase( iCol == BMS - 1 ); pItem.colUsed |= ( (Bitmask)1 ) << ( iCol >= BMS ? BMS - 1 : iCol ); } ExprSetProperty( p, EP_Resolved ); } return p; }
/* ** Generate code for an UPDATE of a virtual table. ** ** The strategy is that we create an ephemerial table that contains ** for each row to be changed: ** ** (A) The original rowid of that row. ** (B) The revised rowid for the row. (note1) ** (C) The content of every column in the row. ** ** Then we loop over this ephemeral table and for each row in ** the ephermeral table call VUpdate. ** ** When finished, drop the ephemeral table. ** ** (note1) Actually, if we know in advance that (A) is always the same ** as (B) we only store (A), then duplicate (A) when pulling ** it out of the ephemeral table before calling VUpdate. */ static void updateVirtualTable( Parse pParse, /* The parsing context */ SrcList pSrc, /* The virtual table to be modified */ Table pTab, /* The virtual table */ ExprList pChanges, /* The columns to change in the UPDATE statement */ Expr pRowid, /* Expression used to recompute the rowid */ int aXRef, /* Mapping from columns of pTab to entries in pChanges */ Expr pWhere /* WHERE clause of the UPDATE statement */ ) { Vdbe v = pParse.pVdbe; /* Virtual machine under construction */ ExprList pEList = 0; /* The result set of the SELECT statement */ Select pSelect = 0; /* The SELECT statement */ Expr pExpr; /* Temporary expression */ int ephemTab; /* Table holding the result of the SELECT */ int i; /* Loop counter */ int addr; /* Address of top of loop */ int iReg; /* First register in set passed to OP_VUpdate */ sqlite3 db = pParse.db; /* Database connection */ const char *pVTab = (const char*)sqlite3GetVTable(db, pTab); SelectDest dest; /* Construct the SELECT statement that will find the new values for ** all updated rows. */ pEList = sqlite3ExprListAppend(pParse, 0, sqlite3CreateIdExpr(pParse, "_rowid_")); if( pRowid ){ pEList = sqlite3ExprListAppend(pParse, pEList, sqlite3ExprDup(db, pRowid,0), 0); } Debug.Assert( pTab.iPKey<0 ); for(i=0; i<pTab.nCol; i++){ if( aXRef[i]>=0 ){ pExpr = sqlite3ExprDup(db, pChanges.a[aXRef[i]].pExpr,0); }else{ pExpr = sqlite3CreateIdExpr(pParse, pTab.aCol[i].zName); } pEList = sqlite3ExprListAppend(pParse, pEList, pExpr); } pSelect = sqlite3SelectNew(pParse, pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0); /* Create the ephemeral table into which the update results will ** be stored. */ Debug.Assert( v ); ephemTab = pParse.nTab++; sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab.nCol+1+(pRowid!=0)); /* fill the ephemeral table */ sqlite3SelectDestInit(dest, SRT_Table, ephemTab); sqlite3Select(pParse, pSelect, ref dest); /* Generate code to scan the ephemeral table and call VUpdate. */ iReg = ++pParse.nMem; pParse.nMem += pTab.nCol+1; addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg); sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid 1:0), iReg+1); for(i=0; i<pTab.nCol; i++){ sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i); } sqlite3VtabMakeWritable(pParse, pTab); sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab.nCol+2, iReg, pVTab, P4_VTAB); sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); sqlite3VdbeJumpHere(v, addr); sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0); /* Cleanup */ sqlite3SelectDelete(pSelect); }
public static void BeginTrigger(Parse parse, Token name1, Token name2, TK trTm, TK op, IdList columns, SrcList tableName, Expr when, bool isTemp, int noErr) { Context ctx = parse.Ctx; // The database connection Debug.Assert(name1 != null); // pName1.z might be NULL, but not pName1 itself Debug.Assert(name2 != null); Debug.Assert(op == TK.INSERT || op == TK.UPDATE || op == TK.DELETE); Debug.Assert(op > 0 && op < (TK)0xff); Trigger trigger = null; // The new trigger int db; // The database to store the trigger in Token name = null; // The unqualified db name if (isTemp) { // If TEMP was specified, then the trigger name may not be qualified. if (name2.length > 0) { parse.ErrorMsg("temporary trigger may not have qualified name"); goto trigger_cleanup; } db = 1; name = name1; } else { // Figure out the db that the the trigger will be created in db = parse.TwoPartName(name1, name2, ref name); if (db < 0) { goto trigger_cleanup; } } if (tableName == null || ctx.MallocFailed) { goto trigger_cleanup; } // A long-standing parser bug is that this syntax was allowed: // CREATE TRIGGER attached.demo AFTER INSERT ON attached.tab .... // ^^^^^^^^ // To maintain backwards compatibility, ignore the database name on pTableName if we are reparsing our of SQLITE_MASTER. if (ctx.Init.Busy && db != 1) { C._tagfree(ctx, ref tableName.Ids[0].Database); tableName.Ids[0].Database = null; } // If the trigger name was unqualified, and the table is a temp table, then set iDb to 1 to create the trigger in the temporary database. // If sqlite3SrcListLookup() returns 0, indicating the table does not exist, the error is caught by the block below. //? if (tableName == null) goto trigger_cleanup; Table table = Delete.SrcListLookup(parse, tableName); // Table that the trigger fires off of if (ctx.Init.Busy == null && name2.length == 0 && table != null && table.Schema == ctx.DBs[1].Schema) { db = 1; } // Ensure the table name matches database name and that the table exists if (ctx.MallocFailed) { goto trigger_cleanup; } Debug.Assert(tableName.Srcs == 1); DbFixer sFix = new DbFixer(); // State vector for the DB fixer if (sFix.FixInit(parse, db, "trigger", name) && sFix.FixSrcList(tableName)) { goto trigger_cleanup; } table = Delete.SrcListLookup(parse, tableName); if (table == null) { // The table does not exist. if (ctx.Init.DB == 1) { // Ticket #3810. // Normally, whenever a table is dropped, all associated triggers are dropped too. But if a TEMP trigger is created on a non-TEMP table // and the table is dropped by a different database connection, the trigger is not visible to the database connection that does the // drop so the trigger cannot be dropped. This results in an "orphaned trigger" - a trigger whose associated table is missing. ctx.Init.OrphanTrigger = true; } goto trigger_cleanup; } if (E.IsVirtual(table)) { parse.ErrorMsg("cannot create triggers on virtual tables"); goto trigger_cleanup; } // Check that the trigger name is not reserved and that no trigger of the specified name exists string nameAsString = Parse.NameFromToken(ctx, name); if (nameAsString == null || parse.CheckObjectName(nameAsString) != RC.OK) { goto trigger_cleanup; } Debug.Assert(Btree.SchemaMutexHeld(ctx, db, null)); if (ctx.DBs[db].Schema.TriggerHash.Find(nameAsString, nameAsString.Length, (Trigger)null) != null) { if (noErr == 0) { parse.ErrorMsg("trigger %T already exists", name); } else { Debug.Assert(!ctx.Init.Busy); parse.CodeVerifySchema(db); } goto trigger_cleanup; } // Do not create a trigger on a system table if (table.Name.StartsWith("sqlite_", StringComparison.InvariantCultureIgnoreCase)) { parse.ErrorMsg("cannot create trigger on system table"); parse.Errs++; goto trigger_cleanup; } // INSTEAD of triggers are only for views and views only support INSTEAD of triggers. if (table.Select != null && trTm != TK.INSTEAD) { parse.ErrorMsg("cannot create %s trigger on view: %S", (trTm == TK.BEFORE ? "BEFORE" : "AFTER"), tableName, 0); goto trigger_cleanup; } if (table.Select == null && trTm == TK.INSTEAD) { parse.ErrorMsg("cannot create INSTEAD OF trigger on table: %S", tableName, 0); goto trigger_cleanup; } #if !OMIT_AUTHORIZATION { int tabDb = Prepare.SchemaToIndex(ctx, table.Schema); // Index of the database holding pTab AUTH code = AUTH.CREATE_TRIGGER; string dbName = ctx.DBs[tabDb].Name; string dbTrigName = (isTemp ? ctx.DBs[1].Name : dbName); if (tabDb == 1 || isTemp) { code = AUTH.CREATE_TEMP_TRIGGER; } if (Auth.Check(parse, code, nameAsString, table.Name, dbTrigName) != 0 || Auth.Check(parse, AUTH.INSERT, E.SCHEMA_TABLE(tabDb), 0, dbName)) { goto trigger_cleanup; } } #endif // INSTEAD OF triggers can only appear on views and BEFORE triggers cannot appear on views. So we might as well translate every // INSTEAD OF trigger into a BEFORE trigger. It simplifies code elsewhere. if (trTm == TK.INSTEAD) { trTm = TK.BEFORE; } // Build the Trigger object trigger = new Trigger(); //: (Trigger *)_tagalloc(db, sizeof(Trigger), true); if (trigger == null) { goto trigger_cleanup; } trigger.Name = name; trigger.Table = tableName.Ids[0].Name; //: _tagstrdup(ctx, tableName->Ids[0].Name); trigger.Schema = ctx.DBs[db].Schema; trigger.TabSchema = table.Schema; trigger.OP = op; trigger.TRtm = (trTm == TK.BEFORE ? TRIGGER.BEFORE : TRIGGER.AFTER); trigger.When = Expr.Dup(db, when, E.EXPRDUP_REDUCE); trigger.Columns = Expr.IdListDup(ctx, columns); Debug.Assert(parse.NewTrigger == null); parse.NewTrigger = trigger; trigger_cleanup: C._tagfree(ctx, ref name); Expr.SrcListDelete(ctx, ref tableName); Expr.IdListDelete(ctx, ref columns); Expr.Delete(ctx, ref when); if (parse.NewTrigger == null) { DeleteTrigger(ctx, ref trigger); } else { Debug.Assert(parse.NewTrigger == trigger); } }