diff --git a/manifest b/manifest index ca68d1c0eb..f7b6fd060c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Added\snew\stests\s(CVS\s206) -D 2001-04-07T15:24:33 +C better\shandling\sof\sout-of-memory\serrors\s(CVS\s207) +D 2001-04-11T14:28:42 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4 F Makefile.in 4775f7fd1ed543606bb24fa3fada1bc90a23a6b9 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 @@ -9,12 +9,12 @@ F configure.in 6940e3f88bf3d28a10c73b06ab99fd3a7e039a61 F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47 F doc/report1.txt 734cbae63b1310cc643fe5e9e3da1ab55a79b99e F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4 -F src/build.c 4c5eede16695d5e74bb3004e51923492b66eae62 -F src/dbbe.c dd318ba657bfc8c2fec00c8cf51ea29dbfd284bf +F src/build.c 6afbb6106c1e8c771ffcb81107b755e200310574 +F src/dbbe.c ec82c602c598748204a61a35ab0c31e34ca58223 F src/dbbe.h 7235b15c6c5d8be0c4da469cef9620cee70b1cc8 -F src/dbbegdbm.c d044b9e3a463608ac4f35283c78ac372d5da64c6 -F src/dbbemem.c fa84058f79dd5e6af1ccbb0d41c85e05a6bc19ac -F src/delete.c 7aa9dcb86d5e98c3eb9dee00a459e0ef9b73fbe3 +F src/dbbegdbm.c 9d3a3c18b27f9f2533a3aaa3741e8668bdda7e98 +F src/dbbemem.c b62821ba8cec4b1d7392157e94f72baf1ff015f2 +F src/delete.c 40ddb169ee98013d976b2dadd140d98f7876f54f F src/ex/README b745b00acce2d892f60c40111dacdfc48e0c1c7a F src/ex/db.c f1419ae6c93e40b5ac6e39fe7efd95d868e6f9d7 F src/ex/db.h 3f2933ee20c147fe494835786e4c6f3a562def4e @@ -23,28 +23,28 @@ F src/ex/dbbemird.c b00aef85656fa0a101dac2c32e12922ad106715a F src/ex/pg.c 2bbf6a94f37226d06337868b6bf4d7affc60197f F src/ex/pg.h 23a4ac807b0546ec2bb6239ec8bd3e06926572cd F src/ex/sizes.tcl f54bad4a2ac567624be59131a6ee42d71b41a3d7 -F src/expr.c 745383609b65d504a2cc04ac4d9389e9c8e2bc80 -F src/insert.c 4bc1cab84f7805d560a1417734a532843e30b762 -F src/main.c fe5c26620c46770539056525d8a79e3afb6e75e8 +F src/expr.c c4c24c3af1eba094a816522eb0e085bed518ee16 +F src/insert.c aa528e20a787af85432a61daaea6df394bd251d7 +F src/main.c 92ce30a89f622ba36cc8b7d912829e14a480722c F src/pager.h 889c5cf517ad30704e295540793c893ac843fd5f -F src/parse.y 1ba81d3b75f37ca868aa0ab990bb977fd41519eb -F src/printf.c af0dc65c293427272e1949c7807b1d88f10004fd +F src/parse.y 8fc096948994a7ffbf61ba13129cc589f794a9cb +F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9 F src/random.c b36c3f57dc80c8f354e6bfbf39cf1e1de021d54a -F src/select.c a6bfdaa92d4614e79bf18129283c5163faa291fc -F src/shell.c c1785b4af18192056adbe894f8626a7e7bdf47aa +F src/select.c 52bb7d081ac00dfad3687d52c917d2d90165331d +F src/shell.c d9c64418765d90909e9e200b207ff9e355afb5c4 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in 3e5906f72608f0fd4394dfbb1d7e8d35b8353677 -F src/sqliteInt.h 054c00c2b3deaedf4034b85d69287abeb4c15c96 -F src/table.c 5be76051a8ed6f6bfa641f4adc52529efa34fbf9 +F src/sqliteInt.h fc1000f023b41882bbdb8db4f80172f77b44307b +F src/table.c adcaf074f6c1075e86359174e68701fa2acfc4d6 F src/tclsqlite.c 83dcbf015ea0319c2a97514b8b812a12d621e40a -F src/test1.c 7d88c76725ce5b881cff2a283662fe068d792002 -F src/tokenize.c 8fc3936eefad84f1fff19e0892ed0542eb9ac7b3 -F src/update.c 8365b3922ea098330d1e20862d6e64911e4e03d0 -F src/util.c aec315b834bad444c9e0e90efd9d2eaeeb37c90c -F src/vdbe.c 5f5be704686ed328275c35815e39d041a0c6cbb6 +F src/test1.c abb3cb427e735ae87e6533f5b3b7164b7da91bc4 +F src/tokenize.c 0118b57702cb6550769316e8443b06760b067acf +F src/update.c 0cf789656a936d4356668393267692fa4b03ffc6 +F src/util.c 1b396ac34e30dd6222d82e996c17b161bbc906bc +F src/vdbe.c ee5a6ab95c8c84497f6685ebde153da6fb06e825 F src/vdbe.h dc1205da434c6a9da03b5d6b089270bbc8e6d437 -F src/where.c 459bf37ac7849599da400420984b3306484b4cbb -F test/all.test 15cac2f6b2d4c55bf896212aff3cc9d6597b0490 +F src/where.c 0c542fc44bd85152dfb8507862cfe2e60c629e9f +F test/all.test 4ba0807feec7f29bf90c4f0ffd10b3801634461d F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb F test/dbbe.test a022fe2d983848f786e17ef1fc6809cfd37fb02c F test/delete.test 50b9b1f06c843d591741dba7869433a105360dbf @@ -56,7 +56,7 @@ F test/insert.test dbd3bd189edb61fddbe66c236694ef23352429f1 F test/insert2.test 732405e30331635af8d159fccabe835eea5cd0c6 F test/lock.test bca7d53de73138b1f670a2fbdb1f481ff7eaa45a F test/main.test da635f9e078cd21ddf074e727381a715064489ff -F test/printf.test 18e44e4e154e13cba74d67b85202172d37ddb5ed +F test/printf.test 4c71871e1a75a2dacb673945fc13ddb30168798f F test/rowid.test 128453599def7435e988216f7fe89c7450b8a9a3 F test/select1.test 824d9d5007dffd6a45edde79e89c0a04c36e3ebe F test/select2.test 04ac3bd69298f58c7d0883159bab42ab9ad6021c @@ -66,9 +66,9 @@ F test/select5.test e2b9d51d88cbd6c307c2c05b0ef55fe7ba811ac2 F test/sort.test 838cd862642ed9a2c47e1a17b5c33da452b4552e F test/subselect.test bf8b251a92fb091973c1c469ce499dc9648a41d5 F test/table.test c1704fead1af27d67850a934d531848ce5bee4a7 -F test/tableapi.test 9ecb98590d1b6ebcb4b791697bd5275e73bba530 +F test/tableapi.test 4778414470ba150983d03b9858334effe720c2e0 F test/tclsqlite.test d2aa55926874783b2401f0146e839f773c6796e1 -F test/tester.tcl dba25c97cc89f109a9350f12792f17b24202d65f +F test/tester.tcl 39a707dac2b6048d9c9e07a98d3256d50069fe47 F test/trans.test 82556605d48f56ad4679e95478d70546a763f26a F test/update.test 72c0c93310483b86dc904a992220c5b84c7ce100 F test/vacuum.test b95d8119a0a83dc6c4ac63888f8872f06199e065 @@ -86,7 +86,7 @@ F www/arch.fig 4f246003b7da23bd63b8b0af0618afb4ee3055c8 F www/arch.png 8dae0766d42ed3de9ed013c1341a5792bcf633e6 F www/arch.tcl a40380c1fe0080c43e6cc5c20ed70731511b06be F www/c_interface.tcl ddca19005c47dd5a15882addc02fff5de83d8ed9 -F www/changes.tcl 64e5779a681bda4b34d0403c33aaf8902d8cf056 +F www/changes.tcl cad714ae8d6e5c19e8d14282dbfbe7c61c5cc4ee F www/crosscompile.tcl c99efacb3aefaa550c6e80d91b240f55eb9fd33e F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c F www/fileformat.tcl cfb7fba80b7275555281ba2f256c00734bcdd1c9 @@ -97,7 +97,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2 F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad -P 8f0d98193e4ba913fa31d5f8d5adc46ad9d346a1 -R b4947cdaa9daf5e442bdf2c2d5dbb11c +P 2507ec40610d8034ccf9dcb58a16934065e6f120 +R 0329ee9c40e806471f7c325d51a23bb6 U drh -Z 46e79499f1b58c1b58c6255886d7f82a +Z 598daf9af8c7873df8fb5f69d495d703 diff --git a/manifest.uuid b/manifest.uuid index 153fed2cfe..67d0873d5d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2507ec40610d8034ccf9dcb58a16934065e6f120 \ No newline at end of file +86b30cd0975dfea9424b9f9f0d4194aa71ce508b \ No newline at end of file diff --git a/src/build.c b/src/build.c index 9c71383bd4..df2526960b 100644 --- a/src/build.c +++ b/src/build.c @@ -33,7 +33,7 @@ ** COPY ** VACUUM ** -** $Id: build.c,v 1.26 2001/04/04 11:48:57 drh Exp $ +** $Id: build.c,v 1.27 2001/04/11 14:28:42 drh Exp $ */ #include "sqliteInt.h" @@ -49,6 +49,7 @@ */ void sqliteExec(Parse *pParse){ int rc = SQLITE_OK; + if( sqlite_malloc_failed ) return; if( pParse->pVdbe ){ if( pParse->explain ){ rc = sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg, @@ -96,8 +97,10 @@ Expr *sqliteExpr(int op, Expr *pLeft, Expr *pRight, Token *pToken){ ** text between the two given tokens. */ void sqliteExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){ - pExpr->span.z = pLeft->z; - pExpr->span.n = pRight->n + (int)pRight->z - (int)pLeft->z; + if( pExpr ){ + pExpr->span.z = pLeft->z; + pExpr->span.n = pRight->n + (int)pRight->z - (int)pLeft->z; + } } /* @@ -167,13 +170,13 @@ Index *sqliteFindIndex(sqlite *db, char *zName){ ** Remove the given index from the index hash table, and free ** its memory structures. ** -** The index is removed from the database hash table, but it is -** not unlinked from the Table that is being indexed. Unlinking -** from the Table must be done by the calling function. +** The index is removed from the database hash table if db!=NULL. +** But it is not unlinked from the Table that is being indexed. +** Unlinking from the Table must be done by the calling function. */ static void sqliteDeleteIndex(sqlite *db, Index *pIndex){ int h; - if( pIndex->zName ){ + if( pIndex->zName && db ){ h = sqliteHashNoCase(pIndex->zName, 0) % N_HASH; if( db->apIdxHash[h]==pIndex ){ db->apIdxHash[h] = pIndex->pHash; @@ -195,6 +198,11 @@ static void sqliteDeleteIndex(sqlite *db, Index *pIndex){ ** This routine just deletes the data structure. It does not unlink ** the table data structure from the hash table. But does it destroy ** memory structures of the indices associated with the table. +** +** Indices associated with the table are unlinked from the "db" +** data structure if db!=NULL. If db==NULL, indices attached to +** the table are deleted, but it is assumed they have already been +** unlinked. */ void sqliteDeleteTable(sqlite *db, Table *pTable){ int i; @@ -236,6 +244,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){ pParse->sFirstToken = *pStart; zName = sqliteTableNameFromToken(pName); + if( zName==0 ) return; pTable = sqliteFindTable(pParse->db, zName); if( pTable!=0 ){ sqliteSetNString(&pParse->zErrMsg, "table ", 0, pName->z, pName->n, @@ -252,11 +261,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){ return; } pTable = sqliteMalloc( sizeof(Table) ); - if( pTable==0 ){ - sqliteSetString(&pParse->zErrMsg, "out of memory", 0); - pParse->nErr++; - return; - } + if( pTable==0 ) return; pTable->zName = zName; pTable->pHash = 0; pTable->nCol = 0; @@ -275,10 +280,10 @@ void sqliteAddColumn(Parse *pParse, Token *pName){ if( (p = pParse->pNewTable)==0 ) return; if( (p->nCol & 0x7)==0 ){ p->aCol = sqliteRealloc( p->aCol, (p->nCol+8)*sizeof(p->aCol[0])); - } - if( p->aCol==0 ){ - p->nCol = 0; - return; + if( p->aCol==0 ){ + p->nCol = 0; + return; + } } memset(&p->aCol[p->nCol], 0, sizeof(p->aCol[0])); pz = &p->aCol[p->nCol++].zName; @@ -323,13 +328,14 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){ int h; int addMeta; /* True to insert a meta records into the file */ - if( pParse->nErr ) return; + if( pEnd==0 || pParse->nErr || sqlite_malloc_failed ) return; p = pParse->pNewTable; - addMeta = p!=0 && pParse->db->nTable==1; + if( p==0 ) return; + addMeta = pParse->db->nTable==1; /* Add the table to the in-memory representation of the database */ - if( p!=0 && pParse->explain==0 ){ + if( pParse->explain==0 ){ h = sqliteHashNoCase(p->zName, 0) % N_HASH; p->pHash = pParse->db->apTblHash[h]; pParse->db->apTblHash[h] = p; @@ -381,8 +387,11 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){ ** an error for the parser to find and return NULL. */ Table *sqliteTableFromToken(Parse *pParse, Token *pTok){ - char *zName = sqliteTableNameFromToken(pTok); - Table *pTab = sqliteFindTable(pParse->db, zName); + char *zName; + Table *pTab; + zName = sqliteTableNameFromToken(pTok); + if( zName==0 ) return 0; + pTab = sqliteFindTable(pParse->db, zName); sqliteFree(zName); if( pTab==0 ){ sqliteSetNString(&pParse->zErrMsg, "no such table: ", 0, @@ -401,6 +410,7 @@ void sqliteDropTable(Parse *pParse, Token *pName){ Vdbe *v; int base; + if( pParse->nErr || sqlite_malloc_failed ) return; pTable = sqliteTableFromToken(pParse, pName); if( pTable==0 ) return; if( pTable->readOnly ){ @@ -486,6 +496,8 @@ void sqliteCreateIndex( int i, j, h; Token nullId; /* Fake token for an empty ID list */ + if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index; + /* ** Find the table that is to be indexed. Return early if not found. */ @@ -512,6 +524,7 @@ void sqliteCreateIndex( zName = 0; sqliteSetString(&zName, pTab->zName, "__primary_key", 0); } + if( zName==0 ) goto exit_create_index; if( sqliteFindIndex(pParse->db, zName) ){ sqliteSetString(&pParse->zErrMsg, "index ", zName, " already exists", 0); @@ -541,11 +554,7 @@ void sqliteCreateIndex( */ pIndex = sqliteMalloc( sizeof(Index) + strlen(zName) + 1 + sizeof(int)*pList->nId ); - if( pIndex==0 ){ - sqliteSetString(&pParse->zErrMsg, "out of memory", 0); - pParse->nErr++; - goto exit_create_index; - } + if( pIndex==0 ) goto exit_create_index; pIndex->aiColumn = (int*)&pIndex[1]; pIndex->zName = (char*)&pIndex->aiColumn[pList->nId]; strcpy(pIndex->zName, zName); @@ -656,7 +665,9 @@ void sqliteDropIndex(Parse *pParse, Token *pName){ char *zName; Vdbe *v; + if( pParse->nErr || sqlite_malloc_failed ) return; zName = sqliteTableNameFromToken(pName); + if( zName==0 ) return; pIndex = sqliteFindIndex(pParse->db, zName); sqliteFree(zName); if( pIndex==0 ){ @@ -714,8 +725,8 @@ ExprList *sqliteExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){ int i; if( pList==0 ){ pList = sqliteMalloc( sizeof(ExprList) ); + if( pList==0 ) return 0; } - if( pList==0 ) return 0; if( (pList->nExpr & 7)==0 ){ int n = pList->nExpr + 8; pList->a = sqliteRealloc(pList->a, n*sizeof(pList->a[0])); @@ -751,6 +762,8 @@ void sqliteExprListDelete(ExprList *pList){ /* ** Append a new element to the given IdList. Create a new IdList if ** need be. +** +** A new IdList is returned, or NULL if malloc() fails. */ IdList *sqliteIdListAppend(IdList *pList, Token *pToken){ if( pList==0 ){ @@ -761,13 +774,20 @@ IdList *sqliteIdListAppend(IdList *pList, Token *pToken){ pList->a = sqliteRealloc(pList->a, (pList->nId+8)*sizeof(pList->a[0]) ); if( pList->a==0 ){ pList->nId = 0; - return pList; + sqliteIdListDelete(pList); + return 0; } } memset(&pList->a[pList->nId], 0, sizeof(pList->a[0])); if( pToken ){ - sqliteSetNString(&pList->a[pList->nId].zName, pToken->z, pToken->n, 0); - sqliteDequote(pList->a[pList->nId].zName); + char **pz = &pList->a[pList->nId].zName; + sqliteSetNString(pz, pToken->z, pToken->n, 0); + if( *pz==0 ){ + sqliteIdListDelete(pList); + return 0; + }else{ + sqliteDequote(*pz); + } } pList->nId++; return pList; @@ -793,6 +813,11 @@ void sqliteIdListDelete(IdList *pList){ for(i=0; inId; i++){ sqliteFree(pList->a[i].zName); sqliteFree(pList->a[i].zAlias); + if( pList->a[i].pSelect ){ + sqliteFree(pList->a[i].zName); + sqliteSelectDelete(pList->a[i].pSelect); + sqliteDeleteTable(0, pList->a[i].pTab); + } } sqliteFree(pList->a); sqliteFree(pList); @@ -824,6 +849,7 @@ void sqliteCopy( Index *pIdx; zTab = sqliteTableNameFromToken(pTableName); + if( sqlite_malloc_failed || zTab==0 ) goto copy_cleanup; pTab = sqliteFindTable(pParse->db, zTab); sqliteFree(zTab); if( pTab==0 ){ @@ -891,6 +917,7 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){ char *zName; Vdbe *v; + if( pParse->nErr || sqlite_malloc_failed ) return; if( pTableName ){ zName = sqliteTableNameFromToken(pTableName); }else{ @@ -933,6 +960,7 @@ void sqliteBeginTransaction(Parse *pParse){ DbbeMethods *pM; sqlite *db; if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; + if( pParse->nErr || sqlite_malloc_failed ) return; if( db->flags & SQLITE_InTrans ) return; pM = pParse->db->pBe->x; if( pM && pM->BeginTransaction ){ @@ -953,6 +981,7 @@ void sqliteCommitTransaction(Parse *pParse){ DbbeMethods *pM; sqlite *db; if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; + if( pParse->nErr || sqlite_malloc_failed ) return; if( (db->flags & SQLITE_InTrans)==0 ) return; pM = pParse->db->pBe->x; if( pM && pM->Commit ){ @@ -973,6 +1002,7 @@ void sqliteRollbackTransaction(Parse *pParse){ DbbeMethods *pM; sqlite *db; if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; + if( pParse->nErr || sqlite_malloc_failed ) return; if( (db->flags & SQLITE_InTrans)==0 ) return; pM = pParse->db->pBe->x; if( pM && pM->Rollback ){ diff --git a/src/dbbe.c b/src/dbbe.c index 7f0d3bf4d7..8c57cba6d9 100644 --- a/src/dbbe.c +++ b/src/dbbe.c @@ -30,7 +30,7 @@ ** relatively simple to convert to a different database such ** as NDBM, SDBM, or BerkeleyDB. ** -** $Id: dbbe.c,v 1.26 2001/04/04 21:22:14 drh Exp $ +** $Id: dbbe.c,v 1.27 2001/04/11 14:28:42 drh Exp $ */ #include "sqliteInt.h" #include @@ -65,6 +65,7 @@ Dbbe *sqliteDbbeOpen( return sqliteGdbmOpen(zName, writeFlag, createFlag, pzErrMsg); } +#if 0 /* NOT USED */ /* ** Translate the name of an SQL table (or index) into the name ** of a file that holds the key/data pairs for that table or @@ -115,3 +116,4 @@ char *sqliteDbbeNameToFile( zFile[i] = 0; return zFile; } +#endif /* NOT USED */ diff --git a/src/dbbegdbm.c b/src/dbbegdbm.c index c91fd6eb27..142dd08632 100644 --- a/src/dbbegdbm.c +++ b/src/dbbegdbm.c @@ -30,7 +30,7 @@ ** relatively simple to convert to a different database such ** as NDBM, SDBM, or BerkeleyDB. ** -** $Id: dbbegdbm.c,v 1.6 2001/04/04 11:48:57 drh Exp $ +** $Id: dbbegdbm.c,v 1.7 2001/04/11 14:28:42 drh Exp $ */ #include "sqliteInt.h" #include @@ -185,6 +185,7 @@ static int sqliteGdbmOpenCursor( if( pCursr==0 ) return SQLITE_NOMEM; if( zTable ){ zFile = sqliteFileOfTable(pBe, zTable); + if( zFile==0 ) return SQLITE_NOMEM; for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){ if( strcmp(pFile->zName,zFile)==0 ) break; } diff --git a/src/dbbemem.c b/src/dbbemem.c index f90473e529..b37b20e66f 100644 --- a/src/dbbemem.c +++ b/src/dbbemem.c @@ -30,7 +30,7 @@ ** Nothing is ever written to disk using this backend. All information ** is forgotten when the program exits. ** -** $Id: dbbemem.c,v 1.13 2001/04/04 11:48:58 drh Exp $ +** $Id: dbbemem.c,v 1.14 2001/04/11 14:28:42 drh Exp $ */ #include "sqliteInt.h" #include @@ -156,6 +156,7 @@ static void ArrayRehash(Array *array, int new_size){ ArrayElem *x; /* Element being copied to new hash table */ new_ht = sqliteMalloc( new_size*sizeof(struct _Array_ht) ); + if( new_ht==0 ){ ArrayClear(array); return; } if( array->ht ) sqliteFree(array->ht); array->ht = new_ht; array->htsize = new_size; @@ -305,8 +306,13 @@ static Datum ArrayInsert(Array *array, Datum key, Datum data){ memcpy(new_elem->key.p, key.p, key.n); array->count++; if( array->htsize==0 ) ArrayRehash(array,4); + if( array->htsize==0 ) return nil; if( array->count > array->htsize ){ ArrayRehash(array,array->htsize*2); + if( array->htsize==0 ){ + sqliteFree(new_elem); + return nil; + } } h = hraw & (array->htsize-1); elem = array->ht[h].chain; @@ -468,6 +474,7 @@ static int sqliteMemOpenCursor( if( zTable ){ Datum key; zName = sqliteNameOfTable(zTable); + if( zName==0 ) return SQLITE_NOMEM; key.p = zName; key.n = strlen(zName); pTble = ArrayFind(&pBe->tables, key).p; @@ -690,6 +697,7 @@ static int sqliteMemPut( Datum data, key; data.n = nData; data.p = sqliteMalloc( data.n ); + if( data.p==0 ) return SQLITE_NOMEM; memcpy(data.p, pData, data.n); key.n = nKey; key.p = pKey; diff --git a/src/delete.c b/src/delete.c index c4da286220..75c6407471 100644 --- a/src/delete.c +++ b/src/delete.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** -** $Id: delete.c,v 1.8 2001/03/20 22:05:00 drh Exp $ +** $Id: delete.c,v 1.9 2001/04/11 14:28:42 drh Exp $ */ #include "sqliteInt.h" @@ -45,12 +45,18 @@ void sqliteDeleteFrom( Index *pIdx; /* For looping over indices of the table */ int base; /* Index of the first available table cursor */ + if( pParse->nErr || sqlite_malloc_failed ){ + pTabList = 0; + goto delete_from_cleanup; + } + /* Locate the table which we want to delete. This table has to be ** put in an IdList structure because some of the subroutines we ** will be calling are designed to work with multiple tables and expect ** an IdList* parameter instead of just a Table* parameger. */ pTabList = sqliteIdListAppend(0, pTableName); + if( pTabList==0 ) goto delete_from_cleanup; for(i=0; inId; i++){ pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); if( pTabList->a[i].pTab==0 ){ diff --git a/src/expr.c b/src/expr.c index 0cad95c7c0..29ee44c559 100644 --- a/src/expr.c +++ b/src/expr.c @@ -24,7 +24,7 @@ ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions. ** -** $Id: expr.c,v 1.23 2001/04/04 21:22:14 drh Exp $ +** $Id: expr.c,v 1.24 2001/04/11 14:28:42 drh Exp $ */ #include "sqliteInt.h" @@ -123,13 +123,14 @@ static int sqliteIsRowid(const char *z){ ** the number of errors seen and leaves an error message on pParse->zErrMsg. */ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){ - if( pExpr==0 ) return 0; + if( pExpr==0 || pTabList==0 ) return 0; switch( pExpr->op ){ /* A lone identifier */ case TK_ID: { int cnt = 0; /* Number of matches */ int i; /* Loop counter */ char *z = sqliteStrNDup(pExpr->token.z, pExpr->token.n); + if( z==0 ) return 1; for(i=0; inId; i++){ int j; Table *pTab = pTabList->a[i].pTab; @@ -177,6 +178,11 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){ assert( pRight && pRight->op==TK_ID ); zLeft = sqliteStrNDup(pLeft->token.z, pLeft->token.n); zRight = sqliteStrNDup(pRight->token.z, pRight->token.n); + if( zLeft==0 || zRight==0 ){ + sqliteFree(zLeft); + sqliteFree(zRight); + return 1; + } pExpr->iTable = -1; for(i=0; inId; i++){ int j; @@ -480,6 +486,7 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){ void sqliteExprCode(Parse *pParse, Expr *pExpr){ Vdbe *v = pParse->pVdbe; int op; + if( v==0 || pExpr==0 ) return; switch( pExpr->op ){ case TK_PLUS: op = OP_Add; break; case TK_MINUS: op = OP_Subtract; break; @@ -683,6 +690,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){ Vdbe *v = pParse->pVdbe; int op = 0; + if( v==0 || pExpr==0 ) return; switch( pExpr->op ){ case TK_LT: op = OP_Lt; break; case TK_LE: op = OP_Le; break; @@ -769,6 +777,7 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){ Vdbe *v = pParse->pVdbe; int op = 0; + if( v==0 || pExpr==0 ) return; switch( pExpr->op ){ case TK_LT: op = OP_Ge; break; case TK_LE: op = OP_Gt; break; @@ -896,8 +905,7 @@ static int appendAggInfo(Parse *pParse){ int amt = pParse->nAgg + 8; pParse->aAgg = sqliteRealloc(pParse->aAgg, amt*sizeof(pParse->aAgg[0])); if( pParse->aAgg==0 ){ - sqliteSetString(&pParse->zErrMsg, "out of memory", 0); - pParse->nErr++; + pParse->nAgg = 0; return -1; } } diff --git a/src/insert.c b/src/insert.c index 553a1c8516..7756b10b94 100644 --- a/src/insert.c +++ b/src/insert.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements. ** -** $Id: insert.c,v 1.12 2001/01/15 22:51:11 drh Exp $ +** $Id: insert.c,v 1.13 2001/04/11 14:28:42 drh Exp $ */ #include "sqliteInt.h" @@ -60,9 +60,12 @@ void sqliteInsert( int base; /* First available cursor */ int iCont, iBreak; /* Beginning and end of the loop over srcTab */ + if( pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup; + /* Locate the table into which we will be inserting new information. */ zTab = sqliteTableNameFromToken(pTableName); + if( zTab==0 ) goto insert_cleanup; pTab = sqliteFindTable(pParse->db, zTab); sqliteFree(zTab); if( pTab==0 ){ @@ -94,10 +97,11 @@ void sqliteInsert( srcTab = pParse->nTab++; sqliteVdbeAddOp(v, OP_OpenTbl, srcTab, 1, 0, 0); rc = sqliteSelect(pParse, pSelect, SRT_Table, srcTab); - if( rc ) goto insert_cleanup; + if( rc || pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup; assert( pSelect->pEList ); nColumn = pSelect->pEList->nExpr; }else{ + assert( pList!=0 ); srcTab = -1; assert( pList ); nColumn = pList->nExpr; diff --git a/src/main.c b/src/main.c index faab88ee67..431aacea6e 100644 --- a/src/main.c +++ b/src/main.c @@ -26,7 +26,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.27 2001/04/06 16:13:43 drh Exp $ +** $Id: main.c,v 1.28 2001/04/11 14:28:42 drh Exp $ */ #include "sqliteInt.h" #include @@ -157,8 +157,8 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){ */ vdbe = sqliteVdbeCreate(db); if( vdbe==0 ){ - sqliteSetString(pzErrMsg, "out of memory",0); - return 1; + sqliteSetString(pzErrMsg, "out of memory"); + return SQLITE_NOMEM; } sqliteVdbeAddOpList(vdbe, sizeof(initProg)/sizeof(initProg[0]), initProg); rc = sqliteVdbeExec(vdbe, sqliteOpenCb, db, pzErrMsg, @@ -179,8 +179,6 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){ pTab->readOnly = 1; } db->flags |= SQLITE_Initialized; - }else{ - sqliteStrRealloc(pzErrMsg); } return rc; } @@ -216,17 +214,13 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){ /* Allocate the sqlite data structure */ db = sqliteMalloc( sizeof(sqlite) ); if( pzErrMsg ) *pzErrMsg = 0; - if( db==0 ){ - sqliteSetString(pzErrMsg, "out of memory", 0); - sqliteStrRealloc(pzErrMsg); - return 0; - } + if( db==0 ) goto no_mem_on_open; /* Open the backend database driver */ db->pBe = sqliteDbbeOpen(zFilename, (mode&0222)!=0, mode!=0, pzErrMsg); if( db->pBe==0 ){ - sqliteStrRealloc(pzErrMsg); sqliteFree(db); + sqliteStrRealloc(pzErrMsg); return 0; } @@ -235,14 +229,21 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){ /* Attempt to read the schema */ rc = sqliteInit(db, pzErrMsg); - if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ + if( sqlite_malloc_failed ){ + goto no_mem_on_open; + }else if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ sqlite_close(db); return 0; }else /* if( pzErrMsg ) */{ - free(*pzErrMsg); + sqliteFree(*pzErrMsg); *pzErrMsg = 0; } return db; + +no_mem_on_open: + sqliteSetString(pzErrMsg, "out of memory", 0); + sqliteStrRealloc(pzErrMsg); + return 0; } /* @@ -336,13 +337,21 @@ int sqlite_exec( if( pzErrMsg ) *pzErrMsg = 0; if( (db->flags & SQLITE_Initialized)==0 ){ int rc = sqliteInit(db, pzErrMsg); - if( rc!=SQLITE_OK ) return rc; + if( rc!=SQLITE_OK ){ + sqliteStrRealloc(pzErrMsg); + return rc; + } } memset(&sParse, 0, sizeof(sParse)); sParse.db = db; sParse.xCallback = xCallback; sParse.pArg = pArg; sqliteRunParser(&sParse, zSql, pzErrMsg); + if( sqlite_malloc_failed ){ + sqliteSetString(pzErrMsg, "out of memory", 0); + sParse.rc = SQLITE_NOMEM; + } + sqliteStrRealloc(pzErrMsg); return sParse.rc; } @@ -352,7 +361,7 @@ int sqlite_exec( ** an integer number of milliseconds passed in as the first ** argument. */ -static int sqlite_default_busy_callback( +static int sqliteDefaultBusyCallback( void *Timeout, /* Maximum amount of time to wait */ const char *NotUsed, /* The name of the table that is busy */ int count /* Number of times table has been busy */ @@ -407,7 +416,7 @@ void sqlite_busy_handler( */ void sqlite_busy_timeout(sqlite *db, int ms){ if( ms>0 ){ - sqlite_busy_handler(db, sqlite_default_busy_callback, (void*)ms); + sqlite_busy_handler(db, sqliteDefaultBusyCallback, (void*)ms); }else{ sqlite_busy_handler(db, 0, 0); } diff --git a/src/parse.y b/src/parse.y index 68c30214e6..1049150f6d 100644 --- a/src/parse.y +++ b/src/parse.y @@ -26,7 +26,7 @@ ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.27 2001/04/04 11:48:58 drh Exp $ +** @(#) $Id: parse.y,v 1.28 2001/04/11 14:28:42 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -170,9 +170,11 @@ cmd ::= select(X). { select(A) ::= oneselect(X). {A = X;} select(A) ::= select(X) joinop(Y) oneselect(Z). { + if( Z ){ Z->op = Y; Z->pPrior = X; - A = Z; + } + A = Z; } %type joinop {int} joinop(A) ::= UNION. {A = TK_UNION;} @@ -236,11 +238,11 @@ orderby_opt(A) ::= . {A = 0;} orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;} sortlist(A) ::= sortlist(X) COMMA sortitem(Y) sortorder(Z). { A = sqliteExprListAppend(X,Y,0); - A->a[A->nExpr-1].sortOrder = Z; /* 0 for ascending order, 1 for decending */ + if( A ) A->a[A->nExpr-1].sortOrder = Z; /* 0=ascending, 1=decending */ } sortlist(A) ::= sortitem(Y) sortorder(Z). { A = sqliteExprListAppend(0,Y,0); - A->a[0].sortOrder = Z; + if( A ) A->a[0].sortOrder = Z; } sortitem(A) ::= expr(X). {A = X;} @@ -297,13 +299,13 @@ item(A) ::= INTEGER(X). {A = sqliteExpr(TK_INTEGER, 0, 0, &X);} item(A) ::= PLUS INTEGER(X). {A = sqliteExpr(TK_INTEGER, 0, 0, &X);} item(A) ::= MINUS INTEGER(X). { A = sqliteExpr(TK_UMINUS, 0, 0, 0); - A->pLeft = sqliteExpr(TK_INTEGER, 0, 0, &X); + if( A ) A->pLeft = sqliteExpr(TK_INTEGER, 0, 0, &X); } item(A) ::= FLOAT(X). {A = sqliteExpr(TK_FLOAT, 0, 0, &X);} item(A) ::= PLUS FLOAT(X). {A = sqliteExpr(TK_FLOAT, 0, 0, &X);} item(A) ::= MINUS FLOAT(X). { A = sqliteExpr(TK_UMINUS, 0, 0, 0); - A->pLeft = sqliteExpr(TK_FLOAT, 0, 0, &X); + if( A ) A->pLeft = sqliteExpr(TK_FLOAT, 0, 0, &X); } item(A) ::= STRING(X). {A = sqliteExpr(TK_STRING, 0, 0, &X);} item(A) ::= NULL. {A = sqliteExpr(TK_NULL, 0, 0, 0);} @@ -397,43 +399,43 @@ expr(A) ::= PLUS(B) expr(X). [UMINUS] { } expr(A) ::= LP(B) select(X) RP(E). { A = sqliteExpr(TK_SELECT, 0, 0, 0); - A->pSelect = X; + if( A ) A->pSelect = X; sqliteExprSpan(A,&B,&E); } expr(A) ::= expr(W) BETWEEN expr(X) AND expr(Y). { ExprList *pList = sqliteExprListAppend(0, X, 0); pList = sqliteExprListAppend(pList, Y, 0); A = sqliteExpr(TK_BETWEEN, W, 0, 0); - A->pList = pList; + if( A ) A->pList = pList; sqliteExprSpan(A,&W->span,&Y->span); } expr(A) ::= expr(W) NOT BETWEEN expr(X) AND expr(Y). { ExprList *pList = sqliteExprListAppend(0, X, 0); pList = sqliteExprListAppend(pList, Y, 0); A = sqliteExpr(TK_BETWEEN, W, 0, 0); - A->pList = pList; + if( A ) A->pList = pList; A = sqliteExpr(TK_NOT, A, 0, 0); sqliteExprSpan(A,&W->span,&Y->span); } expr(A) ::= expr(X) IN LP exprlist(Y) RP(E). { A = sqliteExpr(TK_IN, X, 0, 0); - A->pList = Y; + if( A ) A->pList = Y; sqliteExprSpan(A,&X->span,&E); } expr(A) ::= expr(X) IN LP select(Y) RP(E). { A = sqliteExpr(TK_IN, X, 0, 0); - A->pSelect = Y; + if( A ) A->pSelect = Y; sqliteExprSpan(A,&X->span,&E); } expr(A) ::= expr(X) NOT IN LP exprlist(Y) RP(E). { A = sqliteExpr(TK_IN, X, 0, 0); - A->pList = Y; + if( A ) A->pList = Y; A = sqliteExpr(TK_NOT, A, 0, 0); sqliteExprSpan(A,&X->span,&E); } expr(A) ::= expr(X) NOT IN LP select(Y) RP(E). { A = sqliteExpr(TK_IN, X, 0, 0); - A->pSelect = Y; + if( A ) A->pSelect = Y; A = sqliteExpr(TK_NOT, A, 0, 0); sqliteExprSpan(A,&X->span,&E); } diff --git a/src/printf.c b/src/printf.c index b099523dcc..bbde922343 100644 --- a/src/printf.c +++ b/src/printf.c @@ -563,6 +563,7 @@ static int vxprintf( n += i + 1; if( n>etBUFSIZE ){ bufpt = zExtra = sqliteMalloc( n ); + if( bufpt==0 ) return -1; }else{ bufpt = buf; } diff --git a/src/select.c b/src/select.c index c3abbe4e53..1a6eb4fefd 100644 --- a/src/select.c +++ b/src/select.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements. ** -** $Id: select.c,v 1.30 2001/04/04 11:48:58 drh Exp $ +** $Id: select.c,v 1.31 2001/04/11 14:28:42 drh Exp $ */ #include "sqliteInt.h" @@ -33,25 +33,33 @@ ** structure. */ Select *sqliteSelectNew( - ExprList *pEList, - IdList *pSrc, - Expr *pWhere, - ExprList *pGroupBy, - Expr *pHaving, - ExprList *pOrderBy, - int isDistinct + ExprList *pEList, /* which columns to include in the result */ + IdList *pSrc, /* the FROM clause -- which tables to scan */ + Expr *pWhere, /* the WHERE clause */ + ExprList *pGroupBy, /* the GROUP BY clause */ + Expr *pHaving, /* the HAVING clause */ + ExprList *pOrderBy, /* the ORDER BY clause */ + int isDistinct /* true if the DISTINCT keyword is present */ ){ Select *pNew; pNew = sqliteMalloc( sizeof(*pNew) ); - if( pNew==0 ) return 0; - pNew->pEList = pEList; - pNew->pSrc = pSrc; - pNew->pWhere = pWhere; - pNew->pGroupBy = pGroupBy; - pNew->pHaving = pHaving; - pNew->pOrderBy = pOrderBy; - pNew->isDistinct = isDistinct; - pNew->op = TK_SELECT; + if( pNew==0 ){ + sqliteExprListDelete(pEList); + sqliteIdListDelete(pSrc); + sqliteExprDelete(pWhere); + sqliteExprListDelete(pGroupBy); + sqliteExprDelete(pHaving); + sqliteExprListDelete(pOrderBy); + }else{ + pNew->pEList = pEList; + pNew->pSrc = pSrc; + pNew->pWhere = pWhere; + pNew->pGroupBy = pGroupBy; + pNew->pHaving = pHaving; + pNew->pOrderBy = pOrderBy; + pNew->isDistinct = isDistinct; + pNew->op = TK_SELECT; + } return pNew; } @@ -103,6 +111,7 @@ static int selectInnerLoop( ){ Vdbe *v = pParse->pVdbe; int i; + if( v==0 ) return 0; /* Pull the requested columns. */ @@ -117,8 +126,9 @@ static int selectInnerLoop( } } - /* If the current result is not distinct, skip the rest - ** of the processing for the current row. + /* If the DISTINCT keyword was present on the SELECT statement + ** and this row has been seen before, then do not make this row + ** part of the result. */ if( distinct>=0 ){ int lbl = sqliteVdbeMakeLabel(v); @@ -229,7 +239,7 @@ static void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){ Vdbe *v = pParse->pVdbe; int i; - if( pParse->colNamesSet ) return; + if( pParse->colNamesSet || v==0 || sqlite_malloc_failed ) return; pParse->colNamesSet = 1; sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0); for(i=0; inExpr; i++){ @@ -241,6 +251,7 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){ continue; } p = pEList->a[i].pExpr; + if( p==0 ) continue; if( p->span.z && p->span.z[0] ){ addr = sqliteVdbeAddOp(v,OP_ColumnName, i, 0, 0, 0); sqliteVdbeChangeP3(v, addr, p->span.z, p->span.n); @@ -299,8 +310,12 @@ static const char *selectOpName(int id){ */ static int fillInColumnList(Parse *pParse, Select *p){ int i, j; - IdList *pTabList = p->pSrc; - ExprList *pEList = p->pEList; + IdList *pTabList; + ExprList *pEList; + + if( p==0 || p->pSrc==0 ) return 1; + pTabList = p->pSrc; + pEList = p->pEList; /* Look up every table in the table list. */ @@ -309,6 +324,17 @@ static int fillInColumnList(Parse *pParse, Select *p){ /* This routine has run before! No need to continue */ return 0; } + if( pTabList->a[i].zName==0 ){ + /* No table name is given. Instead, there is a (SELECT ...) statement + ** the results of which should be used in place of the table. The + ** was this is implemented is that the (SELECT ...) writes its results + ** into a temporary table which is then scanned like any other table. + */ + sqliteSetString(&pParse->zErrMsg, + "(SELECT...) in a FROM clause is not yet implemented.", 0); + pParse->nErr++; + return 1; + } pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); if( pTabList->a[i].pTab==0 ){ sqliteSetString(&pParse->zErrMsg, "no such table: ", @@ -326,10 +352,13 @@ static int fillInColumnList(Parse *pParse, Select *p){ Table *pTab = pTabList->a[i].pTab; for(j=0; jnCol; j++){ Expr *pExpr = sqliteExpr(TK_DOT, 0, 0, 0); + if( pExpr==0 ) break; pExpr->pLeft = sqliteExpr(TK_ID, 0, 0, 0); + if( pExpr->pLeft==0 ) break; pExpr->pLeft->token.z = pTab->zName; pExpr->pLeft->token.n = strlen(pTab->zName); pExpr->pRight = sqliteExpr(TK_ID, 0, 0, 0); + if( pExpr->pRight==0 ) break; pExpr->pRight->token.z = pTab->aCol[j].zName; pExpr->pRight->token.n = strlen(pTab->aCol[j].zName); pExpr->span.z = ""; @@ -366,7 +395,7 @@ static int matchOrderbyToColumn( int i, j; ExprList *pEList; - assert( pSelect && pOrderBy ); + if( pSelect==0 || pOrderBy==0 ) return 1; if( mustComplete ){ for(i=0; inExpr; i++){ pOrderBy->a[i].done = 0; } } @@ -426,10 +455,6 @@ Vdbe *sqliteGetVdbe(Parse *pParse){ if( v==0 ){ v = pParse->pVdbe = sqliteVdbeCreate(pParse->db); } - if( v==0 ){ - sqliteSetString(&pParse->zErrMsg, "out of memory", 0); - pParse->nErr++; - } return v; } @@ -447,7 +472,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ /* Make sure there is no ORDER BY clause on prior SELECTs. Only the ** last SELECT in the series may have an ORDER BY. */ - assert( p->pPrior!=0 ); + if( p==0 || p->pPrior==0 ) return 1; pPrior = p->pPrior; if( pPrior->pOrderBy ){ sqliteSetString(&pParse->zErrMsg,"ORDER BY clause should come after ", @@ -651,6 +676,8 @@ int sqliteSelect( int distinct; /* Table to use for the distinct set */ int base; /* First cursor available for use */ + if( sqlite_malloc_failed || pParse->nErr || p==0 ) return 1; + /* If there is are a sequence of queries, do the earlier ones first. */ if( p->pPrior ){ @@ -686,6 +713,7 @@ int sqliteSelect( return 1; } pEList = p->pEList; + if( pEList==0 ) return 1; /* Allocate a temporary table to use for the DISTINCT set, if ** necessary. This must be done early to allocate the cursor before @@ -820,15 +848,8 @@ int sqliteSelect( /* Begin generating code. */ - v = pParse->pVdbe; - if( v==0 ){ - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db); - } - if( v==0 ){ - sqliteSetString(&pParse->zErrMsg, "out of memory", 0); - pParse->nErr++; - return 1; - } + v = sqliteGetVdbe(pParse); + if( v==0 ) return 1; if( pOrderBy ){ sqliteVdbeAddOp(v, OP_SortOpen, 0, 0, 0, 0); } diff --git a/src/shell.c b/src/shell.c index 8202b985cc..d3f43f6718 100644 --- a/src/shell.c +++ b/src/shell.c @@ -24,7 +24,7 @@ ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. ** -** $Id: shell.c,v 1.30 2001/04/04 21:22:14 drh Exp $ +** $Id: shell.c,v 1.31 2001/04/11 14:28:42 drh Exp $ */ #include #include @@ -60,7 +60,7 @@ static sqlite *db = 0; ** The interface is like "readline" but no command-line editing ** is done. */ -static char *getline(char *zPrompt){ +static char *getline(char *zPrompt, FILE *in){ char *zLine; int nLine; int n; @@ -81,7 +81,7 @@ static char *getline(char *zPrompt){ zLine = realloc(zLine, nLine); if( zLine==0 ) return 0; } - if( fgets(&zLine[n], nLine - n, stdin)==0 ){ + if( fgets(&zLine[n], nLine - n, in)==0 ){ if( n==0 ){ free(zLine); return 0; @@ -110,11 +110,11 @@ static char *getline(char *zPrompt){ ** zPrior is a string of prior text retrieved. If not the empty ** string, then issue a continuation prompt. */ -static char *one_input_line(const char *zPrior, int isatty){ +static char *one_input_line(const char *zPrior, FILE *in){ char *zPrompt; char *zResult; - if( !isatty ){ - return getline(0); + if( in!=0 ){ + return getline(0, in); } if( zPrior && zPrior[0] ){ zPrompt = " ...> "; @@ -133,6 +133,7 @@ static char *one_input_line(const char *zPrior, int isatty){ */ struct callback_data { sqlite *db; /* The database */ + int echoOn; /* True to echo input commands */ int cnt; /* Number of records displayed so far */ FILE *out; /* Write results here */ int mode; /* An output mode setting */ @@ -389,23 +390,23 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){ ** This routine should print text sufficient to recreate the table. */ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){ - struct callback_data *pData = (struct callback_data *)pArg; + struct callback_data *p = (struct callback_data *)pArg; if( nArg!=3 ) return 1; - fprintf(pData->out, "%s;\n", azArg[2]); + fprintf(p->out, "%s;\n", azArg[2]); if( strcmp(azArg[1],"table")==0 ){ struct callback_data d2; - d2 = *pData; + d2 = *p; d2.mode = MODE_List; d2.escape = '\t'; strcpy(d2.separator,"\t"); - fprintf(pData->out, "COPY '%s' FROM STDIN;\n", azArg[0]); - sqlite_exec_printf(pData->db, + fprintf(p->out, "COPY '%s' FROM STDIN;\n", azArg[0]); + sqlite_exec_printf(p->db, "SELECT * FROM '%q'", callback, &d2, 0, azArg[0] ); - fprintf(pData->out, "\\.\n"); + fprintf(p->out, "\\.\n"); } - fprintf(pData->out, "VACUUM '%s';\n", azArg[0]); + fprintf(p->out, "VACUUM '%s';\n", azArg[0]); return 0; } @@ -414,6 +415,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){ */ static char zHelp[] = ".dump ?TABLE? ... Dump the database in an text format\n" + ".echo ON|OFF Turn command echo on or off\n" ".exit Exit this program\n" ".explain Set output mode suitable for EXPLAIN\n" ".header ON|OFF Turn display of headers on or off\n" @@ -424,6 +426,9 @@ static char zHelp[] = ".mode insert TABLE Generate SQL insert statements for TABLE\n" ".output FILENAME Send output to FILENAME\n" ".output stdout Send output to the screen\n" + ".read FILENAME Execute SQL in FILENAME\n" + ".reindex ?TABLE? Rebuild indices\n" +/* ".rename OLD NEW Change the name of a table or index\n" */ ".schema ?TABLE? Show the CREATE statements\n" ".separator STRING Change separator string for \"list\" mode\n" ".tables ?PATTERN? List names of tables matching a pattern\n" @@ -431,6 +436,9 @@ static char zHelp[] = ".width NUM NUM ... Set column widths for \"column\" mode\n" ; +/* Forward reference */ +static void process_input(struct callback_data *p, FILE *in); + /* ** If an input line begins with "." then invoke this routine to ** process that line. @@ -491,6 +499,21 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){ } }else + if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 ){ + int j; + char *z = azArg[1]; + int val = atoi(azArg[1]); + for(j=0; z[j]; j++){ + if( isupper(z[j]) ) z[j] = tolower(z[j]); + } + if( strcmp(z,"on")==0 ){ + val = 1; + }else if( strcmp(z,"yes")==0 ){ + val = 1; + } + p->echoOn = val; + }else + if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ exit(0); }else @@ -559,6 +582,8 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){ }else{ sprintf(p->zDestTable,"table"); } + }else { + fprintf(stderr,"mode should be on of: column html insert line list\n"); } }else @@ -577,6 +602,50 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){ } }else + if( c=='r' && strncmp(azArg[0], "read", n)==0 && nArg==2 ){ + FILE *alt = fopen(azArg[1], "r"); + if( alt==0 ){ + fprintf(stderr,"can't open \"%s\"\n", azArg[1]); + }else{ + process_input(p, alt); + fclose(alt); + } + }else + + if( c=='r' && strncmp(azArg[0], "reindex", n)==0 ){ + char **azResult; + int nRow, rc; + char *zErrMsg; + int i; + char *zSql; + if( nArg==1 ){ + rc = sqlite_get_table(db, + "SELECT name, sql FROM sqlite_master " + "WHERE type='index'", + &azResult, &nRow, 0, &zErrMsg + ); + }else{ + rc = sqlite_get_table_printf(db, + "SELECT name, sql FROM sqlite_master " + "WHERE type='index' AND tbl_name LIKE '%q'", + &azResult, &nRow, 0, &zErrMsg, azArg[1] + ); + } + for(i=1; rc==SQLITE_OK && i<=nRow; i++){ + extern char *sqlite_mprintf(const char *, ...); + zSql = sqlite_mprintf( + "DROP INDEX '%q';\n%s;\nVACUUM '%q';", + azResult[i*2], azResult[i*2+1], azResult[i*2]); + if( p->echoOn ) printf("%s\n", zSql); + rc = sqlite_exec(db, zSql, 0, 0, &zErrMsg); + } + sqlite_free_table(azResult); + if( zErrMsg ){ + fprintf(stderr,"Error: %s\n", zErrMsg); + free(zErrMsg); + } + }else + if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ struct callback_data data; char *zErrMsg = 0; @@ -685,12 +754,64 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){ } } +static char *Argv0; +static void process_input(struct callback_data *p, FILE *in){ + char *zLine; + char *zSql = 0; + int nSql = 0; + char *zErrMsg; + while( (zLine = one_input_line(zSql, in))!=0 ){ + if( p->echoOn ) printf("%s\n", zLine); + if( zLine && zLine[0]=='.' ){ + do_meta_command(zLine, db, p); + free(zLine); + continue; + } + if( zSql==0 ){ + int i; + for(i=0; zLine[i] && isspace(zLine[i]); i++){} + if( zLine[i]!=0 ){ + nSql = strlen(zLine); + zSql = malloc( nSql+1 ); + strcpy(zSql, zLine); + } + }else{ + int len = strlen(zLine); + zSql = realloc( zSql, nSql + len + 2 ); + if( zSql==0 ){ + fprintf(stderr,"%s: out of memory!\n", Argv0); + exit(1); + } + strcpy(&zSql[nSql++], "\n"); + strcpy(&zSql[nSql], zLine); + nSql += len; + } + free(zLine); + if( zSql && sqlite_complete(zSql) ){ + p->cnt = 0; + if( sqlite_exec(db, zSql, callback, p, &zErrMsg)!=0 + && zErrMsg!=0 ){ + if( in!=0 && !p->echoOn ) printf("%s\n",zSql); + printf("SQL error: %s\n", zErrMsg); + free(zErrMsg); + zErrMsg = 0; + } + free(zSql); + zSql = 0; + nSql = 0; + } + } + if( zSql ){ + printf("Incomplete SQL: %s\n", zSql); + free(zSql); + } +} + int main(int argc, char **argv){ char *zErrMsg = 0; - char *argv0 = argv[0]; struct callback_data data; - int echo = 0; + Argv0 = argv[0]; memset(&data, 0, sizeof(data)); data.mode = MODE_List; strcpy(data.separator,"|"); @@ -724,16 +845,16 @@ int main(int argc, char **argv){ argc--; argv++; }else if( strcmp(argv[1],"-echo")==0 ){ - echo = 1; + data.echoOn = 1; argc--; argv++; }else{ - fprintf(stderr,"%s: unknown option: %s\n", argv0, argv[1]); + fprintf(stderr,"%s: unknown option: %s\n", Argv0, argv[1]); return 1; } } if( argc!=2 && argc!=3 ){ - fprintf(stderr,"Usage: %s ?OPTIONS? FILENAME ?SQL?\n", argv0); + fprintf(stderr,"Usage: %s ?OPTIONS? FILENAME ?SQL?\n", Argv0); exit(1); } data.db = db = sqlite_open(argv[1], 0666, &zErrMsg); @@ -757,57 +878,15 @@ int main(int argc, char **argv){ exit(1); } }else{ - char *zLine; - char *zSql = 0; - int nSql = 0; - int istty = isatty(0); - if( istty ){ + if( isatty(0) ){ printf( "SQLite version %s\n" "Enter \".help\" for instructions\n", sqlite_version ); - } - while( (zLine = one_input_line(zSql, istty))!=0 ){ - if( echo ) printf("%s\n", zLine); - if( zLine && zLine[0]=='.' ){ - do_meta_command(zLine, db, &data); - free(zLine); - continue; - } - if( zSql==0 ){ - int i; - for(i=0; zLine[i] && isspace(zLine[i]); i++){} - if( zLine[i]!=0 ){ - nSql = strlen(zLine); - zSql = malloc( nSql+1 ); - strcpy(zSql, zLine); - } - }else{ - int len = strlen(zLine); - zSql = realloc( zSql, nSql + len + 2 ); - if( zSql==0 ){ - fprintf(stderr,"%s: out of memory!\n", argv0); - exit(1); - } - strcpy(&zSql[nSql++], "\n"); - strcpy(&zSql[nSql], zLine); - nSql += len; - } - free(zLine); - if( zSql && sqlite_complete(zSql) ){ - data.cnt = 0; - if( sqlite_exec(db, zSql, callback, &data, &zErrMsg)!=0 - && zErrMsg!=0 ){ - if( !istty && !echo ) printf("%s\n",zSql); - printf("SQL error: %s\n", zErrMsg); - free(zErrMsg); - zErrMsg = 0; - } - free(zSql); - zSql = 0; - nSql = 0; - } + process_input(&data, 0); + }else{ + process_input(&data, stdin); } } sqlite_close(db); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c78ba21746..4a62e12492 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -23,7 +23,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.40 2001/04/07 15:24:33 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.41 2001/04/11 14:28:43 drh Exp $ */ #include "sqlite.h" #include "dbbe.h" @@ -65,6 +65,12 @@ typedef unsigned int u32; # define sqliteStrRealloc(X) #endif +/* +** This variable gets set if malloc() ever fails. After it gets set, +** the SQLite library shuts down permanently. +*/ +extern int sqlite_malloc_failed; + /* ** The following global variables are used for testing and debugging ** only. They only work if MEMORY_DEBUG is defined. @@ -257,8 +263,9 @@ struct IdList { struct { char *zName; /* Text of the identifier. */ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ - Table *pTab; /* An SQL table corresponding to zName */ int idx; /* Index in some Table.aCol[] of a column named zName */ + Table *pTab; /* An SQL table corresponding to zName */ + Select *pSelect; /* A SELECT statement used in place of a table name */ } *a; /* One entry for each identifier on the list */ }; diff --git a/src/table.c b/src/table.c index d713ee1842..8f61d6f2f5 100644 --- a/src/table.c +++ b/src/table.c @@ -140,7 +140,7 @@ int sqlite_get_table( res.nRow = 0; res.nColumn = 0; res.nData = 1; - res.nAlloc = 200; + res.nAlloc = 20; res.rc = SQLITE_OK; res.azResult = malloc( sizeof(char*)*res.nAlloc ); if( res.azResult==0 ){ diff --git a/src/test1.c b/src/test1.c index e976da0d86..64cf61cfc8 100644 --- a/src/test1.c +++ b/src/test1.c @@ -25,7 +25,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.1 2001/04/07 15:24:33 drh Exp $ +** $Id: test1.c,v 1.2 2001/04/11 14:28:43 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -267,6 +267,50 @@ static int sqlite_mprintf_double( return TCL_OK; } +/* +** Usage: sqlite_malloc_fail N +** +** Rig sqliteMalloc() to fail on the N-th call. Turn of this mechanism +** and reset the sqlite_malloc_failed variable is N==0. +*/ +#ifdef MEMORY_DEBUG +static int sqlite_malloc_fail( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + char **argv /* Text of each argument */ +){ + int n; + if( argc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " N\"", 0); + return TCL_ERROR; + } + if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR; + sqlite_iMallocFail = n; + sqlite_malloc_failed = 0; + return TCL_OK; +} +#endif + +/* +** Usage: sqlite_malloc_stat +** +** Return the number of prior calls to sqliteMalloc() and sqliteFree(). +*/ +#ifdef MEMORY_DEBUG +static int sqlite_malloc_stat( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + char **argv /* Text of each argument */ +){ + char zBuf[200]; + sprintf(zBuf, "%d %d %d", sqlite_nMalloc, sqlite_nFree, sqlite_iMallocFail); + Tcl_AppendResult(interp, zBuf, 0); + return TCL_OK; +} +#endif + /* ** Register commands with the TCL interpreter. */ @@ -279,5 +323,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ Tcl_CreateCommand(interp, "sqlite_get_table_printf", test_get_table_printf, 0, 0); Tcl_CreateCommand(interp, "sqlite_close", sqlite_test_close, 0, 0); +#ifdef MEMORY_DEBUG + Tcl_CreateCommand(interp, "sqlite_malloc_fail", sqlite_malloc_fail, 0, 0); + Tcl_CreateCommand(interp, "sqlite_malloc_stat", sqlite_malloc_stat, 0, 0); +#endif return TCL_OK; } diff --git a/src/tokenize.c b/src/tokenize.c index 164eadf0b0..2572b96d1e 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -27,7 +27,7 @@ ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** -** $Id: tokenize.c,v 1.18 2001/04/04 11:48:58 drh Exp $ +** $Id: tokenize.c,v 1.19 2001/04/11 14:28:43 drh Exp $ */ #include "sqliteInt.h" #include @@ -107,7 +107,7 @@ static Keyword aKeywordTable[] = { /* ** This is the hash table */ -#define KEY_HASH_SIZE 69 +#define KEY_HASH_SIZE 71 static Keyword *apHashTable[KEY_HASH_SIZE]; @@ -328,7 +328,7 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){ #ifndef NDEBUG sqliteParserTrace(trace, "parser: "); #endif - while( nErr==0 && i>=0 && zSql[i]!=0 ){ + while( sqlite_malloc_failed==0 && nErr==0 && i>=0 && zSql[i]!=0 ){ int tokenType; if( (pParse->db->flags & SQLITE_Interrupt)!=0 ){ @@ -363,24 +363,6 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){ pParse->db->flags |= SQLITE_VdbeTrace; }else if( sqliteStrNICmp(z,"--vdbe-trace-off--", 18)==0 ){ pParse->db->flags &= ~SQLITE_VdbeTrace; -#ifdef MEMORY_DEBUG - }else if( sqliteStrNICmp(z,"--malloc-fail=",14)==0 ){ - sqlite_iMallocFail = atoi(&z[14]); - }else if( sqliteStrNICmp(z,"--malloc-stats--", 16)==0 ){ - if( pParse->xCallback ){ - static char *azName[4] = {"malloc", "free", "to_fail", 0 }; - char *azArg[4]; - char zVal[3][30]; - sprintf(zVal[0],"%d", sqlite_nMalloc); - sprintf(zVal[1],"%d", sqlite_nFree); - sprintf(zVal[2],"%d", sqlite_iMallocFail); - azArg[0] = zVal[0]; - azArg[1] = zVal[1]; - azArg[2] = zVal[2]; - azArg[3] = 0; - pParse->xCallback(pParse->pArg, 3, azArg, azName); - } -#endif } #endif break; @@ -437,7 +419,6 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){ pParse->pNewTable = 0; } sqliteParseInfoReset(pParse); - sqliteStrRealloc(pzErrMsg); if( nErr>0 && pParse->rc==SQLITE_OK ){ pParse->rc = SQLITE_ERROR; } diff --git a/src/update.c b/src/update.c index 44ad930a5e..dbe7af7388 100644 --- a/src/update.c +++ b/src/update.c @@ -24,7 +24,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.10 2001/03/14 12:35:57 drh Exp $ +** $Id: update.c,v 1.11 2001/04/11 14:28:43 drh Exp $ */ #include "sqliteInt.h" @@ -51,12 +51,15 @@ void sqliteUpdate( ** an expression for the i-th column of the table. ** aXRef[i]==-1 if the i-th column is not changed. */ + if( pParse->nErr || sqlite_malloc_failed ) goto update_cleanup; + /* Locate the table which we want to update. This table has to be ** put in an IdList structure because some of the subroutines we ** will be calling are designed to work with multiple tables and expect ** an IdList* parameter instead of just a Table* parameger. */ pTabList = sqliteIdListAppend(0, pTableName); + if( pTabList==0 ) goto update_cleanup; for(i=0; inId; i++){ pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); if( pTabList->a[i].pTab==0 ){ diff --git a/src/util.c b/src/util.c index 6eb9c235a0..eed1f97e73 100644 --- a/src/util.c +++ b/src/util.c @@ -26,12 +26,18 @@ ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.20 2001/04/05 15:57:13 drh Exp $ +** $Id: util.c,v 1.21 2001/04/11 14:28:43 drh Exp $ */ #include "sqliteInt.h" #include #include +/* +** If malloc() ever fails, this global variable gets set to 1. +** This causes the library to abort and never again function. +*/ +int sqlite_malloc_failed = 0; + /* ** If MEMORY_DEBUG is defined, then use versions of malloc() and ** free() that track memory usage and check for buffer overruns. @@ -58,12 +64,18 @@ void *sqliteMalloc_(int n, char *zFile, int line){ sqlite_nMalloc++; if( sqlite_iMallocFail>=0 ){ sqlite_iMallocFail--; - if( sqlite_iMallocFail==0 ) return 0; + if( sqlite_iMallocFail==0 ){ + sqlite_malloc_failed++; + return 0; + } } if( n==0 ) return 0; k = (n+sizeof(int)-1)/sizeof(int); pi = malloc( (3+k)*sizeof(int)); - if( pi==0 ) return 0; + if( pi==0 ){ + sqlite_malloc_failed++; + return 0; + } pi[0] = 0xdead1122; pi[1] = n; pi[k+2] = 0xdead3344; @@ -131,6 +143,10 @@ void *sqliteRealloc_(void *oldP, int n, char *zFile, int line){ } k = (n + sizeof(int) - 1)/sizeof(int); pi = malloc( (k+3)*sizeof(int) ); + if( pi==0 ){ + sqlite_malloc_failed++; + return 0; + } pi[0] = 0xdead1122; pi[1] = n; pi[k+2] = 0xdead3344; @@ -151,12 +167,21 @@ void *sqliteRealloc_(void *oldP, int n, char *zFile, int line){ /* ** Make a duplicate of a string into memory obtained from malloc() ** Free the original string using sqliteFree(). +** +** This routine is called on all strings that are passed outside of +** the SQLite library. That way clients can free the string using free() +** rather than having to call sqliteFree(). */ void sqliteStrRealloc(char **pz){ char *zNew; if( pz==0 || *pz==0 ) return; zNew = malloc( strlen(*pz) + 1 ); - if( zNew ) strcpy(zNew, *pz); + if( zNew==0 ){ + sqlite_malloc_failed++; + sqliteFree(*pz); + *pz = 0; + } + strcpy(zNew, *pz); sqliteFree(*pz); *pz = zNew; } @@ -191,7 +216,10 @@ char *sqliteStrNDup_(const char *z, int n, char *zFile, int line){ */ void *sqliteMalloc(int n){ void *p = malloc(n); - if( p==0 ) return 0; + if( p==0 ){ + sqlite_malloc_failed++; + return 0; + } memset(p, 0, n); return p; } @@ -218,7 +246,11 @@ void *sqliteRealloc(void *p, int n){ sqliteFree(p); return 0; } - return realloc(p, n); + p = realloc(p, n); + if( p==0 ){ + sqlite_malloc_failed++; + } + return p; } /* @@ -325,6 +357,7 @@ void sqliteSetNString(char **pz, ...){ void sqliteDequote(char *z){ int quote; int i, j; + if( z==0 ) return; quote = z[0]; if( quote!='\'' && quote!='"' ) return; for(i=1, j=0; z[i]; i++){ diff --git a/src/vdbe.c b/src/vdbe.c index 8c0524582a..9df47ed042 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -41,7 +41,7 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.56 2001/04/05 15:57:13 drh Exp $ +** $Id: vdbe.c,v 1.57 2001/04/11 14:28:43 drh Exp $ */ #include "sqliteInt.h" #include @@ -223,8 +223,8 @@ struct Vdbe { */ Vdbe *sqliteVdbeCreate(sqlite *db){ Vdbe *p; - p = sqliteMalloc( sizeof(Vdbe) ); + if( p==0 ) return 0; p->pBe = db->pBe; p->db = db; return p; @@ -368,7 +368,7 @@ void sqliteVdbeDequoteP3(Vdbe *p, int addr){ char *z; if( addr<0 || addr>=p->nOp ) return; z = p->aOp[addr].p3; - sqliteDequote(z); + if( z ) sqliteDequote(z); } /* @@ -381,6 +381,7 @@ void sqliteVdbeCompressSpace(Vdbe *p, int addr){ int i, j; if( addr<0 || addr>=p->nOp ) return; z = p->aOp[addr].p3; + if( z==0 ) return; i = j = 0; while( isspace(z[i]) ){ i++; } while( z[i] ){ @@ -462,6 +463,10 @@ static void AggRehash(Agg *p, int nHash){ if( p->nHash==nHash ) return; size = nHash * sizeof(AggElem*); p->apHash = sqliteRealloc(p->apHash, size ); + if( p->apHash==0 ){ + AggReset(p); + return; + } memset(p->apHash, 0, size); p->nHash = nHash; for(pElem=p->pFirst; pElem; pElem=pElem->pNext){ @@ -534,7 +539,10 @@ static void SetInsert(Set *p, char *zKey){ if( strcmp(pElem->zKey, zKey)==0 ) return; } pElem = sqliteMalloc( sizeof(*pElem) + strlen(zKey) ); - if( pElem==0 ) return; + if( pElem==0 ){ + SetClear(p); + return; + } strcpy(pElem->zKey, zKey); pElem->pNext = p->pAll; p->pAll = pElem; @@ -1004,6 +1012,7 @@ int sqliteVdbeExec( } #endif /* if( pzErrMsg ){ *pzErrMsg = 0; } */ + if( sqlite_malloc_failed ) rc = SQLITE_NOMEM; for(pc=0; rc==SQLITE_OK && pcnOp VERIFY(&& pc>=0); pc++){ pOp = &p->aOp[pc]; @@ -1364,8 +1373,7 @@ int sqliteVdbeExec( Realify(p, nos); copy = aStack[tos].r>aStack[nos].r; }else{ - Stringify(p, tos); - Stringify(p, nos); + if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; copy = sqliteCompare(zStack[tos],zStack[nos])>0; } if( copy ){ @@ -1405,8 +1413,7 @@ int sqliteVdbeExec( Realify(p, nos); copy = aStack[tos].ropcode ){ @@ -1523,8 +1529,7 @@ int sqliteVdbeExec( int nos = tos - 1; int c; VERIFY( if( nos<0 ) goto not_enough_stack; ) - Stringify(p, tos); - Stringify(p, nos); + if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; c = sqliteLikeCompare(zStack[tos], zStack[nos]); POPSTACK; POPSTACK; @@ -1556,8 +1561,7 @@ int sqliteVdbeExec( int nos = tos - 1; int c; VERIFY( if( nos<0 ) goto not_enough_stack; ) - Stringify(p, tos); - Stringify(p, nos); + if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; c = sqliteGlobCompare(zStack[tos], zStack[nos]); POPSTACK; POPSTACK; @@ -3070,7 +3074,7 @@ int sqliteVdbeExec( int nKey; VERIFY( if( tos<0 ) goto not_enough_stack; ) - Stringify(p, tos); + if( Stringify(p, tos) ) goto no_mem; zKey = zStack[tos]; nKey = aStack[tos].n; if( p->agg.nHash<=0 ){ @@ -3086,6 +3090,7 @@ int sqliteVdbeExec( pc = pOp->p2 - 1; }else{ AggInsert(&p->agg, zKey); + if( sqlite_malloc_failed ) goto no_mem; } POPSTACK; break; @@ -3241,10 +3246,11 @@ int sqliteVdbeExec( }else{ int tos = p->tos; if( tos<0 ) goto not_enough_stack; - Stringify(p, tos); + if( Stringify(p, tos) ) goto no_mem; SetInsert(&p->aSet[i], zStack[tos]); POPSTACK; } + if( sqlite_malloc_failed ) goto no_mem; break; } @@ -3258,7 +3264,7 @@ int sqliteVdbeExec( int i = pOp->p1; int tos = p->tos; VERIFY( if( tos<0 ) goto not_enough_stack; ) - Stringify(p, tos); + if( Stringify(p, tos) ) goto no_mem; if( VERIFY( i>=0 && inSet &&) SetTest(&p->aSet[i], zStack[tos])){ pc = pOp->p2 - 1; } @@ -3276,7 +3282,7 @@ int sqliteVdbeExec( int i = pOp->p1; int tos = p->tos; VERIFY( if( tos<0 ) goto not_enough_stack; ) - Stringify(p, tos); + if( Stringify(p, tos) ) goto no_mem; if(VERIFY( i>=0 && inSet &&) !SetTest(&p->aSet[i], zStack[tos])){ pc = pOp->p2 - 1; } @@ -3293,7 +3299,7 @@ int sqliteVdbeExec( int tos = p->tos; int len; VERIFY( if( tos<0 ) goto not_enough_stack; ) - Stringify(p, tos); + if( Stringify(p, tos) ) goto no_mem; #ifdef SQLITE_UTF8 { char *z = zStack[tos]; @@ -3351,7 +3357,7 @@ int sqliteVdbeExec( start = pOp->p1 - 1; } VERIFY( if( p->tos<0 ) goto not_enough_stack; ) - Stringify(p, p->tos); + if( Stringify(p, p->tos) ) goto no_mem; /* "n" will be the number of characters in the input string. ** For iso8859, the number of characters is the number of bytes. diff --git a/src/where.c b/src/where.c index fc0221d51d..13914fc23a 100644 --- a/src/where.c +++ b/src/where.c @@ -25,7 +25,7 @@ ** the WHERE clause of SQL statements. Also found here are subroutines ** to generate VDBE code to evaluate expressions. ** -** $Id: where.c,v 1.13 2001/04/04 11:48:58 drh Exp $ +** $Id: where.c,v 1.14 2001/04/11 14:28:43 drh Exp $ */ #include "sqliteInt.h" @@ -170,8 +170,9 @@ WhereInfo *sqliteWhereBegin( ** return value. */ pWInfo = sqliteMalloc( sizeof(WhereInfo) ); - if( pWInfo==0 ){ + if( sqlite_malloc_failed ){ sqliteFree(aOrder); + sqliteFree(pWInfo); return 0; } pWInfo->pParse = pParse; diff --git a/test/all.test b/test/all.test index 6d04127f3e..9aeb987ef3 100644 --- a/test/all.test +++ b/test/all.test @@ -22,7 +22,7 @@ #*********************************************************************** # This file runs all tests. # -# $Id: all.test,v 1.5 2000/12/10 18:23:51 drh Exp $ +# $Id: all.test,v 1.6 2001/04/11 14:28:43 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -53,6 +53,7 @@ for {set Counter 0} {$Counter<$COUNT} {incr Counter} { set dbprefix $p foreach testfile [lsort -dictionary [glob $testdir/*.test]] { if {[file tail $testfile]=="all.test"} continue + if {[file tail $testfile]=="malloc.test"} continue source $testfile } } @@ -79,4 +80,13 @@ if {$LeakList!=""} { puts " Ok" } +if {[file readable $testdir/malloc.test]} { + for {set Counter 0} {$Counter<$COUNT} {incr Counter} { + foreach p $PREFIXES { + set dbprefix $p + source $testdir/malloc.test + } + } +} + really_finish_test diff --git a/test/printf.test b/test/printf.test index 2fffeb2159..7941ab4c30 100644 --- a/test/printf.test +++ b/test/printf.test @@ -23,7 +23,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the sqlite_*_printf() interface. # -# $Id: printf.test,v 1.1 2001/04/07 15:24:33 drh Exp $ +# $Id: printf.test,v 1.2 2001/04/11 14:28:43 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -39,6 +39,15 @@ foreach v {1 2 5 10 99 100 1000000 999999999 0 -1 -2 -5 -10 -99 -100 -9999999} { do_test printf-1.$n.3 [subst { sqlite_mprintf_int {Three integers: (%-6d) (%-6x) (%-6o)} $v $v $v }] [format {Three integers: (%-6d) (%-6x) (%-6o)} $v $v $v] + do_test printf-1.$n.4 [subst { + sqlite_mprintf_int {Three integers: (%+6d) (%+6x) (%+6o)} $v $v $v + }] [format {Three integers: (%+6d) (%+6x) (%+6o)} $v $v $v] + do_test printf-1.$n.5 [subst { + sqlite_mprintf_int {Three integers: (%06d) (%06x) (%06o)} $v $v $v + }] [format {Three integers: (%06d) (%06x) (%06o)} $v $v $v] + do_test printf-1.$n.6 [subst { + sqlite_mprintf_int {Three integers: (% 6d) (% 6x) (% 6o)} $v $v $v + }] [format {Three integers: (% 6d) (% 6x) (% 6o)} $v $v $v] incr n } @@ -89,4 +98,15 @@ do_test printf-4.1 { sqlite_mprintf_str {%d %d A quoted string: '%q'} 1 2 {Hi Y'all} } {1 2 A quoted string: 'Hi Y''all'} +do_test printf-5.1 { + set x [sqlite_mprintf_str {%d %d %100000s} 0 0 {Hello}] + string length $x +} {994} +do_test printf-5.2 { + sqlite_mprintf_str {%d %d (%-10.10s) %} -9 -10 {HelloHelloHello} +} {-9 -10 (HelloHello) %} +do_test printf-5.3 { + sqlite_mprintf_str {%% %d %d (%=10s)} 5 6 Hello +} {% 5 6 ( Hello )} + finish_test diff --git a/test/tableapi.test b/test/tableapi.test index c32b6b2935..5cb6a79731 100644 --- a/test/tableapi.test +++ b/test/tableapi.test @@ -24,7 +24,7 @@ # focus of this file is testing the sqlite_exec_printf() and # sqlite_get_table_printf() APIs. # -# $Id: tableapi.test,v 1.1 2001/04/07 15:24:34 drh Exp $ +# $Id: tableapi.test,v 1.2 2001/04/11 14:28:43 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -48,6 +48,49 @@ do_test tableapi-2.1 { SELECT * FROM xyz WHERE b='%q' } {Hi Y'all} } {0 1 2 a b 1 {Hi Y'all}} +do_test tableapi-2.2 { + sqlite_get_table_printf $::dbx { + SELECT * FROM xyz + } {} +} {0 1 2 a b 1 {Hi Y'all}} +do_test tableapi-2.3 { + for {set i 2} {$i<=50} {incr i} { + sqlite_get_table_printf $::dbx \ + "INSERT INTO xyz VALUES($i,'(%s)')" $i + } + sqlite_get_table_printf $::dbx { + SELECT * FROM xyz ORDER BY a + } {} +} {0 50 2 a b 1 {Hi Y'all} 2 (2) 3 (3) 4 (4) 5 (5) 6 (6) 7 (7) 8 (8) 9 (9) 10 (10) 11 (11) 12 (12) 13 (13) 14 (14) 15 (15) 16 (16) 17 (17) 18 (18) 19 (19) 20 (20) 21 (21) 22 (22) 23 (23) 24 (24) 25 (25) 26 (26) 27 (27) 28 (28) 29 (29) 30 (30) 31 (31) 32 (32) 33 (33) 34 (34) 35 (35) 36 (36) 37 (37) 38 (38) 39 (39) 40 (40) 41 (41) 42 (42) 43 (43) 44 (44) 45 (45) 46 (46) 47 (47) 48 (48) 49 (49) 50 (50)} +do_test tableapi-2.3.1 { + sqlite_get_table_printf $::dbx { + SELECT * FROM xyz WHERE a>49 ORDER BY a + } {} +} {0 1 2 a b 50 (50)} +do_test tableapi-2.3.2 { + sqlite_get_table_printf $::dbx { + SELECT * FROM xyz WHERE a>47 ORDER BY a + } {} +} {0 3 2 a b 48 (48) 49 (49) 50 (50)} +do_test tableapi-2.4 { + set ::big_str [sqlite_mprintf_str {%500'* Hello %500'*} 0 0 {}] + sqlite_get_table_printf $::dbx { + INSERT INTO xyz VALUES(51,'%q') + } $::big_str +} {0 0 0} +do_test tableapi-2.5 { + sqlite_get_table_printf $::dbx { + SELECT * FROM xyz WHERE a>49 ORDER BY a; + } {} +} "0 2 2 a b 50 (50) 51 \173$::big_str\175" +do_test tableapi-2.6 { + sqlite_get_table_printf $::dbx { + INSERT INTO xyz VALUES(52,NULL) + } {} + sqlite_get_table_printf $::dbx { + SELECT * FROM xyz WHERE a IN (42,50,52) ORDER BY a DESC + } {} +} {0 3 2 a b 52 NULL 50 (50) 42 (42)} do_test tableapi-99.0 { sqlite_close $::dbx diff --git a/test/tester.tcl b/test/tester.tcl index 9c86030ea0..120ce86e95 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -23,7 +23,7 @@ # This file implements some common TCL routines used for regression # testing the SQLite library # -# $Id: tester.tcl,v 1.14 2001/04/06 16:13:43 drh Exp $ +# $Id: tester.tcl,v 1.15 2001/04/11 14:28:43 drh Exp $ # Make sure tclsqlite was compiled correctly. Abort now with an # error message if not. @@ -183,7 +183,7 @@ proc testif {args} { set ::skip_test 1 } -# The procedure uses the special "--malloc-stats--" macro of SQLite +# The procedure uses the special "sqlite_malloc_stat" command # (which is only available if SQLite is compiled with -DMEMORY_DEBUG=1) # to see how many malloc()s have not been free()ed. The number # of surplus malloc()s is stored in the global variable $::Leak. @@ -191,10 +191,10 @@ proc testif {args} { # in the library. # proc memleak_check {} { - set r [execsql {--malloc-stats--}] - if {$r==""} return - set ::Leak [expr {[lindex $r 0]-[lindex $r 1]}] - # puts "*** $::Leak mallocs have not been freed ***" + if {[info command sqlite_malloc_stat]!=""} { + set r [sqlite_malloc_stat] + set ::Leak [expr {[lindex $r 0]-[lindex $r 1]}] + } } # Run this routine last diff --git a/www/changes.tcl b/www/changes.tcl index 8c2c258af9..4dc8400b9a 100644 --- a/www/changes.tcl +++ b/www/changes.tcl @@ -17,7 +17,8 @@ proc chng {date desc} { puts "

    $desc

" } -chng {2001 Apr 6 (1.0.31)} { +chng {2001 Apr 11 (1.0.31)} { +
  • More robust handling of out-of-memory errors.
  • New tests added to the test suite.
  • }