mattercontrol/Community.CsharpSqlite/src/delete_c.cs
2015-04-08 15:20:10 -07:00

750 lines
No EOL
25 KiB
C#

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;
}
}
}