Add the sqliteErrorMsg() function and use it to generate error message
text during parsing and code generation.  This simplifies the code
somewhat and makes it easier to handle names with a database prefix. (CVS 891)

FossilOrigin-Name: 1d3fc977211abdc7ba3fd51d661863e8ce5aef69
diff --git a/src/build.c b/src/build.c
index 059c69b..a14a72d 100644
--- a/src/build.c
+++ b/src/build.c
@@ -25,7 +25,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.137 2003/03/31 00:30:48 drh Exp $
+** $Id: build.c,v 1.138 2003/03/31 02:12:47 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -1750,20 +1750,17 @@
   assert( pName->nSrc==1 );
   pIndex = sqliteFindIndex(db, pName->a[0].zName, pName->a[0].zDatabase);
   if( pIndex==0 ){
-    sqliteSetString(&pParse->zErrMsg, "no such index: ", pName->a[0].zName, 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, "no such index: %S", pName, 0);
     goto exit_drop_index;
   }
   if( pIndex->autoIndex ){
-    sqliteSetString(&pParse->zErrMsg, "index associated with UNIQUE "
+    sqliteErrorMsg(pParse, "index associated with UNIQUE "
       "or PRIMARY KEY constraint cannot be dropped", 0);
-    pParse->nErr++;
     goto exit_drop_index;
   }
   if( pIndex->iDb>1 ){
-    sqliteSetString(&pParse->zErrMsg, "cannot alter schema of attached "
+    sqliteErrorMsg(pParse, "cannot alter schema of attached "
        "databases", 0);
-    pParse->nErr++;
     goto exit_drop_index;
   }
 #ifndef SQLITE_OMIT_AUTHORIZATION
@@ -2108,9 +2105,7 @@
   if( pParse->nErr || sqlite_malloc_failed ) return;
   if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0) ) return;
   if( db->flags & SQLITE_InTrans ){
-    pParse->nErr++;
-    sqliteSetString(&pParse->zErrMsg, "cannot start a transaction "
-       "within a transaction", 0);
+    sqliteErrorMsg(pParse, "cannot start a transaction within a transaction");
     return;
   }
   sqliteBeginWriteOperation(pParse, 0, 0);
@@ -2128,9 +2123,7 @@
   if( pParse->nErr || sqlite_malloc_failed ) return;
   if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0) ) return;
   if( (db->flags & SQLITE_InTrans)==0 ){
-    pParse->nErr++;
-    sqliteSetString(&pParse->zErrMsg, 
-       "cannot commit - no transaction is active", 0);
+    sqliteErrorMsg(pParse, "cannot commit - no transaction is active");
     return;
   }
   db->flags &= ~SQLITE_InTrans;
@@ -2149,9 +2142,7 @@
   if( pParse->nErr || sqlite_malloc_failed ) return;
   if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0) ) return;
   if( (db->flags & SQLITE_InTrans)==0 ){
-    pParse->nErr++;
-    sqliteSetString(&pParse->zErrMsg,
-       "cannot rollback - no transaction is active", 0);
+    sqliteErrorMsg(pParse, "cannot rollback - no transaction is active");
     return; 
   }
   v = sqliteGetVdbe(pParse);
@@ -2701,10 +2692,7 @@
   sqliteDequote(zName);
   for(i=0; i<db->nDb; i++){
     if( db->aDb[i].zName && sqliteStrICmp(db->aDb[i].zName, zName)==0 ){
-      sqliteSetString(&pParse->zErrMsg, "database \"", zName, 
-         "\" already in use", 0);
-      sqliteFree(zName);
-      pParse->nErr++;
+      sqliteErrorMsg(pParse, "database %z is already in use", zName);
       return;
     }
   }
@@ -2715,8 +2703,7 @@
   sqliteDequote(zFile);
   rc = sqliteBtreeOpen(zFile, 0, MAX_PAGES, &aNew->pBt);
   if( rc ){
-    sqliteSetString(&pParse->zErrMsg, "unable to open database: ", zFile, 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, "unable to open database: %s", zFile);
   }
   sqliteFree(zFile);
   db->flags &= ~SQLITE_Initialized;
@@ -2746,14 +2733,11 @@
     if( sqliteStrNICmp(db->aDb[i].zName, pDbname->z, pDbname->n)==0 ) break;
   }
   if( i>=db->nDb ){
-    sqliteSetNString(&pParse->zErrMsg, "no such database: ", -1,
-        pDbname->z, pDbname->n, 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, "no such database: %T", pDbname);
     return;
   }
   if( i<2 ){
-    sqliteSetString(&pParse->zErrMsg, "cannot detached \"main\" or \"temp\"",0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, "cannot detached database %T", pDbname);
     return;
   }
   sqliteBtreeClose(db->aDb[i].pBt);
diff --git a/src/delete.c b/src/delete.c
index 7fc2ddf..fecb8cc 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.49 2003/03/27 13:50:00 drh Exp $
+** $Id: delete.c,v 1.50 2003/03/31 02:12:47 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -29,12 +29,7 @@
     const char *zDb = pSrc->a[i].zDatabase;
     pTab = sqliteFindTable(pParse->db, zTab, zDb);
     if( pTab==0 ){
-      if( zDb==0 || zDb[0]==0 ){
-        sqliteSetString(&pParse->zErrMsg, "no such table: ", zTab, 0);
-      }else{
-        sqliteSetString(&pParse->zErrMsg, "no such table: ", zDb, ".", zTab, 0);
-      }
-      pParse->nErr++;
+      sqliteErrorMsg(pParse, "no such table: %S", pSrc, 0);
       break;
     }
     pSrc->a[i].pTab = pTab;
@@ -49,10 +44,9 @@
 */
 int sqliteIsReadOnly(Parse *pParse, Table *pTab){
   if( pTab->readOnly || pTab->pSelect ){
-    sqliteSetString(&pParse->zErrMsg, 
-      pTab->pSelect ? "view " : "table ", pTab->zName,
-      " may not be modified", 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, "%s %s may not be modified",
+      pTab->pSelect ? "view" : "table",
+      pTab->zName);
     return 1;
   }
   return 0;
diff --git a/src/expr.c b/src/expr.c
index 576ae2d..caa6de1 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.90 2003/03/27 12:51:25 drh Exp $
+** $Id: expr.c,v 1.91 2003/03/31 02:12:47 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -477,14 +477,10 @@
       }
       sqliteFree(z);
       if( cnt==0 && pExpr->token.z[0]!='"' ){
-        sqliteSetNString(&pParse->zErrMsg, "no such column: ", -1,  
-          pExpr->token.z, pExpr->token.n, 0);
-        pParse->nErr++;
+        sqliteErrorMsg(pParse, "no such column: %T", &pExpr->token);
         return 1;
       }else if( cnt>1 ){
-        sqliteSetNString(&pParse->zErrMsg, "ambiguous column name: ", -1,  
-          pExpr->token.z, pExpr->token.n, 0);
-        pParse->nErr++;
+        sqliteErrorMsg(pParse, "ambiguous column name: %T", &pExpr->token);
         return 1;
       }
       if( pExpr->op==TK_COLUMN ){
@@ -600,16 +596,12 @@
       sqliteFree(zLeft);
       sqliteFree(zRight);
       if( cnt==0 ){
-        sqliteSetNString(&pParse->zErrMsg, "no such column: ", -1,  
-          pLeft->token.z, pLeft->token.n, ".", 1, 
-          pRight->token.z, pRight->token.n, 0);
-        pParse->nErr++;
+        sqliteErrorMsg(pParse, "no such column: %T.%T",
+               &pLeft->token, &pRight->token);
         return 1;
       }else if( cnt>1 ){
-        sqliteSetNString(&pParse->zErrMsg, "ambiguous column name: ", -1,  
-          pLeft->token.z, pLeft->token.n, ".", 1,
-          pRight->token.z, pRight->token.n, 0);
-        pParse->nErr++;
+        sqliteErrorMsg(pParse, "ambiguous column name: %T.%T",
+          &pLeft->token, &pRight->token);
         return 1;
       }
       sqliteExprDelete(pExpr->pLeft);
@@ -647,9 +639,8 @@
         for(i=0; i<pExpr->pList->nExpr; i++){
           Expr *pE2 = pExpr->pList->a[i].pExpr;
           if( !sqliteExprIsConstant(pE2) ){
-            sqliteSetString(&pParse->zErrMsg,
-              "right-hand side of IN operator must be constant", 0);
-            pParse->nErr++;
+            sqliteErrorMsg(pParse,
+              "right-hand side of IN operator must be constant");
             return 1;
           }
           if( sqliteExprCheck(pParse, pE2, 0, 0) ){
@@ -1219,8 +1210,8 @@
     }
     case TK_RAISE: {
       if( !pParse->trigStack ){
-        sqliteSetNString(&pParse->zErrMsg, 
-		"RAISE() may only be used within a trigger-program", -1, 0);
+        sqliteErrorMsg(pParse,
+                       "RAISE() may only be used within a trigger-program");
         pParse->nErr++;
 	return;
       }
diff --git a/src/insert.c b/src/insert.c
index f9f72f8..705bd8e 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.76 2003/03/27 13:50:00 drh Exp $
+** $Id: insert.c,v 1.77 2003/03/31 02:12:47 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -137,11 +137,9 @@
         TK_BEFORE, TK_ROW, 0) ||
     sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT, TK_AFTER, TK_ROW, 0);
   if( pTab->readOnly || (pTab->pSelect && !row_triggers_exist) ){
-    sqliteSetString(&pParse->zErrMsg, 
-      pTab->pSelect ? "view " : "table ",
-      zTab,
-      " may not be modified", 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, "%s %s may not be modified",
+      pTab->pSelect ? "view" : "table",
+      zTab);
     goto insert_cleanup;
   }
 
@@ -245,24 +243,13 @@
   ** of columns to be inserted into the table.
   */
   if( pColumn==0 && nColumn!=pTab->nCol ){
-    char zNum1[30];
-    char zNum2[30];
-    sprintf(zNum1,"%d", nColumn);
-    sprintf(zNum2,"%d", pTab->nCol);
-    sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
-       " has ", zNum2, " columns but ",
-       zNum1, " values were supplied", 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, 
+       "table %S has %d columns but %d values were supplied",
+       pTabList, 0, pTab->nCol, nColumn);
     goto insert_cleanup;
   }
   if( pColumn!=0 && nColumn!=pColumn->nId ){
-    char zNum1[30];
-    char zNum2[30];
-    sprintf(zNum1,"%d", nColumn);
-    sprintf(zNum2,"%d", pColumn->nId);
-    sqliteSetString(&pParse->zErrMsg, zNum1, " values for ",
-       zNum2, " columns", 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId);
     goto insert_cleanup;
   }
 
@@ -292,8 +279,8 @@
         }
       }
       if( j>=pTab->nCol ){
-        sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
-           " has no column named ", pColumn->a[i].zName, 0);
+        sqliteErrorMsg(pParse, "table %S has no column named %s",
+            pTabList, 0, pColumn->a[i].zName);
         pParse->nErr++;
         goto insert_cleanup;
       }
diff --git a/src/select.c b/src/select.c
index 4828355..e1704d2 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.128 2003/03/27 12:51:25 drh Exp $
+** $Id: select.c,v 1.129 2003/03/31 02:12:47 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -120,9 +120,8 @@
     pParse->nErr++;
     jointype = JT_INNER;
   }else if( jointype & JT_RIGHT ){
-    sqliteSetString(&pParse->zErrMsg, 
-      "RIGHT and FULL OUTER JOINs are not currently supported", 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, 
+      "RIGHT and FULL OUTER JOINs are not currently supported");
     jointype = JT_INNER;
   }
   return jointype;
@@ -218,9 +217,8 @@
     if( pTerm->jointype & JT_NATURAL ){
       Table *pTab;
       if( pTerm->pOn || pTerm->pUsing ){
-        sqliteSetString(&pParse->zErrMsg, "a NATURAL join may not have "
+        sqliteErrorMsg(pParse, "a NATURAL join may not have "
            "an ON or USING clause", 0);
-        pParse->nErr++;
         return 1;
       }
       pTab = pTerm->pTab;
@@ -234,9 +232,8 @@
     /* Disallow both ON and USING clauses in the same join
     */
     if( pTerm->pOn && pTerm->pUsing ){
-      sqliteSetString(&pParse->zErrMsg, "cannot have both ON and USING "
-        "clauses in the same join", 0);
-      pParse->nErr++;
+      sqliteErrorMsg(pParse, "cannot have both ON and USING "
+        "clauses in the same join");
       return 1;
     }
 
@@ -268,9 +265,8 @@
       for(j=0; j<pList->nId; j++){
         if( columnIndex(pTerm->pTab, pList->a[j].zName)<0 ||
             columnIndex(pOther->pTab, pList->a[j].zName)<0 ){
-          sqliteSetString(&pParse->zErrMsg, "cannot join using column ",
-            pList->a[j].zName, " - column not present in both tables", 0);
-          pParse->nErr++;
+          sqliteErrorMsg(pParse, "cannot join using column %s - column "
+            "not present in both tables", pList->a[j].zName);
           return 1;
         }
         addWhereTerm(pList->a[j].zName, pTerm->pTab, pOther->pTab, &p->pWhere);
@@ -915,9 +911,7 @@
         sqliteFindTable(pParse->db, pTabList->a[i].zName,
                                     pTabList->a[i].zDatabase);
       if( pTab==0 ){
-        sqliteSetString(&pParse->zErrMsg, "no such table: ", 
-           pTabList->a[i].zName, 0);
-        pParse->nErr++;
+        sqliteErrorMsg(pParse, "no such table: %S", pTabList, i);
         return 1;
       }
       if( pTab->pSelect ){
@@ -1033,10 +1027,9 @@
         }
         if( !tableSeen ){
           if( pName ){
-            sqliteSetNString(&pParse->zErrMsg, "no such table: ", -1, 
-              pName->z, pName->n, 0);
+            sqliteErrorMsg(pParse, "no such table: %T", pName);
           }else{
-            sqliteSetString(&pParse->zErrMsg, "no tables specified", 0);
+            sqliteErrorMsg(pParse, "no tables specified");
           }
           rc = 1;
         }
@@ -1130,11 +1123,9 @@
     if( pOrderBy->a[i].done ) continue;
     if( sqliteExprIsInteger(pE, &iCol) ){
       if( iCol<=0 || iCol>pEList->nExpr ){
-        char zBuf[200];
-        sprintf(zBuf,"ORDER BY position %d should be between 1 and %d",
-           iCol, pEList->nExpr);
-        sqliteSetString(&pParse->zErrMsg, zBuf, 0);
-        pParse->nErr++;
+        sqliteErrorMsg(pParse,
+          "ORDER BY position %d should be between 1 and %d",
+          iCol, pEList->nExpr);
         nErr++;
         break;
       }
@@ -1164,11 +1155,8 @@
       pOrderBy->a[i].done = 1;
     }
     if( iCol<0 && mustComplete ){
-      char zBuf[30];
-      sprintf(zBuf,"%d",i+1);
-      sqliteSetString(&pParse->zErrMsg, "ORDER BY term number ", zBuf, 
-        " does not match any result column", 0);
-      pParse->nErr++;
+      sqliteErrorMsg(pParse,
+        "ORDER BY term number %d does not match any result column", i+1);
       nErr++;
       break;
     }
@@ -1278,9 +1266,8 @@
   if( p==0 || p->pPrior==0 ) return 1;
   pPrior = p->pPrior;
   if( pPrior->pOrderBy ){
-    sqliteSetString(&pParse->zErrMsg,"ORDER BY clause should come after ",
-      selectOpName(p->op), " not before", 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse,"ORDER BY clause should come after %s not before",
+      selectOpName(p->op));
     return 1;
   }
 
@@ -1451,9 +1438,8 @@
   }
   assert( p->pEList && pPrior->pEList );
   if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
-    sqliteSetString(&pParse->zErrMsg, "SELECTs to the left and right of ",
-      selectOpName(p->op), " do not have the same number of result columns", 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, "SELECTs to the left and right of %s"
+      " do not have the same number of result columns", selectOpName(p->op));
     return 1;
   }
 
@@ -1978,9 +1964,8 @@
   ** only a single column may be output.
   */
   if( (eDest==SRT_Mem || eDest==SRT_Set) && pEList->nExpr>1 ){
-    sqliteSetString(&pParse->zErrMsg, "only a single result allowed for "
-       "a SELECT that is part of an expression", 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, "only a single result allowed for "
+       "a SELECT that is part of an expression");
     goto select_end;
   }
 
@@ -2020,9 +2005,7 @@
   }
   if( pHaving ){
     if( pGroupBy==0 ){
-      sqliteSetString(&pParse->zErrMsg, "a GROUP BY clause is required "
-         "before HAVING", 0);
-      pParse->nErr++;
+      sqliteErrorMsg(pParse, "a GROUP BY clause is required before HAVING");
       goto select_end;
     }
     if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pHaving) ){
@@ -2048,16 +2031,13 @@
       }
       if( sqliteExprIsConstant(pE) ){
         if( sqliteExprIsInteger(pE, &iCol)==0 ){
-          sqliteSetString(&pParse->zErrMsg, 
-               "ORDER BY terms must not be non-integer constants", 0);
-          pParse->nErr++;
+          sqliteErrorMsg(pParse,
+             "ORDER BY terms must not be non-integer constants");
           goto select_end;
         }else if( iCol<=0 || iCol>pEList->nExpr ){
-          char zBuf[2000];
-          sprintf(zBuf,"ORDER BY column number %d out of range - should be "
+          sqliteErrorMsg(pParse, 
+             "ORDER BY column number %d out of range - should be "
              "between 1 and %d", iCol, pEList->nExpr);
-          sqliteSetString(&pParse->zErrMsg, zBuf, 0);
-          pParse->nErr++;
           goto select_end;
         }
       }
@@ -2079,16 +2059,13 @@
       }
       if( sqliteExprIsConstant(pE) ){
         if( sqliteExprIsInteger(pE, &iCol)==0 ){
-          sqliteSetString(&pParse->zErrMsg, 
-               "GROUP BY terms must not be non-integer constants", 0);
-          pParse->nErr++;
+          sqliteErrorMsg(pParse,
+            "GROUP BY terms must not be non-integer constants");
           goto select_end;
         }else if( iCol<=0 || iCol>pEList->nExpr ){
-          char zBuf[2000];
-          sprintf(zBuf,"GROUP BY column number %d out of range - should be "
+          sqliteErrorMsg(pParse,
+             "GROUP BY column number %d out of range - should be "
              "between 1 and %d", iCol, pEList->nExpr);
-          sqliteSetString(&pParse->zErrMsg, zBuf, 0);
-          pParse->nErr++;
           goto select_end;
         }
       }
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index bffef93..f4e6999 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.167 2003/03/31 00:30:49 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.168 2003/03/31 02:12:48 drh Exp $
 */
 #include "config.h"
 #include "sqlite.h"
@@ -961,6 +961,7 @@
 char *sqliteMPrintf(const char *,...);
 void sqliteSetString(char **, const char *, ...);
 void sqliteSetNString(char **, ...);
+void sqliteErrorMsg(Parse*, const char*, ...);
 void sqliteDequote(char*);
 int sqliteKeywordCode(const char*, int);
 int sqliteRunParser(Parse*, const char*, char **);
diff --git a/src/trigger.c b/src/trigger.c
index 7e48e4d..ec95daa 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -66,33 +66,29 @@
     goto trigger_cleanup;
   }
   if( tab->iDb>=2 ){
-    sqliteSetString(&pParse->zErrMsg, "triggers may not be added to "
-       "auxiliary database \"", db->aDb[tab->iDb].zName, "\"", 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, "triggers may not be added to auxiliary "
+       "database %s", db->aDb[tab->iDb].zName);
     goto trigger_cleanup;
   }
 
   zName = sqliteStrNDup(pName->z, pName->n);
   if( sqliteHashFind(&(db->aDb[tab->iDb].trigHash), zName,pName->n+1) ){
-    sqliteSetNString(&pParse->zErrMsg, "trigger ", -1,
-        pName->z, pName->n, " already exists", -1, 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, "trigger %T already exists", pName);
     goto trigger_cleanup;
   }
   if( sqliteStrNICmp(tab->zName, "sqlite_", 7)==0 ){
-    sqliteSetString(&pParse->zErrMsg,"cannot create trigger on system table",0);
+    sqliteErrorMsg(pParse, "cannot create trigger on system table");
     pParse->nErr++;
     goto trigger_cleanup;
   }
   if( tab->pSelect && tr_tm != TK_INSTEAD ){
-    sqliteSetString(&pParse->zErrMsg, "cannot create ", 
-        (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", " trigger on view: ",
-        pTableName->a[0].zName, 0);
+    sqliteErrorMsg(pParse, "cannot create %s trigger on view: %S", 
+        (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0);
     goto trigger_cleanup;
   }
   if( !tab->pSelect && tr_tm == TK_INSTEAD ){
-    sqliteSetString(&pParse->zErrMsg, "cannot create INSTEAD OF", 
-        " trigger on table: ", pTableName->a[0].zName, 0);
+    sqliteErrorMsg(pParse, "cannot create INSTEAD OF"
+        " trigger on table: %S", pTableName, 0);
     goto trigger_cleanup;
   }
 #ifndef SQLITE_OMIT_AUTHORIZATION
@@ -362,14 +358,13 @@
     if( pTrigger ) break;
   }
   if( !pTrigger ){
-    sqliteSetString(&pParse->zErrMsg, "no such trigger: ", zName, 0);
+    sqliteErrorMsg(pParse, "no such trigger: %S", pName, 0);
     goto drop_trigger_cleanup;
   }
   assert( pTrigger->iDb>=0 && pTrigger->iDb<db->nDb );
   if( pTrigger->iDb>=2 ){
-    sqliteSetString(&pParse->zErrMsg, "triggers may not be removed from "
-       "auxiliary database \"", db->aDb[pTrigger->iDb].zName, "\"", 0);
-    pParse->nErr++;
+    sqliteErrorMsg(pParse, "triggers may not be removed from "
+       "auxiliary database %s", db->aDb[pTrigger->iDb].zName);
     goto drop_trigger_cleanup;
   }
   pTable = sqliteFindTable(db, pTrigger->table, db->aDb[pTrigger->iDb].zName);
@@ -764,9 +759,7 @@
         }
       }
       if( jj>=pTab->nCol ){
-        sqliteSetString(&pParse->zErrMsg, "no such column: ", 
-            pChanges->a[ii].zName, 0);
-        pParse->nErr++;
+        sqliteErrorMsg(pParse, "no such column: %s", pChanges->a[ii].zName);
         goto trigger_cleanup;
       }
     }
diff --git a/src/update.c b/src/update.c
index fb06030..58ff54b 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.57 2003/03/27 13:50:00 drh Exp $
+** $Id: update.c,v 1.58 2003/03/31 02:12:48 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -140,9 +140,7 @@
       }
     }
     if( j>=pTab->nCol ){
-      sqliteSetString(&pParse->zErrMsg, "no such column: ", 
-         pChanges->a[i].zName, 0);
-      pParse->nErr++;
+      sqliteErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName);
       goto update_cleanup;
     }
 #ifndef SQLITE_OMIT_AUTHORIZATION
diff --git a/src/util.c b/src/util.c
index 8231e98..87e5f44 100644
--- a/src/util.c
+++ b/src/util.c
@@ -14,7 +14,7 @@
 ** This file contains functions for allocating memory, comparing
 ** strings, and stuff like that.
 **
-** $Id: util.c,v 1.58 2003/02/16 22:21:32 drh Exp $
+** $Id: util.c,v 1.59 2003/03/31 02:12:48 drh Exp $
 */
 #include "sqliteInt.h"
 #include <stdarg.h>
@@ -393,6 +393,134 @@
 }
 
 /*
+** Add an error message to pParse->zErrMsg and increment pParse->nErr.
+** The following formatting characters are allowed:
+**
+**      %s      Insert a string
+**      %z      A string that should be freed after use
+**      %d      Insert an integer
+**      %T      Insert a token
+**      %S      Insert the first element of a SrcList
+*/
+void sqliteErrorMsg(Parse *pParse, const char *zFormat, ...){
+  va_list ap;
+  int nByte;
+  int i, j;
+  char *z;
+  static char zNull[] = "NULL";
+
+  pParse->nErr++;
+  nByte = 1 + strlen(zFormat);
+  va_start(ap, zFormat);
+  for(i=0; zFormat[i]; i++){
+    if( zFormat[i]!='%' && zFormat[i+1] ) continue;
+    i++;
+    switch( zFormat[i] ){
+      case 'd': {
+        (void)va_arg(ap, int);
+        nByte += 20;
+        break;
+      }
+      case 'z':
+      case 's': {
+        char *z2 = va_arg(ap, char*);
+        if( z2==0 ) z2 = zNull;
+        nByte += strlen(z2);
+        break;
+      }
+      case 'T': {
+        Token *p = va_arg(ap, Token*);
+        nByte += p->n;
+        break;
+      }
+      case 'S': {
+        SrcList *p = va_arg(ap, SrcList*);
+        int k = va_arg(ap, int);
+        assert( p->nSrc>k && k>=0 );
+        nByte += strlen(p->a[k].zName);
+        if( p->a[k].zDatabase && p->a[k].zDatabase[0] ){
+          nByte += strlen(p->a[k].zDatabase)+1;
+        }
+        break;
+      }
+      default: {
+        nByte++;
+        break;
+      }
+    }
+  }
+  va_end(ap);
+  z = sqliteMalloc( nByte );
+  if( z==0 ) return;
+  sqliteFree(pParse->zErrMsg);
+  pParse->zErrMsg = z;
+  va_start(ap, zFormat);
+  for(i=j=0; zFormat[i]; i++){
+    if( zFormat[i]!='%' ) continue;
+    if( i>j ){
+      memcpy(z, &zFormat[j], i-j);
+      z += i-j;
+    }
+    j = i+2;
+    i++;
+    switch( zFormat[i] ){
+      case 'd': {
+        int x = va_arg(ap, int);
+        sprintf(z, "%d", x);
+        z += strlen(z);
+        break;
+      }
+      case 'z':
+      case 's': {
+        int len;
+        char *z2 = va_arg(ap, char*);
+        if( z2==0 ) z2 = zNull;
+        len = strlen(z2);
+        memcpy(z, z2, len);
+        z += len;
+        if( zFormat[i]=='z' && z2!=zNull ){
+          sqliteFree(z2);
+        }
+        break;
+      }
+      case 'T': {
+        Token *p = va_arg(ap, Token*);
+        memcpy(z, p->z, p->n);
+        z += p->n;
+        break;
+      }
+      case 'S': {
+        int len;
+        SrcList *p = va_arg(ap, SrcList*);
+        int k = va_arg(ap, int);
+        assert( p->nSrc>k && k>=0 );
+        if( p->a[k].zDatabase && p->a[k].zDatabase[0] ){
+          len = strlen(p->a[k].zDatabase);
+          memcpy(z, p->a[k].zDatabase, len);
+          z += len;
+          *(z++) = '.';
+        }
+        len = strlen(p->a[k].zName);
+        memcpy(z, p->a[k].zName, len);
+        z += len;
+        break;
+      }
+      default: {
+        *(z++) = zFormat[i];
+        break;
+      }
+    }
+  }
+  va_end(ap);
+  if( i>j ){
+    memcpy(z, &zFormat[j], i-j);
+    z += i-j;
+  }
+  assert( (z - pParse->zErrMsg) < nByte );
+  *z = 0;
+}
+
+/*
 ** Convert an SQL-style quoted string into a normal string by removing
 ** the quote characters.  The conversion is done in-place.  If the
 ** input does not begin with a quote character, then this routine