Fix the CSE mechanism so that it takes into account column affinity
changes that might be imposed by comparison operators. (CVS 4949)

FossilOrigin-Name: 91cc646e2b0c1d62a1989405cc9384a2c22d98d0
diff --git a/src/delete.c b/src/delete.c
index 92b9ed5..aee9418 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** in order to generate code for DELETE FROM statements.
 **
-** $Id: delete.c,v 1.165 2008/03/31 23:48:04 drh Exp $
+** $Id: delete.c,v 1.166 2008/04/01 05:07:15 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -534,7 +534,7 @@
   if( doMakeRec ){
     sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut);
     sqlite3IndexAffinityStr(v, pIdx);
-    sqlite3ExprExpireColumnCacheLines(pParse, regBase, regBase+nCol);
+    sqlite3ExprCacheAffinityChange(pParse, regBase, nCol+1);
   }
   sqlite3ReleaseTempRange(pParse, regBase, nCol+1);
   return regBase;
diff --git a/src/expr.c b/src/expr.c
index 03855f1..6067aff 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -12,7 +12,7 @@
 ** This file contains routines used for analyzing expressions and
 ** for generating VDBE code that evaluates expressions in SQLite.
 **
-** $Id: expr.c,v 1.361 2008/04/01 03:27:39 drh Exp $
+** $Id: expr.c,v 1.362 2008/04/01 05:07:15 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -202,6 +202,30 @@
 }
 
 /*
+** Generate the operands for a comparison operation.  Before
+** generating the code for each operand, set the EP_AnyAff
+** flag on the expression so that it will be able to used a
+** cached column value that has previously undergone an
+** affinity change.
+*/
+static void codeCompareOperands(
+  Parse *pParse,    /* Parsing and code generating context */
+  Expr *pLeft,      /* The left operand */
+  int *pRegLeft,    /* Register where left operand is stored */
+  int *pFreeLeft,   /* Free this register when done */
+  Expr *pRight,     /* The right operand */
+  int *pRegRight,   /* Register where right operand is stored */
+  int *pFreeRight   /* Write temp register for right operand there */
+){
+  while( pLeft->op==TK_UPLUS ) pLeft = pLeft->pLeft;
+  pLeft->flags |= EP_AnyAff;
+  *pRegLeft = sqlite3ExprCodeTemp(pParse, pLeft, pFreeLeft);
+  while( pRight->op==TK_UPLUS ) pRight = pRight->pLeft;
+  pRight->flags |= EP_AnyAff;
+  *pRegRight = sqlite3ExprCodeTemp(pParse, pRight, pFreeRight);
+}
+
+/*
 ** Generate code for a comparison operator.
 */
 static int codeCompare(
@@ -223,8 +247,8 @@
                            (void*)p4, P4_COLLSEQ);
   sqlite3VdbeChangeP5(pParse->pVdbe, p5);
   if( p5 & SQLITE_AFF_MASK ){
-    sqlite3ExprExpireColumnCacheLines(pParse, in1, in1);
-    sqlite3ExprExpireColumnCacheLines(pParse, in2, in2);
+    sqlite3ExprCacheAffinityChange(pParse, in1, 1);
+    sqlite3ExprCacheAffinityChange(pParse, in2, 1);
   }
   return addr;
 }
@@ -1787,7 +1811,7 @@
           sqlite3ExprCode(pParse, pE2, r1);
           pParse->disableColCache--;
           sqlite3VdbeAddOp4(v, OP_MakeRecord, r1, 1, r2, &affinity, 1);
-          sqlite3ExprExpireColumnCacheLines(pParse, r1, r1);
+          sqlite3ExprCacheAffinityChange(pParse, r1, 1);
           sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2);
         }
         sqlite3ReleaseTempReg(pParse, r1);
@@ -1907,26 +1931,33 @@
 **
 ** There must be an open cursor to pTab in iTable when this routine
 ** is called.  If iColumn<0 then code is generated that extracts the rowid.
+**
+** This routine might attempt to reuse the value of the column that
+** has already been loaded into a register.  The value will always
+** be used if it has not undergone any affinity changes.  But if
+** an affinity change has occurred, then the cached value will only be
+** used if allowAffChng is true.
 */
 int sqlite3ExprCodeGetColumn(
   Parse *pParse,   /* Parsing and code generating context */
   Table *pTab,     /* Description of the table we are reading from */
   int iColumn,     /* Index of the table column */
   int iTable,      /* The cursor pointing to the table */
-  int iReg         /* Store results here */
+  int iReg,        /* Store results here */
+  int allowAffChng /* True if prior affinity changes are OK */
 ){
   Vdbe *v = pParse->pVdbe;
   int i;
+  struct yColCache *p;
 
-  for(i=0; i<pParse->nColCache; i++){
-    if( pParse->aColCache[i].iTable==iTable
-     && pParse->aColCache[i].iColumn==iColumn ){
+  for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
+    if( p->iTable==iTable && p->iColumn==iColumn
+           && (!p->affChange || allowAffChng) ){
 #if 0
       sqlite3VdbeAddOp0(v, OP_Noop);
-      VdbeComment((v, "OPT: tab%d.col%d -> r%d",
-                       iTable, iColumn, pParse->aColCache[i].iReg));
+      VdbeComment((v, "OPT: tab%d.col%d -> r%d", iTable, iColumn, p->iReg));
 #endif
-      return pParse->aColCache[i].iReg;
+      return p->iReg;
     }
   }  
   assert( v!=0 );
@@ -1947,9 +1978,10 @@
   }
   if( pParse->disableColCache==0 ){
     i = pParse->iColCache;
-    pParse->aColCache[i].iTable = iTable;
-    pParse->aColCache[i].iColumn = iColumn;
-    pParse->aColCache[i].iReg = iReg;
+    p = &pParse->aColCache[i];
+    p->iTable = iTable;
+    p->iColumn = iColumn;
+    p->iReg = iReg;
     i++;
     if( i>=ArraySize(pParse->aColCache) ) i = 0;
     if( i>pParse->nColCache ) pParse->nColCache = i;
@@ -1980,30 +2012,25 @@
     for(i=0; i<pParse->nColCache; i++){
       if( pParse->aColCache[i].iTable==iTable ){
         pParse->aColCache[i] = pParse->aColCache[--pParse->nColCache];
+        pParse->iColCache = pParse->nColCache;
       }
     }
-    pParse->iColCache = pParse->nColCache;
   }
 }
 
 /*
-** Expire all column cache entry associated with register between
-** iFrom and iTo, inclusive.  If there are no column cache entries 
-** on those registers then this routine is a no-op.
-**
-** Call this routine when register contents are overwritten to
-** make sure the new register value is not used in place of the
-** value that was overwritten.
+** Record the fact that an affinity change has occurred on iCount
+** registers starting with iStart.
 */
-void sqlite3ExprExpireColumnCacheLines(Parse *pParse, int iFrom, int iTo){
+void sqlite3ExprCacheAffinityChange(Parse *pParse, int iStart, int iCount){
+  int iEnd = iStart + iCount - 1;
   int i;
   for(i=0; i<pParse->nColCache; i++){
     int r = pParse->aColCache[i].iReg;
-    if( r>=iFrom && r<=iTo ){
-      pParse->aColCache[i] = pParse->aColCache[--pParse->nColCache];
+    if( r>=iStart && r<=iEnd ){
+      pParse->aColCache[i].affChange = 1;
     }
   }
-  pParse->iColCache = pParse->nColCache;
 }
 
 /*
@@ -2047,6 +2074,7 @@
 ** Return the register that the value ends up in.
 */
 int sqlite3ExprWritableRegister(Parse *pParse, int iCurrent, int iTarget){
+  int i;
   assert( pParse->pVdbe!=0 );
   if( !usedAsColumnCache(pParse, iCurrent, iCurrent) ){
     return iCurrent;
@@ -2054,7 +2082,12 @@
   if( iCurrent!=iTarget ){
     sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, iCurrent, iTarget);
   }
-  sqlite3ExprExpireColumnCacheLines(pParse, iTarget, iTarget);
+  for(i=0; i<pParse->nColCache; i++){
+    if( pParse->aColCache[i].iReg==iTarget ){
+      pParse->aColCache[i] = pParse->aColCache[--pParse->nColCache];
+      pParse->iColCache = pParse->nColCache;
+    }
+  }
   return iTarget;
 }
 
@@ -2108,7 +2141,8 @@
         inReg = pExpr->iColumn + pParse->ckBase;
       }else{
         inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
-                                 pExpr->iColumn, pExpr->iTable, target);
+                                 pExpr->iColumn, pExpr->iTable, target,
+                                 pExpr->flags & EP_AnyAff);
       }
       break;
     }
@@ -2185,8 +2219,8 @@
       assert( TK_GE==OP_Ge );
       assert( TK_EQ==OP_Eq );
       assert( TK_NE==OP_Ne );
-      r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
-      r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
+      codeCompareOperands(pParse, pExpr->pLeft, &r1, &regFree1,
+                                  pExpr->pRight, &r2, &regFree2);
       codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
                   r1, r2, inReg, SQLITE_STOREP2);
       break;
@@ -2330,7 +2364,7 @@
       if( nExpr ){
         sqlite3ReleaseTempRange(pParse, r1, nExpr);
       }
-      sqlite3ExprExpireColumnCacheLines(pParse, r1, r1+nExpr-1);
+      sqlite3ExprCacheAffinityChange(pParse, r1, nExpr);
       break;
     }
 #ifndef SQLITE_OMIT_SUBQUERY
@@ -2374,7 +2408,7 @@
       }else{
         r2 = regFree2 = sqlite3GetTempReg(pParse);
         sqlite3VdbeAddOp4(v, OP_MakeRecord, r1, 1, r2, &affinity, 1);
-        sqlite3ExprExpireColumnCacheLines(pParse, r1, r1);
+        sqlite3ExprCacheAffinityChange(pParse, r1, 1);
         j5 = sqlite3VdbeAddOp3(v, OP_Found, pExpr->iTable, 0, r2);
       }
       sqlite3VdbeAddOp2(v, OP_AddImm, target, -1);
@@ -2399,8 +2433,8 @@
       struct ExprList_item *pLItem = pExpr->pList->a;
       Expr *pRight = pLItem->pExpr;
 
-      r1 = sqlite3ExprCodeTemp(pParse, pLeft, &regFree1);
-      r2 = sqlite3ExprCodeTemp(pParse, pRight, &regFree2);
+      codeCompareOperands(pParse, pLeft, &r1, &regFree1,
+                                  pRight, &r2, &regFree2);
       r3 = sqlite3GetTempReg(pParse);
       r4 = sqlite3GetTempReg(pParse);
       codeCompare(pParse, pLeft, pRight, OP_Ge,
@@ -2702,8 +2736,8 @@
       assert( TK_GE==OP_Ge );
       assert( TK_EQ==OP_Eq );
       assert( TK_NE==OP_Ne );
-      r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
-      r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
+      codeCompareOperands(pParse, pExpr->pLeft, &r1, &regFree1,
+                                  pExpr->pRight, &r2, &regFree2);
       codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
                   r1, r2, dest, jumpIfNull);
       break;
@@ -2833,8 +2867,8 @@
     case TK_GE:
     case TK_NE:
     case TK_EQ: {
-      r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
-      r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
+      codeCompareOperands(pParse, pExpr->pLeft, &r1, &regFree1,
+                                  pExpr->pRight, &r2, &regFree2);
       codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
                   r1, r2, dest, jumpIfNull);
       break;
diff --git a/src/insert.c b/src/insert.c
index e075400..3188def 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle INSERT statements in SQLite.
 **
-** $Id: insert.c,v 1.234 2008/03/31 23:48:05 drh Exp $
+** $Id: insert.c,v 1.235 2008/04/01 05:07:15 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -1171,7 +1171,7 @@
     sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i);
     sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]);
     sqlite3IndexAffinityStr(v, pIdx);
-    sqlite3ExprExpireColumnCacheLines(pParse, regIdx, regIdx+pIdx->nColumn);
+    sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1);
     sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
 
     /* Find out what action to take in case there is an indexing conflict */
@@ -1286,7 +1286,7 @@
   regRec = sqlite3GetTempReg(pParse);
   sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
   sqlite3TableAffinityStr(v, pTab);
-  sqlite3ExprExpireColumnCacheLines(pParse, regData, regData+pTab->nCol-1);
+  sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol);
 #ifndef SQLITE_OMIT_TRIGGER
   if( newIdx>=0 ){
     sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regRowid);
diff --git a/src/select.c b/src/select.c
index df3c3a7..c7600e8 100644
--- a/src/select.c
+++ b/src/select.c
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.424 2008/03/31 23:48:05 drh Exp $
+** $Id: select.c,v 1.425 2008/04/01 05:07:15 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -669,7 +669,7 @@
       }else{
         int r1 = sqlite3GetTempReg(pParse);
         sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, 1, r1, &p->affinity, 1);
-        sqlite3ExprExpireColumnCacheLines(pParse, regResult, regResult);
+        sqlite3ExprCacheAffinityChange(pParse, regResult, 1);
         sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
         sqlite3ReleaseTempReg(pParse, r1);
       }
@@ -716,7 +716,7 @@
         sqlite3VdbeAddOp2(v, OP_Gosub, 0, iParm);
       }else{
         sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nColumn);
-        sqlite3ExprExpireColumnCacheLines(pParse,regResult,regResult+nColumn-1);
+        sqlite3ExprCacheAffinityChange(pParse, regResult, nColumn);
       }
       break;
     }
@@ -835,7 +835,7 @@
       assert( nColumn==1 );
       j1 = sqlite3VdbeAddOp1(v, OP_IsNull, regRow);
       sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, 1, regRowid, &p->affinity, 1);
-      sqlite3ExprExpireColumnCacheLines(pParse, regRow, regRow);
+      sqlite3ExprCacheAffinityChange(pParse, regRow, 1);
       sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid);
       sqlite3VdbeJumpHere(v, j1);
       break;
@@ -858,8 +858,7 @@
       }
       if( eDest==SRT_Callback ){
         sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iMem, nColumn);
-        sqlite3ExprExpireColumnCacheLines(pParse, pDest->iMem,
-                                                  pDest->iMem+nColumn-1);
+        sqlite3ExprCacheAffinityChange(pParse, pDest->iMem, nColumn);
       }else{
         sqlite3VdbeAddOp2(v, OP_Gosub, 0, iParm);
       }
@@ -2930,7 +2929,7 @@
                       (void*)pF->pFunc, P4_FUNCDEF);
     sqlite3VdbeChangeP5(v, nArg);
     sqlite3ReleaseTempRange(pParse, regAgg, nArg);
-    sqlite3ExprExpireColumnCacheLines(pParse, regAgg, regAgg+nArg-1);
+    sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg);
     if( addrNext ){
       sqlite3VdbeResolveLabel(v, addrNext);
     }
@@ -3441,7 +3440,7 @@
           if( pCol->iSorterColumn>=j ){
             int r1 = j + regBase;
             int r2 = sqlite3ExprCodeGetColumn(pParse, 
-                               pCol->pTab, pCol->iColumn,  pCol->iTable, r1);
+                               pCol->pTab, pCol->iColumn, pCol->iTable, r1, 0);
             if( r1!=r2 ){
               sqlite3VdbeAddOp2(v, OP_SCopy, r2, r1);
             }
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index aeee74a..b6bb441 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.685 2008/04/01 03:27:39 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.686 2008/04/01 05:07:15 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -1139,7 +1139,7 @@
 #define EP_Dequoted   0x0040  /* True if the string has been dequoted */
 #define EP_InfixFunc  0x0080  /* True for an infix function: LIKE, GLOB, etc */
 #define EP_ExpCollate 0x0100  /* Collating sequence specified explicitly */
-#define EP_Constant   0x0200  /* A constant expression */
+#define EP_AnyAff     0x0200  /* Can take a cached column of any affinity */
 
 /*
 ** These macros can be used to test, set, or clear bits in the 
@@ -1469,7 +1469,7 @@
   struct yColCache {
     int iTable;           /* Table cursor number */
     int iColumn;          /* Table column number */
-    char aff;             /* Affinity.  Or 0 if none specified */
+    char affChange;       /* True if this register has had an affinity change */
     int iReg;             /* Register holding value of this column */
   } aColCache[10];     /* One for each valid column cache entry */
   u32 writeMask;       /* Start a write transaction on these databases */
@@ -1838,11 +1838,11 @@
 void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
 WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u8);
 void sqlite3WhereEnd(WhereInfo*);
-int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int);
+int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, int);
 void sqlite3ExprCodeMove(Parse*, int, int);
 void sqlite3ExprClearColumnCache(Parse*, int);
 void sqlite3ExprColumnCacheDisable(Parse*, int);
-void sqlite3ExprExpireColumnCacheLines(Parse*, int, int);
+void sqlite3ExprCacheAffinityChange(Parse*, int, int);
 int sqlite3ExprWritableRegister(Parse*,int,int);
 int sqlite3ExprCode(Parse*, Expr*, int);
 int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
diff --git a/src/update.c b/src/update.c
index daacd02..a22d901 100644
--- a/src/update.c
+++ b/src/update.c
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle UPDATE statements.
 **
-** $Id: update.c,v 1.174 2008/03/31 23:48:05 drh Exp $
+** $Id: update.c,v 1.175 2008/04/01 05:07:15 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -446,7 +446,7 @@
     sqlite3VdbeAddOp3(v, OP_MakeRecord, regCols, pTab->nCol, regRow);
     if( !isView ){
       sqlite3TableAffinityStr(v, pTab);
-      sqlite3ExprExpireColumnCacheLines(pParse, regCols, regCols+pTab->nCol-1);
+      sqlite3ExprCacheAffinityChange(pParse, regCols, pTab->nCol);
     }
     sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
     if( pParse->nErr ) goto update_cleanup;
diff --git a/src/where.c b/src/where.c
index 9a375b9..e70b005 100644
--- a/src/where.c
+++ b/src/where.c
@@ -16,7 +16,7 @@
 ** so is applicable.  Because this module is responsible for selecting
 ** indices, you might also think of this module as the "query optimizer".
 **
-** $Id: where.c,v 1.296 2008/03/31 23:48:05 drh Exp $
+** $Id: where.c,v 1.297 2008/04/01 05:07:15 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -1741,7 +1741,7 @@
   assert( v!=0 );
   sqlite3VdbeAddOp3(v, OP_MakeRecord, regSrc, nColumn, regDest);
   sqlite3IndexAffinityStr(v, pIdx);
-  sqlite3ExprExpireColumnCacheLines(pParse, regSrc, regSrc+nColumn-1);
+  sqlite3ExprCacheAffinityChange(pParse, regSrc, nColumn);
 }