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 "
"
}
-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.
}