Index: src/analyze.c ================================================================== --- src/analyze.c +++ src/analyze.c @@ -1517,18 +1517,21 @@ pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase); } z = argv[2]; if( pIndex ){ + tRowcnt *aiRowEst = 0; int nCol = pIndex->nKeyCol+1; #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 - tRowcnt * const aiRowEst = pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero( - sizeof(tRowcnt) * nCol - ); - if( aiRowEst==0 ) pInfo->db->mallocFailed = 1; -#else - tRowcnt * const aiRowEst = 0; + /* Index.aiRowEst may already be set here if there are duplicate + ** sqlite_stat1 entries for this index. In that case just clobber + ** the old data with the new instead of allocating a new array. */ + if( pIndex->aiRowEst==0 ){ + pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero(sizeof(tRowcnt) * nCol); + if( pIndex->aiRowEst==0 ) pInfo->db->mallocFailed = 1; + } + aiRowEst = pIndex->aiRowEst; #endif pIndex->bUnordered = 0; decodeIntArray((char*)z, nCol, aiRowEst, pIndex->aiRowLogEst, pIndex); if( pIndex->pPartIdxWhere==0 ) pTable->nRowLogEst = pIndex->aiRowLogEst[0]; }else{ Index: test/analyze3.test ================================================================== --- test/analyze3.test +++ test/analyze3.test @@ -14,10 +14,11 @@ # instead of literal constant arguments. # set testdir [file dirname $argv0] source $testdir/tester.tcl +set testprefix analyze3 ifcapable !stat4&&!stat3 { finish_test return } @@ -43,10 +44,13 @@ # analyze3-5.*: Check that the query plans of applicable statements are # invalidated if the values of SQL parameter are modified # using the clear_bindings() or transfer_bindings() APIs. # # analyze3-6.*: Test that the problem fixed by commit [127a5b776d] is fixed. +# +# analyze3-7.*: Test that some memory leaks discovered by fuzz testing +# have been fixed. # proc getvar {varname} { uplevel #0 set $varname } db function var getvar @@ -672,7 +676,20 @@ INSERT INTO t1 VALUES(1,1,'0000'); CREATE INDEX t0b ON t1(b); ANALYZE; SELECT c FROM t1 WHERE b=3 AND a BETWEEN 30 AND hex(1); } {} + +# At one point duplicate stat1 entries were causing a memory leak. +# +reset_db +do_execsql_test 7.2 { + CREATE TABLE t1(a,b,c); + CREATE INDEX t1a ON t1(a); + ANALYZE; + SELECT * FROM sqlite_stat1; + INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES('t1','t1a','12000'); + INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES('t1','t1a','12000'); + ANALYZE sqlite_master; +} finish_test