]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Progress toward a working USING for FULL JOIN.
authordrh <>
Fri, 15 Apr 2022 19:27:02 +0000 (19:27 +0000)
committerdrh <>
Fri, 15 Apr 2022 19:27:02 +0000 (19:27 +0000)
FossilOrigin-Name: fed2646adecb0a05dd674dc1cd2c0ae205078fe552ba93b8d68891c728c67637

manifest
manifest.uuid
src/resolve.c

index 0837f71c44647d248a8d20feda9787293492bf49..cbfe237c21de45529f6e76c3f88338ff88453dd8 100644 (file)
--- 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.
index 4017be264b14556049960ef372c00fbf2f7385d4..422bec2b8cb83b9ddaa91eb4e68658e5a570f353 100644 (file)
@@ -1 +1 @@
-91530990e018580ec5322ace6f0c369a32a3529a0bfb4defb25ca20223a2a80f
\ No newline at end of file
+fed2646adecb0a05dd674dc1cd2c0ae205078fe552ba93b8d68891c728c67637
\ No newline at end of file
index ecc07b296e1100a3161a195a4b20115c70d5294f..83a7c5028e105ea045ddecbb5bf5d39e1e5870cb 100644 (file)
@@ -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; j<pEList->nExpr; 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: