mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-11 01:42:22 +03:00
Add SQLITE_ENABLE_UPDATE_DELETE_LIMIT for views and WITHOUT ROWID tables.
FossilOrigin-Name: 584b88aaf89ab30cb51185396b7b50c6ecba286add023a26ab41d865b9c605ce
This commit is contained in:
95
src/delete.c
95
src/delete.c
@@ -90,6 +90,9 @@ void sqlite3MaterializeView(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Table *pView, /* View definition */
|
||||
Expr *pWhere, /* Optional WHERE clause to be added */
|
||||
ExprList *pOrderBy, /* Optional ORDER BY clause */
|
||||
Expr *pLimit, /* Optional LIMIT clause */
|
||||
Expr *pOffset, /* Optional OFFSET clause */
|
||||
int iCur /* Cursor number for ephemeral table */
|
||||
){
|
||||
SelectDest dest;
|
||||
@@ -106,8 +109,8 @@ void sqlite3MaterializeView(
|
||||
assert( pFrom->a[0].pOn==0 );
|
||||
assert( pFrom->a[0].pUsing==0 );
|
||||
}
|
||||
pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0,
|
||||
SF_IncludeHidden, 0, 0);
|
||||
pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy,
|
||||
SF_IncludeHidden, pLimit, pOffset);
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
|
||||
sqlite3Select(pParse, pSel, &dest);
|
||||
sqlite3SelectDelete(db, pSel);
|
||||
@@ -132,18 +135,23 @@ Expr *sqlite3LimitWhere(
|
||||
Expr *pOffset, /* The OFFSET clause. May be null */
|
||||
char *zStmtType /* Either DELETE or UPDATE. For err msgs. */
|
||||
){
|
||||
Expr *pWhereRowid = NULL; /* WHERE rowid .. */
|
||||
sqlite3 *db = pParse->db;
|
||||
Expr *pLhs = NULL; /* LHS of IN(SELECT...) operator */
|
||||
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 */
|
||||
Table *pTab;
|
||||
|
||||
/* Check that there isn't an ORDER BY without a LIMIT clause.
|
||||
*/
|
||||
if( pOrderBy && (pLimit == 0) ) {
|
||||
if( pOrderBy && pLimit==0 ) {
|
||||
sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
|
||||
goto limit_where_cleanup;
|
||||
sqlite3ExprDelete(pParse->db, pWhere);
|
||||
sqlite3ExprListDelete(pParse->db, pOrderBy);
|
||||
sqlite3ExprDelete(pParse->db, pLimit);
|
||||
sqlite3ExprDelete(pParse->db, pOffset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We only need to generate a select expression if there
|
||||
@@ -164,36 +172,44 @@ Expr *sqlite3LimitWhere(
|
||||
** );
|
||||
*/
|
||||
|
||||
pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
||||
if( pSelectRowid == 0 ) goto limit_where_cleanup;
|
||||
pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid);
|
||||
if( pEList == 0 ) goto limit_where_cleanup;
|
||||
pTab = pSrc->a[0].pTab;
|
||||
if( HasRowid(pTab) ){
|
||||
pLhs = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
||||
pEList = sqlite3ExprListAppend(
|
||||
pParse, 0, sqlite3PExpr(pParse, TK_ROW, 0, 0)
|
||||
);
|
||||
}else{
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
if( pPk->nKeyCol==1 ){
|
||||
pLhs = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[0]].zName);
|
||||
}else{
|
||||
int i;
|
||||
for(i=0; i<pPk->nKeyCol; i++){
|
||||
Expr *p = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName);
|
||||
pEList = sqlite3ExprListAppend(pParse, pEList, p);
|
||||
}
|
||||
pLhs = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
|
||||
if( pLhs ){
|
||||
pLhs->x.pList = sqlite3ExprListDup(db, pEList, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
|
||||
** and the SELECT subtree. */
|
||||
pSrc->a[0].pTab = 0;
|
||||
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
|
||||
if( pSelectSrc == 0 ) {
|
||||
sqlite3ExprListDelete(pParse->db, pEList);
|
||||
goto limit_where_cleanup;
|
||||
}
|
||||
pSrc->a[0].pTab = pTab;
|
||||
|
||||
/* generate the SELECT expression tree. */
|
||||
pSelect = sqlite3SelectNew(pParse,pEList,pSelectSrc,pWhere,0,0,
|
||||
pOrderBy,0,pLimit,pOffset);
|
||||
if( pSelect == 0 ) return 0;
|
||||
pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0,
|
||||
pOrderBy,0,pLimit,pOffset
|
||||
);
|
||||
|
||||
/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
|
||||
pWhereRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0);
|
||||
pInClause = pWhereRowid ? sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0) : 0;
|
||||
pInClause = sqlite3PExpr(pParse, TK_IN, pLhs, 0);
|
||||
sqlite3PExprAddSelect(pParse, pInClause, pSelect);
|
||||
return pInClause;
|
||||
|
||||
limit_where_cleanup:
|
||||
sqlite3ExprDelete(pParse->db, pWhere);
|
||||
sqlite3ExprListDelete(pParse->db, pOrderBy);
|
||||
sqlite3ExprDelete(pParse->db, pLimit);
|
||||
sqlite3ExprDelete(pParse->db, pOffset);
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) */
|
||||
/* && !defined(SQLITE_OMIT_SUBQUERY) */
|
||||
@@ -205,10 +221,13 @@ limit_where_cleanup:
|
||||
** \________/ \________________/
|
||||
** pTabList pWhere
|
||||
*/
|
||||
void sqlite3DeleteFrom(
|
||||
void sqlite3DeleteFromLimit(
|
||||
Parse *pParse, /* The parser context */
|
||||
SrcList *pTabList, /* The table from which we should delete things */
|
||||
Expr *pWhere /* The WHERE clause. May be null */
|
||||
Expr *pWhere, /* The WHERE clause. May be null */
|
||||
ExprList *pOrderBy,
|
||||
Expr *pLimit,
|
||||
Expr *pOffset
|
||||
){
|
||||
Vdbe *v; /* The virtual database engine */
|
||||
Table *pTab; /* The table from which records will be deleted */
|
||||
@@ -253,6 +272,7 @@ void sqlite3DeleteFrom(
|
||||
}
|
||||
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
|
||||
@@ -277,6 +297,16 @@ void sqlite3DeleteFrom(
|
||||
# define isView 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
if( !isView ){
|
||||
pWhere = sqlite3LimitWhere(
|
||||
pParse, pTabList, pWhere, pOrderBy, pLimit, pOffset, "DELETE"
|
||||
);
|
||||
pOrderBy = 0;
|
||||
pLimit = pOffset = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If pTab is really a view, make sure it has been initialized.
|
||||
*/
|
||||
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
|
||||
@@ -324,8 +354,12 @@ void sqlite3DeleteFrom(
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( isView ){
|
||||
sqlite3MaterializeView(pParse, pTab, pWhere, iTabCur);
|
||||
sqlite3MaterializeView(pParse, pTab,
|
||||
pWhere, pOrderBy, pLimit, pOffset, iTabCur
|
||||
);
|
||||
iDataCur = iIdxCur = iTabCur;
|
||||
pOrderBy = 0;
|
||||
pLimit = pOffset = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -569,6 +603,9 @@ delete_from_cleanup:
|
||||
sqlite3AuthContextPop(&sContext);
|
||||
sqlite3SrcListDelete(db, pTabList);
|
||||
sqlite3ExprDelete(db, pWhere);
|
||||
sqlite3ExprListDelete(db, pOrderBy);
|
||||
sqlite3ExprDelete(db, pLimit);
|
||||
sqlite3ExprDelete(db, pOffset);
|
||||
sqlite3DbFree(db, aToOpen);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user