From: drh <> Date: Fri, 15 Apr 2022 19:27:02 +0000 (+0000) Subject: Progress toward a working USING for FULL JOIN. X-Git-Tag: version-3.39.0~206^2~48^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bb3c62a758d65bc4bf5f4eb10f4d48dc998a6602;p=thirdparty%2Fsqlite.git Progress toward a working USING for FULL JOIN. FossilOrigin-Name: fed2646adecb0a05dd674dc1cd2c0ae205078fe552ba93b8d68891c728c67637 --- diff --git a/manifest b/manifest index 0837f71c44..cbfe237c21 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C When\sexpanding\s"*"\sin\sthe\sresult\sset\sof\sa\sSELECT,\sdo\snot\sattach\sa\stable\sname\nto\scolumns\sthat\sare\sin\ssubsequent\sUSING\sclauses. -D 2022-04-15T18:30:48.111 +C Progress\stoward\sa\sworking\sUSING\sfor\sFULL\sJOIN. +D 2022-04-15T19:27:02.160 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -550,7 +550,7 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c fd940149c691684e7c1073c3787a7170e44852b02d1275d2e30a5b58e89cfcaf F src/printf.c 05d8dfd2018bc4fc3ddb8b37eb97ccef7abf985643fa1caebdcf2916ca90fa32 F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c -F src/resolve.c 2d0f29eadbc87154399b2794490a5bfd104daf818d76e59845ab5afb9bf66824 +F src/resolve.c 7ed7a871146da603301ad1d5874a650c855f242a28af8b8a55c921003a142673 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c d36cf25d63f1a2f2dd342cf4ade0d8b14e9ade92b3617627ecc7609d3cf7b624 F src/shell.c.in eb7f10d5e2c47bd014d92ec5db1def21fcc1ed56ffaaa4ee715b6c37c370b47f @@ -1947,11 +1947,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8378e1e0d289627fb294ccd3f5865ef49df3a42b8a5aa211e21be1b42d9da753 -R ec06a8420fc58788f4e87c08a362458b -T *branch * right-join-using -T *sym-right-join-using * -T -sym-right-join * +P 91530990e018580ec5322ace6f0c369a32a3529a0bfb4defb25ca20223a2a80f +R 614ceffda705b0fa9287c7a51b45e503 U drh -Z ea7a56f0892499e7077e3a35b99d310a +Z d653e8290a074ee40bf85ba696b38740 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4017be264b..422bec2b8c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -91530990e018580ec5322ace6f0c369a32a3529a0bfb4defb25ca20223a2a80f \ No newline at end of file +fed2646adecb0a05dd674dc1cd2c0ae205078fe552ba93b8d68891c728c67637 \ No newline at end of file diff --git a/src/resolve.c b/src/resolve.c index ecc07b296e..83a7c5028e 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -207,6 +207,31 @@ Bitmask sqlite3ExprColUsed(Expr *pExpr){ } } +/* +** Create a new expression term for the column specified by pMatch and +** iColumn. Append this new expression term to the FULL JOIN Match set +** in *ppList. Create a new *ppList if this is the first term in the +** set. +*/ +static void extendFJMatch( + Parse *pParse, /* Parsing context */ + ExprList **ppList, /* ExprList to extend */ + SrcItem *pMatch, /* Source table containing the column */ + i16 iColumn /* The column number */ +){ + Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0); + if( pNew ){ + Table *pTab; + pNew->iTable = pMatch->iCursor; + assert( ExprUseYTab(pNew) ); + pTab = pNew->y.pTab = pMatch->pTab; + pNew->iColumn = iColumn==pTab->iPKey ? -1 : iColumn; + assert( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ); + ExprSetProperty(pNew, EP_CanBeNull); + *ppList = sqlite3ExprListAppend(pParse, *ppList, pNew); + } +} + /* ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up ** that name in the set of source tables in pSrcList and make the pExpr @@ -252,8 +277,9 @@ static int lookupName( NameContext *pTopNC = pNC; /* First namecontext in the list */ Schema *pSchema = 0; /* Schema of the expression */ int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */ - Table *pTab = 0; /* Table hold the row */ + Table *pTab = 0; /* Table holding the row */ Column *pCol; /* A column of pTab */ + ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */ assert( pNC ); /* the name context cannot be NULL. */ assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ @@ -310,6 +336,27 @@ static int lookupName( pEList = pItem->pSelect->pEList; for(j=0; jnExpr; j++){ if( sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){ + if( cnt>0 ){ + if( pItem->fg.isUsing==0 + || !nameInUsingClause(pItem->u3.pUsing, zCol) + ){ + sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else + if( (pItem->fg.jointype & JT_RIGHT)==0 ){ + /* An INNER or LEFT JOIN. Use the left-most table */ + continue; + }else + if( (pItem->fg.jointype & JT_LEFT)==0 ){ + /* A RIGHT JOIN. Use the right-most table */ + cnt = 0; + sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else{ + /* For a FULL JOIN, we must construct a coalesce() func */ + extendFJMatch(pParse, &pFJMatch, pMatch, j); + } + } cnt++; cntTab = 2; pMatch = pItem; @@ -339,15 +386,25 @@ static int lookupName( if( pCol->hName==hCol && sqlite3StrICmp(pCol->zCnName, zCol)==0 ){ - /* If there has been exactly one prior match and this match - ** is for the right-hand table of a NATURAL JOIN or is in a - ** USING clause, then skip this match. - */ - if( cnt==1 ){ - if( pItem->fg.isUsing - && nameInUsingClause(pItem->u3.pUsing, zCol) + if( cnt>0 ){ + if( pItem->fg.isUsing==0 + || !nameInUsingClause(pItem->u3.pUsing, zCol) ){ + sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else + if( (pItem->fg.jointype & JT_RIGHT)==0 ){ + /* An INNER or LEFT JOIN. Use the left-most table */ continue; + }else + if( (pItem->fg.jointype & JT_LEFT)==0 ){ + /* A RIGHT JOIN. Use the right-most table */ + cnt = 0; + sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else{ + /* For a FULL JOIN, we must construct a coalesce() func */ + extendFJMatch(pParse, &pFJMatch, pMatch, j); } } cnt++; @@ -606,12 +663,37 @@ static int lookupName( } } + /* Remove any substructure from pExpr + */ + if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ + sqlite3ExprDelete(db, pExpr->pLeft); + pExpr->pLeft = 0; + sqlite3ExprDelete(db, pExpr->pRight); + pExpr->pRight = 0; + } + /* - ** cnt==0 means there was not match. cnt>1 means there were two or - ** more matches. Either way, we have an error. + ** cnt==0 means there was not match. + ** cnt>1 means there were two or more matches. + ** + ** cnt==0 is always an error. cnt>1 is often an error, but might + ** be multiple matches for a NATURAL LEFT JOIN or a LEFT JOIN USING. */ + assert( pFJMatch==0 || cnt>0 ); if( cnt!=1 ){ const char *zErr; + if( pFJMatch ){ + if( pFJMatch->nExpr==cnt-1 ){ + extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); + pExpr->op = TK_FUNCTION; + pExpr->u.zToken = "coalesce"; + pExpr->x.pList = pFJMatch; + goto lookupname_end; + }else{ + sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + } + } zErr = cnt==0 ? "no such column" : "ambiguous column name"; if( zDb ){ sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); @@ -624,6 +706,7 @@ static int lookupName( pParse->checkSchema = 1; pTopNC->nNcErr++; } + assert( pFJMatch==0 ); /* If a column from a table in pSrcList is referenced, then record ** this fact in the pSrcList.a[].colUsed bitmask. Column 0 causes @@ -643,14 +726,6 @@ static int lookupName( pMatch->colUsed |= sqlite3ExprColUsed(pExpr); } - /* Clean up and return - */ - if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){ - sqlite3ExprDelete(db, pExpr->pLeft); - pExpr->pLeft = 0; - sqlite3ExprDelete(db, pExpr->pRight); - pExpr->pRight = 0; - } pExpr->op = eNewExprOp; ExprSetProperty(pExpr, EP_Leaf); lookupname_end: