aboutsummaryrefslogtreecommitdiff
path: root/src/insert.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/insert.c')
-rw-r--r--src/insert.c137
1 files changed, 121 insertions, 16 deletions
diff --git a/src/insert.c b/src/insert.c
index 095298b90..7b253e926 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -577,6 +577,104 @@ void sqlite3AutoincrementEnd(Parse *pParse){
# define autoIncStep(A,B,C)
#endif /* SQLITE_OMIT_AUTOINCREMENT */
+void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){
+ if( pVal->pSrc->nSrc>0 ){
+ SrcItem *pItem = &pVal->pSrc->a[0];
+ sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->regReturn);
+ sqlite3VdbeJumpHere(pParse->pVdbe, pItem->addrFillSub - 1);
+ }
+}
+
+static int multiValueIsConstant(ExprList *pRow){
+ int ii;
+ for(ii=0; ii<pRow->nExpr; ii++){
+ if( 0==sqlite3ExprIsConstant(pRow->a[ii].pExpr) ) return 0;
+ }
+ return 1;
+}
+
+Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){
+ SrcItem *p;
+ SelectDest dest;
+ Select *pSelect = 0;
+
+ if( pParse->db->init.busy
+ || pParse->pNewTrigger
+ || pParse->bHasWith
+ || multiValueIsConstant(pRow)==0
+ || pLeft->pPrior
+ ){
+ int f = SF_Values | SF_MultiValue;
+ if( pLeft->pPrior || pLeft->pSrc->nSrc ){
+ sqlite3MultiValuesEnd(pParse, pLeft);
+ f = SF_Values;
+ }
+ /* This VALUES clause is part of a VIEW or some other schema item. In
+ ** this case the co-routine cannot be coded immediately. */
+ pSelect = sqlite3SelectNew(pParse,pRow,0,0,0,0,0,f,0);
+ pLeft->selFlags &= ~SF_MultiValue;
+ if( pSelect ){
+ pSelect->op = TK_ALL;
+ pSelect->pPrior = pLeft;
+ pLeft = pSelect;
+ }
+ }else{
+
+ if( pLeft->pSrc->nSrc==0 ){
+ /* Co-routine has not yet been started. */
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ Select *pRet;
+
+ if( v==0 ) return pLeft;
+ pRet = sqlite3SelectNew(pParse, 0, 0, 0, 0, 0, 0, 0, 0);
+ if( pRet==0 ) return pLeft;
+ p = &pRet->pSrc->a[0];
+ pRet->pSrc->nSrc = 1;
+
+ p->pSelect = pLeft;
+ p->fg.viaCoroutine = 1;
+ p->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1;
+ p->regReturn = ++pParse->nMem;
+
+ sqlite3VdbeAddOp3(v,OP_InitCoroutine,p->regReturn,0,p->addrFillSub);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, p->regReturn);
+ sqlite3Select(pParse, pLeft, &dest);
+ p->regResult = dest.iSdst;
+ assert( pParse->nErr || dest.iSdst>0 );
+
+ pLeft = pRet;
+ }else{
+ p = &pLeft->pSrc->a[0];
+ }
+
+ if( pParse->nErr==0 ){
+ pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, SF_Values, 0);
+ if( pSelect ){
+ if( p->pSelect->pEList->nExpr!=pSelect->pEList->nExpr ){
+ sqlite3SelectWrongNumTermsError(pParse, pSelect);
+ }else{
+ sqlite3SelectPrep(pParse, pSelect, 0);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( pSelect->pWin ){
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, p->regReturn);
+ dest.iSdst = p->regResult;
+ dest.nSdst = pRow->nExpr;
+ dest.iSDParm = p->regReturn;
+ sqlite3Select(pParse, pSelect, &dest);
+ }else
+#endif
+ {
+ sqlite3ExprCodeExprList(pParse, pSelect->pEList,p->regResult,0,0);
+ sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, p->regReturn);
+ }
+ }
+ sqlite3SelectDelete(pParse->db, pSelect);
+ }
+ }
+ }
+
+ return pLeft;
+}
/* Forward declaration */
static int xferOptimization(
@@ -914,24 +1012,31 @@ void sqlite3Insert(
/* Data is coming from a SELECT or from a multi-row VALUES clause.
** Generate a co-routine to run the SELECT. */
int regYield; /* Register holding co-routine entry-point */
- int addrTop; /* Top of the co-routine */
int rc; /* Result code */
- regYield = ++pParse->nMem;
- addrTop = sqlite3VdbeCurrentAddr(v) + 1;
- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
- sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
- dest.iSdst = bIdListInOrder ? regData : 0;
- dest.nSdst = pTab->nCol;
- rc = sqlite3Select(pParse, pSelect, &dest);
- regFromSelect = dest.iSdst;
- assert( db->pParse==pParse );
- if( rc || pParse->nErr ) goto insert_cleanup;
- assert( db->mallocFailed==0 );
- sqlite3VdbeEndCoroutine(v, regYield);
- sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
- assert( pSelect->pEList );
- nColumn = pSelect->pEList->nExpr;
+ if( pSelect->pSrc->nSrc==1 && pSelect->pSrc->a[0].fg.viaCoroutine ){
+ SrcItem *pItem = &pSelect->pSrc->a[0];
+ dest.iSDParm = regYield = pItem->regReturn;
+ regFromSelect = pItem->regResult;
+ nColumn = pItem->pSelect->pEList->nExpr;
+ }else{
+ int addrTop; /* Top of the co-routine */
+ regYield = ++pParse->nMem;
+ addrTop = sqlite3VdbeCurrentAddr(v) + 1;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
+ dest.iSdst = bIdListInOrder ? regData : 0;
+ dest.nSdst = pTab->nCol;
+ rc = sqlite3Select(pParse, pSelect, &dest);
+ regFromSelect = dest.iSdst;
+ assert( db->pParse==pParse );
+ if( rc || pParse->nErr ) goto insert_cleanup;
+ assert( db->mallocFailed==0 );
+ sqlite3VdbeEndCoroutine(v, regYield);
+ sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
+ assert( pSelect->pEList );
+ nColumn = pSelect->pEList->nExpr;
+ }
/* Set useTempTable to TRUE if the result of the SELECT statement
** should be written into a temporary table (template 4). Set to