Commit of the code
This commit is contained in:
parent
33cee93974
commit
f4c1b0b85c
356 changed files with 175795 additions and 0 deletions
746
Community.CsharpSqlite/src/delete_c.cs
Normal file
746
Community.CsharpSqlite/src/delete_c.cs
Normal file
|
|
@ -0,0 +1,746 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using u8 = System.Byte;
|
||||
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
|
||||
**
|
||||
*/
|
||||
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;
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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 );
|
||||
}
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue