diff --git a/manifest b/manifest index 3345bc2b98..92008934ab 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\sallow\sdot-commands\sto\soccur\sin\sthe\smiddle\sof\sa\sreal\sSQL\scommand.\s(CVS\s385) -D 2002-02-21T02:25:03 +C Change\sthe\sSQLITE_MASTER\sformat\sto\sversion\s2\sin\spreparation\sfor\sadding\sviews.\s(CVS\s386) +D 2002-02-21T12:01:27 F Makefile.in 9fa4277413bf1d9cf91365f07d4108d7d87ed2af F Makefile.template 3372d45f8853afdb70bd30cc6fb50a3cd9069834 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -21,13 +21,13 @@ F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6 F src/btree.c 495275fe14f3b718cf2f691dce979d4c0e1f8e5d F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3 -F src/build.c 088acf87a92b00edda1206ccafac3518660b1b3b +F src/build.c 4e10d8e61971fe900317d00a98f49dd7ceb27c20 F src/delete.c f8ad71be53cf18656b6573de65395852fe817f0c F src/expr.c 7aff65ea0732b07d36925087ad611019103ad69a F src/hash.c 8f7c740ef2eaaa8decfa8751f2be30680b123e46 F src/hash.h d1ce47900c7325af5e41c4feb4855c4bf2b841e7 F src/insert.c eae5aa2e9ac68c4d465e71b2ad34bcbb882979cf -F src/main.c fada622b468c54fb211372f38a27ee636915e2ee +F src/main.c abc0732d4caa676ff8337f278b01f1f1b57538f5 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c F src/os.c f6bc9b7ab530346bb7fef2ed39f2f1f214bc14ea F src/os.h a17596ecc7f38a228b83ecdb661fb03ce44726d6 @@ -36,22 +36,22 @@ F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283 F src/parse.y b82278917959eefd05bd08c90e07a4fa5917ea51 F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe -F src/select.c 282f37b2fdb9367f0230811e1cf7bba48665fb72 +F src/select.c f0cbfd2d9059e0f33837797ee326fbe964a35f09 F src/shell.c cbf48bf0ca35c4e0d8a7d2a86f7724f52c525cd7 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in f57074c84a2c112a5093ba7a9d9636aa9cacc87c -F src/sqliteInt.h e19efd5b1c6accfb3418cbb5c0e8c143fc1d18d7 +F src/sqliteInt.h 338cd586b9322105080a2a31122446e504ac1fc4 F src/table.c c89698bd5bb4b8d14722d6ee7e9be014c383d24a F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f F src/test2.c d410dbd8a90faa466c3ab694fa0aa57f5a773aa6 F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f -F src/tokenize.c 9e98f94469694a763992860596137e78dbae0cc0 +F src/tokenize.c 777b734f9d06f5a9055b6808e5baec18936a4fd9 F src/update.c 95459f94a061860bf8e5716b3426a5ba85c79103 F src/util.c f31f3d6198a0d1296a16f5a6ceec423a932cbbf6 -F src/vdbe.c 18b6e9ed3bcdd76ae74d60a977a6d8a3a1b0f797 -F src/vdbe.h b4d35e159fbb80a74728b4a96e5b789fffce6f57 +F src/vdbe.c 44832d804e109248e9e2abd40daee5f8ac735450 +F src/vdbe.h 002bb8cf884034bea25a9fe901a9c5e9d29bc045 F src/where.c f79bc3179379b46b131a67ab10713779368dceee F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578 @@ -89,7 +89,7 @@ F test/select5.test c2a6c4a003316ee42cbbd689eebef8fdce0db2ac F test/select6.test d9fb417d6cab75a072b547ba6303120f327fd6fd F test/sort.test 3b996ce7ca385f9cd559944ac0f4027a23aa546b F test/subselect.test 335d3dad8d585726c447dfee8d9c4f7383c76b78 -F test/table.test b8be4d3d3ff6b4858516fa274d38075ba06b5004 +F test/table.test 17b0b6eafa3faaee5545b7a94e6c1ff73f0880f3 F test/tableapi.test 51d0c209aa6b1158cb952ec917c656d4ce66e9e4 F test/tclsqlite.test ca8dd89b02ab68bd4540163c24551756a69f6783 F test/temptable.test 0e9934283259a5e637eec756a7eefd6964c0f79b @@ -110,7 +110,7 @@ F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4 F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b F www/c_interface.tcl 63efc40f09e2f0d8fea43d174103248b160fdf0e -F www/changes.tcl e9ac5ffc030ad355fb5973bc2c68e622c3bc9ae2 +F www/changes.tcl 4aee975940a59d43736cdd4cd352c51e1e6426ba F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2 F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060 F www/download.tcl a6d75b8b117cd33dcb090bef7e80d7556d28ebe0 @@ -125,7 +125,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P 953928537c350e6c9df157553114c6d9d44712d5 -R 9ed6435fa0cf461ec879e5b28166d8d6 +P ffb00bf36a9a5851ea4a69f9c7dd7ce412553e3b +R 827e990a5d306ede002baf12f96964e4 U drh -Z a1b5da6dd8cc3bb5ba9dca0ec016a36c +Z 93dfb9234ed5554b3f3a0a3a7b0ddf3a diff --git a/manifest.uuid b/manifest.uuid index 3a0aeeca6b..c3c6ccb035 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ffb00bf36a9a5851ea4a69f9c7dd7ce412553e3b \ No newline at end of file +b2a9807fed544e83002366149b9a363759338c5d \ No newline at end of file diff --git a/src/build.c b/src/build.c index afa4db7e58..3b4599c2d3 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.77 2002/02/19 13:39:22 drh Exp $ +** $Id: build.c,v 1.78 2002/02/21 12:01:27 drh Exp $ */ #include "sqliteInt.h" #include @@ -473,12 +473,25 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName, int isTemp){ pTable->isTemp = isTemp; if( pParse->pNewTable ) sqliteDeleteTable(db, pParse->pNewTable); pParse->pNewTable = pTable; + + /* Begin generating the code that will insert the table record into + ** the SQLITE_MASTER table. Note in particular that we must go ahead + ** and allocate the record number for the table entry now. Before any + ** PRIMARY KEY or UNIQUE keywords are parsed. Those keywords will cause + ** indices to be created and the table record must come before the + ** indices. Hence, the record number for the table must be allocated + ** now. + */ if( !pParse->initFlag && (v = sqliteGetVdbe(pParse))!=0 ){ sqliteBeginWriteOperation(pParse); if( !isTemp ){ sqliteVdbeAddOp(v, OP_SetCookie, db->file_format, 1); sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2); sqliteVdbeChangeP3(v, -1, MASTER_NAME, P3_STATIC); + sqliteVdbeAddOp(v, OP_NewRecno, 0, 0); + sqliteVdbeAddOp(v, OP_Dup, 0, 0); + sqliteVdbeAddOp(v, OP_String, 0, 0); + sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0); } } } @@ -657,10 +670,11 @@ static void changeCookie(sqlite *db){ */ static int identLength(const char *z){ int n; - for(n=2; *z; n++, z++){ - if( *z=='\'' ){ n++; } + int needQuote = 0; + for(n=0; *z; n++, z++){ + if( *z=='\'' ){ n++; needQuote=1; } } - return n; + return n + needQuote*2; } /* @@ -668,14 +682,19 @@ static int identLength(const char *z){ ** quote characters as needed. */ static void identPut(char *z, int *pIdx, char *zIdent){ - int i, j; + int i, j, needQuote; i = *pIdx; - z[i++] = '\''; + for(j=0; zIdent[j]; j++){ + if( !isalnum(zIdent[j]) && zIdent[j]!='_' ) break; + } + needQuote = zIdent[j]!=0 || isdigit(zIdent[0]) + || sqliteKeywordCode(zIdent, j)!=TK_ID; + if( needQuote ) z[i++] = '\''; for(j=0; zIdent[j]; j++){ z[i++] = zIdent[j]; if( zIdent[j]=='\'' ) z[i++] = '\''; } - z[i++] = '\''; + if( needQuote ) z[i++] = '\''; z[i] = 0; *pIdx = i; } @@ -769,6 +788,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){ */ if( pSelect ){ Table *pSelTab = sqliteResultSetOfSelect(pParse, 0, pSelect); + if( pSelTab==0 ) return; assert( p->aCol==0 ); p->nCol = pSelTab->nCol; p->aCol = pSelTab->aCol; @@ -788,7 +808,8 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){ } /* If not initializing, then create a record for the new table - ** in the SQLITE_MASTER table of the database. + ** in the SQLITE_MASTER table of the database. The record number + ** for the new table entry should already be on the stack. ** ** If this is a TEMPORARY table, then just create the table. Do not ** make an entry in SQLITE_MASTER. @@ -803,7 +824,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){ sqliteVdbeChangeP3(v, addr, (char *)&p->tnum, P3_POINTER); p->tnum = 0; if( !p->isTemp ){ - sqliteVdbeAddOp(v, OP_NewRecno, 0, 0); + sqliteVdbeAddOp(v, OP_Pull, 1, 0); sqliteVdbeAddOp(v, OP_String, 0, 0); sqliteVdbeChangeP3(v, -1, "table", P3_STATIC); sqliteVdbeAddOp(v, OP_String, 0, 0); diff --git a/src/main.c b/src/main.c index 553f746834..4969f3551f 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.60 2002/02/19 22:42:05 drh Exp $ +** $Id: main.c,v 1.61 2002/02/21 12:01:27 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -51,13 +51,14 @@ static int sqliteOpenCb(void *pDb, int argc, char **argv, char **azColName){ db->next_cookie = db->schema_cookie; break; } + case 'v': case 'i': - case 't': { /* CREATE TABLE and CREATE INDEX statements */ + case 't': { /* CREATE TABLE, CREATE INDEX, or CREATE VIEW statements */ if( argv[3] && argv[3][0] ){ - /* Call the parser to process a CREATE TABLE or CREATE INDEX statement. + /* Call the parser to process a CREATE TABLE, INDEX or VIEW. ** But because sParse.initFlag is set to 1, no VDBE code is generated ** or executed. All the parser does is build the internal data - ** structures that describe the table or index. + ** structures that describe the table, index, or view. */ memset(&sParse, 0, sizeof(sParse)); sParse.db = db; @@ -149,10 +150,15 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){ ** was created to fulfill a PRIMARY KEY or UNIQUE constraint on a table, ** then the "sql" column is NULL. ** - ** If the "type" column has the value "meta", then the "sql" column - ** contains extra information about the database, such as the - ** file format version number. All meta information must be processed - ** before any tables or indices are constructed. + ** In format 1, entries in the sqlite_master table are in a random + ** order. Two passes must be made through the table to initialize + ** internal data structures. The first pass reads table definitions + ** and the second pass read index definitions. Having two passes + ** insures that indices appear after their tables. + ** + ** In format 2, entries appear in chronological order. Only a single + ** pass needs to be made through the table since everything will be + ** in the write order. VIEWs may only occur in format 2. ** ** The following program invokes its callback on the SQL for each ** table then goes back and invokes the callback on the @@ -161,38 +167,70 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){ ** database scheme. */ static VdbeOp initProg[] = { + /* Send the file format to the callback routine + */ { OP_Open, 0, 2, 0}, { OP_String, 0, 0, "file-format"}, { OP_String, 0, 0, 0}, { OP_String, 0, 0, 0}, { OP_ReadCookie, 0, 1, 0}, { OP_Callback, 4, 0, 0}, + + /* Send the initial schema cookie to the callback + */ { OP_String, 0, 0, "schema_cookie"}, { OP_String, 0, 0, 0}, { OP_String, 0, 0, 0}, { OP_ReadCookie, 0, 0, 0}, { OP_Callback, 4, 0, 0}, - { OP_Rewind, 0, 31, 0}, - { OP_Column, 0, 0, 0}, /* 12 */ + + /* Check the file format. If the format number is 2 or more, + ** then do a single pass through the SQLITE_MASTER table. For + ** a format number of less than 2, jump forward to a different + ** algorithm that makes two passes through the SQLITE_MASTER table, + ** once for tables and a second time for indices. + */ + { OP_ReadCookie, 0, 1, 0}, + { OP_Integer, 2, 0, 0}, + { OP_Lt, 0, 23, 0}, + + /* This is the code for doing a single scan through the SQLITE_MASTER + ** table. This code runs for format 2 and greater. + */ + { OP_Rewind, 0, 21, 0}, + { OP_Column, 0, 0, 0}, /* 15 */ + { OP_Column, 0, 1, 0}, + { OP_Column, 0, 3, 0}, + { OP_Column, 0, 4, 0}, + { OP_Callback, 4, 0, 0}, + { OP_Next, 0, 15, 0}, + { OP_Close, 0, 0, 0}, /* 21 */ + { OP_Halt, 0, 0, 0}, + + /* This is the code for doing two passes through SQLITE_MASTER. This + ** code runs for file format 1. + */ + { OP_Rewind, 0, 43, 0}, /* 23 */ + { OP_Column, 0, 0, 0}, /* 24 */ { OP_String, 0, 0, "table"}, - { OP_Ne, 0, 20, 0}, + { OP_Ne, 0, 32, 0}, { OP_Column, 0, 0, 0}, { OP_Column, 0, 1, 0}, { OP_Column, 0, 3, 0}, { OP_Column, 0, 4, 0}, { OP_Callback, 4, 0, 0}, - { OP_Next, 0, 12, 0}, /* 20 */ - { OP_Rewind, 0, 31, 0}, /* 21 */ - { OP_Column, 0, 0, 0}, /* 22 */ + { OP_Next, 0, 24, 0}, /* 32 */ + { OP_Rewind, 0, 43, 0}, /* 33 */ + { OP_Column, 0, 0, 0}, /* 34 */ { OP_String, 0, 0, "index"}, - { OP_Ne, 0, 30, 0}, + { OP_Ne, 0, 42, 0}, { OP_Column, 0, 0, 0}, { OP_Column, 0, 1, 0}, { OP_Column, 0, 3, 0}, { OP_Column, 0, 4, 0}, { OP_Callback, 4, 0, 0}, - { OP_Next, 0, 22, 0}, /* 30 */ - { OP_Close, 0, 0, 0}, /* 31 */ + { OP_Next, 0, 34, 0}, /* 42 */ + { OP_Close, 0, 0, 0}, /* 43 */ { OP_Halt, 0, 0, 0}, }; @@ -209,9 +247,9 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){ db->pBusyArg, db->xBusyCallback); sqliteVdbeDelete(vdbe); if( rc==SQLITE_OK && db->nTable==0 ){ - db->file_format = FILE_FORMAT; + db->file_format = 2; } - if( rc==SQLITE_OK && db->file_format>FILE_FORMAT ){ + if( rc==SQLITE_OK && db->file_format>2 ){ sqliteSetString(pzErrMsg, "unsupported file format", 0); rc = SQLITE_ERROR; } diff --git a/src/select.c b/src/select.c index ef6cd216e8..6183275793 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.63 2002/02/19 22:42:05 drh Exp $ +** $Id: select.c,v 1.64 2002/02/21 12:01:27 drh Exp $ */ #include "sqliteInt.h" @@ -760,7 +760,10 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ pTab = p->pSrc->a[0].pTab; /* If we get to here, it means the query is of the correct form. - ** Check to make sure we have an index. + ** Check to make sure we have an index and make pIdx point to the + ** appropriate index. If the min() or max() is on an INTEGER PRIMARY + ** key column, no index is necessary so set pIdx to NULL. If no + ** usable index is found, return 0. */ if( iCol<0 ){ pIdx = 0; @@ -772,7 +775,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ if( pIdx==0 ) return 0; } - /* Identify column names if we will be using in the callback. This + /* Identify column names if we will be using the callback. This ** step is skipped if the output is going to a table or a memory cell. */ v = sqliteGetVdbe(pParse); @@ -781,7 +784,10 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ generateColumnNames(pParse, p->pSrc, p->pEList); } - /* Begin generating code + /* Generating code to find the min or the max. Basically all we have + ** to do is find the first or the last entry in the chosen index. If + ** the min() or max() is on the INTEGER PRIMARY KEY, then find the first + ** or last entry in the main table. */ if( !pParse->schemaVerified && (pParse->db->flags & SQLITE_InTrans)==0 ){ sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 302a2b4949..2eece76f9d 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.88 2002/02/19 22:42:05 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.89 2002/02/21 12:01:27 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -30,11 +30,6 @@ #define MAX_PAGES 100 #define TEMP_PAGES 25 -/* -** File format version number -*/ -#define FILE_FORMAT 1 - /* ** Integers of known sizes. These typedefs might change for architectures ** where the sizes very. Preprocessor macros are available so that the @@ -555,6 +550,7 @@ void sqliteRealToSortable(double r, char *); void sqliteSetString(char **, const char *, ...); void sqliteSetNString(char **, ...); void sqliteDequote(char*); +int sqliteKeywordCode(const char*, int); int sqliteRunParser(Parse*, const char*, char **); void sqliteExec(Parse*); Expr *sqliteExpr(int, Expr*, Expr*, Token*); diff --git a/src/tokenize.c b/src/tokenize.c index 18e2368567..cd44b1517e 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -15,7 +15,7 @@ ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** -** $Id: tokenize.c,v 1.36 2002/02/17 00:30:36 drh Exp $ +** $Id: tokenize.c,v 1.37 2002/02/21 12:01:27 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -116,7 +116,7 @@ static Keyword *apHashTable[KEY_HASH_SIZE]; ** keyword. If it is a keyword, the token code of that keyword is ** returned. If the input is not a keyword, TK_ID is returned. */ -static int sqliteKeywordCode(const char *z, int n){ +int sqliteKeywordCode(const char *z, int n){ int h; Keyword *p; if( aKeywordTable[0].len==0 ){ diff --git a/src/vdbe.c b/src/vdbe.c index f9f0885046..acacaa91d3 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -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.120 2002/02/19 22:42:05 drh Exp $ +** $Id: vdbe.c,v 1.121 2002/02/21 12:01:27 drh Exp $ */ #include "sqliteInt.h" #include @@ -72,6 +72,7 @@ struct Cursor { Bool keyAsData; /* The OP_Column command works on key instead of data */ Bool atFirst; /* True if pointing to first entry */ Bool useRandomRowid; /* Generate new record numbers semi-randomly */ + Bool nullRow; /* True if pointing to a row with no data */ Btree *pBt; /* Separate file holding temporary table */ }; typedef struct Cursor Cursor; @@ -867,30 +868,30 @@ static char *zOpName[] = { 0, "Close", "MoveTo", "NewRecno", "PutIntKey", "PutStrKey", "Distinct", "Found", "NotFound", "IsUnique", "NotExists", "Delete", "Column", - "KeyAsData", "Recno", "FullKey", "Last", - "Rewind", "Next", "Destroy", "Clear", - "CreateIndex", "CreateTable", "IntegrityCk", "IdxPut", - "IdxDelete", "IdxRecno", "IdxGT", "IdxGE", - "MemLoad", "MemStore", "ListWrite", "ListRewind", - "ListRead", "ListReset", "SortPut", "SortMakeRec", - "SortMakeKey", "Sort", "SortNext", "SortCallback", - "SortReset", "FileOpen", "FileRead", "FileColumn", - "AggReset", "AggFocus", "AggIncr", "AggNext", - "AggSet", "AggGet", "SetInsert", "SetFound", - "SetNotFound", "MakeRecord", "MakeKey", "MakeIdxKey", - "IncrKey", "Goto", "If", "Halt", - "ColumnCount", "ColumnName", "Callback", "NullCallback", - "Integer", "String", "Pop", "Dup", - "Pull", "Push", "MustBeInt", "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", - "Limit", + "KeyAsData", "Recno", "FullKey", "NullRow", + "Last", "Rewind", "Next", "Destroy", + "Clear", "CreateIndex", "CreateTable", "IntegrityCk", + "IdxPut", "IdxDelete", "IdxRecno", "IdxGT", + "IdxGE", "MemLoad", "MemStore", "ListWrite", + "ListRewind", "ListRead", "ListReset", "SortPut", + "SortMakeRec", "SortMakeKey", "Sort", "SortNext", + "SortCallback", "SortReset", "FileOpen", "FileRead", + "FileColumn", "AggReset", "AggFocus", "AggIncr", + "AggNext", "AggSet", "AggGet", "SetInsert", + "SetFound", "SetNotFound", "MakeRecord", "MakeKey", + "MakeIdxKey", "IncrKey", "Goto", "If", + "Halt", "ColumnCount", "ColumnName", "Callback", + "NullCallback", "Integer", "String", "Pop", + "Dup", "Pull", "Push", "MustBeInt", + "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", "Limit", }; /* @@ -2591,6 +2592,7 @@ case OP_Open: { } cleanupCursor(&p->aCsr[i]); memset(&p->aCsr[i], 0, sizeof(Cursor)); + p->aCsr[i].nullRow = 1; do{ rc = sqliteBtreeCursor(pX, p2, wrFlag, &p->aCsr[i].pCursor); switch( rc ){ @@ -2648,6 +2650,7 @@ case OP_OpenTemp: { pCx = &p->aCsr[i]; cleanupCursor(pCx); memset(pCx, 0, sizeof(*pCx)); + pCx->nullRow = 1; rc = sqliteBtreeOpen(0, 0, TEMP_PAGES, &pCx->pBt); if( rc==SQLITE_OK ){ rc = sqliteBtreeBeginTrans(pCx->pBt); @@ -2708,6 +2711,7 @@ case OP_MoveTo: { sqliteBtreeMoveto(pC->pCursor, zStack[tos], aStack[tos].n, &res); pC->recnoIsValid = 0; } + pC->nullRow = 0; sqlite_search_count++; if( res<0 ){ sqliteBtreeNext(pC->pCursor, &res); @@ -2892,8 +2896,12 @@ case OP_NotExists: { assert( aStack[tos].flags & STK_Int ); iKey = intToKey(aStack[tos].i); rx = sqliteBtreeMoveto(pCrsr, (char*)&iKey, sizeof(int), &res); + p->aCsr[i].lastRecno = aStack[tos].i; + p->aCsr[i].recnoIsValid = res==0; + p->aCsr[i].nullRow = 0; if( rx!=SQLITE_OK || res!=0 ){ - pc = pOp->p2 - 1; + pc = pOp->p2 - 1; + p->aCsr[i].recnoIsValid = 0; } } POPSTACK; @@ -2982,6 +2990,7 @@ case OP_NewRecno: { goto abort_due_to_error; } } + pC->recnoIsValid = 0; } VERIFY( NeedStack(p, p->tos+1); ) p->tos++; @@ -3044,6 +3053,7 @@ case OP_PutStrKey: { } rc = sqliteBtreeInsert(p->aCsr[i].pCursor, zKey, nKey, zStack[tos], aStack[tos].n); + p->aCsr[i].recnoIsValid = 0; } POPSTACK; POPSTACK; @@ -3113,7 +3123,9 @@ case OP_Column: { ** is coming from the key or the data of the record. */ pCrsr = pC->pCursor; - if( pC->keyAsData ){ + if( pC->nullRow ){ + payloadSize = 0; + }else if( pC->keyAsData ){ sqliteBtreeKeySize(pCrsr, &payloadSize); xRead = sqliteBtreeKey; }else{ @@ -3199,6 +3211,9 @@ case OP_Recno: { int v; if( p->aCsr[i].recnoIsValid ){ v = p->aCsr[i].lastRecno; + }else if( p->aCsr[i].nullRow ){ + aStack[tos].flags = STK_Null; + break; }else{ sqliteBtreeKey(pCrsr, 0, sizeof(u32), (char*)&v); v = keyToInt(v); @@ -3248,6 +3263,22 @@ case OP_FullKey: { break; } +/* Opcode: NullRow P1 * * +** +** Move the cursor P1 to a null row. Any OP_Column operations +** that occur while the cursor is on the null row will always push +** a NULL onto the stack. +*/ +case OP_NullRow: { + int i = pOp->p1; + BtCursor *pCrsr; + + if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ + p->aCsr[i].nullRow = 1; + } + break; +} + /* Opcode: Last P1 P2 * ** ** The next use of the Recno or Column or Next instruction for P1 @@ -3263,7 +3294,7 @@ case OP_Last: { if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ int res; sqliteBtreeLast(pCrsr, &res); - p->aCsr[i].atFirst = res==0; + p->aCsr[i].nullRow = res; if( res && pOp->p2>0 ){ pc = pOp->p2 - 1; } @@ -3287,6 +3318,7 @@ case OP_Rewind: { int res; sqliteBtreeFirst(pCrsr, &res); p->aCsr[i].atFirst = res==0; + p->aCsr[i].nullRow = res; if( res && pOp->p2>0 ){ pc = pOp->p2 - 1; } @@ -3308,6 +3340,7 @@ case OP_Next: { if( VERIFY( i>=0 && inCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){ int res; rc = sqliteBtreeNext(pCrsr, &res); + p->aCsr[i].nullRow = res; if( res==0 ){ pc = pOp->p2 - 1; sqlite_search_count++; diff --git a/src/vdbe.h b/src/vdbe.h index 2f10d591f8..4bde7b342c 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.43 2002/02/19 15:00:08 drh Exp $ +** $Id: vdbe.h,v 1.44 2002/02/21 12:01:28 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -97,113 +97,114 @@ typedef struct VdbeOp VdbeOp; #define OP_KeyAsData 25 #define OP_Recno 26 #define OP_FullKey 27 -#define OP_Last 28 -#define OP_Rewind 29 -#define OP_Next 30 +#define OP_NullRow 28 +#define OP_Last 29 +#define OP_Rewind 30 +#define OP_Next 31 -#define OP_Destroy 31 -#define OP_Clear 32 -#define OP_CreateIndex 33 -#define OP_CreateTable 34 -#define OP_IntegrityCk 35 +#define OP_Destroy 32 +#define OP_Clear 33 +#define OP_CreateIndex 34 +#define OP_CreateTable 35 +#define OP_IntegrityCk 36 -#define OP_IdxPut 36 -#define OP_IdxDelete 37 -#define OP_IdxRecno 38 -#define OP_IdxGT 39 -#define OP_IdxGE 40 +#define OP_IdxPut 37 +#define OP_IdxDelete 38 +#define OP_IdxRecno 39 +#define OP_IdxGT 40 +#define OP_IdxGE 41 -#define OP_MemLoad 41 -#define OP_MemStore 42 +#define OP_MemLoad 42 +#define OP_MemStore 43 -#define OP_ListWrite 43 -#define OP_ListRewind 44 -#define OP_ListRead 45 -#define OP_ListReset 46 +#define OP_ListWrite 44 +#define OP_ListRewind 45 +#define OP_ListRead 46 +#define OP_ListReset 47 -#define OP_SortPut 47 -#define OP_SortMakeRec 48 -#define OP_SortMakeKey 49 -#define OP_Sort 50 -#define OP_SortNext 51 -#define OP_SortCallback 52 -#define OP_SortReset 53 +#define OP_SortPut 48 +#define OP_SortMakeRec 49 +#define OP_SortMakeKey 50 +#define OP_Sort 51 +#define OP_SortNext 52 +#define OP_SortCallback 53 +#define OP_SortReset 54 -#define OP_FileOpen 54 -#define OP_FileRead 55 -#define OP_FileColumn 56 +#define OP_FileOpen 55 +#define OP_FileRead 56 +#define OP_FileColumn 57 -#define OP_AggReset 57 -#define OP_AggFocus 58 -#define OP_AggIncr 59 -#define OP_AggNext 60 -#define OP_AggSet 61 -#define OP_AggGet 62 +#define OP_AggReset 58 +#define OP_AggFocus 59 +#define OP_AggIncr 60 +#define OP_AggNext 61 +#define OP_AggSet 62 +#define OP_AggGet 63 -#define OP_SetInsert 63 -#define OP_SetFound 64 -#define OP_SetNotFound 65 +#define OP_SetInsert 64 +#define OP_SetFound 65 +#define OP_SetNotFound 66 -#define OP_MakeRecord 66 -#define OP_MakeKey 67 -#define OP_MakeIdxKey 68 -#define OP_IncrKey 69 +#define OP_MakeRecord 67 +#define OP_MakeKey 68 +#define OP_MakeIdxKey 69 +#define OP_IncrKey 70 -#define OP_Goto 70 -#define OP_If 71 -#define OP_Halt 72 +#define OP_Goto 71 +#define OP_If 72 +#define OP_Halt 73 -#define OP_ColumnCount 73 -#define OP_ColumnName 74 -#define OP_Callback 75 -#define OP_NullCallback 76 +#define OP_ColumnCount 74 +#define OP_ColumnName 75 +#define OP_Callback 76 +#define OP_NullCallback 77 -#define OP_Integer 77 -#define OP_String 78 -#define OP_Pop 79 -#define OP_Dup 80 -#define OP_Pull 81 -#define OP_Push 82 -#define OP_MustBeInt 83 +#define OP_Integer 78 +#define OP_String 79 +#define OP_Pop 80 +#define OP_Dup 81 +#define OP_Pull 82 +#define OP_Push 83 +#define OP_MustBeInt 84 -#define OP_Add 84 -#define OP_AddImm 85 -#define OP_Subtract 86 -#define OP_Multiply 87 -#define OP_Divide 88 -#define OP_Remainder 89 -#define OP_BitAnd 90 -#define OP_BitOr 91 -#define OP_BitNot 92 -#define OP_ShiftLeft 93 -#define OP_ShiftRight 94 -#define OP_AbsValue 95 -#define OP_Precision 96 -#define OP_Min 97 -#define OP_Max 98 -#define OP_Like 99 -#define OP_Glob 100 -#define OP_Eq 101 -#define OP_Ne 102 -#define OP_Lt 103 -#define OP_Le 104 -#define OP_Gt 105 -#define OP_Ge 106 -#define OP_IsNull 107 -#define OP_NotNull 108 -#define OP_Negative 109 -#define OP_And 110 -#define OP_Or 111 -#define OP_Not 112 -#define OP_Concat 113 -#define OP_Noop 114 +#define OP_Add 85 +#define OP_AddImm 86 +#define OP_Subtract 87 +#define OP_Multiply 88 +#define OP_Divide 89 +#define OP_Remainder 90 +#define OP_BitAnd 91 +#define OP_BitOr 92 +#define OP_BitNot 93 +#define OP_ShiftLeft 94 +#define OP_ShiftRight 95 +#define OP_AbsValue 96 +#define OP_Precision 97 +#define OP_Min 98 +#define OP_Max 99 +#define OP_Like 100 +#define OP_Glob 101 +#define OP_Eq 102 +#define OP_Ne 103 +#define OP_Lt 104 +#define OP_Le 105 +#define OP_Gt 106 +#define OP_Ge 107 +#define OP_IsNull 108 +#define OP_NotNull 109 +#define OP_Negative 110 +#define OP_And 111 +#define OP_Or 112 +#define OP_Not 113 +#define OP_Concat 114 +#define OP_Noop 115 -#define OP_Strlen 115 -#define OP_Substr 116 +#define OP_Strlen 116 +#define OP_Substr 117 -#define OP_Limit 117 +#define OP_Limit 118 -#define OP_MAX 117 +#define OP_MAX 118 /* ** Prototypes for the VDBE interface. See comments on the implementation diff --git a/test/table.test b/test/table.test index cfe64c6015..4d3931dd6b 100644 --- a/test/table.test +++ b/test/table.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the CREATE TABLE statement. # -# $Id: table.test,v 1.14 2002/02/18 18:30:33 drh Exp $ +# $Id: table.test,v 1.15 2002/02/21 12:01:28 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -282,8 +282,8 @@ do_test table-7.1 { desc text, asc text, explain int, - vacuum boolean, - delimiters varchar(10), + [14_vac] boolean, + fuzzy_dog_12 varchar(10), begin blob, end clob ) @@ -300,7 +300,7 @@ do_test table-7.3 { execsql2 { SELECT * FROM weird; } -} {desc a asc b explain 9 vacuum 0 delimiters xyz begin hi end y'all} +} {desc a asc b explain 9 14_vac 0 fuzzy_dog_12 xyz begin hi end y'all} # Try out the CREATE TABLE AS syntax # @@ -309,7 +309,20 @@ do_test table-8.1 { CREATE TABLE t2 AS SELECT * FROM weird; SELECT * FROM t2; } -} {desc a asc b explain 9 vacuum 0 delimiters xyz begin hi end y'all} +} {desc a asc b explain 9 14_vac 0 fuzzy_dog_12 xyz begin hi end y'all} +do_test table-8.1.1 { + execsql { + SELECT sql FROM sqlite_master WHERE name='t2'; + } +} {{CREATE TABLE t2( + 'desc', + 'asc', + 'explain', + '14_vac', + fuzzy_dog_12, + 'begin', + 'end' +)}} do_test table-8.2 { execsql { CREATE TABLE 't3''xyz'(a,b,c); @@ -319,10 +332,15 @@ do_test table-8.2 { } {1 2 3} do_test table-8.3 { execsql2 { - CREATE TABLE [t4'abc] AS SELECT count(*), min(a), max(b+c) FROM [t3'xyz]; + CREATE TABLE [t4'abc] AS SELECT count(*) as cnt, max(b+c) FROM [t3'xyz]; SELECT * FROM [t4'abc]; } -} {count(*) 1 min(a) 1 max(b+c) 5} +} {cnt 1 max(b+c) 5} +do_test table-8.3.1 { + execsql { + SELECT sql FROM sqlite_master WHERE name='t4''abc' + } +} {{CREATE TABLE 't4''abc'(cnt,'max(b+c)')}} do_test table-8.4 { execsql2 { CREATE TEMPORARY TABLE t5 AS SELECT count(*) AS [y'all] FROM [t3'xyz]; @@ -335,16 +353,21 @@ do_test table-8.5 { execsql2 { SELECT * FROM [t4'abc]; } -} {count(*) 1 min(a) 1 max(b+c) 5} +} {cnt 1 max(b+c) 5} do_test table-8.6 { execsql2 { SELECT * FROM t2; } -} {desc a asc b explain 9 vacuum 0 delimiters xyz begin hi end y'all} +} {desc a asc b explain 9 14_vac 0 fuzzy_dog_12 xyz begin hi end y'all} do_test table-8.7 { catchsql { SELECT * FROM t5; } } {1 {no such table: t5}} +do_test table-8.8 { + catchsql { + CREATE TABLE t5 AS SELECT * FROM no_such_table; + } +} {1 {no such table: no_such_table}} finish_test diff --git a/www/changes.tcl b/www/changes.tcl index 10a59b9079..64e5dcd7d9 100644 --- a/www/changes.tcl +++ b/www/changes.tcl @@ -23,6 +23,8 @@ chng {2002 Feb * (2.3.4)} {
  • SELECT min() or max() of an indexed column with no WHERE or GROUP BY clause is handled as a special case which avoids a complete table scan.
  • Automatically generated ROWIDs are now sequential.
  • +
  • Do not allow dot-commands of the command-line shell to occur in the + middle of a real SQL command.
  • } chng {2002 Feb 18 (2.3.3)} {