Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -368,27 +368,21 @@ ** the iField-th column of the vector expression pVector. ** ** The caller owns the returned Expr object and is responsible for ** ensuring that the returned value eventually gets freed. ** -** Ownership of pVector is controlled by the takeOwnership parameter. If -** takeOwnership is true, this routine takes responsibility for freeing -** pVector, and may do so before returning, hence the caller must not reference -** pVector again. If takeOwnership is false, then the caller takes -** responsibility for freeing pVector and must ensure the pVector remains -** valid as long as the returned value remains in use. +** The caller retains ownership of pVector and must ensure that pVector +** remains valid as long as the returned value is in use. */ Expr *sqlite3ExprForVectorField( Parse *pParse, /* Parsing context */ Expr *pVector, /* The vector. List of expressions or a sub-SELECT */ - int iField, /* Which column of the vector to return */ - int takeOwnership /* True to take ownership of pVector before returning */ + int iField /* Which column of the vector to return */ ){ Expr *pRet; - assert( sqlite3ExprIsVector(pVector) ); - /* FIXME: Add support for takeOwnership!=0 */ assert( takeOwnership==0 ); - if( pVector->flags & EP_xIsSelect ){ + if( pVector->op==TK_SELECT ){ + assert( pVector->flags & EP_xIsSelect ); /* The TK_SELECT_COLUMN Expr node: ** ** pLeft: pVector containing TK_SELECT ** pRight: pVector if ownership taken ** iColumn: Index of a column in pVector @@ -403,11 +397,12 @@ */ pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, pVector, 0, 0); if( pRet ) pRet->iColumn = iField; assert( pRet==0 || pRet->iTable==0 ); }else{ - pRet = sqlite3ExprDup(pParse->db, pVector->x.pList->a[iField].pExpr, 0); + if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr; + pRet = sqlite3ExprDup(pParse->db, pVector, 0); } return pRet; } #endif /* !define(SQLITE_OMIT_SUBQUERY) */ @@ -1437,10 +1432,59 @@ /* Avoid leaking memory if malloc has failed. */ sqlite3ExprDelete(db, pExpr); sqlite3ExprListDelete(db, pList); return 0; } + +/* +** pColumns and pExpr for a vector assignment, like this: +** +** (a,b,c) = (expr1,expr2,expr3) +** Or: (a,b,c) = (SELECT x,y,z FROM ....) +** +** For each term of the vector assignment, append new entries to the +** expression list. In the case of a subquery on the LHS, append +** TK_SELECT_COLUMN expressions. +*/ +ExprList *sqlite3ExprListAppendVector( + Parse *pParse, /* Parsing context */ + ExprList *pList, /* List to which to append. Might be NULL */ + IdList *pColumns, /* List of names of LHS of the assignment */ + Expr *pExpr /* Vector expression to be appended. Might be NULL */ +){ + sqlite3 *db = pParse->db; + int n; + int i; + if( pColumns==0 ) goto vector_append_error; + if( pExpr==0 ) goto vector_append_error; + n = sqlite3ExprVectorSize(pExpr); + if( pColumns->nId!=n ){ + sqlite3ErrorMsg(pParse, "%d columns assigned %d values", + pColumns->nId, n); + goto vector_append_error; + } + for(i=0; ia[pList->nExpr-1].zName = pColumns->a[i].zName; + pColumns->a[i].zName = 0; + } + } + if( pExpr->op==TK_SELECT ){ + if( pList && pList->a[0].pExpr ){ + assert( pList->a[0].pExpr->op==TK_SELECT_COLUMN ); + pList->a[0].pExpr->pRight = pExpr; + pExpr = 0; + } + } + +vector_append_error: + sqlite3ExprDelete(db, pExpr); + sqlite3IdListDelete(db, pColumns); + return pList; +} /* ** Set the sort order for the last element on the given ExprList. */ void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder){ Index: src/parse.y ================================================================== --- src/parse.y +++ src/parse.y @@ -787,14 +787,20 @@ setlist(A) ::= setlist(A) COMMA nm(X) EQ expr(Y). { A = sqlite3ExprListAppend(pParse, A, Y.pExpr); sqlite3ExprListSetName(pParse, A, &X, 1); } +setlist(A) ::= setlist(A) COMMA LP idlist(X) RP EQ expr(Y). { + A = sqlite3ExprListAppendVector(pParse, A, X, Y.pExpr); +} setlist(A) ::= nm(X) EQ expr(Y). { A = sqlite3ExprListAppend(pParse, 0, Y.pExpr); sqlite3ExprListSetName(pParse, A, &X, 1); } +setlist(A) ::= LP idlist(X) RP EQ expr(Y). { + A = sqlite3ExprListAppendVector(pParse, 0, X, Y.pExpr); +} ////////////////////////// The INSERT command ///////////////////////////////// // cmd ::= with(W) insert_cmd(R) INTO fullname(X) idlist_opt(F) select(S). { sqlite3WithPush(pParse, W, 1); Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -3545,10 +3545,11 @@ Expr *sqlite3ExprAnd(sqlite3*,Expr*, Expr*); Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*); void sqlite3ExprAssignVarNumber(Parse*, Expr*); void sqlite3ExprDelete(sqlite3*, Expr*); ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); +ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); void sqlite3ExprListSetSortOrder(ExprList*,int); void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*); void sqlite3ExprListDelete(sqlite3*, ExprList*); u32 sqlite3ExprListFlags(const ExprList*); @@ -4274,8 +4275,8 @@ #endif int sqlite3ExprVectorSize(Expr *pExpr); int sqlite3ExprIsVector(Expr *pExpr); Expr *sqlite3VectorFieldSubexpr(Expr*, int); -Expr *sqlite3ExprForVectorField(Parse*,Expr*,int,int); +Expr *sqlite3ExprForVectorField(Parse*,Expr*,int); #endif /* SQLITEINT_H */ Index: src/whereexpr.c ================================================================== --- src/whereexpr.c +++ src/whereexpr.c @@ -1186,12 +1186,12 @@ if( nLeft==sqlite3ExprVectorSize(pExpr->pRight) ){ int i; for(i=0; ipLeft); i++){ int idxNew; Expr *pNew; - Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i, 0); - Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i, 0); + Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i); + Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i); pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0); idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC); exprAnalyze(pSrc, pWC, idxNew); markTermAsChild(pWC, idxNew, idxTerm); ADDED test/rowvalue7.test Index: test/rowvalue7.test ================================================================== --- /dev/null +++ test/rowvalue7.test @@ -0,0 +1,58 @@ +# 2016-08-18 +# +# 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. +# +#*********************************************************************** +# The focus of this file is vector assignments in the SET clause of +# an UPDATE statement. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix rowvalue7 + +do_execsql_test 1.1 { + CREATE TABLE t1(a,b,c,d); + CREATE INDEX t1x ON t1(a,b); + INSERT INTO t1(a,b,c,d) VALUES(1,2,0,0),(3,4,0,0),(5,6,0,0); + CREATE TABLE t2(w,x,y,z); + CREATE INDEX t2x ON t2(w,x); + INSERT INTO t2(w,x,y,z) VALUES(1,2,11,22),(8,9,88,99),(3,5,33,55),(5,6,55,66); + + SELECT *,'|' FROM t1 ORDER BY a; +} {1 2 0 0 | 3 4 0 0 | 5 6 0 0 |} + +do_execsql_test 1.2 { + UPDATE t1 SET (c,d) = (SELECT y,z FROM t2 WHERE (w,x)=(a,b)); + SELECT *,'|' FROM t1 ORDER BY a; +} {1 2 11 22 | 3 4 {} {} | 5 6 55 66 |} + +do_execsql_test 1.3 { + UPDATE t1 SET (c,d) = (SELECT y,z FROM t2 WHERE w=a); + SELECT *,'|' FROM t1 ORDER BY a; +} {1 2 11 22 | 3 4 33 55 | 5 6 55 66 |} + +do_execsql_test 1.4 { + UPDATE t1 SET (c) = 99 WHERE a=3; + SELECT *,'|' FROM t1 ORDER BY a; +} {1 2 11 22 | 3 4 99 55 | 5 6 55 66 |} + +do_execsql_test 1.5 { + UPDATE t1 SET b = 8, (c,d) = (123,456) WHERE a=3; + SELECT *,'|' FROM t1 ORDER BY a; +} {1 2 11 22 | 3 8 123 456 | 5 6 55 66 |} + +do_catchsql_test 2.1 { + UPDATE t1 SET (c,d) = (SELECT x,y,z FROM t2 WHERE w=a); +} {1 {2 columns assigned 3 values}} + +do_catchsql_test 2.2 { + UPDATE t1 SET (b,c,d) = (SELECT x,y FROM t2 WHERE w=a); +} {1 {3 columns assigned 2 values}} + +finish_test