Update the authorizer API so that it reports the database that table and
indices belong to and so that it reports when actions are taken in response
to a trigger. (CVS 928)

FossilOrigin-Name: c675a5504138f34cae6def782b5d3add2c67d2bc
diff --git a/src/auth.c b/src/auth.c
index b47e657..47b6600 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -14,7 +14,7 @@
 ** systems that do not need this facility may omit it by recompiling
 ** the library with -DSQLITE_OMIT_AUTHORIZATION=1
 **
-** $Id: auth.c,v 1.5 2003/04/16 20:24:52 drh Exp $
+** $Id: auth.c,v 1.6 2003/04/22 20:30:38 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -52,7 +52,7 @@
 */
 int sqlite_set_authorizer(
   sqlite *db,
-  int (*xAuth)(void*,int,const char*,const char*),
+  int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
   void *pArg
 ){
   db->xAuth = xAuth;
@@ -94,7 +94,12 @@
   Table *pTab;          /* The table being read */
   const char *zCol;     /* Name of the column of the table */
   int iSrc;             /* Index in pTabList->a[] of table being read */
+  const char *zDBase;   /* Name of database being accessed */
+  const char *zTrig;    /* Name of the trigger doing the accessing */
+  TriggerStack *pStack; /* The stack of current triggers */
 
+  pStack = pParse->trigStack;
+  zTrig = pStack ? pStack->pTrigger->name : 0;
   if( db->xAuth==0 ) return;
   assert( pExpr->op==TK_COLUMN );
   iSrc = pExpr->iTable - base;
@@ -104,7 +109,6 @@
     /* This must be an attempt to read the NEW or OLD pseudo-tables
     ** of a trigger.
     */
-    TriggerStack *pStack = pParse->trigStack;
     assert( pStack!=0 );
     assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx );
     pTab = pStack->pTab;
@@ -119,12 +123,19 @@
   }else{
     zCol = "ROWID";
   }
-  rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol);
+  assert( pExpr->iDb>=0 && pExpr->iDb<db->nDb );
+  zDBase = db->aDb[pExpr->iDb].zName;
+  rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase, zTrig);
   if( rc==SQLITE_IGNORE ){
     pExpr->op = TK_NULL;
   }else if( rc==SQLITE_DENY ){
-    sqliteSetString(&pParse->zErrMsg,"access to ",
-        pTab->zName, ".", zCol, " is prohibited", 0);
+    if( db->nDb>2 || pExpr->iDb!=0 ){
+      sqliteSetString(&pParse->zErrMsg,"access to ", zDBase, ".",
+          pTab->zName, ".", zCol, " is prohibited", 0);
+    }else{
+      sqliteSetString(&pParse->zErrMsg,"access to ", pTab->zName, ".",
+                      zCol, " is prohibited", 0);
+    }
     pParse->nErr++;
     pParse->rc = SQLITE_AUTH;
   }else if( rc!=SQLITE_OK ){
@@ -142,14 +153,18 @@
   Parse *pParse,
   int code,
   const char *zArg1,
-  const char *zArg2
+  const char *zArg2,
+  const char *zArg3
 ){
   sqlite *db = pParse->db;
   int rc;
+  const char *zTrigName;
+
   if( db->xAuth==0 ){
     return SQLITE_OK;
   }
-  rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2);
+  zTrigName = pParse->trigStack ? pParse->trigStack->pTrigger->name : 0;
+  rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, zTrigName);
   if( rc==SQLITE_DENY ){
     sqliteSetString(&pParse->zErrMsg, "not authorized", 0);
     pParse->rc = SQLITE_AUTH;
diff --git a/src/build.c b/src/build.c
index 730d782..27cf944 100644
--- a/src/build.c
+++ b/src/build.c
@@ -23,7 +23,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.148 2003/04/21 18:48:46 drh Exp $
+** $Id: build.c,v 1.149 2003/04/22 20:30:38 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -453,12 +453,13 @@
   if( pParse->iDb==1 ) isTemp = 1;
 #ifndef SQLITE_OMIT_AUTHORIZATION
   assert( (isTemp & 1)==isTemp );
-  if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0) ){
-    sqliteFree(zName);
-    return;
-  }
   {
     int code;
+    char *zDb = isTemp ? "temp" : "main";
+    if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
+      sqliteFree(zName);
+      return;
+    }
     if( isView ){
       if( isTemp ){
         code = SQLITE_CREATE_TEMP_VIEW;
@@ -472,7 +473,7 @@
         code = SQLITE_CREATE_TABLE;
       }
     }
-    if( sqliteAuthCheck(pParse, code, zName, 0) ){
+    if( sqliteAuthCheck(pParse, code, zName, 0, zDb) ){
       sqliteFree(zName);
       return;
     }
@@ -1218,12 +1219,15 @@
   pTable = sqliteTableFromToken(pParse, pName);
   if( pTable==0 ) return;
   iDb = pTable->iDb;
+  assert( iDb>=0 && iDb<db->nDb );
 #ifndef SQLITE_OMIT_AUTHORIZATION
-  if( sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pTable->iDb),0)){
-    return;
-  }
   {
     int code;
+    const char *zTab = SCHEMA_TABLE(pTable->iDb);
+    const char *zDb = db->aDb[pTable->iDb].zName;
+    if( sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
+      return;
+    }
     if( isView ){
       if( iDb==1 ){
         code = SQLITE_DROP_TEMP_VIEW;
@@ -1237,10 +1241,10 @@
         code = SQLITE_DROP_TABLE;
       }
     }
-    if( sqliteAuthCheck(pParse, code, pTable->zName, 0) ){
+    if( sqliteAuthCheck(pParse, code, pTable->zName, 0, zDb) ){
       return;
     }
-    if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTable->zName, 0) ){
+    if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTable->zName, 0, zDb) ){
       return;
     }
   }
@@ -1600,15 +1604,19 @@
   /* Check for authorization to create an index.
   */
 #ifndef SQLITE_OMIT_AUTHORIZATION
-  assert( isTemp==0 || isTemp==1 );
-  assert( pTab->iDb==pParse->iDb || isTemp==1 );
-  if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0) ){
-    goto exit_create_index;
-  }
-  i = SQLITE_CREATE_INDEX;
-  if( isTemp ) i = SQLITE_CREATE_TEMP_INDEX;
-  if( sqliteAuthCheck(pParse, i, zName, pTab->zName) ){
-    goto exit_create_index;
+  {
+    const char *zDb = db->aDb[pTab->iDb].zName;
+
+    assert( isTemp==0 || isTemp==1 );
+    assert( pTab->iDb==pParse->iDb || isTemp==1 );
+    if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
+      goto exit_create_index;
+    }
+    i = SQLITE_CREATE_INDEX;
+    if( isTemp ) i = SQLITE_CREATE_TEMP_INDEX;
+    if( sqliteAuthCheck(pParse, i, zName, pTab->zName, zDb) ){
+      goto exit_create_index;
+    }
   }
 #endif
 
@@ -1813,11 +1821,13 @@
   {
     int code = SQLITE_DROP_INDEX;
     Table *pTab = pIndex->pTable;
-    if( sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pIndex->iDb), 0) ){
+    const char *zDb = db->aDb[pIndex->iDb].zName;
+    const char *zTab = SCHEMA_TABLE(pIndex->iDb);
+    if( sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
       goto exit_drop_index;
     }
     if( pIndex->iDb ) code = SQLITE_DROP_TEMP_INDEX;
-    if( sqliteAuthCheck(pParse, code, pIndex->zName, pTab->zName) ){
+    if( sqliteAuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){
       goto exit_drop_index;
     }
   }
@@ -2035,7 +2045,7 @@
 
   if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
   if( pParse->nErr || sqlite_malloc_failed ) return;
-  if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0) ) return;
+  if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ) return;
   if( db->flags & SQLITE_InTrans ){
     sqliteErrorMsg(pParse, "cannot start a transaction within a transaction");
     return;
@@ -2053,7 +2063,7 @@
 
   if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
   if( pParse->nErr || sqlite_malloc_failed ) return;
-  if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0) ) return;
+  if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ) return;
   if( (db->flags & SQLITE_InTrans)==0 ){
     sqliteErrorMsg(pParse, "cannot commit - no transaction is active");
     return;
@@ -2072,7 +2082,7 @@
 
   if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
   if( pParse->nErr || sqlite_malloc_failed ) return;
-  if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0) ) return;
+  if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ) return;
   if( (db->flags & SQLITE_InTrans)==0 ){
     sqliteErrorMsg(pParse, "cannot rollback - no transaction is active");
     return; 
diff --git a/src/copy.c b/src/copy.c
index 076b0a4..69711dc 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code used to implement the COPY command.
 **
-** $Id: copy.c,v 1.2 2003/04/15 19:22:23 drh Exp $
+** $Id: copy.c,v 1.3 2003/04/22 20:30:39 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -39,6 +39,7 @@
   int addr, end;
   Index *pIdx;
   char *zFile = 0;
+  const char *zDb;
   sqlite *db = pParse->db;
 
 
@@ -48,8 +49,10 @@
   if( pTab==0 || sqliteIsReadOnly(pParse, pTab) ) goto copy_cleanup;
   zFile = sqliteStrNDup(pFilename->z, pFilename->n);
   sqliteDequote(zFile);
-  if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, zFile)
-      || sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile) ){
+  assert( pTab->iDb>=0 && pTab->iDb<db->nDb );
+  zDb = db->aDb[pTab->iDb].zName;
+  if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb)
+      || sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile, zDb) ){
     goto copy_cleanup;
   }
   v = sqliteGetVdbe(pParse);
diff --git a/src/delete.c b/src/delete.c
index 9d88de4..8899e71 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
 ** to handle DELETE FROM statements.
 **
-** $Id: delete.c,v 1.52 2003/04/17 22:57:53 drh Exp $
+** $Id: delete.c,v 1.53 2003/04/22 20:30:39 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -58,6 +58,7 @@
 ){
   Vdbe *v;               /* The virtual database engine */
   Table *pTab;           /* The table from which records will be deleted */
+  const char *zDb;       /* Name of database holding pTab */
   int end, addr;         /* A couple addresses of generated code */
   int i;                 /* Loop counter */
   WhereInfo *pWInfo;     /* Information about the WHERE clause */
@@ -97,7 +98,9 @@
   }
   if( sqliteIsReadOnly(pParse, pTab) ) goto delete_from_cleanup;
   assert( pTab->pSelect==0 );  /* This table is not a view */
-  if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0) ){
+  assert( pTab->iDb<db->nDb );
+  zDb = db->aDb[pTab->iDb].zName;
+  if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
     goto delete_from_cleanup;
   }
 
diff --git a/src/expr.c b/src/expr.c
index 059b7bb..90f00f3 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.93 2003/04/19 17:27:25 drh Exp $
+** $Id: expr.c,v 1.94 2003/04/22 20:30:39 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -432,6 +432,8 @@
       int cnt = 0;      /* Number of matches */
       int i;            /* Loop counter */
       char *z;
+      int iDb = -1;
+
       assert( pExpr->token.z );
       z = sqliteStrNDup(pExpr->token.z, pExpr->token.n);
       sqliteDequote(z);
@@ -440,11 +442,13 @@
         int j;
         Table *pTab = pTabList->a[i].pTab;
         if( pTab==0 ) continue;
+        iDb = pTab->iDb;
         assert( pTab->nCol>0 );
         for(j=0; j<pTab->nCol; j++){
           if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){
             cnt++;
             pExpr->iTable = i + base;
+            pExpr->iDb = pTab->iDb;
             if( j==pTab->iPKey ){
               /* Substitute the record number for the INTEGER PRIMARY KEY */
               pExpr->iColumn = -1;
@@ -470,9 +474,10 @@
           }
         } 
       }
-      if( cnt==0 && sqliteIsRowid(z) ){
+      if( cnt==0 && iDb>=0 && sqliteIsRowid(z) ){
         pExpr->iColumn = -1;
         pExpr->iTable = base;
+        pExpr->iDb = iDb;
         cnt = 1 + (pTabList->nSrc>1);
         pExpr->op = TK_COLUMN;
         pExpr->dataType = SQLITE_SO_NUM;
@@ -544,11 +549,15 @@
             continue;
           }
         }
-        if( 0==(cntTab++) ) pExpr->iTable = i + base;
+        if( 0==(cntTab++) ){
+          pExpr->iTable = i + base;
+          pExpr->iDb = pTab->iDb;
+        }
         for(j=0; j<pTab->nCol; j++){
           if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
             cnt++;
             pExpr->iTable = i + base;
+            pExpr->iDb = pTab->iDb;
             /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
             pExpr->iColumn = j==pTab->iPKey ? -1 : j;
             pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK;
@@ -563,11 +572,15 @@
         int t = 0;
         if( pTriggerStack->newIdx != -1 && sqliteStrICmp("new", zLeft) == 0 ){
           pExpr->iTable = pTriggerStack->newIdx;
+          assert( pTriggerStack->pTab );
+          pExpr->iDb = pTriggerStack->pTab->iDb;
           cntTab++;
           t = 1;
         }
         if( pTriggerStack->oldIdx != -1 && sqliteStrICmp("old", zLeft) == 0 ){
           pExpr->iTable = pTriggerStack->oldIdx;
+          assert( pTriggerStack->pTab );
+          pExpr->iDb = pTriggerStack->pTab->iDb;
           cntTab++;
           t = 1;
         }
diff --git a/src/insert.c b/src/insert.c
index f777331..940536f 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.80 2003/04/20 17:29:24 drh Exp $
+** $Id: insert.c,v 1.81 2003/04/22 20:30:39 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -93,6 +93,7 @@
 ){
   Table *pTab;          /* The table to insert into */
   char *zTab;           /* Name of the table into which we are inserting */
+  const char *zDb;      /* Name of the database holding this table */
   int i, j, idx;        /* Loop counters */
   Vdbe *v;              /* Generate code into this virtual machine */
   Index *pIdx;          /* For looping over indices of the table */
@@ -126,7 +127,9 @@
   if( pTab==0 ){
     goto insert_cleanup;
   }
-  if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0) ){
+  assert( pTab->iDb<db->nDb );
+  zDb = db->aDb[pTab->iDb].zName;
+  if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
     goto insert_cleanup;
   }
 
diff --git a/src/pragma.c b/src/pragma.c
index 1466cc1..6b26048 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code used to implement the PRAGMA command.
 **
-** $Id: pragma.c,v 1.3 2003/04/15 01:19:49 drh Exp $
+** $Id: pragma.c,v 1.4 2003/04/22 20:30:39 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -107,7 +107,7 @@
     zRight = sqliteStrNDup(pRight->z, pRight->n);
     sqliteDequote(zRight);
   }
-  if( sqliteAuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight) ){
+  if( sqliteAuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, 0) ){
     sqliteFree(zLeft);
     sqliteFree(zRight);
     return;
diff --git a/src/select.c b/src/select.c
index 3aef002..f311fa7 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.132 2003/04/17 22:57:54 drh Exp $
+** $Id: select.c,v 1.133 2003/04/22 20:30:39 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -1917,7 +1917,7 @@
   int rc = 1;            /* Value to return from this function */
 
   if( sqlite_malloc_failed || pParse->nErr || p==0 ) return 1;
-  if( sqliteAuthCheck(pParse, SQLITE_SELECT, 0, 0) ) return 1;
+  if( sqliteAuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
 
   /* If there is are a sequence of queries, do the earlier ones first.
   */
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 7d9e12b..377b9f5 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -12,7 +12,7 @@
 ** This header file defines the interface that the SQLite library
 ** presents to client programs.
 **
-** @(#) $Id: sqlite.h.in,v 1.44 2003/04/03 15:46:04 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.45 2003/04/22 20:30:39 drh Exp $
 */
 #ifndef _SQLITE_H_
 #define _SQLITE_H_
@@ -513,7 +513,7 @@
 */
 int sqlite_set_authorizer(
   sqlite*,
-  int (*xAuth)(void*,int,const char*,const char*),
+  int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
   void *pUserData
 );
 
@@ -522,7 +522,10 @@
 ** be one of the values below.  These values signify what kind of operation
 ** is to be authorized.  The 3rd and 4th parameters to the authorization
 ** function will be parameters or NULL depending on which of the following
-** codes is used as the second parameter.
+** codes is used as the second parameter.  The 5th parameter is the name
+** of the database ("main", "temp", etc.) if applicable.  The 6th parameter
+** is the name of the trigger that is responsible for the access attempt,
+** or NULL if this access attempt is directly from input SQL code.
 **
 **                                          Arg-3           Arg-4
 */
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 135523a..c95a567 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.177 2003/04/21 18:48:47 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.178 2003/04/22 20:30:39 drh Exp $
 */
 #include "config.h"
 #include "sqlite.h"
@@ -105,6 +105,9 @@
 #ifndef UINT8_TYPE
 # define UINT8_TYPE unsigned char
 #endif
+#ifndef INT8_TYPE
+# define INT8_TYPE signed char
+#endif
 #ifndef INTPTR_TYPE
 # if SQLITE_PTR_SZ==4
 #   define INTPTR_TYPE int
@@ -115,6 +118,7 @@
 typedef UINT32_TYPE u32;           /* 4-byte unsigned integer */
 typedef UINT16_TYPE u16;           /* 2-byte unsigned integer */
 typedef UINT8_TYPE u8;             /* 1-byte unsigned integer */
+typedef INT8_TYPE i8;              /* 1-byte signed integer */
 typedef INTPTR_TYPE ptr;           /* Big enough to hold a pointer */
 typedef unsigned INTPTR_TYPE uptr; /* Big enough to hold a pointer */
 
@@ -290,7 +294,8 @@
   void *pTraceArg;                       /* Argument to the trace function */
 #endif
 #ifndef SQLITE_OMIT_AUTHORIZATION
-  int (*xAuth)(void*,int,const char*,const char*); /* Access Auth function */
+  int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
+                                /* Access authorization function */
   void *pAuthArg;               /* 1st argument to the access auth function */
 #endif
 };
@@ -580,7 +585,8 @@
 struct Expr {
   u8 op;                 /* Operation performed by this node */
   u8 dataType;           /* Either SQLITE_SO_TEXT or SQLITE_SO_NUM */
-  u16 flags;             /* Various flags.  See below */
+  i8 iDb;                /* Database referenced by this expression */
+  u8 flags;              /* Various flags.  See below */
   Expr *pLeft, *pRight;  /* Left and right subnodes */
   ExprList *pList;       /* A list of expressions used as function arguments
                          ** or in "<expr> IN (<expr-list)" */
@@ -953,9 +959,8 @@
   int oldIdx;          /* Index of vdbe cursor to "old" temp table */
   int orconf;          /* Current orconf policy */
   int ignoreJump;      /* where to jump to for a RAISE(IGNORE) */
-  Trigger *pTrigger;
-
-  TriggerStack *pNext;
+  Trigger *pTrigger;   /* The trigger currently being coded */
+  TriggerStack *pNext; /* Next trigger down on the trigger stack */
 };
 
 /*
@@ -1113,7 +1118,7 @@
 void sqliteDeferForeignKey(Parse*, int);
 #ifndef SQLITE_OMIT_AUTHORIZATION
   void sqliteAuthRead(Parse*,Expr*,SrcList*,int);
-  int sqliteAuthCheck(Parse*,int, const char*, const char*);
+  int sqliteAuthCheck(Parse*,int, const char*, const char*, const char*);
 #else
 # define sqliteAuthRead(a,b,c,d)
 # define sqliteAuthCheck(a,b,c,d)    SQLITE_OK
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index dd21cad..b1b446f 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -11,7 +11,7 @@
 *************************************************************************
 ** A TCL Interface to SQLite
 **
-** $Id: tclsqlite.c,v 1.46 2003/04/03 15:46:04 drh Exp $
+** $Id: tclsqlite.c,v 1.47 2003/04/22 20:30:39 drh Exp $
 */
 #ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */
 
@@ -53,6 +53,7 @@
   char *zBusy;          /* The busy callback routine */
   char *zBegin;         /* The begin-transaction callback routine */
   char *zCommit;        /* The commit-transaction callback routine */
+  char *zAuth;          /* The authorization callback routine */
   SqlFunc *pFunc;       /* List of SQL functions */
   int rc;               /* Return code of most recent sqlite_exec() */
 };
@@ -268,6 +269,9 @@
   if( pDb->zCommit ){
     Tcl_Free(pDb->zCommit);
   }
+  if( pDb->zAuth ){
+    Tcl_Free(pDb->zAuth);
+  }
   Tcl_Free((char*)pDb);
 }
 
@@ -351,6 +355,76 @@
     sqlite_set_result_string(context, Tcl_GetStringResult(p->interp), -1);
   }
 }
+#ifndef SQLITE_OMIT_AUTHORIZATION
+/*
+** This is the authentication function.  It appends the authentication
+** type code and the two arguments to zCmd[] then invokes the result
+** on the interpreter.  The reply is examined to determine if the
+** authentication fails or succeeds.
+*/
+static int auth_callback(
+  void *pArg,
+  int code,
+  const char *zArg1,
+  const char *zArg2,
+  const char *zArg3,
+  const char *zArg4
+){
+  char *zCode;
+  Tcl_DString str;
+  int rc;
+  const char *zReply;
+  SqliteDb *pDb = (SqliteDb*)pArg;
+
+  switch( code ){
+    case SQLITE_COPY              : zCode="SQLITE_COPY"; break;
+    case SQLITE_CREATE_INDEX      : zCode="SQLITE_CREATE_INDEX"; break;
+    case SQLITE_CREATE_TABLE      : zCode="SQLITE_CREATE_TABLE"; break;
+    case SQLITE_CREATE_TEMP_INDEX : zCode="SQLITE_CREATE_TEMP_INDEX"; break;
+    case SQLITE_CREATE_TEMP_TABLE : zCode="SQLITE_CREATE_TEMP_TABLE"; break;
+    case SQLITE_CREATE_TEMP_TRIGGER: zCode="SQLITE_CREATE_TEMP_TRIGGER"; break;
+    case SQLITE_CREATE_TEMP_VIEW  : zCode="SQLITE_CREATE_TEMP_VIEW"; break;
+    case SQLITE_CREATE_TRIGGER    : zCode="SQLITE_CREATE_TRIGGER"; break;
+    case SQLITE_CREATE_VIEW       : zCode="SQLITE_CREATE_VIEW"; break;
+    case SQLITE_DELETE            : zCode="SQLITE_DELETE"; break;
+    case SQLITE_DROP_INDEX        : zCode="SQLITE_DROP_INDEX"; break;
+    case SQLITE_DROP_TABLE        : zCode="SQLITE_DROP_TABLE"; break;
+    case SQLITE_DROP_TEMP_INDEX   : zCode="SQLITE_DROP_TEMP_INDEX"; break;
+    case SQLITE_DROP_TEMP_TABLE   : zCode="SQLITE_DROP_TEMP_TABLE"; break;
+    case SQLITE_DROP_TEMP_TRIGGER : zCode="SQLITE_DROP_TEMP_TRIGGER"; break;
+    case SQLITE_DROP_TEMP_VIEW    : zCode="SQLITE_DROP_TEMP_VIEW"; break;
+    case SQLITE_DROP_TRIGGER      : zCode="SQLITE_DROP_TRIGGER"; break;
+    case SQLITE_DROP_VIEW         : zCode="SQLITE_DROP_VIEW"; break;
+    case SQLITE_INSERT            : zCode="SQLITE_INSERT"; break;
+    case SQLITE_PRAGMA            : zCode="SQLITE_PRAGMA"; break;
+    case SQLITE_READ              : zCode="SQLITE_READ"; break;
+    case SQLITE_SELECT            : zCode="SQLITE_SELECT"; break;
+    case SQLITE_TRANSACTION       : zCode="SQLITE_TRANSACTION"; break;
+    case SQLITE_UPDATE            : zCode="SQLITE_UPDATE"; break;
+    default                       : zCode="????"; break;
+  }
+  Tcl_DStringInit(&str);
+  Tcl_DStringAppend(&str, pDb->zAuth, -1);
+  Tcl_DStringAppendElement(&str, zCode);
+  Tcl_DStringAppendElement(&str, zArg1 ? zArg1 : "");
+  Tcl_DStringAppendElement(&str, zArg2 ? zArg2 : "");
+  Tcl_DStringAppendElement(&str, zArg3 ? zArg3 : "");
+  Tcl_DStringAppendElement(&str, zArg4 ? zArg4 : "");
+  rc = Tcl_GlobalEval(pDb->interp, Tcl_DStringValue(&str));
+  Tcl_DStringFree(&str);
+  zReply = Tcl_GetStringResult(pDb->interp);
+  if( strcmp(zReply,"SQLITE_OK")==0 ){
+    rc = SQLITE_OK;
+  }else if( strcmp(zReply,"SQLITE_DENY")==0 ){
+    rc = SQLITE_DENY;
+  }else if( strcmp(zReply,"SQLITE_IGNORE")==0 ){
+    rc = SQLITE_IGNORE;
+  }else{
+    rc = 999;
+  }
+  return rc;
+}
+#endif /* SQLITE_OMIT_AUTHORIZATION */
 
 /*
 ** The "sqlite" command below creates a new Tcl command for each
@@ -369,16 +443,17 @@
   SqliteDb *pDb = (SqliteDb*)cd;
   int choice;
   static const char *DB_strs[] = {
-    "begin_hook",         "busy",              "changes",
-    "close",              "commit_hook",       "complete",
-    "errorcode",          "eval",              "function",
-    "last_insert_rowid",  "timeout",           0
+    "authorizer",         "begin_hook",        "busy",
+    "changes",            "close",             "commit_hook",
+    "complete",           "errorcode",         "eval",
+    "function",           "last_insert_rowid", "timeout",
+    0                    
   };
   enum DB_enum {
-    DB_BEGIN_HOOK,        DB_BUSY,             DB_CHANGES,
-    DB_CLOSE,             DB_COMMIT_HOOK,      DB_COMPLETE,
-    DB_ERRORCODE,         DB_EVAL,             DB_FUNCTION,
-    DB_LAST_INSERT_ROWID, DB_TIMEOUT,          
+    DB_AUTHORIZER,        DB_BEGIN_HOOK,       DB_BUSY,
+    DB_CHANGES,           DB_CLOSE,            DB_COMMIT_HOOK,
+    DB_COMPLETE,          DB_ERRORCODE,        DB_EVAL,
+    DB_FUNCTION,          DB_LAST_INSERT_ROWID,DB_TIMEOUT,
   };
 
   if( objc<2 ){
@@ -391,6 +466,57 @@
 
   switch( (enum DB_enum)choice ){
 
+  /*    $db authorizer ?CALLBACK?
+  **
+  ** Invoke the given callback to authorize each SQL operation as it is
+  ** compiled.  5 arguments are appended to the callback before it is
+  ** invoked:
+  **
+  **   (1) The authorization type (ex: SQLITE_CREATE_TABLE, SQLITE_INSERT, ...)
+  **   (2) First descriptive name (depends on authorization type)
+  **   (3) Second descriptive name
+  **   (4) Name of the database (ex: "main", "temp")
+  **   (5) Name of trigger that is doing the access
+  **
+  ** The callback should return on of the following strings: SQLITE_OK,
+  ** SQLITE_IGNORE, or SQLITE_DENY.  Any other return value is an error.
+  **
+  ** If this method is invoked with no arguments, the current authorization
+  ** callback string is returned.
+  */
+  case DB_AUTHORIZER: {
+    if( objc>3 ){
+      Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?");
+    }else if( objc==2 ){
+      if( pDb->zBegin ){
+        Tcl_AppendResult(interp, pDb->zAuth, 0);
+      }
+    }else{
+      char *zAuth;
+      int len;
+      if( pDb->zAuth ){
+        Tcl_Free(pDb->zAuth);
+      }
+      zAuth = Tcl_GetStringFromObj(objv[2], &len);
+      if( zAuth && len>0 ){
+        pDb->zAuth = Tcl_Alloc( len + 1 );
+        strcpy(pDb->zAuth, zAuth);
+      }else{
+        pDb->zAuth = 0;
+      }
+#ifndef SQLITE_OMIT_AUTHORIZATION
+      if( pDb->zAuth ){
+        pDb->interp = interp;
+        sqlite_set_authorizer(pDb->db, auth_callback, pDb);
+      }else{
+        sqlite_set_authorizer(pDb->db, 0, 0);
+      }
+#endif
+    }
+    break;
+  }
+
+
   /*    $db begin_callback ?CALLBACK?
   **
   ** Invoke the given callback at the beginning of every SQL transaction.
diff --git a/src/test1.c b/src/test1.c
index 402b2c2..92ce28c 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test1.c,v 1.22 2003/02/16 22:21:32 drh Exp $
+** $Id: test1.c,v 1.23 2003/04/22 20:30:40 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -588,125 +588,6 @@
   return TCL_OK;
 }
 
-#ifndef SQLITE_OMIT_AUTHORIZATION
-/*
-** Information used by the authentication function. 
-*/
-typedef struct AuthInfo AuthInfo;
-struct AuthInfo {
-  Tcl_Interp *interp;    /* Interpreter to use */
-  int nCmd;              /* Number of characters in zCmd[] */
-  char zCmd[500];        /* Command to invoke */
-};
-
-/*
-** We create a single static authenticator.  This won't work in a
-** multi-threaded environment, but the test fixture is not multithreaded.
-** And be making it static, we don't have to worry about deallocating
-** after a test in order to void memory leaks.
-*/
-static AuthInfo authInfo;
-
-/*
-** This is the authentication function.  It appends the authentication
-** type code and the two arguments to zCmd[] then invokes the result
-** on the interpreter.  The reply is examined to determine if the
-** authentication fails or succeeds.
-*/
-static int auth_callback(
-  void *NotUsed, 
-  int code,
-  const char *zArg1,
-  const char *zArg2
-){
-  char *zCode;
-  Tcl_DString str;
-  int rc;
-  const char *zReply;
-  switch( code ){
-    case SQLITE_COPY              : zCode="SQLITE_COPY"; break;
-    case SQLITE_CREATE_INDEX      : zCode="SQLITE_CREATE_INDEX"; break;
-    case SQLITE_CREATE_TABLE      : zCode="SQLITE_CREATE_TABLE"; break;
-    case SQLITE_CREATE_TEMP_INDEX : zCode="SQLITE_CREATE_TEMP_INDEX"; break;
-    case SQLITE_CREATE_TEMP_TABLE : zCode="SQLITE_CREATE_TEMP_TABLE"; break;
-    case SQLITE_CREATE_TEMP_TRIGGER: zCode="SQLITE_CREATE_TEMP_TRIGGER"; break;
-    case SQLITE_CREATE_TEMP_VIEW  : zCode="SQLITE_CREATE_TEMP_VIEW"; break;
-    case SQLITE_CREATE_TRIGGER    : zCode="SQLITE_CREATE_TRIGGER"; break;
-    case SQLITE_CREATE_VIEW       : zCode="SQLITE_CREATE_VIEW"; break;
-    case SQLITE_DELETE            : zCode="SQLITE_DELETE"; break;
-    case SQLITE_DROP_INDEX        : zCode="SQLITE_DROP_INDEX"; break;
-    case SQLITE_DROP_TABLE        : zCode="SQLITE_DROP_TABLE"; break;
-    case SQLITE_DROP_TEMP_INDEX   : zCode="SQLITE_DROP_TEMP_INDEX"; break;
-    case SQLITE_DROP_TEMP_TABLE   : zCode="SQLITE_DROP_TEMP_TABLE"; break;
-    case SQLITE_DROP_TEMP_TRIGGER : zCode="SQLITE_DROP_TEMP_TRIGGER"; break;
-    case SQLITE_DROP_TEMP_VIEW    : zCode="SQLITE_DROP_TEMP_VIEW"; break;
-    case SQLITE_DROP_TRIGGER      : zCode="SQLITE_DROP_TRIGGER"; break;
-    case SQLITE_DROP_VIEW         : zCode="SQLITE_DROP_VIEW"; break;
-    case SQLITE_INSERT            : zCode="SQLITE_INSERT"; break;
-    case SQLITE_PRAGMA            : zCode="SQLITE_PRAGMA"; break;
-    case SQLITE_READ              : zCode="SQLITE_READ"; break;
-    case SQLITE_SELECT            : zCode="SQLITE_SELECT"; break;
-    case SQLITE_TRANSACTION       : zCode="SQLITE_TRANSACTION"; break;
-    case SQLITE_UPDATE            : zCode="SQLITE_UPDATE"; break;
-    default                       : zCode="????"; break;
-  }
-  Tcl_DStringInit(&str);
-  Tcl_DStringAppend(&str, authInfo.zCmd, -1);
-  Tcl_DStringAppendElement(&str, zCode);
-  Tcl_DStringAppendElement(&str, zArg1 ? zArg1 : "");
-  Tcl_DStringAppendElement(&str, zArg2 ? zArg2 : "");
-  rc = Tcl_GlobalEval(authInfo.interp, Tcl_DStringValue(&str));
-  Tcl_DStringFree(&str);
-  zReply = Tcl_GetStringResult(authInfo.interp);
-  if( strcmp(zReply,"SQLITE_OK")==0 ){
-    rc = SQLITE_OK;
-  }else if( strcmp(zReply,"SQLITE_DENY")==0 ){
-    rc = SQLITE_DENY;
-  }else if( strcmp(zReply,"SQLITE_IGNORE")==0 ){
-    rc = SQLITE_IGNORE;
-  }else{
-    rc = 999;
-  }
-  return rc;
-}
-
-/*
-** This routine creates a new authenticator.  It fills in the zCmd[]
-** field of the authentication function state variable and then registers
-** the authentication function with the SQLite library.
-*/
-static int test_set_authorizer(
-  void *NotUsed,
-  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
-  int argc,              /* Number of arguments */
-  char **argv            /* Text of each argument */
-){
-  sqlite *db;
-  char *zCmd;
-  if( argc!=3 ){
-    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 
-         " DB CALLBACK\"", 0);
-    return TCL_ERROR;
-  }
-  if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
-  zCmd = argv[2];
-  if( zCmd[0]==0 ){
-    sqlite_set_authorizer(db, 0, 0);
-    return TCL_OK;
-  }
-  if( strlen(zCmd)>sizeof(authInfo.zCmd) ){
-    Tcl_AppendResult(interp, "command too big", 0);
-    return TCL_ERROR;
-  }
-  authInfo.interp = interp;
-  authInfo.nCmd = strlen(zCmd);
-  strcpy(authInfo.zCmd, zCmd);
-  sqlite_set_authorizer(db, auth_callback, 0);
-  return TCL_OK;
-}
-#endif /* SQLITE_OMIT_AUTHORIZATION */
-
-
 /*
 ** Usage:  sqlite_compile  DB  SQL  TAILVAR
 **
@@ -878,9 +759,6 @@
      { "sqlite_register_test_function",  (Tcl_CmdProc*)test_register_func    },
      { "sqlite_abort",                   (Tcl_CmdProc*)sqlite_abort          },
      { "sqlite_datatypes",               (Tcl_CmdProc*)sqlite_datatypes      },
-#ifndef SQLITE_OMIT_AUTHORIZATION
-     { "sqlite_set_authorizer",          (Tcl_CmdProc*)test_set_authorizer   },
-#endif
 #ifdef MEMORY_DEBUG
      { "sqlite_malloc_fail",             (Tcl_CmdProc*)sqlite_malloc_fail    },
      { "sqlite_malloc_stat",             (Tcl_CmdProc*)sqlite_malloc_stat    },
diff --git a/src/trigger.c b/src/trigger.c
index 7124add..a2fc91f 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -98,11 +98,13 @@
 #ifndef SQLITE_OMIT_AUTHORIZATION
   {
     int code = SQLITE_CREATE_TRIGGER;
+    const char *zDb = db->aDb[tab->iDb].zName;
+    const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb;
     if( tab->iDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
-    if( sqliteAuthCheck(pParse, code, zName, tab->zName) ){
+    if( sqliteAuthCheck(pParse, code, zName, tab->zName, zDbTrig) ){
       goto trigger_cleanup;
     }
-    if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(tab->iDb), 0)){
+    if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(tab->iDb), 0, zDb)){
       goto trigger_cleanup;
     }
   }
@@ -397,9 +399,11 @@
 #ifndef SQLITE_OMIT_AUTHORIZATION
   {
     int code = SQLITE_DROP_TRIGGER;
+    const char *zDb = db->aDb[pTrigger->iDb].zName;
+    const char *zTab = SCHEMA_TABLE(pTrigger->iDb);
     if( pTrigger->iDb ) code = SQLITE_DROP_TEMP_TRIGGER;
-    if( sqliteAuthCheck(pParse, code, pTrigger->name, pTable->zName) ||
-      sqliteAuthCheck(pParse, SQLITE_DELETE, SCHEMA_TABLE(pTrigger->iDb),0) ){
+    if( sqliteAuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) ||
+      sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
       goto drop_trigger_cleanup;
     }
   }
diff --git a/src/update.c b/src/update.c
index b77e1bb..d320033 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.61 2003/04/20 17:29:24 drh Exp $
+** $Id: update.c,v 1.62 2003/04/22 20:30:40 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -139,7 +139,7 @@
     {
       int rc;
       rc = sqliteAuthCheck(pParse, SQLITE_UPDATE, pTab->zName,
-                           pTab->aCol[j].zName);
+                           pTab->aCol[j].zName, db->aDb[pTab->iDb].zName);
       if( rc==SQLITE_DENY ){
         goto update_cleanup;
       }else if( rc==SQLITE_IGNORE ){