]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the sqlite3_error_offset() interface. Use it in the CLI to provide
authordrh <>
Fri, 24 Dec 2021 20:22:13 +0000 (20:22 +0000)
committerdrh <>
Fri, 24 Dec 2021 20:22:13 +0000 (20:22 +0000)
better context for error messages.

FossilOrigin-Name: b518ce77439852759bc0901071f36d622b1314c9bf3d29c279dfcc405188b975

manifest
manifest.uuid
src/loadext.c
src/main.c
src/printf.c
src/shell.c.in
src/sqlite.h.in
src/sqlite3ext.h
src/sqliteInt.h
src/test1.c
src/util.c

index a6c925887db454302280744600f293dd99d34f6f..086b4676d3101b79ba9a9f3ee1d3d3e43f084c99 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sminor\sproblem\sin\sthe\sCLI\sintroduced\sby\s[d156123885abe6bf],\sapparently.
-D 2021-12-24T19:44:11.241
+C Add\sthe\ssqlite3_error_offset()\sinterface.\s\sUse\sit\sin\sthe\sCLI\sto\sprovide\nbetter\scontext\sfor\serror\smessages.
+D 2021-12-24T20:22:13.535
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -513,8 +513,8 @@ F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
 F src/insert.c e0293a6f686e18cb2c9dd0619a731518e0109d7e1f1db1932974659e7843cfd1
 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
-F src/loadext.c e1dcff1c916bf6834e150b492eddda5d9792453182d2ad64294d2266b6e93c4c
-F src/main.c 674a0fdfc2808e1d5a78b2eefe2ec3f93428cf82f0f6c013d577df1a1caa5940
+F src/loadext.c 95db1fe62c5973f1c5d9c53f6083e21a73ece14cdd47eeca0639691332e85c4d
+F src/main.c d7719ea81cc8ea81253cb2af0603d089407ea70dd5742f53a81ee3c168186344
 F src/malloc.c ef796bcc0e81d845d59a469f1cf235056caf9024172fd524e32136e65593647b
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
@@ -546,21 +546,21 @@ F src/pcache1.c 54881292a9a5db202b2c0ac541c5e3ef9a5e8c4f1c1383adb2601d5499a60e65
 F src/pragma.c c536665ce8431c8b1efbf7e0a5c01852f49f7bf28f1954f8118b2d28e4a3797f
 F src/pragma.h 87330ed2fbfa2a1274de93ca0ab850fba336189228cb256089202c3b52766fad
 F src/prepare.c 40961a1170a4c4151a90dae29dd00fc6c155f1af8246abeeeb8f0a10b3fb9719
-F src/printf.c 9565aeb5af5376fd23c993b8da1ac37008fad65435a703316eef9f41229f702d
+F src/printf.c 975f1f5417f2526365b6e6d7f22332e3e11806dad844701d92846292b654ba9a
 F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
 F src/resolve.c 6144db65157ff96fe27e3e6f784ab331b3de35db233cd0a6e93278a5d23fcf06
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c a7a3d9f54eb24821ec5f67f2e5589b68a5d42d46fc5849d7376886777d93a85a
-F src/shell.c.in 1b567fc5463470fb22f3e45a5dbf05ee2ce9dd9066b2973c4a2bdd7daa74bb5f
-F src/sqlite.h.in 5124aa99d3eb9b6a4e5030cb035076ed060d2dac020cd8fc8fba3022725d54af
+F src/shell.c.in c2f60a11f4a0720cd952d14ec20331543fb185a982e10a2385c5715e129369fd
+F src/sqlite.h.in a5e0d6bd47e67aabf1475986d36bdcc7bfa9e06566790ebf8e3aa7fa551c9f99
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
-F src/sqlite3ext.h 8ff2fd2c166150b2e48639f5e506fb44e29f1a3f65031710b9e89d1c126ac839
-F src/sqliteInt.h e7dd69a85c53461c937d50c97f8efd9761b14f9bcfb63450809e85c100c08538
+F src/sqlite3ext.h 01eb85e4f2759a5ee79c183f4b2877889d4ffdc49d27ae74529c9579e3c8c0ef
+F src/sqliteInt.h 418dd1a178abcc40b86b39cee972083822b17e84888fe56ed7a3b00b473d8bb5
 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
 F src/tclsqlite.c 48f291e1a7e672a7204884d4c164a8ed3a522ff087c361ada2991f5d54e987f6
-F src/test1.c 37b323d74aef3156394478bd09dff855332077e537b37c9a7e455c03cb04da09
+F src/test1.c c24fe3a13ff78f5cbc29ac7049123e03ee1f33c6601646a45766c5f8d5db7f92
 F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
 F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
 F src/test4.c 7c4420e01c577b5c4add2cb03119743b1a357543d347773b9e717195ea967159
@@ -620,7 +620,7 @@ F src/trigger.c 2ef56f0b7b75349a5557d0604b475126329c2e1a02432e7d49c4c710613e8254
 F src/update.c d6f5c7b9e072660757ac7d58175aca11c07cb95ebbb297ae7f38853700f52328
 F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
-F src/util.c 569349b0bddcbfbc661856f446adb92e1b0a47b3cbef548da9fc5aa639d7964c
+F src/util.c 89e51820bcb468ff3877a8d942f5cc807208087f021227e0927693e928a195bc
 F src/vacuum.c 6c38ddc52f0619865c91dae9c441d4d48bf3040d7dc1bc5b22da1e45547ed0b3
 F src/vdbe.c b63594839cbf770a29e2b3b81570971a3d2c1c995c0586aade09bb548142113a
 F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
@@ -1934,8 +1934,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 8af8c153f8c3fe16db1c2280571e005838c4ea3c48929ad7660a1686e49ed255
-R b3820a24a997ecbc0d96c676f9253e0d
+P 37e6e10f4364f556c7503c80408fc62895cdccdd0372fb2b63aaca02c3a1ee30
+R dc8a0d1713a6a184c2032656b8115b23
+T *branch * improved-error-context
+T *sym-improved-error-context *
+T -sym-trunk *
 U drh
-Z 32815daf40ab0d51da0a9e14b1eae912
+Z 3c3faf346f54edef0799304c35a23ad2
 # Remove this line to create a well-formed Fossil manifest.
index b59febd5a351576ec2d99879c845f2a7dc162625..4479c72102c2289035bafffdb8a7d17ac0233208 100644 (file)
@@ -1 +1 @@
-37e6e10f4364f556c7503c80408fc62895cdccdd0372fb2b63aaca02c3a1ee30
\ No newline at end of file
+b518ce77439852759bc0901071f36d622b1314c9bf3d29c279dfcc405188b975
\ No newline at end of file
index 4edefec0c911505d41f98fe58c6d4317808cd365..681e12c4e798525296e7e4e18da61cfc53849044 100644 (file)
@@ -485,6 +485,8 @@ static const sqlite3_api_routines sqlite3Apis = {
   sqlite3_total_changes64,
   /* Version 3.37.0 and later */
   sqlite3_autovacuum_pages,
+  /* Version 3.38.0 and later */
+  sqlite3_error_offset,
 };
 
 /* True if x is the directory separator character
index 1b7853f6095d4b3e31929ab8550c255a45955e9b..07c67d33d72ff3a5b94dec9baaebf5b9dc19fd86 100644 (file)
@@ -2597,6 +2597,19 @@ const char *sqlite3_errmsg(sqlite3 *db){
   return z;
 }
 
+/*
+** Return the byte offset of the most recent error
+*/
+int sqlite3_error_offset(sqlite3 *db){
+  int iOffset = -1;
+  if( db && sqlite3SafetyCheckSickOrOk(db) ){
+    sqlite3_mutex_enter(db->mutex);
+    iOffset = db->errByteOffset;
+    sqlite3_mutex_leave(db->mutex);
+  }
+  return iOffset;
+}
+
 #ifndef SQLITE_OMIT_UTF16
 /*
 ** Return UTF-16 encoded English language explanation of the most recent
index 6128e5cb4c69afcf68764669777e1611c0efc720..9fea2d6ef0cea9c22491c15dd67b48038d897c76 100644 (file)
@@ -855,6 +855,7 @@ void sqlite3_str_vappendf(
         assert( bArgList==0 );
         if( pToken && pToken->n ){
           sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n);
+          sqlite3RecordErrorByteOffset(pAccum->db, pToken->z);
         }
         length = width = 0;
         break;
@@ -909,6 +910,30 @@ void sqlite3_str_vappendf(
   }/* End for loop over the format string */
 } /* End of function */
 
+
+/*
+** The z string points to the first character of a token that is
+** associated with an error.  If db does not already have an error
+** byte offset recorded, try to compute the error byte offset for
+** z and set the error byte offset in db.
+*/
+void sqlite3RecordErrorByteOffset(sqlite3 *db, const char *z){
+  const Parse *pParse;
+  const char *zText;
+  const char *zEnd;
+  assert( z!=0 );
+  if( NEVER(db==0) ) return;
+  if( db->errByteOffset!=(-2) ) return;
+  pParse = db->pParse;
+  if( NEVER(pParse==0) ) return;
+  zText =pParse->zTail;
+  if( NEVER(zText==0) ) return;
+  zEnd = &zText[strlen(zText)];
+  if( SQLITE_WITHIN(z,zText,zEnd) ){
+    db->errByteOffset = (int)(z-zText);
+  }
+}
+
 /*
 ** Enlarge the memory allocation on a StrAccum object so that it is
 ** able to accept at least N more bytes of text.
index c6f623f5b7ef27533c4308fef96c841817b04231..c8d769256082d494f69b059c8bfcd36c96915867 100644 (file)
@@ -2519,6 +2519,47 @@ static void set_table_name(ShellState *p, const char *zName){
   z[n] = 0;
 }
 
+/*
+** Maybe construct two lines of text that point out the position of a
+** syntax error.  Return a pointer to the text, in memory obtained from
+** sqlite3_malloc().  Or, if the most recent error does not involve a
+** specific token that we can point to, return an empty string.
+**
+** In all cases, the memory returned is obtained from sqlite3_malloc64()
+** and should be released by the caller invoking sqlite3_free().
+*/
+static char *shell_error_context(const char *zSql, sqlite3 *db){
+  int iOffset;
+  size_t len;
+  char *zCode;
+  char *zMsg;
+  int i;
+  if( db==0
+   || zSql==0
+   || (iOffset = sqlite3_error_offset(db))<0
+  ){
+    return sqlite3_mprintf("");
+  }
+  while( iOffset>50 ){
+    iOffset--;
+    zSql++;
+    while( (zSql[0]&0xc0)==0x80 ){ zSql++; iOffset--; }
+  }
+  len = strlen(zSql);
+  if( len>78 ){
+    len = 78;
+    while( (zSql[len]&0xc0)==0x80 ) len--;
+  }
+  zCode = sqlite3_mprintf("%.*s", len, zSql);
+  for(i=0; zCode[i]; i++){ if( IsSpace(zSql[i]) ) zCode[i] = ' '; }
+  if( iOffset<25 ){
+    zMsg = sqlite3_mprintf("\n  %z\n  %*s^--- error here", zCode, iOffset, "");
+  }else{
+    zMsg = sqlite3_mprintf("\n  %z\n  %*serror here ---^", zCode, iOffset-14, "");
+  }
+  return zMsg;
+}
+
 
 /*
 ** Execute a query statement that will generate SQL output.  Print
@@ -2541,8 +2582,10 @@ static int run_table_dump_query(
   const char *z;
   rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0);
   if( rc!=SQLITE_OK || !pSelect ){
-    utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n", rc,
-                sqlite3_errmsg(p->db));
+    char *zContext = shell_error_context(zSelect, p->db);
+    utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n%s", rc,
+                sqlite3_errmsg(p->db), zContext);
+    sqlite3_free(zContext);
     if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
     return rc;
   }
@@ -2578,12 +2621,16 @@ static int run_table_dump_query(
 static char *save_err_msg(
   sqlite3 *db,           /* Database to query */
   const char *zWhen,     /* Qualifier (format) wrapper */
-  int rc                 /* Error code returned from API */
+  int rc,                /* Error code returned from API */
+  const char *zSql       /* SQL string, or NULL */
 ){
   char *zErr;
-  if( zWhen==0 ) zWhen = "%s (%d)";
-  zErr = sqlite3_mprintf(zWhen, sqlite3_errmsg(db), rc);
+  char *zContext;
+  if( zWhen==0 ) zWhen = "%s (%d)%s";
+  zContext = shell_error_context(zSql, db);
+  zErr = sqlite3_mprintf(zWhen, sqlite3_errmsg(db), rc, zContext);
   shell_check_oom(zErr);
+  sqlite3_free(zContext);
   return zErr;
 }
 
@@ -3518,7 +3565,7 @@ static int shell_exec(
     rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
     if( SQLITE_OK != rc ){
       if( pzErrMsg ){
-        *pzErrMsg = save_err_msg(db, "in prepare, %s (%d)", rc);
+        *pzErrMsg = save_err_msg(db, "in prepare, %s (%d)%s", rc, zSql);
       }
     }else{
       if( !pStmt ){
@@ -3634,7 +3681,7 @@ static int shell_exec(
         zSql = zLeftover;
         while( IsSpace(zSql[0]) ) zSql++;
       }else if( pzErrMsg ){
-        *pzErrMsg = save_err_msg(db, "stepping, %s (%d)", rc);
+        *pzErrMsg = save_err_msg(db, "stepping, %s (%d)", rc, 0);
       }
 
       /* clear saved stmt handle */
index 3c92503a1130e175440bc3a5804b8107315685cc..3aad3690a25be87815504fecd659228b57894d8f 100644 (file)
@@ -3824,13 +3824,14 @@ void sqlite3_free_filename(char*);
 ** sqlite3_extended_errcode() might change with each API call.
 ** Except, there are some interfaces that are guaranteed to never
 ** change the value of the error code.  The error-code preserving
-** interfaces are:
+** interfaces include the following:
 **
 ** <ul>
 ** <li> sqlite3_errcode()
 ** <li> sqlite3_extended_errcode()
 ** <li> sqlite3_errmsg()
 ** <li> sqlite3_errmsg16()
+** <li> sqlite3_error_offset()
 ** </ul>
 **
 ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
@@ -3845,6 +3846,13 @@ void sqlite3_free_filename(char*);
 ** ^(Memory to hold the error message string is managed internally
 ** and must not be freed by the application)^.
 **
+** ^If the most recent error references a specific token in the input
+** SQL, the sqlite3_error_offset() interface returns the byte offset
+** of the start of that token.  ^The byte offset returned by
+** sqlite3_error_offset() assumes that the input SQL is UTF8.
+** ^If the most error does not reference a specific token in the input
+** SQL, then the sqlite3_error_offset() function returns -1.
+**
 ** When the serialized [threading mode] is in use, it might be the
 ** case that a second error occurs on a separate thread in between
 ** the time of the first error and the call to these interfaces.
@@ -3864,6 +3872,7 @@ int sqlite3_extended_errcode(sqlite3 *db);
 const char *sqlite3_errmsg(sqlite3*);
 const void *sqlite3_errmsg16(sqlite3*);
 const char *sqlite3_errstr(int);
+int sqlite3_error_offset(sqlite3 *db);
 
 /*
 ** CAPI3REF: Prepared Statement Object
index 9767daa01dac2110d428096dd9e8fa1be445ad2a..5b8234622733d6fef62c9f54bd264f4d06f56c12 100644 (file)
@@ -344,6 +344,8 @@ struct sqlite3_api_routines {
   int (*autovacuum_pages)(sqlite3*,
      unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
      void*, void(*)(void*));
+  /* Version 3.38.0 and later */
+  int (*error_offset)(sqlite3*);
 };
 
 /*
@@ -655,6 +657,8 @@ typedef int (*sqlite3_loadext_entry)(
 #define sqlite3_total_changes64        sqlite3_api->total_changes64
 /* Version 3.37.0 and later */
 #define sqlite3_autovacuum_pages       sqlite3_api->autovacuum_pages
+/* Version 3.38.0 and later */
+#define sqlite3_error_offset           sqlite3_api->error_offset
 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
 
 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
index 48a94a7b33922419a989c9b369372f51b705591b..092bbccdc6b452acddbc0253165d24ce006dd1dd 100644 (file)
@@ -1527,6 +1527,7 @@ struct sqlite3 {
   u32 nSchemaLock;              /* Do not reset the schema when non-zero */
   unsigned int openFlags;       /* Flags passed to sqlite3_vfs.xOpen() */
   int errCode;                  /* Most recent error code (SQLITE_*) */
+  int errByteOffset;            /* Byte offset of error in SQL statement */
   int errMask;                  /* & result codes with this before returning */
   int iSysErrno;                /* Errno value from last system error */
   u32 dbOptFlags;               /* Flags to enable/disable optimizations */
@@ -4977,6 +4978,7 @@ void sqlite3StrAccumSetError(StrAccum*, u8);
 void sqlite3ResultStrAccum(sqlite3_context*,StrAccum*);
 void sqlite3SelectDestInit(SelectDest*,int,int);
 Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
+void sqlite3RecordErrorByteOffset(sqlite3*,const char*);
 
 void sqlite3BackupRestart(sqlite3_backup *);
 void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
index 8cdb7b285e391244bc6a28bf6872dfb763871ed5..84d888ff37f58c6ce655da45934aa2d21b4a551d 100644 (file)
@@ -4366,6 +4366,34 @@ static int SQLITE_TCLAPI test_errmsg(
   return TCL_OK;
 }
 
+
+/*
+** Usage:   sqlite3_error_offset DB
+**
+** Return the byte offset into the input UTF8 SQL for the most recent
+** error, or -1 of the error does not refer to a specific token.
+*/
+static int SQLITE_TCLAPI test_error_offset(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  sqlite3 *db;
+  int iByteOffset;
+
+  if( objc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", 
+       Tcl_GetString(objv[0]), " DB", 0);
+    return TCL_ERROR;
+  }
+  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+
+  iByteOffset = sqlite3_error_offset(db);
+  Tcl_SetObjResult(interp, Tcl_NewIntObj(zErr, -1));
+  return TCL_OK;
+}
+
 /*
 ** Usage:   test_errmsg16 DB
 **
@@ -8422,6 +8450,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
      { "sqlite3_errcode",               test_errcode       ,0 },
      { "sqlite3_extended_errcode",      test_ex_errcode    ,0 },
      { "sqlite3_errmsg",                test_errmsg        ,0 },
+     { "sqlite3_error_offset",          test_error_offset  ,0 },
      { "sqlite3_errmsg16",              test_errmsg16      ,0 },
      { "sqlite3_open",                  test_open          ,0 },
      { "sqlite3_open16",                test_open16        ,0 },
index 8ea951fa1615df202a84366f4680f86a859296a6..dec205df4301ef03be57ed7c0737e3aea3cc52d5 100644 (file)
@@ -117,7 +117,11 @@ static SQLITE_NOINLINE void  sqlite3ErrorFinish(sqlite3 *db, int err_code){
 void sqlite3Error(sqlite3 *db, int err_code){
   assert( db!=0 );
   db->errCode = err_code;
-  if( err_code || db->pErr ) sqlite3ErrorFinish(db, err_code);
+  if( err_code || db->pErr ){
+    sqlite3ErrorFinish(db, err_code);
+  }else{
+    db->errByteOffset = -1;
+  }
 }
 
 /*
@@ -127,6 +131,7 @@ void sqlite3Error(sqlite3 *db, int err_code){
 void sqlite3ErrorClear(sqlite3 *db){
   assert( db!=0 );
   db->errCode = SQLITE_OK;
+  db->errByteOffset = -1;
   if( db->pErr ) sqlite3ValueSetNull(db->pErr);
 }
 
@@ -147,17 +152,8 @@ void sqlite3SystemError(sqlite3 *db, int rc){
 ** handle "db". The error code is set to "err_code".
 **
 ** If it is not NULL, string zFormat specifies the format of the
-** error string in the style of the printf functions: The following
-** format 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
-**
-** zFormat and any string tokens that follow it are assumed to be
-** encoded in UTF-8.
+** error string.  zFormat and any string tokens that follow it are
+** assumed to be encoded in UTF-8.
 **
 ** To clear the most recent error for sqlite handle "db", sqlite3Error
 ** should be called with err_code set to SQLITE_OK and zFormat set
@@ -181,13 +177,6 @@ void sqlite3ErrorWithMsg(sqlite3 *db, int err_code, const char *zFormat, ...){
 
 /*
 ** 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
 **
 ** This function should be used to report any error that occurs while
 ** compiling an SQL statement (i.e. within sqlite3_prepare()). The
@@ -200,9 +189,11 @@ void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
   char *zMsg;
   va_list ap;
   sqlite3 *db = pParse->db;
+  db->errByteOffset = -2;
   va_start(ap, zFormat);
   zMsg = sqlite3VMPrintf(db, zFormat, ap);
   va_end(ap);
+  if( db->errByteOffset<-1 ) db->errByteOffset = -1;
   if( db->suppressErr ){
     sqlite3DbFree(db, zMsg);
   }else{