static void ResolveAlias(Parse parse, ExprList list, int colId, Expr expr, string type, int subqueries) { Debug.Assert(colId >= 0 && colId < list.Exprs); Expr orig = list.Ids[colId].Expr; // The iCol-th column of the result set Debug.Assert(orig != null); Debug.Assert((orig.Flags & EP.Resolved) != 0); Context ctx = parse.Ctx; // The database connection Expr dup = Expr.Dup(ctx, orig, 0); // Copy of pOrig if (orig.OP != TK.COLUMN && (type.Length == 0 || type[0] != 'G')) { IncrAggFunctionDepth(dup, subqueries); dup = Expr.PExpr_(parse, TK.AS, dup, null, null); if (dup == null) return; if (list.Ids[colId].Alias == 0) list.Ids[colId].Alias = (ushort)(++parse.Alias.length); dup.TableId = list.Ids[colId].Alias; } if (expr.OP == TK.COLLATE) dup = Expr.AddCollateString(parse, dup, expr.u.Token); // Before calling sqlite3ExprDelete(), set the EP_Static flag. This prevents ExprDelete() from deleting the Expr structure itself, // allowing it to be repopulated by the memcpy() on the following line. E.ExprSetProperty(expr, EP.Static); Expr.Delete(ctx, ref expr); expr.memcpy(dup); if (!E.ExprHasProperty(expr, EP.IntValue) && expr.u.Token != null) { Debug.Assert((dup.Flags & (EP.Reduced | EP.TokenOnly)) == 0); dup.u.Token = expr.u.Token; dup.Flags2 |= EP2.MallocedToken; } C._tagfree(ctx, ref dup); }
/* ** 2008 August 18 ** ** 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 routines used for walking the parser tree and ** resolve all identifiers by associating them with a particular ** table and column. ************************************************************************* ** 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-01-05 15:30:36 28d0d7710761114a44a1a3a425a6883c661f06e7 ** ** $Header$ ************************************************************************* */ //#include "sqliteInt.h" //#include <stdlib.h> //#include <string.h> /* ** Turn the pExpr expression into an alias for the iCol-th column of the ** result set in pEList. ** ** If the result set column is a simple column reference, then this routine ** makes an exact copy. But for any other kind of expression, this ** routine make a copy of the result set column as the argument to the ** TK_AS operator. The TK_AS operator causes the expression to be ** evaluated just once and then reused for each alias. ** ** The reason for suppressing the TK_AS term when the expression is a simple ** column reference is so that the column reference will be recognized as ** usable by indices within the WHERE clause processing logic. ** ** Hack: The TK_AS operator is inhibited if zType[0]=='G'. This means ** that in a GROUP BY clause, the expression is evaluated twice. Hence: ** ** SELECT random()%5 AS x, count(*) FROM tab GROUP BY x ** ** Is equivalent to: ** ** SELECT random()%5 AS x, count(*) FROM tab GROUP BY random()%5 ** ** The result of random()%5 in the GROUP BY clause is probably different ** from the result in the result-set. We might fix this someday. Or ** then again, we might not... */ static void resolveAlias( Parse pParse, /* Parsing context */ ExprList pEList, /* A result set */ int iCol, /* A column in the result set. 0..pEList.nExpr-1 */ Expr pExpr, /* Transform this into an alias to the result set */ string zType /* "GROUP" or "ORDER" or "" */ ) { Expr pOrig; /* The iCol-th column of the result set */ Expr pDup; /* Copy of pOrig */ sqlite3 db; /* The database connection */ Debug.Assert( iCol >= 0 && iCol < pEList.nExpr ); pOrig = pEList.a[iCol].pExpr; Debug.Assert( pOrig != null ); Debug.Assert( ( pOrig.flags & EP_Resolved ) != 0 ); db = pParse.db; if ( pOrig.op != TK_COLUMN && ( zType.Length == 0 || zType[0] != 'G' ) ) { pDup = sqlite3ExprDup( db, pOrig, 0 ); pDup = sqlite3PExpr( pParse, TK_AS, pDup, null, null ); if ( pDup == null ) return; if ( pEList.a[iCol].iAlias == 0 ) { pEList.a[iCol].iAlias = (u16)( ++pParse.nAlias ); } pDup.iTable = pEList.a[iCol].iAlias; } else if ( ExprHasProperty( pOrig, EP_IntValue ) || pOrig.u.zToken == null ) { pDup = sqlite3ExprDup( db, pOrig, 0 ); if ( pDup == null ) return; } else { string zToken = pOrig.u.zToken; Debug.Assert( zToken != null ); pOrig.u.zToken = null; pDup = sqlite3ExprDup( db, pOrig, 0 ); pOrig.u.zToken = zToken; if ( pDup == null ) return; Debug.Assert( ( pDup.flags & ( EP_Reduced | EP_TokenOnly ) ) == 0 ); pDup.flags2 |= EP2_MallocedToken; pDup.u.zToken = zToken;// sqlite3DbStrDup( db, zToken ); } if ( ( pExpr.flags & EP_ExpCollate ) != 0 ) { pDup.pColl = pExpr.pColl; pDup.flags |= EP_ExpCollate; } /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This ** prevents ExprDelete() from deleting the Expr structure itself, ** allowing it to be repopulated by the memcpy() on the following line. */ ExprSetProperty( pExpr, EP_Static ); sqlite3ExprDelete( db, ref pExpr ); pExpr.CopyFrom( pDup ); //memcpy(pExpr, pDup, sizeof(*pExpr)); sqlite3DbFree( db, ref pDup ); }
/* ** pEList is the SET clause of an UPDATE statement. Each entry ** in pEList is of the format <id>=<expr>. If any of the entries ** in pEList have an <id> which matches an identifier in pIdList, ** then return TRUE. If pIdList==NULL, then it is considered a ** wildcard that matches anything. Likewise if pEList==NULL then ** it matches anything so always return true. Return false only ** if there is no match. */ static int checkColumnOverlap(IdList pIdList, ExprList pEList) { int e; if (pIdList == null || NEVER(pEList == null)) return 1; for (e = 0; e < pEList.nExpr; e++) { if (sqlite3IdListIndex(pIdList, pEList.a[e].zName) >= 0) return 1; } return 0; }
/// <summary> /// </summary> /// <param name="@object"></param> /// <param name="parameter">ÐèÒª StringComparer.OrdinalIgnoreCase</param> /// <exception cref="System.MemberAccessException" /> /// <exception cref="System.Reflection.TargetInvocationException" /> public static void Mapping(object @object, IDictionary<string, object> parameter) { ThrowHelper.ThrowIfNull(@object, "object"); ThrowHelper.ThrowIfNull(parameter, "parameter"); var type = @object.GetType(); var pds = GetDescriptors(type); PropertyInfo pd = null; Type cls = null; object obj = null; object value = null; for (var i = 0; i < pds.Length; i++) { pd = pds[i]; obj = parameter.GetValue(pd.Name); value = obj; cls = pd.PropertyType; if (obj is string) { var @string = (string)obj; if ([email protected]()) { @string = ConfigUtil.Filter(@string); } if (IsPrimitiveType(cls)) { value = Convert(cls, @string); } } #if CONFG_BEAN else if (obj is BeanConfig) { value = CreateBean((BeanConfig) obj); } else if (obj is IList<BeanConfig>) { var list = new ExprList<object>(); foreach (var beanconfig in (IList<BeanConfig>) obj) { list.Add(CreateBean(beanconfig)); } value = list.ToArray(); } #endif if (cls != null && value != null) { pd.SetValue(@object, value, null); } } }
/* ** Call sqlite3WalkExpr() for every expression in list p or until ** an abort request is seen. */ static int sqlite3WalkExprList( Walker pWalker, ExprList p ) { int i; ExprList_item pItem; if ( p != null ) { for ( i = p.nExpr; i > 0; i-- ) {//, pItem++){ pItem = p.a[p.nExpr - i]; if ( sqlite3WalkExpr( pWalker, ref pItem.pExpr ) != 0 ) return WRC_Abort; } } return WRC_Continue; }
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; }
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; }
/* ** Insert code into "v" that will push the record on the top of the ** stack into the sorter. */ static void pushOntoSorter( Parse pParse, /* Parser context */ ExprList pOrderBy, /* The ORDER BY clause */ Select pSelect, /* The whole SELECT statement */ int regData /* Register holding data to be sorted */ ) { Vdbe v = pParse.pVdbe; int nExpr = pOrderBy.nExpr; int regBase = sqlite3GetTempRange( pParse, nExpr + 2 ); int regRecord = sqlite3GetTempReg( pParse ); sqlite3ExprCacheClear( pParse ); sqlite3ExprCodeExprList( pParse, pOrderBy, regBase, false ); sqlite3VdbeAddOp2( v, OP_Sequence, pOrderBy.iECursor, regBase + nExpr ); sqlite3ExprCodeMove( pParse, regData, regBase + nExpr + 1, 1 ); sqlite3VdbeAddOp3( v, OP_MakeRecord, regBase, nExpr + 2, regRecord ); sqlite3VdbeAddOp2( v, OP_IdxInsert, pOrderBy.iECursor, regRecord ); sqlite3ReleaseTempReg( pParse, regRecord ); sqlite3ReleaseTempRange( pParse, regBase, nExpr + 2 ); if ( pSelect.iLimit != 0 ) { int addr1, addr2; int iLimit; if ( pSelect.iOffset != 0 ) { iLimit = pSelect.iOffset + 1; } else { iLimit = pSelect.iLimit; } addr1 = sqlite3VdbeAddOp1( v, OP_IfZero, iLimit ); sqlite3VdbeAddOp2( v, OP_AddImm, iLimit, -1 ); addr2 = sqlite3VdbeAddOp0( v, OP_Goto ); sqlite3VdbeJumpHere( v, addr1 ); sqlite3VdbeAddOp1( v, OP_Last, pOrderBy.iECursor ); sqlite3VdbeAddOp1( v, OP_Delete, pOrderBy.iECursor ); sqlite3VdbeJumpHere( v, addr2 ); } }
/* Forward Declarations */ //static void substExprList(sqlite3*, ExprList*, int, ExprList); //static void substSelect(sqlite3*, Select *, int, ExprList ); /* ** Scan through the expression pExpr. Replace every reference to ** a column in table number iTable with a copy of the iColumn-th ** entry in pEList. (But leave references to the ROWID column ** unchanged.) ** ** This routine is part of the flattening procedure. A subquery ** whose result set is defined by pEList appears as entry in the ** FROM clause of a SELECT such that the VDBE cursor assigned to that ** FORM clause entry is iTable. This routine make the necessary ** changes to pExpr so that it refers directly to the source table ** of the subquery rather the result set of the subquery. */ static Expr substExpr( sqlite3 db, /* Report malloc errors to this connection */ Expr pExpr, /* Expr in which substitution occurs */ int iTable, /* Table to be substituted */ ExprList pEList /* Substitute expressions */ ) { if ( pExpr == null ) return null; if ( pExpr.op == TK_COLUMN && pExpr.iTable == iTable ) { if ( pExpr.iColumn < 0 ) { pExpr.op = TK_NULL; } else { Expr pNew; Debug.Assert( pEList != null && pExpr.iColumn < pEList.nExpr ); Debug.Assert( pExpr.pLeft == null && pExpr.pRight == null ); pNew = sqlite3ExprDup( db, pEList.a[pExpr.iColumn].pExpr, 0 ); if ( pExpr.pColl != null ) { pNew.pColl = pExpr.pColl; } sqlite3ExprDelete( db, ref pExpr ); pExpr = pNew; } } else { pExpr.pLeft = substExpr( db, pExpr.pLeft, iTable, pEList ); pExpr.pRight = substExpr( db, pExpr.pRight, iTable, pEList ); if ( ExprHasProperty( pExpr, EP_xIsSelect ) ) { substSelect( db, pExpr.x.pSelect, iTable, pEList ); } else { substExprList( db, pExpr.x.pList, iTable, pEList ); } } return pExpr; }
static void substSelect( sqlite3 db, /* Report malloc errors here */ Select p, /* SELECT statement in which to make substitutions */ int iTable, /* Table to be replaced */ ExprList pEList /* Substitute values */ ) { SrcList pSrc; SrcList_item pItem; int i; if ( p == null ) return; substExprList( db, p.pEList, iTable, pEList ); substExprList( db, p.pGroupBy, iTable, pEList ); substExprList( db, p.pOrderBy, iTable, pEList ); p.pHaving = substExpr( db, p.pHaving, iTable, pEList ); p.pWhere = substExpr( db, p.pWhere, iTable, pEList ); substSelect( db, p.pPrior, iTable, pEList ); pSrc = p.pSrc; Debug.Assert( pSrc != null ); /* Even for (SELECT 1) we have: pSrc!=0 but pSrc->nSrc==0 */ if ( ALWAYS( pSrc ) ) { for ( i = pSrc.nSrc; i > 0; i-- )//, pItem++ ) { pItem = pSrc.a[pSrc.nSrc - i]; substSelect( db, pItem.pSelect, iTable, pEList ); } } }
static RC SelectColumnsFromExprList(Parse parse, ExprList list, ref short colsLengthOut, ref Column[] colsOut) { Context ctx = parse.Ctx; // Database connection int j; int colsLength; // Number of columns in the result set Column[] cols; // For looping over result columns if (list != null) { colsLength = list.Exprs; cols = new Column[colsLength]; C.ASSERTCOVERAGE(cols == null); } else { colsLength = 0; cols = null; } colsLengthOut = (short)colsLength; colsOut = cols; int i; Column col; // For looping over result columns for (i = 0; i < colsLength; i++)//, pCol++) { if (cols[i] == null) cols[i] = new Column(); col = cols[i]; // Get an appropriate name for the column Expr p = list.Ids[i].Expr; // Expression for a single result column string name; // Column name if (list.Ids[i].Name != null && (name = list.Ids[i].Name) != null) { } //: name = _tagstrdup(ctx, name); // If the column contains an "AS <name>" phrase, use <name> as the name else { Expr colExpr = p; // The expression that is the result column name while (colExpr.OP == TK.DOT) { colExpr = colExpr.Right; Debug.Assert(colExpr != null); } if (colExpr.OP == TK.COLUMN && C._ALWAYS(colExpr.Table != null)) { // For columns use the column name name int colId = colExpr.ColumnIdx; Table table = colExpr.Table; // Table associated with this expression if (colId < 0) colId = table.PKey; name = C._mtagprintf(ctx, "%s", (colId >= 0 ? table.Cols[colId].Name : "rowid")); } else if (colExpr.OP == TK.ID) { Debug.Assert(!E.ExprHasProperty(colExpr, EP.IntValue)); name = C._mtagprintf(ctx, "%s", colExpr.u.Token); } else name = C._mtagprintf(ctx, "%s", list.Ids[i].Span); // Use the original text of the column expression as its name } if (ctx.MallocFailed) { C._tagfree(ctx, ref name); break; } // Make sure the column name is unique. If the name is not unique, append a integer to the name so that it becomes unique. int nameLength = name.Length; // Size of name in zName[] int cnt; // Index added to make the name unique for (j = cnt = 0; j < i; j++) { if (string.Equals(cols[j].Name, name, StringComparison.OrdinalIgnoreCase)) { name = name.Substring(0, nameLength); string newName = C._mtagprintf(ctx, "%s:%d", name, ++cnt); C._tagfree(ctx, ref name); name = newName; j = -1; if (name == null) break; } } col.Name = name; } if (ctx.MallocFailed) { for (j = 0; j < i; j++) C._tagfree(ctx, ref cols[j].Name); C._tagfree(ctx, ref cols); colsOut = null; colsLengthOut = 0; return RC.NOMEM; } return RC.OK; }
/* ** 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 TriggerStep sqlite3TriggerInsertStep( sqlite3 db, /* The database connection */ Token pTableName, /* Name of the table into which we insert */ IdList pColumn, /* List of columns in pTableName to insert into */ ExprList pEList, /* The VALUE clause: a list of values to be inserted */ Select pSelect, /* A SELECT statement that supplies values */ u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */ ) { TriggerStep pTriggerStep; Debug.Assert( pEList == null || pSelect == null ); Debug.Assert( pEList != null || pSelect != null /*|| db.mallocFailed != 0 */ ); pTriggerStep = triggerStepAllocate( db, TK_INSERT, pTableName ); //if ( pTriggerStep != null ) //{ pTriggerStep.pSelect = sqlite3SelectDup( db, pSelect, EXPRDUP_REDUCE ); pTriggerStep.pIdList = pColumn; pTriggerStep.pExprList = sqlite3ExprListDup( db, pEList, EXPRDUP_REDUCE ); pTriggerStep.orconf = orconf; //} //else //{ // sqlite3IdListDelete( db, ref pColumn ); //} sqlite3ExprListDelete( db, ref pEList ); sqlite3SelectDelete( db, ref pSelect ); return pTriggerStep; }
//----------------------------------------------------------- public void Visit(ExprList node) { VisitChildren(node); }
/* 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). */ //#if isView // #undef isView //#endif //#if 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. */ private 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 */ int onError /* ON CONFLICT strategy */ ) { Vdbe v = pParse.pVdbe; /* Virtual machine under construction */ ExprList pEList = null; /* The result set of the SELECT statement */ Select pSelect = null; /* 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 */ VTable pVTab = sqlite3GetVTable(db, pTab); SelectDest dest = new SelectDest(); /* 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 != null) { pEList = sqlite3ExprListAppend(pParse, pEList, sqlite3ExprDup(db, pRowid, 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, null, null, null, 0, null, null); /* Create the ephemeral table into which the update results will ** be stored. */ Debug.Assert(v != null); ephemTab = pParse.nTab++; sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab.nCol + 1 + ((pRowid != null) ? 1 : 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 != null ? 1 : 0), iReg + 1); for (i = 0; i < pTab.nCol; i++) { sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i + 1 + ((pRowid != null) ? 1 : 0), iReg + 2 + i); } sqlite3VtabMakeWritable(pParse, pTab); sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab.nCol + 2, iReg, pVTab, P4_VTAB); sqlite3VdbeChangeP5(v, (byte)(onError == OE_Default ? OE_Abort : onError)); sqlite3MayAbort(pParse); sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr + 1); sqlite3VdbeJumpHere(v, addr); sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0); /* Cleanup */ sqlite3SelectDelete(db, ref pSelect); }
/* ** This is called to code FOR EACH ROW triggers. ** ** When the code that this function generates is executed, the following ** must be true: ** ** 1. No cursors may be open in the main database. (But newIdx and oldIdx ** can be indices of cursors in temporary tables. See below.) ** ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then ** a temporary vdbe cursor (index newIdx) must be open and pointing at ** a row containing values to be substituted for new.* expressions in the ** trigger program(s). ** ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then ** a temporary vdbe cursor (index oldIdx) must be open and pointing at ** a row containing values to be substituted for old.* expressions in the ** trigger program(s). ** ** If they are not NULL, the piOldColMask and piNewColMask output variables ** are set to values that describe the columns used by the trigger program ** in the OLD.* and NEW.* tables respectively. If column N of the ** pseudo-table is read at least once, the corresponding bit of the output ** mask is set. If a column with an index greater than 32 is read, the ** output mask is set to the special value 0xffffffff. ** */ static int sqlite3CodeRowTrigger( Parse pParse, /* Parse context */ Trigger pTrigger, /* List of triggers on table pTab */ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ ExprList pChanges, /* Changes list for any UPDATE OF triggers */ int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ Table pTab, /* The table to code triggers from */ int newIdx, /* The indice of the "new" row to access */ int oldIdx, /* The indice of the "old" row to access */ int orconf, /* ON CONFLICT policy */ int ignoreJump, /* Instruction to jump to for RAISE(IGNORE) */ ref u32 piOldColMask, /* OUT: Mask of columns used from the OLD.* table */ ref u32 piNewColMask /* OUT: Mask of columns used from the NEW.* table */ ) { Trigger p; sqlite3 db = pParse.db; TriggerStack trigStackEntry = new TriggerStack(); trigStackEntry.oldColMask = 0; trigStackEntry.newColMask = 0; Debug.Assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE); Debug.Assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER); Debug.Assert(newIdx != -1 || oldIdx != -1); for (p = pTrigger; p != null; p = p.pNext) { bool fire_this = false; /* Sanity checking: The schema for the trigger and for the table are ** always defined. The trigger must be in the same schema as the table ** or else it must be a TEMP trigger. */ Debug.Assert(p.pSchema != null); Debug.Assert(p.pTabSchema != null); Debug.Assert(p.pSchema == p.pTabSchema || p.pSchema == db.aDb[1].pSchema); /* Determine whether we should code this trigger */ if ( p.op == op && p.tr_tm == tr_tm && checkColumnOverlap(p.pColumns, pChanges) != 0) { TriggerStack pS; /* Pointer to trigger-stack entry */ for (pS = pParse.trigStack; pS != null && p != pS.pTrigger; pS = pS.pNext) { } if (pS == null) { fire_this = true; } #if FALSE // * Give no warning for recursive triggers. Just do not do them */ else { sqlite3ErrorMsg(pParse, "recursive triggers not supported (%s)", p.name); return(SQLITE_ERROR); } #endif } if (fire_this) { int endTrigger; Expr whenExpr; AuthContext sContext; NameContext sNC; #if !SQLITE_OMIT_TRACE sqlite3VdbeAddOp4(pParse.pVdbe, OP_Trace, 0, 0, 0, sqlite3MPrintf(db, "-- TRIGGER %s", p.name), P4_DYNAMIC); #endif sNC = new NameContext();// memset( &sNC, 0, sizeof(sNC) ) sNC.pParse = pParse; /* Push an entry on to the trigger stack */ trigStackEntry.pTrigger = p; trigStackEntry.newIdx = newIdx; trigStackEntry.oldIdx = oldIdx; trigStackEntry.pTab = pTab; trigStackEntry.pNext = pParse.trigStack; trigStackEntry.ignoreJump = ignoreJump; pParse.trigStack = trigStackEntry; #if !SQLITE_OMIT_AUTHORIZATION sqlite3AuthContextPush(pParse, sContext, p.name); #endif /* code the WHEN clause */ endTrigger = sqlite3VdbeMakeLabel(pParse.pVdbe); whenExpr = sqlite3ExprDup(db, p.pWhen, 0); if (/* db.mallocFailed != 0 || */ sqlite3ResolveExprNames(sNC, ref whenExpr) != 0) { pParse.trigStack = trigStackEntry.pNext; sqlite3ExprDelete(db, ref whenExpr); return(1); } sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, SQLITE_JUMPIFNULL); sqlite3ExprDelete(db, ref whenExpr); codeTriggerProgram(pParse, p.step_list, orconf); /* Pop the entry off the trigger stack */ pParse.trigStack = trigStackEntry.pNext; #if !SQLITE_OMIT_AUTHORIZATION sqlite3AuthContextPop(sContext); #endif sqlite3VdbeResolveLabel(pParse.pVdbe, endTrigger); } } piOldColMask |= trigStackEntry.oldColMask; // if ( piOldColMask != 0 ) piOldColMask |= trigStackEntry.oldColMask; piNewColMask |= trigStackEntry.newColMask; // if ( piNewColMask != 0 ) piNewColMask |= trigStackEntry.newColMask; return(0); }
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; }
/* ** 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; }
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; }
public static ExprList WithExpressions(this ExprList original, IReadOnlyList <IExprExec> newExpressions) => new ExprList(expressions: newExpressions);
static WRC ResolveSelectStep(Walker walker, Select p) { Debug.Assert(p != null); if ((p.SelFlags & SF.Resolved) != 0) { return(WRC.Prune); } NameContext outerNC = walker.u.NC; // Context that contains this SELECT Parse parse = walker.Parse; // Parsing context Context ctx = parse.Ctx; // Database connection // Normally sqlite3SelectExpand() will be called first and will have already expanded this SELECT. However, if this is a subquery within // an expression, sqlite3ResolveExprNames() will be called without a prior call to sqlite3SelectExpand(). When that happens, let // sqlite3SelectPrep() do all of the processing for this SELECT. sqlite3SelectPrep() will invoke both sqlite3SelectExpand() and // this routine in the correct order. if ((p.SelFlags & SF.Expanded) == 0) { p.Prep(parse, outerNC); return(parse.Errs != 0 || ctx.MallocFailed ? WRC.Abort : WRC.Prune); } bool isCompound = (p.Prior != null); // True if p is a compound select int compounds = 0; // Number of compound terms processed so far Select leftmost = p; // Left-most of SELECT of a compound int i; NameContext nc; // Name context of this SELECT while (p != null) { Debug.Assert((p.SelFlags & SF.Expanded) != 0); Debug.Assert((p.SelFlags & SF.Resolved) == 0); p.SelFlags |= SF.Resolved; // Resolve the expressions in the LIMIT and OFFSET clauses. These are not allowed to refer to any names, so pass an empty NameContext. nc = new NameContext(); //: _memset(&nc, 0, sizeof(nc)); nc.Parse = parse; if (Walker.ResolveExprNames(nc, ref p.Limit) || Walker.ResolveExprNames(nc, ref p.Offset)) { return(WRC.Abort); } // Recursively resolve names in all subqueries SrcList.SrcListItem item; for (i = 0; i < p.Src.Srcs; i++) { item = p.Src.Ids[i]; if (item.Select != null) { NameContext nc2; // Used to iterate name contexts int refs = 0; // Refcount for pOuterNC and outer contexts string savedContext = parse.AuthContext; // Count the total number of references to pOuterNC and all of its parent contexts. After resolving references to expressions in // pItem->pSelect, check if this value has changed. If so, then SELECT statement pItem->pSelect must be correlated. Set the // pItem->isCorrelated flag if this is the case. for (nc2 = outerNC; nc2 != null; nc2 = nc2.Next) { refs += nc2.Refs; } if (item.Name != null) { parse.AuthContext = item.Name; } Walker.ResolveSelectNames(parse, item.Select, outerNC); parse.AuthContext = savedContext; if (parse.Errs != 0 || ctx.MallocFailed) { return(WRC.Abort); } for (nc2 = outerNC; nc2 != null; nc2 = nc2.Next) { refs -= nc2.Refs; } Debug.Assert(!item.IsCorrelated && refs <= 0); item.IsCorrelated = (refs != 0); } } // Set up the local name-context to pass to sqlite3ResolveExprNames() to resolve the result-set expression list. nc.NCFlags = NC.AllowAgg; nc.SrcList = p.Src; nc.Next = outerNC; // Resolve names in the result set. ExprList list = p.EList; // Result set expression list Debug.Assert(list != null); for (i = 0; i < list.Exprs; i++) { Expr expr = list.Ids[i].Expr; if (Walker.ResolveExprNames(nc, ref expr)) { return(WRC.Abort); } } // If there are no aggregate functions in the result-set, and no GROUP BY expression, do not allow aggregates in any of the other expressions. Debug.Assert((p.SelFlags & SF.Aggregate) == 0); ExprList groupBy = p.GroupBy; // The GROUP BY clause if (groupBy != null || (nc.NCFlags & NC.HasAgg) != 0) { p.SelFlags |= SF.Aggregate; } else { nc.NCFlags &= ~NC.AllowAgg; } // If a HAVING clause is present, then there must be a GROUP BY clause. if (p.Having != null && groupBy == null) { parse.ErrorMsg("a GROUP BY clause is required before HAVING"); return(WRC.Abort); } // Add the expression list to the name-context before parsing the other expressions in the SELECT statement. This is so that // expressions in the WHERE clause (etc.) can refer to expressions by aliases in the result set. // // Minor point: If this is the case, then the expression will be re-evaluated for each reference to it. nc.EList = p.EList; if (Walker.ResolveExprNames(nc, ref p.Where) || Walker.ResolveExprNames(nc, ref p.Having)) { return(WRC.Abort); } // The ORDER BY and GROUP BY clauses may not refer to terms in outer queries nc.Next = null; nc.NCFlags |= NC.AllowAgg; // Process the ORDER BY clause for singleton SELECT statements. The ORDER BY clause for compounds SELECT statements is handled // below, after all of the result-sets for all of the elements of the compound have been resolved. if (!isCompound && Walker.ResolveOrderGroupBy(nc, p, p.OrderBy, "ORDER")) { return(WRC.Abort); } if (ctx.MallocFailed) { return(WRC.Abort); } // Resolve the GROUP BY clause. At the same time, make sure the GROUP BY clause does not contain aggregate functions. if (groupBy != null) { if (Walker.ResolveOrderGroupBy(nc, p, groupBy, "GROUP") || ctx.MallocFailed) { return(WRC.Abort); } ExprList.ExprListItem item2; for (i = 0; i < groupBy.Exprs; i++) { item2 = groupBy.Ids[i]; if (E.ExprHasProperty(item2.Expr, EP.Agg)) { parse.ErrorMsg("aggregate functions are not allowed in the GROUP BY clause"); return(WRC.Abort); } } } // Advance to the next term of the compound p = p.Prior; compounds++; } // Resolve the ORDER BY on a compound SELECT after all terms of the compound have been resolved. return(isCompound && ResolveCompoundOrderBy(parse, leftmost) != 0 ? WRC.Abort : WRC.Prune); }
/* ** This routine generates the code for the inside of the inner loop ** of a SELECT. ** ** If srcTab and nColumn are both zero, then the pEList expressions ** are evaluated in order to get the data for this row. If nColumn>0 ** then data is pulled from srcTab and pEList is used only to get the ** datatypes for each column. */ static void selectInnerLoop( Parse pParse, /* The parser context */ Select p, /* The complete select statement being coded */ ExprList pEList, /* List of values being extracted */ int srcTab, /* Pull data from this table */ int nColumn, /* Number of columns in the source table */ ExprList pOrderBy, /* If not NULL, sort results using this key */ int distinct, /* If >=0, make sure results are distinct */ SelectDest pDest, /* How to dispose of the results */ int iContinue, /* Jump here to continue with next row */ int iBreak /* Jump here to break out of the inner loop */ ) { Vdbe v = pParse.pVdbe; int i; bool hasDistinct; /* True if the DISTINCT keyword is present */ int regResult; /* Start of memory holding result set */ int eDest = pDest.eDest; /* How to dispose of results */ int iParm = pDest.iParm; /* First argument to disposal method */ int nResultCol; /* Number of result columns */ Debug.Assert( v != null ); if ( NEVER( v == null ) ) return; Debug.Assert( pEList != null ); hasDistinct = distinct >= 0; if ( pOrderBy == null && !hasDistinct ) { codeOffset( v, p, iContinue ); } /* Pull the requested columns. */ if ( nColumn > 0 ) { nResultCol = nColumn; } else { nResultCol = pEList.nExpr; } if ( pDest.iMem == 0 ) { pDest.iMem = pParse.nMem + 1; pDest.nMem = nResultCol; pParse.nMem += nResultCol; } else { Debug.Assert( pDest.nMem == nResultCol ); } regResult = pDest.iMem; if ( nColumn > 0 ) { for ( i = 0; i < nColumn; i++ ) { sqlite3VdbeAddOp3( v, OP_Column, srcTab, i, regResult + i ); } } else if ( eDest != SRT_Exists ) { /* If the destination is an EXISTS(...) expression, the actual ** values returned by the SELECT are not required. */ sqlite3ExprCacheClear( pParse ); sqlite3ExprCodeExprList( pParse, pEList, regResult, eDest == SRT_Output ); } nColumn = nResultCol; /* If the DISTINCT keyword was present on the SELECT statement ** and this row has been seen before, then do not make this row ** part of the result. */ if ( hasDistinct ) { Debug.Assert( pEList != null ); Debug.Assert( pEList.nExpr == nColumn ); codeDistinct( pParse, distinct, iContinue, nColumn, regResult ); if ( pOrderBy == null ) { codeOffset( v, p, iContinue ); } } switch ( eDest ) { /* In this mode, write each query result to the key of the temporary ** table iParm. */ #if !SQLITE_OMIT_COMPOUND_SELECT case SRT_Union: { int r1; r1 = sqlite3GetTempReg( pParse ); sqlite3VdbeAddOp3( v, OP_MakeRecord, regResult, nColumn, r1 ); sqlite3VdbeAddOp2( v, OP_IdxInsert, iParm, r1 ); sqlite3ReleaseTempReg( pParse, r1 ); break; } /* Construct a record from the query result, but instead of ** saving that record, use it as a key to delete elements from ** the temporary table iParm. */ case SRT_Except: { sqlite3VdbeAddOp3( v, OP_IdxDelete, iParm, regResult, nColumn ); break; } #endif /* Store the result as data using a unique key. */ case SRT_Table: case SRT_EphemTab: { int r1 = sqlite3GetTempReg( pParse ); testcase( eDest == SRT_Table ); testcase( eDest == SRT_EphemTab ); sqlite3VdbeAddOp3( v, OP_MakeRecord, regResult, nColumn, r1 ); if ( pOrderBy != null ) { pushOntoSorter( pParse, pOrderBy, p, r1 ); } else { int r2 = sqlite3GetTempReg( pParse ); sqlite3VdbeAddOp2( v, OP_NewRowid, iParm, r2 ); sqlite3VdbeAddOp3( v, OP_Insert, iParm, r1, r2 ); sqlite3VdbeChangeP5( v, OPFLAG_APPEND ); sqlite3ReleaseTempReg( pParse, r2 ); } sqlite3ReleaseTempReg( pParse, r1 ); break; } #if !SQLITE_OMIT_SUBQUERY /* If we are creating a set for an "expr IN (SELECT ...)" construct, ** then there should be a single item on the stack. Write this ** item into the set table with bogus data. */ case SRT_Set: { Debug.Assert( nColumn == 1 ); p.affinity = sqlite3CompareAffinity( pEList.a[0].pExpr, pDest.affinity ); if ( pOrderBy != null ) { /* At first glance you would think we could optimize out the ** ORDER BY in this case since the order of entries in the set ** does not matter. But there might be a LIMIT clause, in which ** case the order does matter */ pushOntoSorter( pParse, pOrderBy, p, regResult ); } else { int r1 = sqlite3GetTempReg( pParse ); sqlite3VdbeAddOp4( v, OP_MakeRecord, regResult, 1, r1, p.affinity, 1 ); sqlite3ExprCacheAffinityChange( pParse, regResult, 1 ); sqlite3VdbeAddOp2( v, OP_IdxInsert, iParm, r1 ); sqlite3ReleaseTempReg( pParse, r1 ); } break; } /* If any row exist in the result set, record that fact and abort. */ case SRT_Exists: { sqlite3VdbeAddOp2( v, OP_Integer, 1, iParm ); /* The LIMIT clause will terminate the loop for us */ break; } /* If this is a scalar select that is part of an expression, then ** store the results in the appropriate memory cell and break out ** of the scan loop. */ case SRT_Mem: { Debug.Assert( nColumn == 1 ); if ( pOrderBy != null ) { pushOntoSorter( pParse, pOrderBy, p, regResult ); } else { sqlite3ExprCodeMove( pParse, regResult, iParm, 1 ); /* The LIMIT clause will jump out of the loop for us */ } break; } #endif // * #if !SQLITE_OMIT_SUBQUERY */ /* Send the data to the callback function or to a subroutine. In the ** case of a subroutine, the subroutine itself is responsible for ** popping the data from the stack. */ case SRT_Coroutine: case SRT_Output: { testcase( eDest == SRT_Coroutine ); testcase( eDest == SRT_Output ); if ( pOrderBy != null ) { int r1 = sqlite3GetTempReg( pParse ); sqlite3VdbeAddOp3( v, OP_MakeRecord, regResult, nColumn, r1 ); pushOntoSorter( pParse, pOrderBy, p, r1 ); sqlite3ReleaseTempReg( pParse, r1 ); } else if ( eDest == SRT_Coroutine ) { sqlite3VdbeAddOp1( v, OP_Yield, pDest.iParm ); } else { sqlite3VdbeAddOp2( v, OP_ResultRow, regResult, nColumn ); sqlite3ExprCacheAffinityChange( pParse, regResult, nColumn ); } break; } #if !SQLITE_OMIT_TRIGGER /* Discard the results. This is used for SELECT statements inside ** the body of a TRIGGER. The purpose of such selects is to call ** user-defined functions that have side effects. We do not care ** about the actual results of the select. */ default: { Debug.Assert( eDest == SRT_Discard ); break; } #endif } /* Jump to the end of the loop if the LIMIT is reached. Except, if ** there is a sorter, in which case the sorter has already limited ** the output for us. */ if ( pOrderBy == null && p.iLimit != 0 ) { sqlite3VdbeAddOp3( v, OP_IfZero, p.iLimit, iBreak, -1 ); } }
//----------------------------------------------------------- public string Visit(ExprList node) { return(VisitChildren(node)); }
static int ResolveCompoundOrderBy(Parse parse, Select select) { ExprList orderBy = select.OrderBy; if (orderBy == null) { return(0); } Context ctx = parse.Ctx; #if true || MAX_COLUMN if (orderBy.Exprs > ctx.Limits[(int)LIMIT.COLUMN]) { parse.ErrorMsg("too many terms in ORDER BY clause"); return(1); } #endif int i; for (i = 0; i < orderBy.Exprs; i++) { orderBy.Ids[i].Done = false; } select.Next = null; while (select.Prior != null) { select.Prior.Next = select; select = select.Prior; } bool moreToDo = true; while (select != null && moreToDo) { moreToDo = false; ExprList list = select.EList; Debug.Assert(list != null); ExprList.ExprListItem item; for (i = 0; i < orderBy.Exprs; i++) { item = orderBy.Ids[i]; Expr pDup; if (item.Done) { continue; } Expr expr = item.Expr; int colId = -1; if (expr.IsInteger(ref colId)) { if (colId <= 0 || colId > list.Exprs) { ResolveOutOfRangeError(parse, "ORDER", i + 1, list.Exprs); return(1); } } else { colId = ResolveAsName(parse, list, expr); if (colId == 0) { Expr dupExpr = Expr.Dup(ctx, expr, 0); if (!ctx.MallocFailed) { Debug.Assert(dupExpr != null); colId = ResolveOrderByTermToExprList(parse, select, dupExpr); } Expr.Delete(ctx, ref dupExpr); } } if (colId > 0) { // Convert the ORDER BY term into an integer column number iCol, taking care to preserve the COLLATE clause if it exists Expr newExpr = Expr.Expr_(ctx, TK.INTEGER, null); if (newExpr == null) { return(1); } newExpr.Flags |= EP.IntValue; newExpr.u.I = colId; if (item.Expr == expr) { item.Expr = newExpr; } else { Debug.Assert(item.Expr.OP == TK.COLLATE); Debug.Assert(item.Expr.Left == expr); item.Expr.Left = newExpr; } Expr.Delete(ctx, ref expr); item.OrderByCol = (ushort)colId; item.Done = true; } else { moreToDo = true; } } select = select.Next; } for (i = 0; i < orderBy.Exprs; i++) { if (!orderBy.Ids[i].Done) { parse.ErrorMsg("%r ORDER BY term does not match any column in the result set", i + 1); return(1); } } return(0); }
/* ** 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); }
/* ** Return a list of all triggers on table pTab if there exists at least ** one trigger that must be fired when an operation of type 'op' is ** performed on the table, and, if that operation is an UPDATE, if at ** least one of the columns in pChanges is being modified. */ static Trigger sqlite3TriggersExist( Parse pParse, /* Parse context */ Table pTab, /* The table the contains the triggers */ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ ExprList pChanges, /* Columns that change in an UPDATE statement */ out int pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ ) { int mask = 0; Trigger pList = null; Trigger p; if ( ( pParse.db.flags & SQLITE_EnableTrigger ) != 0 ) { pList = sqlite3TriggerList( pParse, pTab ); } Debug.Assert( pList == null || IsVirtual( pTab ) == false ); for ( p = pList; p != null; p = p.pNext ) { if ( p.op == op && checkColumnOverlap( p.pColumns, pChanges ) != 0 ) { mask |= p.tr_tm; } } //if ( pMask != 0 ) { pMask = mask; } return ( mask != 0 ? pList : null ); }
/* ** 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 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); }
///////////////////////////////////////////////////////////////////////////////// private Expression GenerateConvert(ExprCall pExpr) { PREDEFMETH pm = pExpr.PredefinedMethod; Expression e; Type t; if (pm == PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED) { // If we have a user defined conversion, then we'll have the object // as the first element, and another list as a second element. This list // contains a TYPEOF as the first element, and the METHODINFO for the call // as the second. ExprList list = (ExprList)pExpr.OptionalArguments; ExprList list2 = (ExprList)list.OptionalNextListNode; e = GetExpression(list.OptionalElement); t = ((ExprTypeOf)list2.OptionalElement).SourceType.AssociatedSystemType; if (e.Type.MakeByRefType() == t) { // We're trying to convert from a type to its by ref type. Don't do that. return(e); } Debug.Assert((pExpr.Flags & EXPRFLAG.EXF_UNBOXRUNTIME) == 0); MethodInfo m = ((ExprMethodInfo)list2.OptionalNextListNode).MethodInfo; if (pm == PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED) { return(Expression.Convert(e, t, m)); } return(Expression.ConvertChecked(e, t, m)); } else { Debug.Assert(pm == PREDEFMETH.PM_EXPRESSION_CONVERT || pm == PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED); // If we have a standard conversion, then we'll have some object as // the first list element (ie a WRAP or a CALL), and then a TYPEOF // as the second list element. ExprList list = (ExprList)pExpr.OptionalArguments; e = GetExpression(list.OptionalElement); t = ((ExprTypeOf)list.OptionalNextListNode).SourceType.AssociatedSystemType; if (e.Type.MakeByRefType() == t) { // We're trying to convert from a type to its by ref type. Don't do that. return(e); } if ((pExpr.Flags & EXPRFLAG.EXF_UNBOXRUNTIME) != 0) { // If we want to unbox this thing, return that instead of the convert. return(Expression.Unbox(e, t)); } if (pm == PREDEFMETH.PM_EXPRESSION_CONVERT) { return(Expression.Convert(e, t)); } return(Expression.ConvertChecked(e, t)); } }
/* ** 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 */ }
///////////////////////////////////////////////////////////////////////////////// private Expression GenerateBinaryOperator(ExprCall pExpr) { ExprList list = (ExprList)pExpr.OptionalArguments; Debug.Assert(list != null); Expression arg1 = GetExpression(list.OptionalElement); Expression arg2 = GetExpression(list.OptionalNextListNode); switch (pExpr.PredefinedMethod) { case PREDEFMETH.PM_EXPRESSION_ADD: return(Expression.Add(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_AND: return(Expression.And(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_DIVIDE: return(Expression.Divide(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_EQUAL: return(Expression.Equal(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR: return(Expression.ExclusiveOr(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_GREATERTHAN: return(Expression.GreaterThan(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL: return(Expression.GreaterThanOrEqual(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT: return(Expression.LeftShift(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_LESSTHAN: return(Expression.LessThan(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL: return(Expression.LessThanOrEqual(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_MODULO: return(Expression.Modulo(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_MULTIPLY: return(Expression.Multiply(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_NOTEQUAL: return(Expression.NotEqual(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_OR: return(Expression.Or(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT: return(Expression.RightShift(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_SUBTRACT: return(Expression.Subtract(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_ORELSE: return(Expression.OrElse(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_ANDALSO: return(Expression.AndAlso(arg1, arg2)); // Checked case PREDEFMETH.PM_EXPRESSION_ADDCHECKED: return(Expression.AddChecked(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED: return(Expression.MultiplyChecked(arg1, arg2)); case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED: return(Expression.SubtractChecked(arg1, arg2)); default: Debug.Fail("Invalid Predefined Method in GenerateBinaryOperator"); throw Error.InternalCompilerError(); } }
/* ** Given a an expression list (which is really the list of expressions ** that form the result set of a SELECT statement) compute appropriate ** column names for a table that would hold the expression list. ** ** All column names will be unique. ** ** Only the column names are computed. Column.zType, Column.zColl, ** and other fields of Column are zeroed. ** ** Return SQLITE_OK on success. If a memory allocation error occurs, ** store NULL in paCol and 0 in pnCol and return SQLITE_NOMEM. */ static int selectColumnsFromExprList( Parse pParse, /* Parsing context */ ExprList pEList, /* Expr list from which to derive column names */ ref int pnCol, /* Write the number of columns here */ ref Column[] paCol /* Write the new column list here */ ) { sqlite3 db = pParse.db; /* Database connection */ int i, j; /* Loop counters */ int cnt; /* Index added to make the name unique */ Column[] aCol; Column pCol; /* For looping over result columns */ int nCol; /* Number of columns in the result set */ Expr p; /* Expression for a single result column */ string zName; /* Column name */ int nName; /* Size of name in zName[] */ pnCol = nCol = pEList.nExpr; aCol = paCol = new Column[nCol];//sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); //if ( aCol == null ) // return SQLITE_NOMEM; for ( i = 0; i < nCol; i++ )//, pCol++) { if ( aCol[i] == null ) aCol[i] = new Column(); pCol = aCol[i]; /* Get an appropriate name for the column */ p = pEList.a[i].pExpr; Debug.Assert( p.pRight == null || ExprHasProperty( p.pRight, EP_IntValue ) || p.pRight.u.zToken == null || p.pRight.u.zToken.Length > 0 ); if ( pEList.a[i].zName != null && ( zName = pEList.a[i].zName ) != string.Empty ) { /* If the column contains an "AS <name>" phrase, use <name> as the name */ //zName = sqlite3DbStrDup(db, zName); } else { Expr pColExpr = p; /* The expression that is the result column name */ Table pTab; /* Table associated with this expression */ while ( pColExpr.op == TK_DOT ) pColExpr = pColExpr.pRight; if ( pColExpr.op == TK_COLUMN && ALWAYS( pColExpr.pTab != null ) ) { /* For columns use the column name name */ int iCol = pColExpr.iColumn; pTab = pColExpr.pTab; if ( iCol < 0 ) iCol = pTab.iPKey; zName = sqlite3MPrintf( db, "%s", iCol >= 0 ? pTab.aCol[iCol].zName : "rowid" ); } else if ( pColExpr.op == TK_ID ) { Debug.Assert( !ExprHasProperty( pColExpr, EP_IntValue ) ); zName = sqlite3MPrintf( db, "%s", pColExpr.u.zToken ); } else { /* Use the original text of the column expression as its name */ zName = sqlite3MPrintf( db, "%s", pEList.a[i].zSpan ); } } //if ( db.mallocFailed != 0 ) //{ // sqlite3DbFree( db, ref zName ); // break; //} /* Make sure the column name is unique. If the name is not unique, ** append a integer to the name so that it becomes unique. */ nName = sqlite3Strlen30( zName ); for ( j = cnt = 0; j < i; j++ ) { if ( aCol[j].zName.Equals( zName, StringComparison.OrdinalIgnoreCase ) ) { string zNewName; //zName[nName] = 0; zNewName = sqlite3MPrintf( db, "%s:%d", zName.Substring( 0, nName ), ++cnt ); sqlite3DbFree( db, ref zName ); zName = zNewName; j = -1; if ( zName.Length == 0 ) break; } } pCol.zName = zName; } //if ( db.mallocFailed != 0 ) //{ // for ( j = 0 ; j < i ; j++ ) // { // sqlite3DbFree( db, aCol[j].zName ); // } // sqlite3DbFree( db, aCol ); // paCol = null; // pnCol = 0; // return SQLITE_NOMEM; //} return SQLITE_OK; }
///////////////////////////////////////////////////////////////////////////////// private Expression GenerateUserDefinedBinaryOperator(ExprCall pExpr) { ExprList list = (ExprList)pExpr.OptionalArguments; Expression arg1 = GetExpression(list.OptionalElement); Expression arg2 = GetExpression(((ExprList)list.OptionalNextListNode).OptionalElement); list = (ExprList)list.OptionalNextListNode; MethodInfo methodInfo; bool bIsLifted = false; if (list.OptionalNextListNode is ExprList next) { ExprConstant isLifted = (ExprConstant)next.OptionalElement; Debug.Assert(isLifted != null); bIsLifted = isLifted.Val.Int32Val == 1; methodInfo = ((ExprMethodInfo)next.OptionalNextListNode).MethodInfo; } else { methodInfo = ((ExprMethodInfo)list.OptionalNextListNode).MethodInfo; } switch (pExpr.PredefinedMethod) { case PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED: return(Expression.Add(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_AND_USER_DEFINED: return(Expression.And(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_DIVIDE_USER_DEFINED: return(Expression.Divide(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED: return(Expression.Equal(arg1, arg2, bIsLifted, methodInfo)); case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED: return(Expression.ExclusiveOr(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED: return(Expression.GreaterThan(arg1, arg2, bIsLifted, methodInfo)); case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED: return(Expression.GreaterThanOrEqual(arg1, arg2, bIsLifted, methodInfo)); case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT_USER_DEFINED: return(Expression.LeftShift(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED: return(Expression.LessThan(arg1, arg2, bIsLifted, methodInfo)); case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED: return(Expression.LessThanOrEqual(arg1, arg2, bIsLifted, methodInfo)); case PREDEFMETH.PM_EXPRESSION_MODULO_USER_DEFINED: return(Expression.Modulo(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_MULTIPLY_USER_DEFINED: return(Expression.Multiply(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED: return(Expression.NotEqual(arg1, arg2, bIsLifted, methodInfo)); case PREDEFMETH.PM_EXPRESSION_OR_USER_DEFINED: return(Expression.Or(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED: return(Expression.RightShift(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_SUBTRACT_USER_DEFINED: return(Expression.Subtract(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_ORELSE_USER_DEFINED: return(Expression.OrElse(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_ANDALSO_USER_DEFINED: return(Expression.AndAlso(arg1, arg2, methodInfo)); // Checked case PREDEFMETH.PM_EXPRESSION_ADDCHECKED_USER_DEFINED: return(Expression.AddChecked(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED: return(Expression.MultiplyChecked(arg1, arg2, methodInfo)); case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED: return(Expression.SubtractChecked(arg1, arg2, methodInfo)); default: Debug.Fail("Invalid Predefined Method in GenerateUserDefinedBinaryOperator"); throw Error.InternalCompilerError(); } }
static void substExprList( sqlite3 db, /* Report malloc errors here */ ExprList pList, /* List to scan and in which to make substitutes */ int iTable, /* Table to be substituted */ ExprList pEList /* Substitute values */ ) { int i; if ( pList == null ) return; for ( i = 0; i < pList.nExpr; i++ ) { pList.a[i].pExpr = substExpr( db, pList.a[i].pExpr, iTable, pEList ); } }
///////////////////////////////////////////////////////////////////////////////// private Expression GetExpression(Expr pExpr) { if (pExpr is ExprWrap wrap) { return(_DictionaryOfParameters[(ExprCall)wrap.OptionalExpression]); } else if (pExpr is ExprConstant) { Debug.Assert(pExpr.Type is NullType); return(null); } else { // We can have a convert node or a call of a user defined conversion. ExprCall call = (ExprCall)pExpr; Debug.Assert(call != null); PREDEFMETH pm = call.PredefinedMethod; Debug.Assert(pm == PREDEFMETH.PM_EXPRESSION_CONVERT || pm == PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_NEWARRAYINIT || pm == PREDEFMETH.PM_EXPRESSION_CALL || pm == PREDEFMETH.PM_EXPRESSION_PROPERTY || pm == PREDEFMETH.PM_EXPRESSION_FIELD || pm == PREDEFMETH.PM_EXPRESSION_ARRAYINDEX || pm == PREDEFMETH.PM_EXPRESSION_ARRAYINDEX2 || pm == PREDEFMETH.PM_EXPRESSION_CONSTANT_OBJECT_TYPE || pm == PREDEFMETH.PM_EXPRESSION_NEW || // Binary operators. pm == PREDEFMETH.PM_EXPRESSION_ASSIGN || pm == PREDEFMETH.PM_EXPRESSION_ADD || pm == PREDEFMETH.PM_EXPRESSION_AND || pm == PREDEFMETH.PM_EXPRESSION_DIVIDE || pm == PREDEFMETH.PM_EXPRESSION_EQUAL || pm == PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR || pm == PREDEFMETH.PM_EXPRESSION_GREATERTHAN || pm == PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL || pm == PREDEFMETH.PM_EXPRESSION_LEFTSHIFT || pm == PREDEFMETH.PM_EXPRESSION_LESSTHAN || pm == PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL || pm == PREDEFMETH.PM_EXPRESSION_MODULO || pm == PREDEFMETH.PM_EXPRESSION_MULTIPLY || pm == PREDEFMETH.PM_EXPRESSION_NOTEQUAL || pm == PREDEFMETH.PM_EXPRESSION_OR || pm == PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT || pm == PREDEFMETH.PM_EXPRESSION_SUBTRACT || pm == PREDEFMETH.PM_EXPRESSION_ORELSE || pm == PREDEFMETH.PM_EXPRESSION_ANDALSO || pm == PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_AND_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_DIVIDE_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_LEFTSHIFT_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_MODULO_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_MULTIPLY_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_OR_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_SUBTRACT_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_ORELSE_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_ANDALSO_USER_DEFINED || // Checked binary pm == PREDEFMETH.PM_EXPRESSION_ADDCHECKED || pm == PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED || pm == PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED || pm == PREDEFMETH.PM_EXPRESSION_ADDCHECKED_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED || // Unary operators. pm == PREDEFMETH.PM_EXPRESSION_NOT || pm == PREDEFMETH.PM_EXPRESSION_NEGATE || pm == PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_NEGATE_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_UNARYPLUS_USER_DEFINED || // Checked unary pm == PREDEFMETH.PM_EXPRESSION_NEGATECHECKED || pm == PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED || pm == PREDEFMETH.PM_EXPRESSION_NEGATECHECKED_USER_DEFINED || pm == PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED ); switch (pm) { case PREDEFMETH.PM_EXPRESSION_CALL: return(GenerateCall(call)); case PREDEFMETH.PM_EXPRESSION_CONVERT: case PREDEFMETH.PM_EXPRESSION_CONVERT_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED: case PREDEFMETH.PM_EXPRESSION_CONVERTCHECKED_USER_DEFINED: return(GenerateConvert(call)); case PREDEFMETH.PM_EXPRESSION_NEWARRAYINIT: ExprList list = (ExprList)call.OptionalArguments; return (Expression.NewArrayInit( ((ExprTypeOf)list.OptionalElement).SourceType.AssociatedSystemType, GetArgumentsFromArrayInit((ExprArrayInit)list.OptionalNextListNode))); case PREDEFMETH.PM_EXPRESSION_ARRAYINDEX: case PREDEFMETH.PM_EXPRESSION_ARRAYINDEX2: return(GenerateArrayIndex(call)); case PREDEFMETH.PM_EXPRESSION_NEW: return(GenerateNew(call)); case PREDEFMETH.PM_EXPRESSION_PROPERTY: return(GenerateProperty(call)); case PREDEFMETH.PM_EXPRESSION_FIELD: return(GenerateField(call)); case PREDEFMETH.PM_EXPRESSION_CONSTANT_OBJECT_TYPE: return(GenerateConstantType(call)); case PREDEFMETH.PM_EXPRESSION_ASSIGN: return(GenerateAssignment(call)); case PREDEFMETH.PM_EXPRESSION_ADD: case PREDEFMETH.PM_EXPRESSION_AND: case PREDEFMETH.PM_EXPRESSION_DIVIDE: case PREDEFMETH.PM_EXPRESSION_EQUAL: case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR: case PREDEFMETH.PM_EXPRESSION_GREATERTHAN: case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL: case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT: case PREDEFMETH.PM_EXPRESSION_LESSTHAN: case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL: case PREDEFMETH.PM_EXPRESSION_MODULO: case PREDEFMETH.PM_EXPRESSION_MULTIPLY: case PREDEFMETH.PM_EXPRESSION_NOTEQUAL: case PREDEFMETH.PM_EXPRESSION_OR: case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT: case PREDEFMETH.PM_EXPRESSION_SUBTRACT: case PREDEFMETH.PM_EXPRESSION_ORELSE: case PREDEFMETH.PM_EXPRESSION_ANDALSO: // Checked case PREDEFMETH.PM_EXPRESSION_ADDCHECKED: case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED: case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED: return(GenerateBinaryOperator(call)); case PREDEFMETH.PM_EXPRESSION_ADD_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_AND_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_DIVIDE_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_EQUAL_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_EXCLUSIVEOR_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_GREATERTHAN_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_GREATERTHANOREQUAL_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_LEFTSHIFT_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_LESSTHAN_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_LESSTHANOREQUAL_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_MODULO_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_MULTIPLY_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_NOTEQUAL_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_OR_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_RIGHTSHIFT_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_SUBTRACT_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_ORELSE_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_ANDALSO_USER_DEFINED: // Checked case PREDEFMETH.PM_EXPRESSION_ADDCHECKED_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_MULTIPLYCHECKED_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_SUBTRACTCHECKED_USER_DEFINED: return(GenerateUserDefinedBinaryOperator(call)); case PREDEFMETH.PM_EXPRESSION_NOT: case PREDEFMETH.PM_EXPRESSION_NEGATE: case PREDEFMETH.PM_EXPRESSION_NEGATECHECKED: return(GenerateUnaryOperator(call)); case PREDEFMETH.PM_EXPRESSION_NOT_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_NEGATE_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_UNARYPLUS_USER_DEFINED: case PREDEFMETH.PM_EXPRESSION_NEGATECHECKED_USER_DEFINED: return(GenerateUserDefinedUnaryOperator(call)); default: Debug.Fail("Invalid Predefined Method in GetExpression"); throw Error.InternalCompilerError(); } } }
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; }
/* ** Given an expression list, generate a KeyInfo structure that records ** the collating sequence for each expression in that expression list. ** ** If the ExprList is an ORDER BY or GROUP BY clause then the resulting ** KeyInfo structure is appropriate for initializing a virtual index to ** implement that clause. If the ExprList is the result set of a SELECT ** then the KeyInfo structure is appropriate for initializing a virtual ** index to implement a DISTINCT test. ** ** Space to hold the KeyInfo structure is obtain from malloc. The calling ** function is responsible for seeing that this structure is eventually ** freed. Add the KeyInfo structure to the P4 field of an opcode using ** P4_KEYINFO_HANDOFF is the usual way of dealing with this. */ static KeyInfo keyInfoFromExprList( Parse pParse, ExprList pList ) { sqlite3 db = pParse.db; int nExpr; KeyInfo pInfo; ExprList_item pItem; int i; nExpr = pList.nExpr; pInfo = new KeyInfo();//sqlite3DbMallocZero(db, sizeof(*pInfo) + nExpr*(CollSeq*.Length+1) ); if ( pInfo != null ) { pInfo.aSortOrder = new byte[nExpr];// pInfo.aColl[nExpr]; pInfo.aColl = new CollSeq[nExpr]; pInfo.nField = (u16)nExpr; pInfo.enc = db.aDbStatic[0].pSchema.enc;// ENC(db); pInfo.db = db; for ( i = 0; i < nExpr; i++ ) {//, pItem++){ pItem = pList.a[i]; CollSeq pColl; pColl = sqlite3ExprCollSeq( pParse, pItem.pExpr ); if ( pColl == null ) { pColl = db.pDfltColl; } pInfo.aColl[i] = pColl; pInfo.aSortOrder[i] = (byte)pItem.sortOrder; } } return pInfo; }
void sqlite3PrintExprList( ExprList pList ) { int i; for ( i = 0; i < pList.nExpr; i++ ) { sqlite3PrintExpr( pList.a[i].pExpr ); if ( i < pList.nExpr - 1 ) { sqlite3DebugPrintf( ", " ); } } }
/* ** Triggers may access values stored in the old.* or new.* pseudo-table. ** This function returns a 32-bit bitmask indicating which columns of the ** old.* or new.* tables actually are used by triggers. This information ** may be used by the caller, for example, to avoid having to load the entire ** old.* record into memory when executing an UPDATE or DELETE command. ** ** Bit 0 of the returned mask is set if the left-most column of the ** table may be accessed using an [old|new].<col> reference. Bit 1 is set if ** the second leftmost column value is required, and so on. If there ** are more than 32 columns in the table, and at least one of the columns ** with an index greater than 32 may be accessed, 0xffffffff is returned. ** ** It is not possible to determine if the old.rowid or new.rowid column is ** accessed by triggers. The caller must always assume that it is. ** ** Parameter isNew must be either 1 or 0. If it is 0, then the mask returned ** applies to the old.* table. If 1, the new.* table. ** ** Parameter tr_tm must be a mask with one or both of the TRIGGER_BEFORE ** and TRIGGER_AFTER bits set. Values accessed by BEFORE triggers are only ** included in the returned mask if the TRIGGER_BEFORE bit is set in the ** tr_tm parameter. Similarly, values accessed by AFTER triggers are only ** included in the returned mask if the TRIGGER_AFTER bit is set in tr_tm. */ static u32 sqlite3TriggerColmask( Parse pParse, /* Parse context */ Trigger pTrigger, /* List of triggers on table pTab */ ExprList pChanges, /* Changes list for any UPDATE OF triggers */ int isNew, /* 1 for new.* ref mask, 0 for old.* ref mask */ int tr_tm, /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ Table pTab, /* The table to code triggers from */ int orconf /* Default ON CONFLICT policy for trigger steps */ ) { int op = pChanges != null ? TK_UPDATE : TK_DELETE; u32 mask = 0; Trigger p; Debug.Assert( isNew == 1 || isNew == 0 ); for ( p = pTrigger; p != null; p = p.pNext ) { if ( p.op == op && ( tr_tm & p.tr_tm ) != 0 && checkColumnOverlap( p.pColumns, pChanges ) != 0 ) { TriggerPrg pPrg; pPrg = getRowTrigger( pParse, p, pTab, orconf ); if ( pPrg != null ) { mask |= pPrg.aColmask[isNew]; } } } return mask; }
static TriggerStep sqlite3TriggerInsertStep(sqlite3 db, Token pTableName, IdList pColumn, ExprList pEList, int null_5, u8 orconf) { return(sqlite3TriggerInsertStep(db, pTableName, pColumn, pEList, null, orconf)); }
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); }
/* ** This is called to code the required FOR EACH ROW triggers for an operation ** on table pTab. The operation to code triggers for (INSERT, UPDATE or DELETE) ** is given by the op paramater. The tr_tm parameter determines whether the ** BEFORE or AFTER triggers are coded. If the operation is an UPDATE, then ** parameter pChanges is passed the list of columns being modified. ** ** If there are no triggers that fire at the specified time for the specified ** operation on pTab, this function is a no-op. ** ** The reg argument is the address of the first in an array of registers ** that contain the values substituted for the new.* and old.* references ** in the trigger program. If N is the number of columns in table pTab ** (a copy of pTab.nCol), then registers are populated as follows: ** ** Register Contains ** ------------------------------------------------------ ** reg+0 OLD.rowid ** reg+1 OLD.* value of left-most column of pTab ** ... ... ** reg+N OLD.* value of right-most column of pTab ** reg+N+1 NEW.rowid ** reg+N+2 OLD.* value of left-most column of pTab ** ... ... ** reg+N+N+1 NEW.* value of right-most column of pTab ** ** For ON DELETE triggers, the registers containing the NEW.* values will ** never be accessed by the trigger program, so they are not allocated or ** populated by the caller (there is no data to populate them with anyway). ** Similarly, for ON INSERT triggers the values stored in the OLD.* registers ** are never accessed, and so are not allocated by the caller. So, for an ** ON INSERT trigger, the value passed to this function as parameter reg ** is not a readable register, although registers (reg+N) through ** (reg+N+N+1) are. ** ** Parameter orconf is the default conflict resolution algorithm for the ** trigger program to use (REPLACE, IGNORE etc.). Parameter ignoreJump ** is the instruction that control should jump to if a trigger program ** raises an IGNORE exception. */ static void sqlite3CodeRowTrigger( Parse pParse, /* Parse context */ Trigger pTrigger, /* List of triggers on table pTab */ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ ExprList pChanges, /* Changes list for any UPDATE OF triggers */ int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ Table pTab, /* The table to code triggers from */ int reg, /* The first in an array of registers (see above) */ int orconf, /* ON CONFLICT policy */ int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ ) { Trigger p; /* Used to iterate through pTrigger list */ Debug.Assert( op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE ); Debug.Assert( tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER ); Debug.Assert( ( op == TK_UPDATE ) == ( pChanges != null ) ); for ( p = pTrigger; p != null; p = p.pNext ) { /* Sanity checking: The schema for the trigger and for the table are ** always defined. The trigger must be in the same schema as the table ** or else it must be a TEMP trigger. */ Debug.Assert( p.pSchema != null ); Debug.Assert( p.pTabSchema != null ); Debug.Assert( p.pSchema == p.pTabSchema || p.pSchema == pParse.db.aDb[1].pSchema ); /* Determine whether we should code this trigger */ if ( p.op == op && p.tr_tm == tr_tm && checkColumnOverlap( p.pColumns, pChanges ) != 0 ) { sqlite3CodeRowTriggerDirect( pParse, p, pTab, reg, orconf, ignoreJump ); } } }
/* ** 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 TriggerStep sqlite3TriggerInsertStep( sqlite3 db, Token pTableName, IdList pColumn, ExprList pEList, int null_5, u8 orconf ) { return sqlite3TriggerInsertStep( db, pTableName, pColumn, pEList, null, orconf ); }
public static TriggerStep TriggerInsertStep(Context ctx, Token tableName, IdList column, ExprList list, Select select, OE orconf) { Debug.Assert(list == null || select == null); Debug.Assert(list != null || select != null || ctx.MallocFailed); TriggerStep triggerStep = TriggerStepAllocate(ctx, TK.INSERT, tableName); if (triggerStep != null) { triggerStep.Select = Select.Dup(ctx, select, E.EXPRDUP_REDUCE); triggerStep.IdList = column; triggerStep.ExprList = Expr.ListDup(ctx, list, E.EXPRDUP_REDUCE); triggerStep.Orconf = orconf; } else { Expr.IdListDelete(ctx, ref column); } Expr.ListDelete(ctx, ref list); Select.Delete(ctx, ref select); return(triggerStep); }
/* ** Construct a trigger step that implements an UPDATE statement and return ** a pointer to that trigger step. The parser calls this routine when it ** sees an UPDATE statement inside the body of a CREATE TRIGGER. */ static TriggerStep sqlite3TriggerUpdateStep( sqlite3 db, /* The database connection */ Token pTableName, /* Name of the table to be updated */ ExprList pEList, /* The SET clause: list of column and new values */ Expr pWhere, /* The WHERE clause */ u8 orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ ) { TriggerStep pTriggerStep; pTriggerStep = triggerStepAllocate( db, TK_UPDATE, pTableName ); //if ( pTriggerStep != null ) //{ pTriggerStep.pExprList = sqlite3ExprListDup( db, pEList, EXPRDUP_REDUCE ); pTriggerStep.pWhere = sqlite3ExprDup( db, pWhere, EXPRDUP_REDUCE ); pTriggerStep.orconf = orconf; //} sqlite3ExprListDelete( db, ref pEList ); sqlite3ExprDelete( db, ref pWhere ); return pTriggerStep; }
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 }
/* ** 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; }
public override void GetUsedIdents(ScopeChecker.UsedIdents usedIdents) { ExprList.ForEach(cmd => cmd.GetUsedIdents(usedIdents)); usedIdents.AddProcFuncIdent(Ident); }
/* ** 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 TriggerStep TriggerInsertStep(Context ctx, Token tableName, IdList column, ExprList list, int null_5, OE orconf) { return(TriggerInsertStep(ctx, tableName, column, list, null, orconf)); }