From 6a535340bc31d7178cd8422a09a7ef144bf9ea4d Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 19 Oct 2001 16:44:56 +0000 Subject: [PATCH] Version 2.0.6 (CVS 291) FossilOrigin-Name: 8467d84fc6e67bd93051f54338a8f6c9b1711ee1 --- VERSION | 2 +- manifest | 38 +++---- manifest.uuid | 2 +- src/build.c | 10 +- src/select.c | 10 +- src/shell.c | 10 +- src/sqliteInt.h | 4 +- src/table.c | 26 +++-- src/tclsqlite.c | 69 ++++++------ src/tokenize.c | 4 +- src/vdbe.c | 55 +++++++-- src/vdbe.h | 83 +++++++------- test/select1.test | 34 +++++- www/c_interface.tcl | 264 ++++++++++++++++++++++++++++---------------- www/changes.tcl | 3 +- www/lang.tcl | 12 +- 16 files changed, 399 insertions(+), 227 deletions(-) diff --git a/VERSION b/VERSION index e01025862f..157e54f3e4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.5 +2.0.6 diff --git a/manifest b/manifest index 0be6cb473e..a51e29b2f3 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index da1b629dd9..212e4ba1b7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -22948fc685299ca888907eea68edb8a6e87c3f49 \ No newline at end of file +8467d84fc6e67bd93051f54338a8f6c9b1711ee1 \ No newline at end of file diff --git a/src/build.c b/src/build.c index fc45a3441e..a87a74b442 100644 --- a/src/build.c +++ b/src/build.c @@ -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 @@ -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; diff --git a/src/select.c b/src/select.c index 49e8f02cd1..c0e8961a6a 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.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; } diff --git a/src/shell.c b/src/shell.c index 726dd92619..4eaab29a60 100644 --- a/src/shell.c +++ b/src/shell.c @@ -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 #include @@ -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; iw ) 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( wactualWidth) ){ @@ -290,6 +291,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){ } } } + if( azArg==0 ) break; for(i=0; iactualWidth) ){ @@ -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; iout,"\n"); } + if( azArg==0 ) break; fprintf(p->out,""); for(i=0; iout,""); @@ -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; i0 ? ",": ""; @@ -365,6 +370,7 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){ } } fprintf(p->out,");\n"); + break; } } return 0; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 92d01724ba..8054316ece 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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 diff --git a/src/table.c b/src/table.c index e27c714601..39480dae19 100644 --- a/src/table.c +++ b/src/table.c @@ -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; irc = SQLITE_NOMEM; - return 1; + if( argv!=0 ){ + for(i=0; irc = 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; } diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 7893b73b5b..19e7ddb279 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -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); + if( azCol==0 || (cbData->once && cbData->zArray[0]) ){ + Tcl_SetVar2(cbData->interp, cbData->zArray, "*", "", 0); + for(i=0; iinterp, cbData->zArray, "*", azN[i], + TCL_LIST_ELEMENT|TCL_APPEND_VALUE); + } + cbData->once = 0; + } + if( azCol!=0 ){ + if( cbData->zArray[0] ){ for(i=0; iinterp, cbData->zArray, "*", azN[i], - TCL_LIST_ELEMENT|TCL_APPEND_VALUE); + 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); +#else + Tcl_SetVar2(cbData->interp, cbData->zArray, azN[i], z, 0); +#endif + } + }else{ + for(i=0; iinterp, azN[i], Tcl_DStringValue(&dCol), 0); + Tcl_DStringFree(&dCol); +#else + Tcl_SetVar(cbData->interp, azN[i], z, 0); +#endif } } - for(i=0; iinterp, cbData->zArray, azN[i], - Tcl_DStringValue(&dCol), 0); - Tcl_DStringFree(&dCol); -#else - Tcl_SetVar2(cbData->interp, cbData->zArray, azN[i], z, 0); -#endif - } - }else{ - for(i=0; iinterp, azN[i], Tcl_DStringValue(&dCol), 0); - Tcl_DStringFree(&dCol); -#else - 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 @@ -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; diff --git a/src/vdbe.h b/src/vdbe.h index 4a60fb68ee..7475a7eac0 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -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_NullCallback 75 -#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_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 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_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 112 -#define OP_Substr 113 +#define OP_Strlen 113 +#define OP_Substr 114 -#define OP_MAX 113 +#define OP_MAX 114 /* ** Prototypes for the VDBE interface. See comments on the implementation diff --git a/test/select1.test b/test/select1.test index adcee10496..98d94fa9a3 100644 --- a/test/select1.test +++ b/test/select1.test @@ -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 diff --git a/www/c_interface.tcl b/www/c_interface.tcl index 30d0a7d772..e0bb8f2776 100644 --- a/www/c_interface.tcl +++ b/www/c_interface.tcl @@ -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 { @@ -20,7 +20,7 @@ puts { a C or C++ program. This document gives an overview of the C/C++ programming interface.

-

The API

+

The Core API

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:

 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 */
 
-

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: +

+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.

- -
-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
-);
-
-
- -

All of the above definitions are included in the "sqlite.h" -header file that comes in the source tree.

-

Opening a database

Use the sqlite_open() function to open an existing SQLite @@ -225,13 +139,57 @@ argv[i] == 0

The names of the columns are contained in the fourth argument.

+

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 +

+argv == 0
+
+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.

+

The callback function should normally return 0. If the callback function returns non-zero, the query is immediately aborted and sqlite_exec() will return SQLITE_ABORT.

-

The sqlite_exec() function returns an integer to indicate -success or failure of the operation. The following are possible -return values:

+

Error Codes

+ +

+The sqlite_exec() 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: +

+ +
+#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 */
+
+ +

+The meanings of these various return values are as follows: +

@@ -332,6 +290,80 @@ a database constraint.
+

The Extended API

+ +

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: +

+ +
+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
+);
+
+
+ +

All of the above definitions are included in the "sqlite.h" +header file that comes in the source tree.

+

Querying without using a callback function

The sqlite_get_table() function is a wrapper around @@ -375,10 +407,46 @@ result[8] = "zadok"

Notice that the "host" value for the "dummy" record is NULL so the result[] array contains a NULL pointer at that slot.

+

If the result set of a query is empty, then by default +sqlite_get_table() 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:

+ +
+SELECT employee_name, login, host FROM users WHERE employee_name IS NULL; +
+ +

+The default behavior gives this results: +

+ +
+nrow = 0
+ncolumn = 0
+result = 0
+
+ +

+But if the EMPTY_RESULT_CALLBACKS pragma is ON, then the following +is returned: +

+ +
+nrow = 0
+ncolumn = 3
+result[0] = "employee_name"
+result[1] = "login"
+result[2] = "host"
+
+

Memory to hold the information returned by sqlite_get_table() is obtained from malloc(). But the calling function should not try to free this information directly. Instead, pass the complete table -to sqlite_free_table() when the table is no longer needed.

+to sqlite_free_table() when the table is no longer needed. +It is safe to call sqlite_free_table() will a NULL pointer such +as would be returned if the result set is empty.

The sqlite_get_table() routine returns the same integer result code as sqlite_exec().

diff --git a/www/changes.tcl b/www/changes.tcl index fc60b3f6d3..edfa84828b 100644 --- a/www/changes.tcl +++ b/www/changes.tcl @@ -17,7 +17,8 @@ proc chng {date desc} { puts "

    $desc

" } -chng {2001 Oct ?? (2.0.6)} { +chng {2001 Oct 19 (2.0.6)} { +
  • Added the EMPTY_RESULT_CALLBACKS pragma
  • Support for UTF-8 and ISO8859 characters in column and table names.
  • Bug fix: Compute correct table names with the FULL_COLUMN_NAMES pragma is turned on.
  • diff --git a/www/lang.tcl b/www/lang.tcl index 7e6a4c089b..c66207ff4c 100644 --- a/www/lang.tcl +++ b/www/lang.tcl @@ -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 { @@ -637,6 +637,16 @@ with caution.

    be invoked once for each DELETE, INSERT, or UPDATE operation. The argument is the number of rows that were changed.

    +
  • PRAGMA empty_result_callbacks = ON; +
    PRAGMA empty_result_callbacks = OFF;

    +

    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 "argv" parameter to the callback is set to NULL + because there is no data to report. But the second "argc" and + fourth "columnNames" 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.

    +
  • PRAGMA full_column_names = ON;
    PRAGMA full_column_names = OFF;

    The column names reported in an SQLite callback are normally just