]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Version 2.0.6 (CVS 291)
authordrh <drh@noemail.net>
Fri, 19 Oct 2001 16:44:56 +0000 (16:44 +0000)
committerdrh <drh@noemail.net>
Fri, 19 Oct 2001 16:44:56 +0000 (16:44 +0000)
FossilOrigin-Name: 8467d84fc6e67bd93051f54338a8f6c9b1711ee1

16 files changed:
VERSION
manifest
manifest.uuid
src/build.c
src/select.c
src/shell.c
src/sqliteInt.h
src/table.c
src/tclsqlite.c
src/tokenize.c
src/vdbe.c
src/vdbe.h
test/select1.test
www/c_interface.tcl
www/changes.tcl
www/lang.tcl

diff --git a/VERSION b/VERSION
index e01025862f79350522cba47477d9bb6c51729e12..157e54f3e4d5b4431fcdec18168b6365937a0a7b 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.5
+2.0.6
index 0be6cb473e39e172872223f4a6a6209fd0b2cd68..a51e29b2f370a176c498a3d6a27c9f679e39bde8 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,9 +1,9 @@
-C Support\sfor\sUTF-8\sand\sISO8859\scharacters\sin\sidentifiers.\s\sBug\sfix\sin\nthe\scolumn\sname\sgenerator\sfor\sselects\s(was\scoreing).\s(CVS\s290)
-D 2001-10-18T12:34:47
+C Version\s2.0.6\s(CVS\s291)
+D 2001-10-19T16:44:56
 F Makefile.in 6801df952cb1df64aa32e4de85fed24511d28efd
 F Makefile.template 1fdb891f14083ee0b63cf7282f91529634438e7a
 F README 93d2977cc5c6595c448de16bdefc312b9d401533
-F VERSION e14d2010c343ae28a0dd038c3850eae3a88a9307
+F VERSION be4a9c5c382f200a4575c3dc5133f6359a33606a
 F aclocal.m4 11faa843caa38fd451bc6aeb43e248d1723a269d
 F config.guess f38b1e93d1e0fa6f5a6913e9e7b12774b9232588
 F config.log 6a73d03433669b10a3f0c221198c3f26b9413914
@@ -21,7 +21,7 @@ F publish.sh badcd69b8e3a8bc69b162c4c9d7c209b2a0b119e
 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
 F src/btree.c 97653e88bc4b7396226b93c878b153c77f1d3d03
 F src/btree.h 57d653ef5137b91f2a068aaf71a2905468dd2cb7
-F src/build.c fe71d516148226bd6249403e82f8d07129206489
+F src/build.c d18081e69b23390cb6baaaf6f6c804b93775a0be
 F src/delete.c 6fe2191c49c4a31336e2fac11b3ad665ddcd4246
 F src/expr.c c1381b8229a5573b0928ede962e45c1c49d067af
 F src/hash.c b7ced0735287c142a3b2db46c3cae3e6826afb75
@@ -36,21 +36,21 @@ F src/pager.h a0d4c5ae271914aa07b62aee0707997d6932b6ca
 F src/parse.y 148e4cd134d3cbd816dcb0df50e49e498faa6ba4
 F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9
 F src/random.c 2a9cc2c9716d14815fd4c2accf89d87a1143e46b
-F src/select.c 0e8089c5ae84fa3eb7e64b40350832983918e7a4
-F src/shell.c cb8c41f1b2173efd212dab3f35f1fc6bf32ead76
+F src/select.c 10957c2fd52ee36fbb40171c36377c9f94dd36ea
+F src/shell.c 71597951753b56a97fea1c7a30908f31e635c00c
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in b95c161abf1d58bceb05290fa3f657d8f388fc11
-F src/sqliteInt.h acfd52eb2949abb847b1be93687e93e3663231b2
-F src/table.c abd0adbe0fee39d995287b3bcccd908d174dfcac
-F src/tclsqlite.c 0b947866c89fe5b683fc86e8419b7f8da3cebf7d
+F src/sqliteInt.h 52577abf2805ba148972f69788ed49c64064fa31
+F src/table.c 4e8cbabfbc6018fdd0615f7c5d06fb431cd56505
+F src/tclsqlite.c 7d205aeda449047f86b39a6c55731a1ded7a7ab5
 F src/test1.c e4b31f62ea71963cbae44338acf477a04fc8fc49
 F src/test2.c e9f99aa5ee73872819259d6612c11e55e1644321
 F src/test3.c 4a0d7b882fdae731dbb759f512ad867122452f96
-F src/tokenize.c 2b95e67bcb5f68e7fe4a5a426fcbe0891d7aaa54
+F src/tokenize.c 59ddae1501de472e9a6274a1cbf451170c52488c
 F src/update.c c916182c6bfbc8a6f20c24920c4560fece6c9569
 F src/util.c 4da3be37d0fd3c640d2d3033503768afdc8e5387
-F src/vdbe.c 01617df84381c3ace10feb370b8d1f72f275c1ab
-F src/vdbe.h 86fc2ef42f48024c9a2e1b7fb01eda22b65a5295
+F src/vdbe.c e326ff611c9d2c273fdcb86b4661b464f2af128b
+F src/vdbe.h f8407fd6b644bc001b1e7c65460c9962f6a15f6b
 F src/where.c 22fe910c7c8e2736eb37e9861343e90c0b513c86
 F test/all.test a2320eb40b462f25bd3e33115b1cabf3791450dd
 F test/bigrow.test a35f2de9948b24e427fb292c35947795efe182d0
@@ -74,7 +74,7 @@ F test/printf.test 3cb415073754cb8ff076f26173143c3cd293a9da
 F test/quick.test b6ec50f808efc06595fd324bf4f3fabadb9c7e9c
 F test/quote.test 286db944717afa9a9bf829dd85e59185c65d5435
 F test/rowid.test 427bfbbe9684fe7a2f851aa05badaae6d4972ce8
-F test/select1.test 129c0188565383c3d22858161b96fefd3f7fa09f
+F test/select1.test 75af194669ff9f4fe42c6fd070d9ec3b268354bb
 F test/select2.test f91c903e2bab0e9d45274855a981eebf846d5e32
 F test/select3.test 5e1fe8e5a4e63fb2827ab3b89527e0fd4ae35259
 F test/select4.test 29a2ffb187f3d8b6ca42a0a6b619e9cabe12e228
@@ -101,20 +101,20 @@ F tool/report1.txt 9eae07f26a8fc53889b45fc833a66a33daa22816
 F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf
 F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4
 F www/arch.tcl 03b521d252575f93b9c52f7c8b0007011512fcfb
-F www/c_interface.tcl a59ee0835d1b33fcddab7d4fd65cf9e50f7d2dc7
-F www/changes.tcl 5ff43653f6387ce7b6e0a8aa384855b3f3b76550
+F www/c_interface.tcl 6c5989670e014de44dce6580cbde0eea965dadbb
+F www/changes.tcl 7f249321a6ccb4abb79e56b801dede791a44c237
 F www/crosscompile.tcl c99efacb3aefaa550c6e80d91b240f55eb9fd33e
 F www/download.tcl 3e51c9ff1326b0a182846134987301310dff7d60
 F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c
 F www/index.tcl 68c815d64b35b2dcc4d4f6845827df71c6869f9f
-F www/lang.tcl 37d4a44bdafe42a270b9e28554651278086ce806
+F www/lang.tcl 1899ec4fb77cd69de335ebd998253de869f34d8e
 F www/mingw.tcl fc5f4ba9d336b6e8c97347cc6496d6162461ef60
 F www/opcode.tcl 4365ad9798872491dbd7d3071510ebe461785ac3
 F www/speed.tcl ab7d6d3bc898472bd94320a5d3c63de928d4804b
 F www/sqlite.tcl 6a21242a272e9c0939a04419a51c3d50cae33e3e
 F www/tclsqlite.tcl 13d50723f583888fc80ae1a38247c0ab415066fa
 F www/vdbe.tcl bb7d620995f0a987293e9d4fb6185a3b077e9b44
-P e2d84f71ed39cbb75884205521aa9e316ab3b56c
-R 3132f2509f739a21413aa9b99beee504
+P 22948fc685299ca888907eea68edb8a6e87c3f49
+R 517debf090e30b01a34d90ad2b9ba498
 U drh
-Z 7eb9951a3a4ed42d9ed165321f9fc396
+Z 0b94093f141bb1ed6e93360cffc751d6
index da1b629dd9013709030bde6799ab3021fd83dce6..212e4ba1b7fd1a4b0ea555ba7e29f10e7b1b2a91 100644 (file)
@@ -1 +1 @@
-22948fc685299ca888907eea68edb8a6e87c3f49
\ No newline at end of file
+8467d84fc6e67bd93051f54338a8f6c9b1711ee1
\ No newline at end of file
index fc45a3441e9d1351f734a92f7f8c6a5531ee403c..a87a74b4426799dff424ddde01c6fb72c798c601 100644 (file)
@@ -25,7 +25,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.50 2001/10/15 00:44:36 drh Exp $
+** $Id: build.c,v 1.51 2001/10/19 16:44:57 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -1449,6 +1449,14 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
     }
   }else
 
+  if( sqliteStrICmp(zLeft, "empty_result_callbacks")==0 ){
+    if( getBoolean(zRight) ){
+      db->flags |= SQLITE_NullCallback;
+    }else{
+      db->flags &= ~SQLITE_NullCallback;
+    }
+  }else
+
   if( sqliteStrICmp(zLeft, "table_info")==0 ){
     Table *pTab;
     Vdbe *v;
index 49e8f02cd194f6c5c3a1a8a0c57692c263144ded..c0e8961a6a336f0ec3098abb72c7facf5bda9e0a 100644 (file)
@@ -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.41 2001/10/18 12:34:47 drh Exp $
+** $Id: select.c,v 1.42 2001/10/19 16:44:57 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -994,5 +994,13 @@ int sqliteSelect(
     generateSortTail(v, pEList->nExpr);
   }
   pParse->nTab = base;
+
+
+  /* Issue a null callback if that is what the user wants.
+  */
+  if( (pParse->db->flags & SQLITE_NullCallback)!=0 && eDest==SRT_Callback ){
+    sqliteVdbeAddOp(v, OP_NullCallback, pEList->nExpr, 0);
+  }
+
   return 0;
 }
index 726dd92619e7a3357debc4a2c4b5a2e4bc3a2ea4..4eaab29a60725304135dfda4391a1e4de70a6da1 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains code to implement the "sqlite" command line
 ** utility for accessing SQLite databases.
 **
-** $Id: shell.c,v 1.36 2001/10/06 16:33:03 drh Exp $
+** $Id: shell.c,v 1.37 2001/10/19 16:44:57 drh Exp $
 */
 #include <stdlib.h>
 #include <string.h>
@@ -244,6 +244,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
   switch( p->mode ){
     case MODE_Line: {
       int w = 5;
+      if( azArg==0 ) break;
       for(i=0; i<nArg; i++){
         int len = strlen(azCol[i]);
         if( len>w ) w = len;
@@ -266,7 +267,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
           if( w<=0 ){
             w = strlen(azCol[i] ? azCol[i] : "");
             if( w<10 ) w = 10;
-            n = strlen(azArg[i] ? azArg[i] : "");
+            n = strlen(azArg && azArg[i] ? azArg[i] : "");
             if( w<n ) w = n;
           }
           if( i<ArraySize(p->actualWidth) ){
@@ -290,6 +291,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
           }
         }
       }
+      if( azArg==0 ) break;
       for(i=0; i<nArg; i++){
         int w;
         if( i<ArraySize(p->actualWidth) ){
@@ -309,6 +311,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
           fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator);
         }
       }
+      if( azArg==0 ) break;
       for(i=0; i<nArg; i++){
         char *z = azArg[i];
         if( z==0 ) z = "";
@@ -342,6 +345,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
         }
         fprintf(p->out,"</TR>\n");
       }
+      if( azArg==0 ) break;
       fprintf(p->out,"<TR>");
       for(i=0; i<nArg; i++){
         fprintf(p->out,"<TD>");
@@ -352,6 +356,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
       break;
     }
     case MODE_Insert: {
+      if( azArg==0 ) break;
       fprintf(p->out,"INSERT INTO %s VALUES(",p->zDestTable);
       for(i=0; i<nArg; i++){
         char *zSep = i>0 ? ",": "";
@@ -365,6 +370,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
         }
       }
       fprintf(p->out,");\n");
+      break;
     }
   }      
   return 0;
index 92d01724ba15d7b5e3f1ba3008742a33d7f86dd7..8054316ece11f1c46fbc654aee03b03a3282fd24 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.63 2001/10/18 12:34:47 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.64 2001/10/19 16:44:57 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -166,6 +166,8 @@ struct sqlite {
 #define SQLITE_CountRows      0x00000040  /* Count rows changed by INSERT, */
                                           /*   DELETE, or UPDATE and return */
                                           /*   the count using a callback. */
+#define SQLITE_NullCallback   0x00000080  /* Invoke the callback once if the */
+                                          /*   result set is empty */
 
 /*
 ** Current file format version
index e27c7146014845f156aa5ea5e6070472355990ed..39480dae1999a4aab5afa4a7e4178d90e36eba90 100644 (file)
@@ -47,7 +47,7 @@ static int sqlite_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
   /* Make sure there is enough space in p->azResult to hold everything
   ** we need to remember from this invocation of the callback.
   */
-  if( p->nRow==0 ){
+  if( p->nRow==0 && argv!=0 ){
     p->nColumn = nCol;
     need = nCol*2;
   }else{
@@ -83,20 +83,22 @@ static int sqlite_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
 
   /* Copy over the row data
   */
-  for(i=0; i<nCol; i++){
-    if( argv[i]==0 ){
-      z = 0;
-    }else{
-      z = malloc( strlen(argv[i])+1 );
-      if( z==0 ){
-        p->rc = SQLITE_NOMEM;
-        return 1;
+  if( argv!=0 ){
+    for(i=0; i<nCol; i++){
+      if( argv[i]==0 ){
+        z = 0;
+      }else{
+        z = malloc( strlen(argv[i])+1 );
+        if( z==0 ){
+          p->rc = SQLITE_NOMEM;
+          return 1;
+        }
+        strcpy(z, argv[i]);
       }
-      strcpy(z, argv[i]);
+      p->azResult[p->nData++] = z;
     }
-    p->azResult[p->nData++] = z;
+    p->nRow++;
   }
-  p->nRow++;
   return 0;
 }
 
index 7893b73b5ba1fcb0673934c8fade0b124d2f01da..19e7ddb279bd1610cba1ea8a046930202dbb253e 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** A TCL Interface to SQLite
 **
-** $Id: tclsqlite.c,v 1.25 2001/10/18 12:34:47 drh Exp $
+** $Id: tclsqlite.c,v 1.26 2001/10/19 16:44:57 drh Exp $
 */
 #ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */
 
@@ -72,42 +72,44 @@ static int DbEvalCallback(
 #ifdef UTF_TRANSLATION_NEEDED
   Tcl_DString dCol;
 #endif
-  if( cbData->zArray[0] ){
-    if( cbData->once ){
-      Tcl_SetVar2(cbData->interp, cbData->zArray, "*", "", 0);
-      for(i=0; i<nCol; i++){
-        Tcl_SetVar2(cbData->interp, cbData->zArray, "*", azN[i],
-           TCL_LIST_ELEMENT|TCL_APPEND_VALUE);
-      }
-    }
+  if( azCol==0 || (cbData->once && cbData->zArray[0]) ){
+    Tcl_SetVar2(cbData->interp, cbData->zArray, "*", "", 0);
     for(i=0; i<nCol; i++){
-      char *z = azCol[i];
-      if( z==0 ) z = "";
+      Tcl_SetVar2(cbData->interp, cbData->zArray, "*", azN[i],
+         TCL_LIST_ELEMENT|TCL_APPEND_VALUE);
+    }
+    cbData->once = 0;
+  }
+  if( azCol!=0 ){
+    if( cbData->zArray[0] ){
+      for(i=0; i<nCol; i++){
+        char *z = azCol[i];
+        if( z==0 ) z = "";
 #ifdef UTF_TRANSLATION_NEEDED
-      Tcl_DStringInit(&dCol);
-      Tcl_ExternalToUtfDString(NULL, z, -1, &dCol);
-      Tcl_SetVar2(cbData->interp, cbData->zArray, azN[i], 
-            Tcl_DStringValue(&dCol), 0);
-      Tcl_DStringFree(&dCol);
+        Tcl_DStringInit(&dCol);
+        Tcl_ExternalToUtfDString(NULL, z, -1, &dCol);
+        Tcl_SetVar2(cbData->interp, cbData->zArray, azN[i], 
+              Tcl_DStringValue(&dCol), 0);
+        Tcl_DStringFree(&dCol);
 #else
-      Tcl_SetVar2(cbData->interp, cbData->zArray, azN[i], z, 0);
+        Tcl_SetVar2(cbData->interp, cbData->zArray, azN[i], z, 0);
 #endif
-    }
-  }else{
-    for(i=0; i<nCol; i++){
-      char *z = azCol[i];
-      if( z==0 ) z = "";
+      }
+    }else{
+      for(i=0; i<nCol; i++){
+        char *z = azCol[i];
+        if( z==0 ) z = "";
 #ifdef UTF_TRANSLATION_NEEDED
-      Tcl_DStringInit(&dCol);
-      Tcl_ExternalToUtfDString(NULL, z, -1, &dCol);
-      Tcl_SetVar(cbData->interp, azN[i], Tcl_DStringValue(&dCol), 0);
-      Tcl_DStringFree(&dCol);
+        Tcl_DStringInit(&dCol);
+        Tcl_ExternalToUtfDString(NULL, z, -1, &dCol);
+        Tcl_SetVar(cbData->interp, azN[i], Tcl_DStringValue(&dCol), 0);
+        Tcl_DStringFree(&dCol);
 #else
-      Tcl_SetVar(cbData->interp, azN[i], z, 0);
+        Tcl_SetVar(cbData->interp, azN[i], z, 0);
 #endif
+      }
     }
   }
-  cbData->once = 0;
   rc = Tcl_EvalObj(cbData->interp, cbData->pCode);
   if( rc==TCL_CONTINUE ) rc = TCL_OK;
   cbData->tcl_rc = rc;
@@ -128,6 +130,7 @@ static int DbEvalCallback2(
 ){
   Tcl_Obj *pList = (Tcl_Obj*)clientData;
   int i;
+  if( azCol==0 ) return 0;
   for(i=0; i<nCol; i++){
     Tcl_Obj *pElem;
     if( azCol[i] && *azCol[i] ){
index ba64ddb24890614bb75f4b72bf93c3815b2da443..944766fbdd2496c25b5ca048bf5f3c5218feb5b6 100644 (file)
@@ -15,7 +15,7 @@
 ** individual tokens and sends those tokens one-by-one over to the
 ** parser for analysis.
 **
-** $Id: tokenize.c,v 1.28 2001/10/18 12:34:48 drh Exp $
+** $Id: tokenize.c,v 1.29 2001/10/19 16:44:57 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -331,7 +331,7 @@ static int sqliteGetToken(const unsigned char *z, int *tokenType){
         break;
       }
       for(i=1; isIdChar[z[i]]; i++){}
-      *tokenType = sqliteKeywordCode(z, i);
+      *tokenType = sqliteKeywordCode((char*)z, i);
       return i;
     }
   }
index c1cdebf7a3027e0b8c750286d8101ceeb32011a7..92a5cc69e0fd15625eb6c954af0658ab977d6573 100644 (file)
@@ -30,7 +30,7 @@
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.86 2001/10/18 12:34:48 drh Exp $
+** $Id: vdbe.c,v 1.87 2001/10/19 16:44:57 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -202,6 +202,7 @@ struct Vdbe {
   int nSet;           /* Number of sets allocated */
   Set *aSet;          /* An array of sets */
   int nFetch;         /* Number of OP_Fetch instructions executed */
+  int nCallback;      /* Number of callbacks invoked so far */
 };
 
 /*
@@ -811,17 +812,17 @@ static char *zOpName[] = { 0,
   "AggGet",            "SetInsert",         "SetFound",          "SetNotFound",
   "SetClear",          "MakeRecord",        "MakeKey",           "MakeIdxKey",
   "Goto",              "If",                "Halt",              "ColumnCount",
-  "ColumnName",        "Callback",          "Integer",           "String",
-  "Null",              "Pop",               "Dup",               "Pull",
-  "Add",               "AddImm",            "Subtract",          "Multiply",
-  "Divide",            "Remainder",         "BitAnd",            "BitOr",
-  "BitNot",            "ShiftLeft",         "ShiftRight",        "AbsValue",
-  "Precision",         "Min",               "Max",               "Like",
-  "Glob",              "Eq",                "Ne",                "Lt",
-  "Le",                "Gt",                "Ge",                "IsNull",
-  "NotNull",           "Negative",          "And",               "Or",
-  "Not",               "Concat",            "Noop",              "Strlen",
-  "Substr",          
+  "ColumnName",        "Callback",          "NullCallback",      "Integer",
+  "String",            "Null",              "Pop",               "Dup",
+  "Pull",              "Add",               "AddImm",            "Subtract",
+  "Multiply",          "Divide",            "Remainder",         "BitAnd",
+  "BitOr",             "BitNot",            "ShiftLeft",         "ShiftRight",
+  "AbsValue",          "Precision",         "Min",               "Max",
+  "Like",              "Glob",              "Eq",                "Ne",
+  "Lt",                "Le",                "Gt",                "Ge",
+  "IsNull",            "NotNull",           "Negative",          "And",
+  "Or",                "Not",               "Concat",            "Noop",
+  "Strlen",            "Substr",          
 };
 
 /*
@@ -1194,6 +1195,7 @@ case OP_ColumnCount: {
   p->azColName = sqliteRealloc(p->azColName, (pOp->p1+1)*sizeof(char*));
   if( p->azColName==0 ) goto no_mem;
   p->azColName[pOp->p1] = 0;
+  p->nCallback = 0;
   break;
 }
 
@@ -1207,6 +1209,7 @@ case OP_ColumnCount: {
 */
 case OP_ColumnName: {
   p->azColName[pOp->p1] = pOp->p3 ? pOp->p3 : "";
+  p->nCallback = 0;
   break;
 }
 
@@ -1231,11 +1234,38 @@ case OP_Callback: {
     if( xCallback(pArg, pOp->p1, &zStack[i], p->azColName)!=0 ){
       rc = SQLITE_ABORT;
     }
+    p->nCallback++;
   }
   PopStack(p, pOp->p1);
   break;
 }
 
+/* Opcode: NullCallback P1 * *
+**
+** Invoke the callback function once with the 2nd argument (the
+** number of columns) equal to P1 and with the 4th argument (the
+** names of the columns) set according to prior OP_ColumnName and
+** OP_ColumnCount instructions.  This is all like the regular
+** OP_Callback or OP_SortCallback opcodes.  But the 3rd argument
+** which normally contains a pointer to an array of pointers to
+** data is NULL.
+**
+** The callback is only invoked if there have been no prior calls
+** to OP_Callback or OP_SortCallback.
+**
+** This opcode is used to report the number and names of columns
+** in cases where the result set is empty.
+*/
+case OP_NullCallback: {
+  if( xCallback!=0 && p->nCallback==0 ){
+    if( xCallback(pArg, pOp->p1, 0, p->azColName)!=0 ){
+      rc = SQLITE_ABORT;
+    }
+    p->nCallback++;
+  }
+  break;
+}
+
 /* Opcode: Concat P1 P2 P3
 **
 ** Look at the first P1 elements of the stack.  Append them all 
@@ -3376,6 +3406,7 @@ case OP_SortCallback: {
     if( xCallback(pArg, pOp->p1, (char**)zStack[i], p->azColName) ){
       rc = SQLITE_ABORT;
     }
+    p->nCallback++;
   }
   POPSTACK;
   break;
index 4a60fb68ee06a8d86793d4c83509f26712815739..7475a7eac0e76faba8eac780d3475cd33d55c726 100644 (file)
@@ -15,7 +15,7 @@
 ** or VDBE.  The VDBE implements an abstract machine that runs a
 ** simple program to access and modify the underlying database.
 **
-** $Id: vdbe.h,v 1.29 2001/10/13 02:59:09 drh Exp $
+** $Id: vdbe.h,v 1.30 2001/10/19 16:44:57 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -155,50 +155,51 @@ typedef struct VdbeOp VdbeOp;
 #define OP_ColumnCount        72
 #define OP_ColumnName         73
 #define OP_Callback           74
-
-#define OP_Integer            75
-#define OP_String             76
-#define OP_Null               77
-#define OP_Pop                78
-#define OP_Dup                79
-#define OP_Pull               80
-
-#define OP_Add                81
-#define OP_AddImm             82
-#define OP_Subtract           83
-#define OP_Multiply           84
-#define OP_Divide             85
-#define OP_Remainder          86
-#define OP_BitAnd             87
-#define OP_BitOr              88
-#define OP_BitNot             89
-#define OP_ShiftLeft          90
-#define OP_ShiftRight         91
-#define OP_AbsValue           92
-#define OP_Precision          93
-#define OP_Min                94
-#define OP_Max                95
-#define OP_Like               96
-#define OP_Glob               97
-#define OP_Eq                 98
-#define OP_Ne                 99
-#define OP_Lt                100
-#define OP_Le                101
-#define OP_Gt                102
-#define OP_Ge                103
-#define OP_IsNull            104
-#define OP_NotNull           105
-#define OP_Negative          106
-#define OP_And               107
-#define OP_Or                108
-#define OP_Not               109
-#define OP_Concat            110
-#define OP_Noop              111
-
-#define OP_Strlen            112
-#define OP_Substr            113
-
-#define OP_MAX               113
+#define OP_NullCallback       75
+
+#define OP_Integer            76
+#define OP_String             77
+#define OP_Null               78
+#define OP_Pop                79
+#define OP_Dup                80
+#define OP_Pull               81
+
+#define OP_Add                82
+#define OP_AddImm             83
+#define OP_Subtract           84
+#define OP_Multiply           85
+#define OP_Divide             86
+#define OP_Remainder          87
+#define OP_BitAnd             88
+#define OP_BitOr              89
+#define OP_BitNot             90
+#define OP_ShiftLeft          91
+#define OP_ShiftRight         92
+#define OP_AbsValue           93
+#define OP_Precision          94
+#define OP_Min                95
+#define OP_Max                96
+#define OP_Like               97
+#define OP_Glob               98
+#define OP_Eq                 99
+#define OP_Ne                100
+#define OP_Lt                101
+#define OP_Le                102
+#define OP_Gt                103
+#define OP_Ge                104
+#define OP_IsNull            105
+#define OP_NotNull           106
+#define OP_Negative          107
+#define OP_And               108
+#define OP_Or                109
+#define OP_Not               110
+#define OP_Concat            111
+#define OP_Noop              112
+
+#define OP_Strlen            113
+#define OP_Substr            114
+
+#define OP_MAX               114
 
 /*
 ** Prototypes for the VDBE interface.  See comments on the implementation
index adcee104960f3fc74d3bb73d4765c50fb77371dc..98d94fa9a3e7d962a3e60ff630a6ad1b4b8b6bb3 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the SELECT statement.
 #
-# $Id: select1.test,v 1.13 2001/10/18 12:34:48 drh Exp $
+# $Id: select1.test,v 1.14 2001/10/19 16:44:58 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -414,4 +414,36 @@ do_test select1-8.5 {
   }
 } {1 -3 1 -3}
 
+
+# Check the behavior when the result set is empty
+#
+do_test select1-9.1 {
+  catch {unset r}
+  set r(*) {}
+  db eval {SELECT * FROM test1 WHERE f1<0} r {}
+  set r(*)
+} {}
+do_test select1-9.2 {
+  execsql {PRAGMA empty_result_callbacks=on}
+  set r(*) {}
+  db eval {SELECT * FROM test1 WHERE f1<0} r {}
+  set r(*)
+} {f1 f2}
+do_test select1-9.3 {
+  set r(*) {}
+  db eval {SELECT * FROM test1 WHERE f1<(select count(*) from test2)} r {}
+  set r(*)
+} {f1 f2}
+do_test select1-9.4 {
+  set r(*) {}
+  db eval {SELECT * FROM test1 ORDER BY f1} r {}
+  set r(*)
+} {f1 f2}
+do_test select1-9.5 {
+  set r(*) {}
+  db eval {SELECT * FROM test1 WHERE f1<0 ORDER BY f1} r {}
+  set r(*)
+} {f1 f2}
+unset r
+
 finish_test
index 30d0a7d772495f8da63f68d16424f32dc51537ac..e0bb8f2776f30e5b10ee3a710dbeb177426e5c39 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Run this Tcl script to generate the sqlite.html file.
 #
-set rcsid {$Id: c_interface.tcl,v 1.17 2001/10/12 17:30:05 drh Exp $}
+set rcsid {$Id: c_interface.tcl,v 1.18 2001/10/19 16:44:58 drh Exp $}
 
 puts {<html>
 <head>
@@ -20,7 +20,7 @@ puts {
 a C or C++ program.  This document gives an overview of the C/C++
 programming interface.</p>
 
-<h2>The API</h2>
+<h2>The Core API</h2>
 
 <p>The interface to the SQLite library consists of three core functions,
 one opaque data structure, and some constants used as return values.
@@ -28,6 +28,7 @@ The core interface is as follows:</p>
 
 <blockquote><pre>
 typedef struct sqlite sqlite;
+#define SQLITE_OK           0   /* Successful result */
 
 sqlite *sqlite_open(const char *dbname, int mode, char **errmsg);
 
@@ -40,101 +41,14 @@ int sqlite_exec(
   void*,
   char **errmsg
 );
-
-#define SQLITE_OK           0   /* Successful result */
-#define SQLITE_ERROR        1   /* SQL error or missing database */
-#define SQLITE_INTERNAL     2   /* An internal logic error in SQLite */
-#define SQLITE_PERM         3   /* Access permission denied */
-#define SQLITE_ABORT        4   /* Callback routine requested an abort */
-#define SQLITE_BUSY         5   /* The database file is locked */
-#define SQLITE_LOCKED       6   /* A table in the database is locked */
-#define SQLITE_NOMEM        7   /* A malloc() failed */
-#define SQLITE_READONLY     8   /* Attempt to write a readonly database */
-#define SQLITE_INTERRUPT    9   /* Operation terminated by sqlite_interrupt() */
-#define SQLITE_IOERR       10   /* Some kind of disk I/O error occurred */
-#define SQLITE_CORRUPT     11   /* The database disk image is malformed */
-#define SQLITE_NOTFOUND    12   /* (Internal Only) Table or record not found */
-#define SQLITE_FULL        13   /* Insertion failed because database is full */
-#define SQLITE_CANTOPEN    14   /* Unable to open the database file */
-#define SQLITE_PROTOCOL    15   /* Database lock protocol error */
-#define SQLITE_EMPTY       16   /* (Internal Only) Database table is empty */
-#define SQLITE_SCHEMA      17   /* The database schema changed */
-#define SQLITE_TOOBIG      18   /* Too much data for one row of a table */
-#define SQLITE_CONSTRAINT  19   /* Abort due to contraint violation */
 </pre></blockquote>
 
-<p>Only the three core routines shown above are required to use
-SQLite.  But there are many other functions that provide 
-useful interfaces.  These extended routines are as follows:
+<p>
+The above is all you really need to know in order to use SQLite
+in your C or C++ programs.  There are other convenience functions
+available (and described below) but we will begin by describing
+the core functions shown above.
 </p>
-
-<blockquote><pre>
-int sqlite_get_table(
-  sqlite*,
-  char *sql,
-  char ***result,
-  int *nrow,
-  int *ncolumn,
-  char **errmsg
-);
-
-void sqlite_free_table(char**);
-
-void sqlite_interrupt(sqlite*);
-
-int sqlite_complete(const char *sql);
-
-void sqlite_busy_handler(sqlite*, int (*)(void*,const char*,int), void*);
-
-void sqlite_busy_timeout(sqlite*, int ms);
-
-const char sqlite_version[];
-
-const char sqlite_encoding[];
-
-int sqlite_exec_printf(
-  sqlite*,
-  char *sql,
-  int (*)(void*,int,char**,char**),
-  void*,
-  char **errmsg,
-  ...
-);
-
-int sqlite_exec_vprintf(
-  sqlite*,
-  char *sql,
-  int (*)(void*,int,char**,char**),
-  void*,
-  char **errmsg,
-  va_list
-);
-
-int sqlite_get_table_printf(
-  sqlite*,
-  char *sql,
-  char ***result,
-  int *nrow,
-  int *ncolumn,
-  char **errmsg,
-  ...
-);
-
-int sqlite_get_table_vprintf(
-  sqlite*,
-  char *sql,
-  char ***result,
-  int *nrow,
-  int *ncolumn,
-  char **errmsg,
-  va_list
-);
-
-</pre></blockquote>
-
-<p>All of the above definitions are included in the "sqlite.h"
-header file that comes in the source tree.</p>
-
 <h2>Opening a database</h2>
 
 <p>Use the <b>sqlite_open()</b> function to open an existing SQLite
@@ -225,13 +139,57 @@ argv[i] == 0
 </pre></blockquote>
 <p>The names of the columns are contained in the fourth argument.</p>
 
+<p>If the EMPTY_RESULT_CALLBACKS pragma is set to ON and the result of
+a query is an empty set, then the callback is invoked once with the
+third parameter (argv) set to 0.  In other words
+<blockquote><pre>
+argv == 0
+</pre></blockquote>
+The second parameter (argc)
+and the fourth parameter (columnNames) are still valid
+and can be used to determine the number and names of the result
+columns if there had been a result.
+The default behavior is not to invoke the callback at all if the
+result set is empty.</p>
+
 <p>The callback function should normally return 0.  If the callback
 function returns non-zero, the query is immediately aborted and 
 <b>sqlite_exec()</b> will return SQLITE_ABORT.</p>
 
-<p>The <b>sqlite_exec()</b> function returns an integer to indicate
-success or failure of the operation.  The following are possible
-return values:</p>
+<h2>Error Codes</h2>
+
+<p>
+The <b>sqlite_exec()</b> function normally returns SQLITE_OK.  But
+if something goes wrong it can return a different value to indicate
+the type of error.  Here is a complete list of the return codes:
+</p>
+
+<blockquote><pre>
+#define SQLITE_OK           0   /* Successful result */
+#define SQLITE_ERROR        1   /* SQL error or missing database */
+#define SQLITE_INTERNAL     2   /* An internal logic error in SQLite */
+#define SQLITE_PERM         3   /* Access permission denied */
+#define SQLITE_ABORT        4   /* Callback routine requested an abort */
+#define SQLITE_BUSY         5   /* The database file is locked */
+#define SQLITE_LOCKED       6   /* A table in the database is locked */
+#define SQLITE_NOMEM        7   /* A malloc() failed */
+#define SQLITE_READONLY     8   /* Attempt to write a readonly database */
+#define SQLITE_INTERRUPT    9   /* Operation terminated by sqlite_interrupt() */
+#define SQLITE_IOERR       10   /* Some kind of disk I/O error occurred */
+#define SQLITE_CORRUPT     11   /* The database disk image is malformed */
+#define SQLITE_NOTFOUND    12   /* (Internal Only) Table or record not found */
+#define SQLITE_FULL        13   /* Insertion failed because database is full */
+#define SQLITE_CANTOPEN    14   /* Unable to open the database file */
+#define SQLITE_PROTOCOL    15   /* Database lock protocol error */
+#define SQLITE_EMPTY       16   /* (Internal Only) Database table is empty */
+#define SQLITE_SCHEMA      17   /* The database schema changed */
+#define SQLITE_TOOBIG      18   /* Too much data for one row of a table */
+#define SQLITE_CONSTRAINT  19   /* Abort due to contraint violation */
+</pre></blockquote>
+
+<p>
+The meanings of these various return values are as follows:
+</p>
 
 <blockquote>
 <dl>
@@ -332,6 +290,80 @@ a database constraint.
 </dl>
 </blockquote>
 
+<h2>The Extended API</h2>
+
+<p>Only the three core routines shown above are required to use
+SQLite.  But there are many other functions that provide 
+useful interfaces.  These extended routines are as follows:
+</p>
+
+<blockquote><pre>
+int sqlite_get_table(
+  sqlite*,
+  char *sql,
+  char ***result,
+  int *nrow,
+  int *ncolumn,
+  char **errmsg
+);
+
+void sqlite_free_table(char**);
+
+void sqlite_interrupt(sqlite*);
+
+int sqlite_complete(const char *sql);
+
+void sqlite_busy_handler(sqlite*, int (*)(void*,const char*,int), void*);
+
+void sqlite_busy_timeout(sqlite*, int ms);
+
+const char sqlite_version[];
+
+const char sqlite_encoding[];
+
+int sqlite_exec_printf(
+  sqlite*,
+  char *sql,
+  int (*)(void*,int,char**,char**),
+  void*,
+  char **errmsg,
+  ...
+);
+
+int sqlite_exec_vprintf(
+  sqlite*,
+  char *sql,
+  int (*)(void*,int,char**,char**),
+  void*,
+  char **errmsg,
+  va_list
+);
+
+int sqlite_get_table_printf(
+  sqlite*,
+  char *sql,
+  char ***result,
+  int *nrow,
+  int *ncolumn,
+  char **errmsg,
+  ...
+);
+
+int sqlite_get_table_vprintf(
+  sqlite*,
+  char *sql,
+  char ***result,
+  int *nrow,
+  int *ncolumn,
+  char **errmsg,
+  va_list
+);
+
+</pre></blockquote>
+
+<p>All of the above definitions are included in the "sqlite.h"
+header file that comes in the source tree.</p>
+
 <h2>Querying without using a callback function</h2>
 
 <p>The <b>sqlite_get_table()</b> function is a wrapper around
@@ -375,10 +407,46 @@ result[8] = "zadok"
 <p>Notice that the "host" value for the "dummy" record is NULL so
 the result[] array contains a NULL pointer at that slot.</p>
 
+<p>If the result set of a query is empty, then by default
+<b>sqlite_get_table()</b> will set nrow to 0 and leave its
+result parameter is set to NULL.  But if the EMPTY_RESULT_CALLBACKS
+pragma is ON then the result parameter is initialized to the names
+of the columns only.  For example, consider this query which has
+an empty result set:</p>
+
+<blockquote>
+SELECT employee_name, login, host FROM users WHERE employee_name IS NULL;
+</blockquote>
+
+<p>
+The default behavior gives this results:
+</p>
+
+<blockquote>
+nrow = 0<br>
+ncolumn = 0<br>
+result = 0<br>
+</blockquote>
+
+<p>
+But if the EMPTY_RESULT_CALLBACKS pragma is ON, then the following
+is returned:
+</p>
+
+<blockquote>
+nrow = 0<br>
+ncolumn = 3<br>
+result[0] = "employee_name"<br>
+result[1] = "login"<br>
+result[2] = "host"<br>
+</blockquote>
+
 <p>Memory to hold the information returned by <b>sqlite_get_table()</b>
 is obtained from malloc().  But the calling function should not try
 to free this information directly.  Instead, pass the complete table
-to <b>sqlite_free_table()</b> when the table is no longer needed.</p>
+to <b>sqlite_free_table()</b> when the table is no longer needed.
+It is safe to call <b>sqlite_free_table()</b> will a NULL pointer such
+as would be returned if the result set is empty.</p>
 
 <p>The <b>sqlite_get_table()</b> routine returns the same integer
 result code as <b>sqlite_exec()</b>.</p>
index fc60b3f6d37db387f6c1553a7f81cf7f2e092161..edfa84828bc5a051df65b45746d01228203821a2 100644 (file)
@@ -17,7 +17,8 @@ proc chng {date desc} {
   puts "<DD><P><UL>$desc</UL></P></DD>"
 }
 
-chng {2001 Oct ?? (2.0.6)} {
+chng {2001 Oct 19 (2.0.6)} {
+<li>Added the EMPTY_RESULT_CALLBACKS pragma</li>
 <li>Support for UTF-8 and ISO8859 characters in column and table names.</li>
 <li>Bug fix: Compute correct table names with the FULL_COLUMN_NAMES pragma
     is turned on.</li>
index 7e6a4c089bdad67001525e6bd2c591676aabc912..c66207ff4cba768a48f0265416e16e38d9175f96 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Run this Tcl script to generate the sqlite.html file.
 #
-set rcsid {$Id: lang.tcl,v 1.13 2001/10/15 00:44:36 drh Exp $}
+set rcsid {$Id: lang.tcl,v 1.14 2001/10/19 16:44:58 drh Exp $}
 
 puts {<html>
 <head>
@@ -637,6 +637,16 @@ with caution.</p>
     be invoked once for each DELETE, INSERT, or UPDATE operation.  The
     argument is the number of rows that were changed.</p>
 
+<li><p><b>PRAGMA empty_result_callbacks = ON;
+       <br>PRAGMA empty_result_callbacks = OFF;</b></p>
+    <p>When on, the EMPTY_RESULT_CALLBACKS pragma causes the callback
+    function to be invoked once for each query that has an empty result
+    set.  The third "<b>argv</b>" parameter to the callback is set to NULL
+    because there is no data to report.  But the second "<b>argc</b>" and
+    fourth "<b>columnNames</b>" parameters are valid and can be used to
+    determine the number and names of the columns that would have been in
+    the result set had the set not been empty.</p>
+
 <li><p><b>PRAGMA full_column_names = ON;
        <br>PRAGMA full_column_names = OFF;</b></p>
     <p>The column names reported in an SQLite callback are normally just