using System.Diagnostics; using u32 = System.UInt32; namespace Community.CsharpSqlite { public partial class Sqlite3 { /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** in order to generate code for DELETE FROM statements. ************************************************************************* ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart ** C#-SQLite is an independent reimplementation of the SQLite software library ** ** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2 ** ************************************************************************* */ //#include "sqliteInt.h" /* ** While a SrcList can in general represent multiple tables and subqueries ** (as in the FROM clause of a SELECT statement) in this case it contains ** the name of a single table, as one might find in an INSERT, DELETE, ** or UPDATE statement. Look up that table in the symbol table and ** return a pointer. Set an error message and return NULL if the table ** name is not found or if any other error occurs. ** ** The following fields are initialized appropriate in pSrc: ** ** pSrc->a[0].pTab Pointer to the Table object ** pSrc->a[0].pIndex Pointer to the INDEXED BY index, if there is one ** */ private static Table sqlite3SrcListLookup(Parse pParse, SrcList pSrc) { SrcList_item pItem = pSrc.a[0]; Table pTab; Debug.Assert(pItem != null && pSrc.nSrc == 1); pTab = sqlite3LocateTable(pParse, 0, pItem.zName, pItem.zDatabase); sqlite3DeleteTable(pParse.db, ref pItem.pTab); pItem.pTab = pTab; if (pTab != null) { pTab.nRef++; } if (sqlite3IndexedByLookup(pParse, pItem) != 0) { pTab = null; } return pTab; } /* ** Check to make sure the given table is writable. If it is not ** writable, generate an error message and return 1. If it is ** writable return 0; */ private static bool sqlite3IsReadOnly(Parse pParse, Table pTab, int viewOk) { /* A table is not writable under the following circumstances: ** ** 1) It is a virtual table and no implementation of the xUpdate method ** has been provided, or ** 2) It is a system table (i.e. sqlite_master), this call is not ** part of a nested parse and writable_schema pragma has not ** been specified. ** ** In either case leave an error message in pParse and return non-zero. */ if ( (IsVirtual(pTab) && sqlite3GetVTable(pParse.db, pTab).pMod.pModule.xUpdate == null) || ((pTab.tabFlags & TF_Readonly) != 0 && (pParse.db.flags & SQLITE_WriteSchema) == 0 && pParse.nested == 0) ) { sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab.zName); return true; } #if !SQLITE_OMIT_VIEW if (viewOk == 0 && pTab.pSelect != null) { sqlite3ErrorMsg(pParse, "cannot modify %s because it is a view", pTab.zName); return true; } #endif return false; } #if !SQLITE_OMIT_VIEW && !SQLITE_OMIT_TRIGGER /* ** Evaluate a view and store its result in an ephemeral table. The ** pWhere argument is an optional WHERE clause that restricts the ** set of rows in the view that are to be added to the ephemeral table. */ private static void sqlite3MaterializeView( Parse pParse, /* Parsing context */ Table pView, /* View definition */ Expr pWhere, /* Optional WHERE clause to be added */ int iCur /* VdbeCursor number for ephemerial table */ ) { SelectDest dest = new SelectDest(); Select pDup; sqlite3 db = pParse.db; pDup = sqlite3SelectDup(db, pView.pSelect, 0); if (pWhere != null) { SrcList pFrom; pWhere = sqlite3ExprDup(db, pWhere, 0); pFrom = sqlite3SrcListAppend(db, null, null, null); //if ( pFrom != null ) //{ Debug.Assert(pFrom.nSrc == 1); pFrom.a[0].zAlias = pView.zName;// sqlite3DbStrDup( db, pView.zName ); pFrom.a[0].pSelect = pDup; Debug.Assert(pFrom.a[0].pOn == null); Debug.Assert(pFrom.a[0].pUsing == null); //} //else //{ // sqlite3SelectDelete( db, ref pDup ); //} pDup = sqlite3SelectNew(pParse, null, pFrom, pWhere, null, null, null, 0, null, null); } sqlite3SelectDestInit(dest, SRT_EphemTab, iCur); sqlite3Select(pParse, pDup, ref dest); sqlite3SelectDelete(db, ref pDup); } #endif //* !SQLITE_OMIT_VIEW) && !SQLITE_OMIT_TRIGGER) */ #if (SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !(SQLITE_OMIT_SUBQUERY) /* ** 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; } #endif //* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */ /* ** Generate code for a DELETE FROM statement. ** ** DELETE FROM table_wxyz WHERE a<5 AND b NOT NULL; ** \________/ \________________/ ** pTabList pWhere */ private 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 */ 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; pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, null, out 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); #if !SQLITE_OMIT_AUTHORIZATION string zDb = db.aDb[iDb].zName; /* Name of database holding pTab */ 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)) { VTable pVTab = sqlite3GetVTable(db, pTab); sqlite3VtabMakeWritable(pParse, pTab); sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB); sqlite3VdbeChangeP5(v, OE_Abort); 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; } /* 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 /* ** This routine generates VDBE code that causes a single row of a ** single table to be deleted. ** ** The VDBE must be in a particular state when this routine is called. ** These are the requirements: ** ** 1. A read/write cursor pointing to pTab, the table containing the row ** to be deleted, must be opened as cursor number $iCur. ** ** 2. Read/write cursors for all indices of pTab must be open as ** cursor number base+i for the i-th index. ** ** 3. The record number of the row to be deleted must be stored in ** memory cell iRowid. ** ** This routine generates code to remove both the table record and all ** index entries that point to that record. */ private static void sqlite3GenerateRowDelete( Parse pParse, /* Parsing context */ Table pTab, /* Table containing the row to be deleted */ int iCur, /* VdbeCursor number for the table */ int iRowid, /* Memory cell that contains the rowid to delete */ int count, /* If non-zero, increment the row change counter */ Trigger pTrigger, /* List of triggers to (potentially) fire */ int onconf /* Default ON CONFLICT policy for triggers */ ) { Vdbe v = pParse.pVdbe; /* Vdbe */ int iOld = 0; /* First register in OLD.* array */ int iLabel; /* Label resolved to end of generated code */ /* Vdbe is guaranteed to have been allocated by this stage. */ Debug.Assert(v != null); /* Seek cursor iCur to the row to delete. If this row no longer exists ** (this can happen if a trigger program has already deleted it), do ** not attempt to delete it or fire any DELETE triggers. */ iLabel = sqlite3VdbeMakeLabel(v); sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid); /* If there are any triggers to fire, allocate a range of registers to ** use for the old.* references in the triggers. */ if (sqlite3FkRequired(pParse, pTab, null, 0) != 0 || pTrigger != null) { u32 mask; /* Mask of OLD.* columns in use */ int iCol; /* Iterator used while populating OLD.* */ /* TODO: Could use temporary registers here. Also could attempt to ** avoid copying the contents of the rowid register. */ mask = sqlite3TriggerColmask( pParse, pTrigger, null, 0, TRIGGER_BEFORE | TRIGGER_AFTER, pTab, onconf ); mask |= sqlite3FkOldmask(pParse, pTab); iOld = pParse.nMem + 1; pParse.nMem += (1 + pTab.nCol); /* Populate the OLD.* pseudo-table register array. These values will be ** used by any BEFORE and AFTER triggers that exist. */ sqlite3VdbeAddOp2(v, OP_Copy, iRowid, iOld); for (iCol = 0; iCol < pTab.nCol; iCol++) { if (mask == 0xffffffff || (mask & (1 << iCol)) != 0) { sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol, iOld + iCol + 1); } } /* Invoke BEFORE DELETE trigger programs. */ sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, null, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel ); /* Seek the cursor to the row to be deleted again. It may be that ** the BEFORE triggers coded above have already removed the row ** being deleted. Do not attempt to delete the row a second time, and ** do not fire AFTER triggers. */ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid); /* Do FK processing. This call checks that any FK constraints that ** refer to this table (i.e. constraints attached to other tables) ** are not violated by deleting this row. */ sqlite3FkCheck(pParse, pTab, iOld, 0); } /* Delete the index and table entries. Skip this step if pTab is really ** a view (in which case the only effect of the DELETE statement is to ** fire the INSTEAD OF triggers). */ if (pTab.pSelect == null) { sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0); sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count != 0 ? (int)OPFLAG_NCHANGE : 0)); if (count != 0) { sqlite3VdbeChangeP4(v, -1, pTab.zName, P4_TRANSIENT); } } /* 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 deleted. */ sqlite3FkActions(pParse, pTab, null, iOld); /* Invoke AFTER DELETE trigger programs. */ sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, null, TRIGGER_AFTER, pTab, iOld, onconf, iLabel ); /* Jump here if the row had already been deleted before any BEFORE ** trigger programs were invoked. Or if a trigger program throws a ** RAISE(IGNORE) exception. */ sqlite3VdbeResolveLabel(v, iLabel); } /* ** This routine generates VDBE code that causes the deletion of all ** index entries associated with a single row of a single table. ** ** The VDBE must be in a particular state when this routine is called. ** These are the requirements: ** ** 1. A read/write cursor pointing to pTab, the table containing the row ** to be deleted, must be opened as cursor number "iCur". ** ** 2. Read/write cursors for all indices of pTab must be open as ** cursor number iCur+i for the i-th index. ** ** 3. The "iCur" cursor must be pointing to the row that is to be ** deleted. */ private static void sqlite3GenerateRowIndexDelete( Parse pParse, /* Parsing and code generating context */ Table pTab, /* Table containing the row to be deleted */ int iCur, /* VdbeCursor number for the table */ int nothing /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */ ) { int[] aRegIdx = null; sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); } private static void sqlite3GenerateRowIndexDelete( Parse pParse, /* Parsing and code generating context */ Table pTab, /* Table containing the row to be deleted */ int iCur, /* VdbeCursor number for the table */ int[] aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */ ) { int i; Index pIdx; int r1; for (i = 1, pIdx = pTab.pIndex; pIdx != null; i++, pIdx = pIdx.pNext) { if (aRegIdx != null && aRegIdx[i - 1] == 0) continue; r1 = sqlite3GenerateIndexKey(pParse, pIdx, iCur, 0, false); sqlite3VdbeAddOp3(pParse.pVdbe, OP_IdxDelete, iCur + i, r1, pIdx.nColumn + 1); } } /* ** Generate code that will assemble an index key and put it in register ** regOut. The key with be for index pIdx which is an index on pTab. ** iCur is the index of a cursor open on the pTab table and pointing to ** the entry that needs indexing. ** ** Return a register number which is the first in a block of ** registers that holds the elements of the index key. The ** block of registers has already been deallocated by the time ** this routine returns. */ private static int sqlite3GenerateIndexKey( Parse pParse, /* Parsing context */ Index pIdx, /* The index for which to generate a key */ int iCur, /* VdbeCursor number for the pIdx.pTable table */ int regOut, /* Write the new index key to this register */ bool doMakeRec /* Run the OP_MakeRecord instruction if true */ ) { Vdbe v = pParse.pVdbe; int j; Table pTab = pIdx.pTable; int regBase; int nCol; nCol = pIdx.nColumn; regBase = sqlite3GetTempRange(pParse, nCol + 1); sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regBase + nCol); for (j = 0; j < nCol; j++) { int idx = pIdx.aiColumn[j]; if (idx == pTab.iPKey) { sqlite3VdbeAddOp2(v, OP_SCopy, regBase + nCol, regBase + j); } else { sqlite3VdbeAddOp3(v, OP_Column, iCur, idx, regBase + j); sqlite3ColumnDefault(v, pTab, idx, -1); } } if (doMakeRec) { string zAff; if (pTab.pSelect != null || (pParse.db.flags & SQLITE_IdxRealAsInt) != 0) { zAff = ""; } else { zAff = sqlite3IndexAffinityStr(v, pIdx); } sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol + 1, regOut); sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT); } sqlite3ReleaseTempRange(pParse, regBase, nCol + 1); return regBase; } } }