Tighter binding of views, triggers, and indices to their respective
databases.  Ticket #323.  Much more testing needs to be done to the
sqliteFix...() routines in attach.c. (CVS 990)

FossilOrigin-Name: 7202d4f1a8853368954a967b7ccca9d8a6645a2e
diff --git a/src/attach.c b/src/attach.c
index e1c8a3c..77be3bc 100644
--- a/src/attach.c
+++ b/src/attach.c
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code used to implement the ATTACH and DETACH commands.
 **
-** $Id: attach.c,v 1.3 2003/04/15 19:22:23 drh Exp $
+** $Id: attach.c,v 1.4 2003/05/31 16:21:12 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -128,3 +128,136 @@
     sqliteResetInternalSchema(db, i);
   }
 }
+
+/*
+** Initialize a DbFixer structure.  This routine must be called prior
+** to passing the structure to one of the sqliteFixAAAA() routines below.
+**
+** The return value indicates whether or not fixation is required.  TRUE
+** means we do need to fix the database references, FALSE means we do not.
+*/
+int sqliteFixInit(
+  DbFixer *pFix,      /* The fixer to be initialized */
+  Parse *pParse,      /* Error messages will be written here */
+  int iDb,            /* This is the database that must must be used */
+  const char *zType,  /* "view", "trigger", or "index" */
+  const Token *pName  /* Name of the view, trigger, or index */
+){
+  sqlite *db;
+
+  if( iDb<0 || iDb==1 ) return 0;
+  db = pParse->db;
+  assert( db->nDb>iDb );
+  pFix->zDb = db->aDb[iDb].zName;
+  pFix->zType = zType;
+  pFix->pName = pName;
+  return 1;
+}
+
+/*
+** The following set of routines walk through the parse tree and assign
+** a specific database to all table references where the database name
+** was left unspecified in the original SQL statement.  The pFix structure
+** must have been initialized by a prior call to sqliteFixInit().
+**
+** These routines are used to make sure that an index, trigger, or
+** view in one database does not refer to objects in a different database.
+** (Exception: indices, triggers, and views in the TEMP database are
+** allowed to refer to anything.)  If a reference is explicitly made
+** to an object in a different database, an error message is added to
+** pParse->zErrMsg and these routines return non-zero.  If everything
+** checks out, these routines return 0.
+*/
+int sqliteFixSrcList(
+  DbFixer *pFix,       /* Context of the fixation */
+  SrcList *pList       /* The Source list to check and modify */
+){
+  int i;
+  const char *zDb;
+
+  if( pList==0 ) return 0;
+  zDb = pFix->zDb;
+  for(i=0; i<pList->nSrc; i++){
+    if( pList->a[i].zDatabase==0 ){
+      pList->a[i].zDatabase = sqliteStrDup(zDb);
+    }else if( sqliteStrICmp(pList->a[i].zDatabase,zDb)!=0 ){
+      sqliteErrorMsg(pFix->pParse,
+         "%s %.*s cannot reference objects in database %s",
+         pFix->zType, pFix->pName->n, pFix->pName->z, pList->a[i].zDatabase);
+      return 1;
+    }
+    if( sqliteFixSelect(pFix, pList->a[i].pSelect) ) return 1;
+    if( sqliteFixExpr(pFix, pList->a[i].pOn) ) return 1;
+  }
+  return 0;
+}
+int sqliteFixSelect(
+  DbFixer *pFix,       /* Context of the fixation */
+  Select *pSelect      /* The SELECT statement to be fixed to one database */
+){
+  while( pSelect ){
+    if( sqliteFixExprList(pFix, pSelect->pEList) ){
+      return 1;
+    }
+    if( sqliteFixSrcList(pFix, pSelect->pSrc) ){
+      return 1;
+    }
+    if( sqliteFixExpr(pFix, pSelect->pWhere) ){
+      return 1;
+    }
+    if( sqliteFixExpr(pFix, pSelect->pHaving) ){
+      return 1;
+    }
+    pSelect = pSelect->pPrior;
+  }
+  return 0;
+}
+int sqliteFixExpr(
+  DbFixer *pFix,     /* Context of the fixation */
+  Expr *pExpr        /* The expression to be fixed to one database */
+){
+  while( pExpr ){
+    if( sqliteFixSelect(pFix, pExpr->pSelect) ){
+      return 1;
+    }
+    if( sqliteFixExprList(pFix, pExpr->pList) ){
+      return 1;
+    }
+    if( sqliteFixExpr(pFix, pExpr->pRight) ){
+      return 1;
+    }
+    pExpr = pExpr->pLeft;
+  }
+  return 0;
+}
+int sqliteFixExprList(
+  DbFixer *pFix,     /* Context of the fixation */
+  ExprList *pList    /* The expression to be fixed to one database */
+){
+  int i;
+  if( pList==0 ) return 0;
+  for(i=0; i<pList->nExpr; i++){
+    if( sqliteFixExpr(pFix, pList->a[i].pExpr) ){
+      return 1;
+    }
+  }
+  return 0;
+}
+int sqliteFixTriggerStep(
+  DbFixer *pFix,     /* Context of the fixation */
+  TriggerStep *pStep /* The trigger step be fixed to one database */
+){
+  while( pStep ){
+    if( sqliteFixSelect(pFix, pStep->pSelect) ){
+      return 1;
+    }
+    if( sqliteFixExpr(pFix, pStep->pWhere) ){
+      return 1;
+    }
+    if( sqliteFixExprList(pFix, pStep->pExprList) ){
+      return 1;
+    }
+    pStep = pStep->pNext;
+  }
+  return 0;
+}
diff --git a/src/build.c b/src/build.c
index 100b701..36d083f 100644
--- a/src/build.c
+++ b/src/build.c
@@ -23,7 +23,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.154 2003/05/17 19:04:04 drh Exp $
+** $Id: build.c,v 1.155 2003/05/31 16:21:12 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -118,6 +118,12 @@
 ** of that table and (optionally) the name of the database
 ** containing the table.  Return NULL if not found.
 **
+** If zDatabase is 0, all databases are searched for the
+** table and the first matching table is returned.  (No checking
+** for duplicate table names is done.)  The search order is
+** TEMP first, then MAIN, then any auxiliary databases added
+** using the ATTACH command.
+**
 ** See also sqliteLocateTable().
 */
 Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){
@@ -137,38 +143,22 @@
 ** a particular database table given the name
 ** of that table and (optionally) the name of the database
 ** containing the table.  Return NULL if not found.
+** Also leave an error message in pParse->zErrMsg.
 **
-** If pParse->useDb is not negative, then the table must be
-** located in that database.  If a different database is specified,
-** an error message is generated into pParse->zErrMsg.
+** The difference between this routine and sqliteFindTable()
+** is that this routine leaves an error message in pParse->zErrMsg
+** where sqliteFindTable() does not.
 */
 Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){
-  sqlite *db;
-  const char *zUse;
   Table *p;
-  db = pParse->db;
-  if( pParse->useDb<0 ){
-    p = sqliteFindTable(db, zName, zDbase);
-  }else {
-    assert( pParse->useDb<db->nDb );
-    assert( db->aDb[pParse->useDb].pBt!=0 );
-    zUse = db->aDb[pParse->useDb].zName;
-    if( zDbase && pParse->useDb!=1 && sqliteStrICmp(zDbase, zUse)!=0 ){
-      sqliteErrorMsg(pParse,"cannot use database %s in this context", zDbase);
-      return 0;
-    }
-    p = sqliteFindTable(db, zName, zUse);
-    if( p==0 && pParse->useDb==1 && zDbase==0 ){
-      p = sqliteFindTable(db, zName, 0);
-    }
-  }
+
+  p = sqliteFindTable(pParse->db, zName, zDbase);
   if( p==0 ){
     if( zDbase ){
       sqliteErrorMsg(pParse, "no such table: %s.%s", zDbase, zName);
-    }else if( (pParse->useDb==0 || pParse->useDb>=2) 
-               && sqliteFindTable(db, zName, 0)!=0 ){
+    }else if( sqliteFindTable(pParse->db, zName, 0)!=0 ){
       sqliteErrorMsg(pParse, "table \"%s\" is not in database \"%s\"",
-         zName, zUse);
+         zName, zDbase);
     }else{
       sqliteErrorMsg(pParse, "no such table: %s", zName);
     }
@@ -181,6 +171,12 @@
 ** a particular index given the name of that index
 ** and the name of the database that contains the index.
 ** Return NULL if not found.
+**
+** If zDatabase is 0, all databases are searched for the
+** table and the first matching index is returned.  (No checking
+** for duplicate index names is done.)  The search order is
+** TEMP first, then MAIN, then any auxiliary databases added
+** using the ATTACH command.
 */
 Index *sqliteFindIndex(sqlite *db, const char *zName, const char *zDb){
   Index *p = 0;
@@ -1046,6 +1042,7 @@
   int n;
   const char *z;
   Token sEnd;
+  DbFixer sFix;
 
   sqliteStartTable(pParse, pBegin, pName, isTemp, 1);
   p = pParse->pNewTable;
@@ -1053,6 +1050,12 @@
     sqliteSelectDelete(pSelect);
     return;
   }
+  if( sqliteFixInit(&sFix, pParse, p->iDb, "view", pName)
+    && sqliteFixSelect(&sFix, pSelect)
+  ){
+    sqliteSelectDelete(pSelect);
+    return;
+  }
 
   /* Make a copy of the entire SELECT statement that defines the view.
   ** This will force all the Expr.token.z values to be dynamically
@@ -1534,10 +1537,17 @@
   Index *pIndex;   /* The index to be created */
   char *zName = 0;
   int i, j;
-  Token nullId;             /* Fake token for an empty ID list */
+  Token nullId;    /* Fake token for an empty ID list */
+  DbFixer sFix;    /* For assigning database names to pTable */
   sqlite *db = pParse->db;
 
   if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index;
+  if( !isTemp && pParse->initFlag 
+     && sqliteFixInit(&sFix, pParse, pParse->iDb, "index", pName)
+     && sqliteFixSrcList(&sFix, pTable)
+  ){
+    goto exit_create_index;
+  }
 
   /*
   ** Find the table that is to be indexed.  Return early if not found.
diff --git a/src/expr.c b/src/expr.c
index fbd1516..d6b9b12 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.95 2003/05/02 14:32:13 drh Exp $
+** $Id: expr.c,v 1.96 2003/05/31 16:21:12 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -191,6 +191,7 @@
   if( pNew==0 ) return 0;
   pNew->nSrc = p->nSrc;
   for(i=0; i<p->nSrc; i++){
+    pNew->a[i].zDatabase = sqliteStrDup(p->a[i].zDatabase);
     pNew->a[i].zName = sqliteStrDup(p->a[i].zName);
     pNew->a[i].zAlias = sqliteStrDup(p->a[i].zAlias);
     pNew->a[i].jointype = p->a[i].jointype;
diff --git a/src/main.c b/src/main.c
index a4c1b69..8a9194e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.131 2003/05/17 17:35:12 drh Exp $
+** $Id: main.c,v 1.132 2003/05/31 16:21:12 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -80,7 +80,6 @@
         sParse.db = pData->db;
         sParse.initFlag = 1;
         sParse.iDb = atoi(argv[4]);
-        sParse.useDb = -1;
         sParse.newTnum = atoi(argv[2]);
         sParse.useCallback = 1;
         sqliteRunParser(&sParse, argv[3], pData->pzErrMsg);
@@ -332,7 +331,6 @@
   sParse.xCallback = sqliteInitCallback;
   sParse.pArg = (void*)&initData;
   sParse.initFlag = 1;
-  sParse.useDb = -1;
   sParse.useCallback = 1;
   if( iDb==0 ){
     sqliteRunParser(&sParse,
@@ -623,7 +621,6 @@
   sParse.db = db;
   sParse.xCallback = xCallback;
   sParse.pArg = pArg;
-  sParse.useDb = -1;
   sParse.useCallback = ppVm==0;
   if( db->xTrace ) db->xTrace(db->pTraceArg, zSql);
   sqliteRunParser(&sParse, zSql, pzErrMsg);
diff --git a/src/select.c b/src/select.c
index a4b3005..1b6d45e 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.139 2003/05/17 17:35:12 drh Exp $
+** $Id: select.c,v 1.140 2003/05/31 16:21:13 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -1657,6 +1657,7 @@
     if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){
       sqliteDeleteTable(0, pSrc->a[iFrom].pTab);
     }
+    sqliteFree(pSrc->a[iFrom].zDatabase);
     sqliteFree(pSrc->a[iFrom].zName);
     sqliteFree(pSrc->a[iFrom].zAlias);
     if( nSubSrc>1 ){
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 7836dbd..67d4669 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.188 2003/05/29 17:50:55 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.189 2003/05/31 16:21:13 drh Exp $
 */
 #include "config.h"
 #include "sqlite.h"
@@ -857,7 +857,6 @@
                        ** while generating expressions.  Normally false */
   u8 iDb;              /* Index of database whose schema is being parsed */
   u8 useCallback;      /* True if callbacks should be used to report results */
-  int useDb;           /* Restrict references to tables in this database */
   int newTnum;         /* Table number to use when reparsing CREATE TABLEs */
   int nErr;            /* Number of errors seen */
   int nTab;            /* Number of previously allocated VDBE cursors */
@@ -905,6 +904,7 @@
   IdList *pColumns;       /* If this is an UPDATE OF <column-list> trigger,
                              the <column-list> is stored here */
   int foreach;            /* One of TK_ROW or TK_STATEMENT */
+  Token *pNameToken;      /* Token containing zName. Use during parsing only */
 
   TriggerStep *step_list; /* Link list of trigger program steps             */
   Trigger *pNext;         /* Next trigger associated with the table */
@@ -1002,6 +1002,19 @@
 };
 
 /*
+** The following structure contains information used by the sqliteFix...
+** routines as they walk the parse tree to make database references
+** explicit.  
+*/
+typedef struct DbFixer DbFixer;
+struct DbFixer {
+  Parse *pParse;      /* The parsing context.  Error messages written here */
+  const char *zDb;    /* Make sure all objects are contained in this database */
+  const char *zType;  /* Type of the container - used for error messages */
+  const Token *pName; /* Name of the container - used for error messages */
+};
+
+/*
  * This global flag is set for performance testing of triggers. When it is set
  * SQLite will perform the overhead of building new and old trigger references 
  * even when no triggers exist
@@ -1171,3 +1184,9 @@
 void sqliteDetach(Parse*, Token*);
 int sqliteBtreeFactory(const sqlite *db, const char *zFilename,
                        int mode, int nPg, Btree **ppBtree);
+int sqliteFixInit(DbFixer*, Parse*, int, const char*, const Token*);
+int sqliteFixSrcList(DbFixer*, SrcList*);
+int sqliteFixSelect(DbFixer*, Select*);
+int sqliteFixExpr(DbFixer*, Expr*);
+int sqliteFixExprList(DbFixer*, ExprList*);
+int sqliteFixTriggerStep(DbFixer*, TriggerStep*);
diff --git a/src/trigger.c b/src/trigger.c
index 6ac45fc..eec48e0 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -64,7 +64,12 @@
   */
   if( sqlite_malloc_failed ) goto trigger_cleanup;
   assert( pTableName->nSrc==1 );
+  assert( pTableName->a[0].zDatabase==0 );
+  if( pParse->initFlag ){
+    pTableName->a[0].zDatabase = db->aDb[pParse->iDb].zName;
+  }
   tab = sqliteSrcListLookup(pParse, pTableName);
+  pTableName->a[0].zDatabase = 0;
   if( !tab ){
     goto trigger_cleanup;
   }
@@ -133,6 +138,7 @@
   nt->pWhen = sqliteExprDup(pWhen);
   nt->pColumns = sqliteIdListDup(pColumns);
   nt->foreach = foreach;
+  nt->pNameToken = pName;
   assert( pParse->pNewTrigger==0 );
   pParse->pNewTrigger = nt;
 
@@ -152,8 +158,9 @@
   TriggerStep *pStepList, /* The triggered program */
   Token *pAll             /* Token that describes the complete CREATE TRIGGER */
 ){
-  Trigger *nt;              /* The trigger whose construction is finishing up */
+  Trigger *nt = 0;          /* The trigger whose construction is finishing up */
   sqlite *db = pParse->db;  /* The database */
+  DbFixer sFix;
 
   if( pParse->nErr || pParse->pNewTrigger==0 ) goto triggerfinish_cleanup;
   nt = pParse->pNewTrigger;
@@ -163,6 +170,11 @@
     pStepList->pTrig = nt;
     pStepList = pStepList->pNext;
   }
+  if( sqliteFixInit(&sFix, pParse, nt->iDb, "trigger", nt->pNameToken) 
+          && sqliteFixTriggerStep(&sFix, nt->step_list) ){
+    goto triggerfinish_cleanup;
+  }
+  nt->pNameToken = 0;
 
   /* if we are not initializing, and this trigger is not on a TEMP table, 
   ** build the sqlite_master entry
@@ -201,15 +213,15 @@
     Table *pTab;
     sqliteHashInsert(&db->aDb[nt->iDb].trigHash, 
                      nt->name, strlen(nt->name)+1, nt);
-    pTab = sqliteLocateTable(pParse, nt->table, 0);
+    pTab = sqliteLocateTable(pParse, nt->table, db->aDb[nt->iTabDb].zName);
     assert( pTab!=0 );
     nt->pNext = pTab->pTrigger;
     pTab->pTrigger = nt;
-  }else{
-    sqliteDeleteTrigger(nt);
+    nt = 0;
   }
 
 triggerfinish_cleanup:
+  sqliteDeleteTrigger(nt);
   sqliteDeleteTrigger(pParse->pNewTrigger);
   pParse->pNewTrigger = 0;
   sqliteDeleteTriggerStep(pStepList);
@@ -548,6 +560,36 @@
 }
 
 /*
+** Convert the pStep->target token into a SrcList and return a pointer
+** to that SrcList.
+**
+** This routine adds a specific database name, if needed, to the target when
+** forming the SrcList.  This prevents a trigger in one database from
+** referring to a target in another database.  An exception is when the
+** trigger is in TEMP in which case it can refer to any other database it
+** wants.
+*/
+static SrcList *targetSrcList(
+  Parse *pParse,       /* The parsing context */
+  TriggerStep *pStep   /* The trigger containing the target token */
+){
+  Token sDb;           /* Dummy database name token */
+  int iDb;             /* Index of the database to use */
+  SrcList *pSrc;       /* SrcList to be returned */
+
+  iDb = pStep->pTrig->iDb;
+  if( iDb==0 || iDb>=2 ){
+    assert( iDb<pParse->db->nDb );
+    sDb.z = pParse->db->aDb[iDb].zName;
+    sDb.n = strlen(sDb.z);
+    pSrc = sqliteSrcListAppend(0, &sDb, &pStep->target);
+  } else {
+    pSrc = sqliteSrcListAppend(0, &pStep->target, 0);
+  }
+  return pSrc;
+}
+
+/*
 ** Generate VDBE code for zero or more statements inside the body of a
 ** trigger.  
 */
@@ -561,11 +603,9 @@
 
   while( pTriggerStep ){
     int saveNTab = pParse->nTab;
-    int saveUseDb = pParse->useDb;
+ 
     orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
     pParse->trigStack->orconf = orconf;
-    pParse->useDb = pTriggerStep->pTrig->iDb;
-    if( pParse->useDb==1 ) pParse->useDb = -1;
     switch( pTriggerStep->op ){
       case TK_SELECT: {
 	Select * ss = sqliteSelectDup(pTriggerStep->pSelect);		  
@@ -577,7 +617,7 @@
       }
       case TK_UPDATE: {
         SrcList *pSrc;
-        pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
+        pSrc = targetSrcList(pParse, pTriggerStep);
         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
         sqliteUpdate(pParse, pSrc,
 		sqliteExprListDup(pTriggerStep->pExprList), 
@@ -587,7 +627,7 @@
       }
       case TK_INSERT: {
         SrcList *pSrc;
-        pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
+        pSrc = targetSrcList(pParse, pTriggerStep);
         sqliteInsert(pParse, pSrc,
           sqliteExprListDup(pTriggerStep->pExprList), 
           sqliteSelectDup(pTriggerStep->pSelect), 
@@ -597,7 +637,7 @@
       case TK_DELETE: {
         SrcList *pSrc;
         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
-        pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
+        pSrc = targetSrcList(pParse, pTriggerStep);
         sqliteDeleteFrom(pParse, pSrc, sqliteExprDup(pTriggerStep->pWhere));
         sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
         break;
@@ -606,7 +646,6 @@
         assert(0);
     } 
     pParse->nTab = saveNTab;
-    pParse->useDb = saveUseDb;
     pTriggerStep = pTriggerStep->pNext;
   }