]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Constant propagation is now restricted to just the WHERE clause. The
authordrh <drh@noemail.net>
Fri, 27 Jul 2018 16:57:11 +0000 (16:57 +0000)
committerdrh <drh@noemail.net>
Fri, 27 Jul 2018 16:57:11 +0000 (16:57 +0000)
mechanism is changed to take affinity and collation into account.  This
seems to give correct answers.  But the search for constant propagation
costs 4 million cycles in the speed test.

FossilOrigin-Name: 82c67efb723dba387964f690cd459b420e59e3367d9589016597a76531596391

manifest
manifest.uuid
src/expr.c
src/select.c
src/sqliteInt.h
src/treeview.c
src/wherecode.c
src/whereexpr.c

index 73c3e5b372b5058957c917474512fe2bc07b55dd..79cc142c4adf9baa82b3bc8c947505c49ab4b2cd 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sa\stest\scase\sdemonstrating\sthe\scollation\sproblem\swith\sconstant\spropagation.
-D 2018-07-26T23:54:19.662
+C Constant\spropagation\sis\snow\srestricted\sto\sjust\sthe\sWHERE\sclause.\sThe\nmechanism\sis\schanged\sto\stake\saffinity\sand\scollation\sinto\saccount.\s\sThis\nseems\sto\sgive\scorrect\sanswers.\s\sBut\sthe\ssearch\sfor\sconstant\spropagation\ncosts\s4\smillion\scycles\sin\sthe\sspeed\stest.
+D 2018-07-27T16:57:11.322
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6
@@ -450,7 +450,7 @@ F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
 F src/dbpage.c 4aa7f26198934dbd002e69418220eae3dbc71b010bbac32bd78faf86b52ce6c3
 F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91
 F src/delete.c 4c8c7604277a2041647f96b78f4b9a47858e9217e4fb333d35e7b5ab32c5b57f
-F src/expr.c 8187c1be1003230f027bfc5f5a9830aa43d55beb67d87f4a2589d125749fbcfd
+F src/expr.c af489eb4dac501d06c1f05b9f27f7fc37a05582b9f9b7724e0e1ccc78ae8a530
 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
 F src/fkey.c b1da9ef8dc834603bb0d28972378a7ce65897847f9a1e89ab800bbdf24c788ee
 F src/func.c 7c288b4ce309b5a8b8473514b88e1f8e69a80134509a8c0db8e39c858e367e7f
@@ -498,12 +498,12 @@ F src/printf.c 7f6f3cba8e0c49c19e30a1ff4e9aeda6e06814dcbad4b664a69e1b6cb6e7e365
 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
 F src/resolve.c 797088662ed61102485e3070ba3b3f7828bd5ef6a588223ba6865d77d52f6cea
 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
-F src/select.c 81c6517a8d7b6aae805aaa4171187390f72988d1ceec343ad2f51be09513b289
+F src/select.c 7d2a980be754def09d3458c9eba5ac781ec2962c3415e6ce6d3e00ca100468f8
 F src/shell.c.in f6ebd05c461805a7c708333cd645e74e0a93560d2118f5adb73a75d8c9cf6b01
 F src/sqlite.h.in c6451bb876adced3aba5b1682c6317d215c5eceaba21a6ce979e71a0b8d0bf95
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
-F src/sqliteInt.h 0da0d929642cacf99963e406d34e2e3be891a30168f99aefc1cb81c2c545a09b
+F src/sqliteInt.h fafe020e6fa39964ffff8ff1b48e2a7f0b394e03c722a746629751d2d31721ae
 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
 F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@@ -562,7 +562,7 @@ F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394
 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
 F src/tokenize.c 01e96d1b639c3eb0b9ef90616e766d453935c554f1f7aa86b6db937b79554b97
-F src/treeview.c 26c5674083674377b0f41737647802e93ac31f1da89c9b9648501d8a34a44698
+F src/treeview.c e7a7f90552bb418533cdd0309b5eb71d4effa50165b880fc8c2001e613577e5f
 F src/trigger.c 4ace6d1d5ba9a89822deb287317f33c810440526eafe185c2d8a48c31df1e995
 F src/update.c 7b7c768dc415a8d2eb9fd2cea8b524cb29cf354f319700e22f94f262d3f507cb
 F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88
@@ -585,8 +585,8 @@ F src/wal.h 8de5d2d3de0956d6f6cb48c83a4012d5f227b8fe940f3a349a4b7e85ebcb492a
 F src/walker.c ba7225773931760cf60bf22f34d0cce2588df7ce5ce0f215a52eb88234b55ac4
 F src/where.c 2d313b446758317b60626763d0e1285e04b04c061ce94945dcfffad9525badc1
 F src/whereInt.h b90ef9b9707ef750eab2a7a080c48fb4900315033274689def32d0cf5a81ebe4
-F src/wherecode.c fe23a55294b4c94bf658d2a6eb7996170dd563bf33af4c3e5d71aff3483e4b08
-F src/whereexpr.c 571618c67a3eb5ce0f1158c2792c1aee9b4a4a264392fc4fb1b35467f80abf9a
+F src/wherecode.c 2c552dfe50d06e0916dbd49a180e4bf0accfce6d17d46a2dfeea8f75d2b5861b
+F src/whereexpr.c 7d30c744f37e8bd53811f88fd1d949f6145d24ce77a6f51b252e2b903dc4434e
 F src/window.c c61434ce7e35b7d76b3321dec39e10e79061c10eed1e3d7976c87dbdd77aefb5
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
@@ -1753,7 +1753,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 57eb2abd5b270d65be5e0f138f0d46899fa6091df3ba20b0ea7ef04244a15d48
-R 4901cf7d6c93780d74bf2f77c4b5611e
+P 50add839fd95665bd67a6ae5de8346fd09e83904bbcbad26fad280dff86d9e93
+R 50f5324ffb4955a3d4663001ed61326d
 U drh
-Z e93689647fb1d0f73e5b2a94dfb414a1
+Z f7259f5866467f9069a6566db546a485
index bcb436196328d05720e4d3f4eb4602c86ed28af5..f10ad29d7319d186e96e4062009bd47c47d0cf82 100644 (file)
@@ -1 +1 @@
-50add839fd95665bd67a6ae5de8346fd09e83904bbcbad26fad280dff86d9e93
\ No newline at end of file
+82c67efb723dba387964f690cd459b420e59e3367d9589016597a76531596391
\ No newline at end of file
index 6e5100a224cb97166ade0ad31b763025ee898be7..69270959fb698b38c4a869daa374cda3ea3b9f1a 100644 (file)
@@ -328,6 +328,15 @@ CollSeq *sqlite3BinaryCompareCollSeq(
   return pColl;
 }
 
+/*
+** Return true if CollSeq is the default built-in BINARY.
+*/
+int sqlite3IsBinary(const CollSeq *p){
+  if( p==0 ) return 1;
+  if( sqlite3_stricmp(p->zName,"BINARY")==0 ) return 1;
+  return 0;
+}
+
 /*
 ** Generate code for a comparison operator.
 */
@@ -1839,6 +1848,9 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
       testcase( pExpr->op==TK_COLUMN );
       testcase( pExpr->op==TK_AGG_FUNCTION );
       testcase( pExpr->op==TK_AGG_COLUMN );
+      if( ExprHasProperty(pExpr, EP_FixedCol) ){
+        return WRC_Continue;
+      }
       if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){
         return WRC_Continue;
       }
@@ -1927,7 +1939,7 @@ static int exprNodeIsConstantOrGroupBy(Walker *pWalker, Expr *pExpr){
     Expr *p = pGroupBy->a[i].pExpr;
     if( sqlite3ExprCompare(0, pExpr, p, -1)<2 ){
       CollSeq *pColl = sqlite3ExprNNCollSeq(pWalker->pParse, p);
-      if( sqlite3_stricmp("BINARY", pColl->zName)==0 ){
+      if( sqlite3IsBinary(pColl) ){
         return WRC_Prune;
       }
     }
@@ -3581,6 +3593,10 @@ expr_code_doover:
     }
     case TK_COLUMN: {
       int iTab = pExpr->iTable;
+      if( ExprHasProperty(pExpr, EP_FixedCol) ){
+        pExpr = pExpr->pLeft;
+        goto expr_code_doover;
+      }
       if( iTab<0 ){
         if( pParse->iSelfTab<0 ){
           /* Generating CHECK constraints or inserting into partial index */
@@ -4935,14 +4951,15 @@ int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){
       if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
     }else if( pA->op==TK_COLLATE ){
       if( sqlite3_stricmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
-    }else if( pA->op!=TK_UPLUS && strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
+    }else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
       return 2;
     }
   }
   if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2;
   if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){
     if( combinedFlags & EP_xIsSelect ) return 2;
-    if( sqlite3ExprCompare(pParse, pA->pLeft, pB->pLeft, iTab) ) return 2;
+    if( (combinedFlags & EP_FixedCol)==0
+     && sqlite3ExprCompare(pParse, pA->pLeft, pB->pLeft, iTab) ) return 2;
     if( sqlite3ExprCompare(pParse, pA->pRight, pB->pRight, iTab) ) return 2;
     if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2;
     assert( (combinedFlags & EP_Reduced)==0 );
index 2060524c495ec84db28abee2625d6849d4d2bc66..d74af409898e5fbd5032cecc9d68cbdc03f89234 100644 (file)
@@ -4075,15 +4075,15 @@ static int flattenSubquery(
 #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
 
 /*
-** A structure to keep track of all of the column values that must be
-** constant in a WHERE clause.
+** A structure to keep track of all of the column values that fixed to
+** a known value due to WHERE clause constraints of the form COLUMN=VALUE.
 */
 typedef struct WhereConst WhereConst;
 struct WhereConst {
-  sqlite3 *db;     /* Database pointer, used by sqlite3DbRealloc() */
+  Parse *pParse;   /* Parsing context */
   int nConst;      /* Number for COLUMN=CONSTANT terms */
   int nChng;       /* Number of times a constant is propagated */
-  Expr **apExpr;   /* [i*2] is COLUMN and [i*2+1] is CONSTANT */
+  Expr **apExpr;   /* [i*2] is COLUMN and [i*2+1] is VALUE */
 };
 
 /*
@@ -4095,23 +4095,26 @@ static void constInsert(
   Expr *pValue
 ){
   pConst->nConst++;
-  pConst->apExpr = sqlite3DbReallocOrFree(pConst->db, pConst->apExpr,
+  pConst->apExpr = sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr,
                          pConst->nConst*2*sizeof(Expr*));
   if( pConst->apExpr==0 ){
     pConst->nConst = 0;
   }else{
-    while( pValue->op==TK_UPLUS ) pValue = pValue->pLeft;
+    if( ExprHasProperty(pValue, EP_FixedCol) ) pValue = pValue->pLeft;
     pConst->apExpr[pConst->nConst*2-2] = pColumn;
     pConst->apExpr[pConst->nConst*2-1] = pValue;
   }
 }
 
 /*
-** Find all instances of COLUMN=CONSTANT or CONSTANT=COLUMN in pExpr that
-** must be true (that are part of the AND-connected terms) and add each
-** to pConst.
+** Find all terms of COLUMN=VALUE or VALUE=COLUMN in pExpr where VALUE
+** is a constant expression and where the term must be true because it
+** is part of the AND-connected terms of the expression.  For each term
+** found, add it to the pConst structure.
 */
 static void findConstInWhere(WhereConst *pConst, Expr *pExpr){
+  Expr *pRight, *pLeft;
+  CollSeq *pColl;
   if( pExpr==0 ) return;
   if( ExprHasProperty(pExpr, EP_FromJoin) ) return;
   if( pExpr->op==TK_AND ){
@@ -4120,13 +4123,22 @@ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){
     return;
   }
   if( pExpr->op!=TK_EQ ) return;
-  assert( pExpr->pRight!=0 );
-  assert( pExpr->pLeft!=0 );
-  if( pExpr->pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pExpr->pLeft) ){
-    constInsert(pConst, pExpr->pRight, pExpr->pLeft);
+  pRight = pExpr->pRight;
+  pLeft = pExpr->pLeft;
+  assert( pRight!=0 );
+  assert( pLeft!=0 );
+  pColl = sqlite3BinaryCompareCollSeq(pConst->pParse, pLeft, pRight);
+  if( !sqlite3IsBinary(pColl) ) return;
+  if( pRight->op==TK_COLUMN
+   && !ExprHasProperty(pRight, EP_FixedCol)
+   && sqlite3ExprIsConstant(pLeft)
+  ){
+    constInsert(pConst, pRight, pLeft);
   }else
-  if( pExpr->pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pExpr->pRight) ){
-    constInsert(pConst, pExpr->pLeft, pExpr->pRight);
+  if( pLeft->op==TK_COLUMN
+   && !ExprHasProperty(pLeft, EP_FixedCol)
+   && sqlite3ExprIsConstant(pRight) ){
+    constInsert(pConst, pLeft, pRight);
   }
 }
 
@@ -4140,17 +4152,19 @@ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){
   int i;
   WhereConst *pConst;
   if( pExpr->op!=TK_COLUMN ) return WRC_Continue;
+  if( ExprHasProperty(pExpr, EP_FixedCol) ) return WRC_Continue;
   pConst = pWalker->u.pConst;
   for(i=0; i<pConst->nConst; i++){
     Expr *pColumn = pConst->apExpr[i*2];
     if( pColumn==pExpr ) continue;
     if( pColumn->iTable!=pExpr->iTable ) continue;
     if( pColumn->iColumn!=pExpr->iColumn ) continue;
-    /* A match is found.  Transform the COLUMN into a CONSTANT */
+    /* A match is found.  Add the EP_FixedCol property */
     pConst->nChng++;
     ExprClearProperty(pExpr, EP_Leaf);
-    pExpr->op = TK_UPLUS;
-    pExpr->pLeft = sqlite3ExprDup(pConst->db, pConst->apExpr[i*2+1], 0);
+    ExprSetProperty(pExpr, EP_FixedCol);
+    assert( pExpr->pLeft==0 );
+    pExpr->pLeft = sqlite3ExprDup(pConst->pParse->db, pConst->apExpr[i*2+1], 0);
     break;
   }
   return WRC_Prune;
@@ -4163,7 +4177,7 @@ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){
 ** CONSTANT=COLUMN that must be tree (in other words, if the terms top-level
 ** AND-connected terms that are not part of a ON clause from a LEFT JOIN)
 ** then throughout the query replace all other occurrences of COLUMN
-** with CONSTANT.
+** with CONSTANT within the WHERE clause.
 **
 ** For example, the query:
 **
@@ -4174,6 +4188,24 @@ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){
 **      SELECT * FROM t1, t2, t3 WHERE t1.a=39 AND t2.b=39 AND t3.c=39
 **
 ** Return true if any transformations where made and false if not.
+**
+** Implementation note:  Constant propagation is tricky due to affinity
+** and collating sequence interactions.  Consider this example:
+**
+**    CREATE TABLE t1(a INT,b TEXT);
+**    INSERT INTO t1 VALUES(123,'0123');
+**    SELECT * FROM t1 WHERE a=123 AND b=a;
+**    SELECT * FROM t1 WHERE a=123 AND b=123;
+**
+** The two SELECT statements above should return different answers.  b=a
+** is alway true because the comparison uses numeric affinity, but b=123
+** is false because it uses text affinity and '0123' is not the same as '123'.
+** To work around this, the expression tree is not actually changed from
+** "b=a" to "b=123" but rather the "a" in "b=a" is tagged with EP_FixedCol
+** and the "123" value is hung off of the pLeft pointer.  Code generator
+** routines know to generate the constant "123" instead of looking up the
+** column value.  Also, to avoid collation problems, this optimization is
+** only attempted if the "a=123" term uses the default BINARY collation.
 */
 static int propagateConstants(
   Parse *pParse,   /* The parsing context */
@@ -4182,7 +4214,7 @@ static int propagateConstants(
   WhereConst x;
   Walker w;
   int nChng = 0;
-  x.db = pParse->db;
+  x.pParse = pParse;
   do{
     x.nConst = 0;
     x.nChng = 0;
@@ -4196,8 +4228,8 @@ static int propagateConstants(
       w.xSelectCallback2 = 0;
       w.walkerDepth = 0;
       w.u.pConst = &x;
-      sqlite3WalkSelect(&w, p);
-      sqlite3DbFree(x.db, x.apExpr);
+      sqlite3WalkExpr(&w, p->pWhere);
+      sqlite3DbFree(x.pParse->db, x.apExpr);
       nChng += x.nChng;
     }
   }while( x.nChng );  
index 9dbe6833980fec50af640e60a74d8b1e8a4886b2..7c4d46a40a484f7cf3c6be281e61bf4e2d06a7c0 100644 (file)
@@ -2479,7 +2479,7 @@ struct Expr {
 #define EP_FromJoin  0x000001 /* Originates in ON/USING clause of outer join */
 #define EP_Agg       0x000002 /* Contains one or more aggregate functions */
 #define EP_HasFunc   0x000004 /* Contains one or more functions of any kind */
-                  /* 0x000008 // available for use */
+#define EP_FixedCol  0x000008 /* TK_Column with a known fixed value */
 #define EP_Distinct  0x000010 /* Aggregate function with DISTINCT keyword */
 #define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
 #define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
@@ -4171,6 +4171,7 @@ int sqlite3MemdbInit(void);
 const char *sqlite3ErrStr(int);
 int sqlite3ReadSchema(Parse *pParse);
 CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int);
+int sqlite3IsBinary(const CollSeq*);
 CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName);
 CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
 CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, Expr *pExpr);
index 7605fa2cb31eb45645317b5bfe32ff5e0f720ab9..cde776d0fa2ee6f4f93ef16dd83b4002e08a5829 100644 (file)
@@ -374,6 +374,9 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
         sqlite3TreeViewLine(pView, "{%d:%d}%s",
                              pExpr->iTable, pExpr->iColumn, zFlgs);
       }
+      if( ExprHasProperty(pExpr, EP_FixedCol) ){
+        sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+      }
       break;
     }
     case TK_INTEGER: {
index 3bb220d2edcb530358f90f5c475b83563a9bde9c..1f24c578b36a357f9d588f3d40b090b0a0675735 100644 (file)
@@ -883,7 +883,7 @@ static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){
 static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
   int rc = WRC_Continue;
   struct CCurHint *pHint = pWalker->u.pCCurHint;
-  if( pExpr->op==TK_COLUMN ){
+  if( pExpr->op==TK_COLUMN && !ExprHasProperty(pExpr, EP_FixedCol) ){
     if( pExpr->iTable!=pHint->iTabCur ){
       Vdbe *v = pWalker->pParse->pVdbe;
       int reg = ++pWalker->pParse->nMem;   /* Register for column value */
index 6f6e660ad205e20eac38400d9f842c03ddf4c457..4abadb18781d528ad5099421425a21b1dfc89c8b 100644 (file)
@@ -859,7 +859,7 @@ static int termIsEquivalence(Parse *pParse, Expr *pExpr){
     return 0;
   }
   pColl = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight);
-  if( pColl==0 || sqlite3StrICmp(pColl->zName, "BINARY")==0 ) return 1;
+  if( sqlite3IsBinary(pColl) ) return 1;
   return sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight);
 }
 
@@ -1450,7 +1450,7 @@ void sqlite3WhereClauseClear(WhereClause *pWC){
 */
 Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){
   Bitmask mask;
-  if( p->op==TK_COLUMN ){
+  if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){
     return sqlite3WhereGetMask(pMaskSet, p->iTable);
   }else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){
     assert( p->op!=TK_IF_NULL_ROW );