/ Check-in [205d0b88]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:All of the malloc test cases run. Still seeing failures in malloc4.test. (CVS 4272)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 205d0b881d541db65837ce6cf44d58d607635bc2
User & Date: drh 2007-08-22 22:04:37
Context
2007-08-23
02:47
Improvements to memory leak detection. The --backtrace=NNN option is now recognized by tester.tcl. Memory leak summaries are automatically written to the file ./memleak.txt and each leak is tagged with the test in which it occurred. The quick.test script runs on Linux with no errors and no leaks. (CVS 4273) check-in: 21f6b310 user: drh tags: trunk
2007-08-22
22:04
All of the malloc test cases run. Still seeing failures in malloc4.test. (CVS 4272) check-in: 205d0b88 user: drh tags: trunk
20:18
The malloc.test script now passes all tests with no errors. (CVS 4271) check-in: db818430 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/mem2.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
449
450
451
452
453
454
455









456
457
458
459
460
461
462
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains the C functions that implement a memory
** allocation subsystem for use by SQLite.  
**
** $Id: mem2.c,v 1.6 2007/08/22 20:18:22 drh Exp $
*/

/*
** This version of the memory allocator is used only if the
** SQLITE_MEMDEBUG macro is defined and SQLITE_OMIT_MEMORY_ALLOCATION
** is not defined.
*/
................................................................................
  mem.iFail = iFail+1;
  if( iRepeat>=0 ){
    mem.iReset = iRepeat;
  }
  mem.iFailCnt = 0;
  return n;
}










/*
** The following two routines are used to assert that no memory
** allocations occur between one call and the next.  The use of
** these routines does not change the computed results in any way.
** These routines are like asserts.
*/







|







 







>
>
>
>
>
>
>
>
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains the C functions that implement a memory
** allocation subsystem for use by SQLite.  
**
** $Id: mem2.c,v 1.7 2007/08/22 22:04:37 drh Exp $
*/

/*
** This version of the memory allocator is used only if the
** SQLITE_MEMDEBUG macro is defined and SQLITE_OMIT_MEMORY_ALLOCATION
** is not defined.
*/
................................................................................
  mem.iFail = iFail+1;
  if( iRepeat>=0 ){
    mem.iReset = iRepeat;
  }
  mem.iFailCnt = 0;
  return n;
}

/*
** This routine returns the number of successful mallocs remaining until
** the next simulated malloc failure.  -1 is returned if no simulated
** failure is currently scheduled.
*/
int sqlite3_memdebug_pending(void){
  return mem.iFail-1;
}

/*
** The following two routines are used to assert that no memory
** allocations occur between one call and the next.  The use of
** these routines does not change the computed results in any way.
** These routines are like asserts.
*/

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
534
535
536
537
538
539
540

541

542
543
544
545
546
547
548
....
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887

2888


2889
2890
2891
2892
2893
2894
2895
....
3159
3160
3161
3162
3163
3164
3165

3166
3167
3168

3169
3170
3171
3172
3173
3174
3175
....
3482
3483
3484
3485
3486
3487
3488

3489

3490
3491
3492
3493
3494
3495
3496
....
3606
3607
3608
3609
3610
3611
3612

3613

3614
3615
3616
3617
3618
3619
3620
....
4435
4436
4437
4438
4439
4440
4441

4442

4443
4444
4445
4446
4447
4448
4449
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.367 2007/08/22 18:54:33 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
/*
** Change the size of the pager hash table to N.  N must be a power
** of two.
*/
static void pager_resize_hash_table(Pager *pPager, int N){
  PgHdr **aHash, *pPg;
  assert( N>0 && (N&(N-1))==0 );

  aHash = sqlite3MallocZero( sizeof(aHash[0])*N );

  if( aHash==0 ){
    /* Failure to rehash is not an error.  It is only a performance hit. */
    return;
  }
  sqlite3_free(pPager->aHash);
  pPager->nHash = N;
  pPager->aHash = aHash;
................................................................................
  */
  for(i=0; i<=1; i++){

    /* Loop through all the SQLite pagers opened by the current thread. */
    Pager *pPager = sqlite3PagerList;
    for( ; pPager && (nReq<0 || nReleased<nReq); pPager=pPager->pNext){
      PgHdr *pPg;
      int rc;

      /* In-memory databases should not appear on the pager list */
      assert( !MEMDB );

      /* Skip pagers that are currently in use by the b-tree layer */
      if( pPager->iInUseDB ) continue;

      /* For each pager, try to free as many pages as possible (without 
      ** calling fsync() if this is the first iteration of the outermost 
      ** loop).
      */

      while( SQLITE_OK==(rc = pager_recycle(pPager, i, &pPg)) && pPg) {


        /* We've found a page to free. At this point the page has been 
        ** removed from the page hash-table, free-list and synced-list 
        ** (pFirstSynced). It is still in the all pages (pAll) list. 
        ** Remove it from this list before freeing.
        **
        ** Todo: Check the Pager.pStmt list to make sure this is Ok. It 
        ** probably is though.
................................................................................
      pager_resize_hash_table(pPager,
         pPager->nHash<256 ? 256 : pPager->nHash*2);
      if( pPager->nHash==0 ){
        rc = SQLITE_NOMEM;
        goto pager_allocate_out;
      }
    }

    pPg = sqlite3_malloc( sizeof(*pPg) + pPager->pageSize
                            + sizeof(u32) + pPager->nExtra
                            + MEMDB*sizeof(PgHistory) );

    if( pPg==0 ){
      rc = SQLITE_NOMEM;
      goto pager_allocate_out;
    }
    memset(pPg, 0, sizeof(*pPg));
    if( MEMDB ){
      memset(PGHDR_TO_HIST(pPg, pPager), 0, sizeof(PgHistory));
................................................................................
  int rc;
  assert( !MEMDB );
  assert( pPager->state>=PAGER_RESERVED );
  assert( pPager->journalOpen==0 );
  assert( pPager->useJournal );
  assert( pPager->aInJournal==0 );
  sqlite3PagerPagecount(pPager);

  pPager->aInJournal = sqlite3MallocZero( pPager->dbSize/8 + 1 );

  if( pPager->aInJournal==0 ){
    rc = SQLITE_NOMEM;
    goto failed_to_open_journal;
  }

  if( pPager->tempFile ){
    flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL);
................................................................................
    ** by this connection. Instead of deleting the journal file it was 
    ** kept open and truncated to 0 bytes.
    */
    assert( pPager->nRec==0 );
    assert( pPager->origDbSize==0 );
    assert( pPager->aInJournal==0 );
    sqlite3PagerPagecount(pPager);

    pPager->aInJournal = sqlite3MallocZero( pPager->dbSize/8 + 1 );

    if( !pPager->aInJournal ){
      rc = SQLITE_NOMEM;
    }else{
      pPager->origDbSize = pPager->dbSize;
      rc = writeJournalHdr(pPager);
    }
  }
................................................................................
    return SQLITE_OK;
  }
  if( !pPager->journalOpen ){
    pPager->stmtAutoopen = 1;
    return SQLITE_OK;
  }
  assert( pPager->journalOpen );

  pPager->aInStmt = sqlite3MallocZero( pPager->dbSize/8 + 1 );

  if( pPager->aInStmt==0 ){
    /* sqlite3OsLock(pPager->fd, SHARED_LOCK); */
    return SQLITE_NOMEM;
  }
#ifndef NDEBUG
  rc = sqlite3OsFileSize(pPager->jfd, &pPager->stmtJSize);
  if( rc ) goto stmt_begin_failed;







|







 







>

>







 







|











>
|
>
>







 







>



>







 







>

>







 







>

>







 







>

>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
....
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
....
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
....
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
....
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
....
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.368 2007/08/22 22:04:37 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
/*
** Change the size of the pager hash table to N.  N must be a power
** of two.
*/
static void pager_resize_hash_table(Pager *pPager, int N){
  PgHdr **aHash, *pPg;
  assert( N>0 && (N&(N-1))==0 );
  pagerLeave(pPager);
  aHash = sqlite3MallocZero( sizeof(aHash[0])*N );
  pagerEnter(pPager);
  if( aHash==0 ){
    /* Failure to rehash is not an error.  It is only a performance hit. */
    return;
  }
  sqlite3_free(pPager->aHash);
  pPager->nHash = N;
  pPager->aHash = aHash;
................................................................................
  */
  for(i=0; i<=1; i++){

    /* Loop through all the SQLite pagers opened by the current thread. */
    Pager *pPager = sqlite3PagerList;
    for( ; pPager && (nReq<0 || nReleased<nReq); pPager=pPager->pNext){
      PgHdr *pPg;
      int rc = SQLITE_OK;

      /* In-memory databases should not appear on the pager list */
      assert( !MEMDB );

      /* Skip pagers that are currently in use by the b-tree layer */
      if( pPager->iInUseDB ) continue;

      /* For each pager, try to free as many pages as possible (without 
      ** calling fsync() if this is the first iteration of the outermost 
      ** loop).
      */
      while( (nReq<0 || nReleased<nReq) &&
             SQLITE_OK==(rc = pager_recycle(pPager, i, &pPg)) &&
             pPg
      ) {
        /* We've found a page to free. At this point the page has been 
        ** removed from the page hash-table, free-list and synced-list 
        ** (pFirstSynced). It is still in the all pages (pAll) list. 
        ** Remove it from this list before freeing.
        **
        ** Todo: Check the Pager.pStmt list to make sure this is Ok. It 
        ** probably is though.
................................................................................
      pager_resize_hash_table(pPager,
         pPager->nHash<256 ? 256 : pPager->nHash*2);
      if( pPager->nHash==0 ){
        rc = SQLITE_NOMEM;
        goto pager_allocate_out;
      }
    }
    pagerLeave(pPager);
    pPg = sqlite3_malloc( sizeof(*pPg) + pPager->pageSize
                            + sizeof(u32) + pPager->nExtra
                            + MEMDB*sizeof(PgHistory) );
    pagerEnter(pPager);
    if( pPg==0 ){
      rc = SQLITE_NOMEM;
      goto pager_allocate_out;
    }
    memset(pPg, 0, sizeof(*pPg));
    if( MEMDB ){
      memset(PGHDR_TO_HIST(pPg, pPager), 0, sizeof(PgHistory));
................................................................................
  int rc;
  assert( !MEMDB );
  assert( pPager->state>=PAGER_RESERVED );
  assert( pPager->journalOpen==0 );
  assert( pPager->useJournal );
  assert( pPager->aInJournal==0 );
  sqlite3PagerPagecount(pPager);
  pagerLeave(pPager);
  pPager->aInJournal = sqlite3MallocZero( pPager->dbSize/8 + 1 );
  pagerEnter(pPager);
  if( pPager->aInJournal==0 ){
    rc = SQLITE_NOMEM;
    goto failed_to_open_journal;
  }

  if( pPager->tempFile ){
    flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL);
................................................................................
    ** by this connection. Instead of deleting the journal file it was 
    ** kept open and truncated to 0 bytes.
    */
    assert( pPager->nRec==0 );
    assert( pPager->origDbSize==0 );
    assert( pPager->aInJournal==0 );
    sqlite3PagerPagecount(pPager);
    pagerLeave(pPager);
    pPager->aInJournal = sqlite3MallocZero( pPager->dbSize/8 + 1 );
    pagerEnter(pPager);
    if( !pPager->aInJournal ){
      rc = SQLITE_NOMEM;
    }else{
      pPager->origDbSize = pPager->dbSize;
      rc = writeJournalHdr(pPager);
    }
  }
................................................................................
    return SQLITE_OK;
  }
  if( !pPager->journalOpen ){
    pPager->stmtAutoopen = 1;
    return SQLITE_OK;
  }
  assert( pPager->journalOpen );
  pagerLeave(pPager);
  pPager->aInStmt = sqlite3MallocZero( pPager->dbSize/8 + 1 );
  pagerEnter(pPager);
  if( pPager->aInStmt==0 ){
    /* sqlite3OsLock(pPager->fd, SHARED_LOCK); */
    return SQLITE_NOMEM;
  }
#ifndef NDEBUG
  rc = sqlite3OsFileSize(pPager->jfd, &pPager->stmtJSize);
  if( rc ) goto stmt_begin_failed;

Changes to src/test_malloc.c.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
...
245
246
247
248
249
250
251
252
253
254
255
256

257



258
259
260
261
262
263
264
265
266
267



















268
269
270
271
272
273
274
...
278
279
280
281
282
283
284

285
286
287
288
289
290
291
**    May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file contains code used to implement test interfaces to the
** memory allocation subsystem.
**
** $Id: test_malloc.c,v 1.2 2007/08/15 20:41:29 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>

................................................................................
  }
#endif
  return TCL_OK;
}


/*
** Usage:    sqlite3_memdebug_fail  COUNTER  REPEAT
**
** Arrange for a simulated malloc() failure after COUNTER successes.
** If REPEAT is 1 then all subsequent malloc()s fail.   If REPEAT is
** 0 then only a single failure occurs.
**
** Each call to this routine overrides the prior counter value.
** This routine returns the number of simulated failures that have
................................................................................
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  int iFail;
  int iRepeat;
  int nFail = 0;
  if( objc!=3 ){
    Tcl_WrongNumArgs(interp, 1, objv, "COUNTER REPEAT");
    return TCL_ERROR;
  }
  if( Tcl_GetIntFromObj(interp, objv[1], &iFail) ) return TCL_ERROR;

  if( Tcl_GetIntFromObj(interp, objv[2], &iRepeat) ) return TCL_ERROR;



#ifdef SQLITE_MEMDEBUG
  {
    extern int sqlite3_memdebug_fail(int,int);
    nFail = sqlite3_memdebug_fail(iFail, iRepeat);
  }
#endif
  Tcl_SetObjResult(interp, Tcl_NewIntObj(nFail));
  return TCL_OK;
}





















/*
** Register commands with the TCL interpreter.
*/
int Sqlitetest_malloc_Init(Tcl_Interp *interp){
  static struct {
     char *zName;
................................................................................
     { "sqlite3_realloc",            test_realloc                  },
     { "sqlite3_free",               test_free                     },
     { "sqlite3_memory_used",        test_memory_used              },
     { "sqlite3_memory_highwater",   test_memory_highwater         },
     { "sqlite3_memdebug_backtrace", test_memdebug_backtrace       },
     { "sqlite3_memdebug_dump",      test_memdebug_dump            },
     { "sqlite3_memdebug_fail",      test_memdebug_fail            },

  };
  int i;
  for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
    Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
  }
  return TCL_OK;
}







|







 







|







 







|
|



>
|
>
>
>










>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
**    May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file contains code used to implement test interfaces to the
** memory allocation subsystem.
**
** $Id: test_malloc.c,v 1.3 2007/08/22 22:04:37 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>

................................................................................
  }
#endif
  return TCL_OK;
}


/*
** Usage:    sqlite3_memdebug_fail  COUNTER  ?REPEAT?
**
** Arrange for a simulated malloc() failure after COUNTER successes.
** If REPEAT is 1 then all subsequent malloc()s fail.   If REPEAT is
** 0 then only a single failure occurs.
**
** Each call to this routine overrides the prior counter value.
** This routine returns the number of simulated failures that have
................................................................................
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  int iFail;
  int iRepeat;
  int nFail = 0;
  if( objc!=3 && objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "COUNTER ?REPEAT?");
    return TCL_ERROR;
  }
  if( Tcl_GetIntFromObj(interp, objv[1], &iFail) ) return TCL_ERROR;
  if( objc==3 ){
    if( Tcl_GetIntFromObj(interp, objv[2], &iRepeat) ) return TCL_ERROR;
  }else{
    iRepeat = -1;
  }
#ifdef SQLITE_MEMDEBUG
  {
    extern int sqlite3_memdebug_fail(int,int);
    nFail = sqlite3_memdebug_fail(iFail, iRepeat);
  }
#endif
  Tcl_SetObjResult(interp, Tcl_NewIntObj(nFail));
  return TCL_OK;
}


/*
** Usage:    sqlite3_memdebug_pending
**
** Return the number of successful mallocs remaining before the
** next simulated failure.  Return -1 if no simulated failure is
** currently scheduled.
*/
static int test_memdebug_pending(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  extern int sqlite3_memdebug_pending(void);
  Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_memdebug_pending()));
  return TCL_OK;
}


/*
** Register commands with the TCL interpreter.
*/
int Sqlitetest_malloc_Init(Tcl_Interp *interp){
  static struct {
     char *zName;
................................................................................
     { "sqlite3_realloc",            test_realloc                  },
     { "sqlite3_free",               test_free                     },
     { "sqlite3_memory_used",        test_memory_used              },
     { "sqlite3_memory_highwater",   test_memory_highwater         },
     { "sqlite3_memdebug_backtrace", test_memdebug_backtrace       },
     { "sqlite3_memdebug_dump",      test_memdebug_dump            },
     { "sqlite3_memdebug_fail",      test_memdebug_fail            },
     { "sqlite3_memdebug_pending",   test_memdebug_pending         },
  };
  int i;
  for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
    Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
  }
  return TCL_OK;
}

Changes to test/malloc2.test.

4
5
6
7
8
9
10

11
12
13




14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
..
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
..
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************

# This file attempts to check that the library can recover from a malloc()
# failure when sqlite3_global_recover() is invoked.
#




# $Id: malloc2.test,v 1.5 2006/09/04 18:54:14 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
if {[info command sqlite_malloc_stat]==""} {
  puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG=1"
  finish_test
  return
}

ifcapable !globalrecover {
  finish_test
  return
}

# Generate a checksum based on the contents of the database. If the
# checksum of two databases is the same, and the integrity-check passes
# for both, the two databases are identical.
#
proc cksum {db} {
................................................................................
  array set ::mallocopts $args
  set sum [cksum db]

  for {set ::n 1} {true} {incr ::n} {

    # Run the SQL. Malloc number $::n is set to fail. A malloc() failure
    # may or may not be reported.
    sqlite_malloc_fail $::n
    do_test malloc2-$tn.$::n.2 {
      set res [catchsql [string trim $::mallocopts(-sql)]]
      set rc [expr { 
        0==[string compare $res {1 {out of memory}}] ||
        0==[lindex $res 0]
      }]
      if {$rc!=1} {
................................................................................
        puts "Error: $res"
      }
      set rc
    } {1}

    # If $::n is greater than the number of malloc() calls required to
    # execute the SQL, then this test is finished. Break out of the loop.
    if {[lindex [sqlite_malloc_stat] 2]>0} {
      sqlite_malloc_fail -1
      break
    }

    # Nothing should work now, because the allocator should refuse to
    # allocate any memory.
    #
    # Update: SQLite now automatically recovers from a malloc() failure.
    # So the statement in the test below would work. 
if 0 {
    do_test malloc2-$tn.$::n.3 {
      catchsql {SELECT 'nothing should work'}
    } {1 {out of memory}}
}

    # Recover from the malloc failure.
    #
    # Update: The new malloc() failure handling means that a transaction may
    # still be active even if a malloc() has failed. But when these tests were
    # written this was not the case. So do a manual ROLLBACK here so that the
    # tests pass.
................................................................................

    # Checksum the database.
    do_test malloc2-$tn.$::n.5 {
      cksum db
    } $sum

    integrity_check malloc2-$tn.$::n.6
  if {$::nErr>1} return
  }
  unset ::mallocopts
}

do_test malloc2.1.setup {
  execsql {
    CREATE TABLE abc(a, b, c);







>



>
>
>
>
|






|
|
|
|
<
<
<
<
<







 







|







 







|
<
|
<






|
|
|
|
|







 







|







4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29





30
31
32
33
34
35
36
..
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
..
76
77
78
79
80
81
82
83

84

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file attempts to check that the library can recover from a malloc()
# failure when sqlite3_global_recover() is invoked.
#
# (Later:) The sqlite3_global_recover() interface is now a no-op.
# Recovery from malloc() failures is automatic.  But we keep these
# tests around because you can never have too many test cases.
#
# $Id: malloc2.test,v 1.6 2007/08/22 22:04:37 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
ifcapable !memdebug {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return





}

# Generate a checksum based on the contents of the database. If the
# checksum of two databases is the same, and the integrity-check passes
# for both, the two databases are identical.
#
proc cksum {db} {
................................................................................
  array set ::mallocopts $args
  set sum [cksum db]

  for {set ::n 1} {true} {incr ::n} {

    # Run the SQL. Malloc number $::n is set to fail. A malloc() failure
    # may or may not be reported.
    sqlite3_memdebug_fail $::n 1
    do_test malloc2-$tn.$::n.2 {
      set res [catchsql [string trim $::mallocopts(-sql)]]
      set rc [expr { 
        0==[string compare $res {1 {out of memory}}] ||
        0==[lindex $res 0]
      }]
      if {$rc!=1} {
................................................................................
        puts "Error: $res"
      }
      set rc
    } {1}

    # If $::n is greater than the number of malloc() calls required to
    # execute the SQL, then this test is finished. Break out of the loop.
    set nFail [sqlite3_memdebug_fail -1 -1]

    if {$nFail==0} break


    # Nothing should work now, because the allocator should refuse to
    # allocate any memory.
    #
    # Update: SQLite now automatically recovers from a malloc() failure.
    # So the statement in the test below would work. 
    if 0 {
      do_test malloc2-$tn.$::n.3 {
        catchsql {SELECT 'nothing should work'}
      } {1 {out of memory}}
    }

    # Recover from the malloc failure.
    #
    # Update: The new malloc() failure handling means that a transaction may
    # still be active even if a malloc() has failed. But when these tests were
    # written this was not the case. So do a manual ROLLBACK here so that the
    # tests pass.
................................................................................

    # Checksum the database.
    do_test malloc2-$tn.$::n.5 {
      cksum db
    } $sum

    integrity_check malloc2-$tn.$::n.6
    if {$::nErr>1} return
  }
  unset ::mallocopts
}

do_test malloc2.1.setup {
  execsql {
    CREATE TABLE abc(a, b, c);

Changes to test/malloc3.test.

9
10
11
12
13
14
15
16
17
18
19
20
21
22

23
24
25
26
27
28
29
...
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
...
570
571
572
573
574
575
576

577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
...
634
635
636
637
638
639
640
641
642
643
644
645
646
#
#***********************************************************************
#
# This file contains tests to ensure that the library handles malloc() failures
# correctly. The emphasis of these tests are the _prepare(), _step() and
# _finalize() calls.
#
# $Id: malloc3.test,v 1.10 2007/03/28 01:59:34 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
if {[info command sqlite_malloc_stat]==""} {

   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

#--------------------------------------------------------------------------
# NOTES ON RECOVERING FROM A MALLOC FAILURE
................................................................................
        incr pc
      }

      -sql {
        set ::rollback_hook_count 0

        set ac [sqlite3_get_autocommit $::DB]        ;# Auto-Commit
        sqlite_malloc_fail $iFail
        set rc [catch {db eval [lindex $v 1]} msg]   ;# True error occurs
        set nac [sqlite3_get_autocommit $::DB]       ;# New Auto-Commit 


        if {$rc != 0 && $nac && !$ac} {
          # Before [db eval] the auto-commit flag was clear. Now it
          # is set. Since an error occured we assume this was not a
................................................................................
	  # commit - therefore a rollback occured. Check that the
	  # rollback-hook was invoked.
          do_test malloc3-rollback_hook.$iterid {
            set ::rollback_hook_count
          } {1}
        }


        if {$rc == 0} {
            # Successful execution of sql. Our "mallocs-until-failure" 
            # count should be greater than 0. Otherwise a malloc() failed
            # and the error was not reported.
            if {[lindex [sqlite_malloc_stat] 2] <= 0} {
              error "Unreported malloc() failure"
            }

            if {$ac && !$nac} {
              # Before the [db eval] the auto-commit flag was set, now it
              # is clear. We can deduce that a "BEGIN" statement has just
              # been successfully executed.
              set begin_pc $pc
            } 

            incr pc
            set iFail 1
            sqlite_malloc_fail 0
            integrity_check "malloc3-(integrity).$iterid"
        } elseif {[regexp {.*out of memory} $msg]} {
            # Out of memory error, as expected
            integrity_check "malloc3-(integrity).$iterid"
            incr iFail
            if {$nac && !$ac} {

................................................................................
}

# Turn of the Tcl interface's prepared statement caching facility.
db cache size 0

run_test $::run_test_script 9 1
# run_test [lrange $::run_test_script 0 3] 0 63
sqlite_malloc_fail 0
db close

pp_check_for_leaks

finish_test







|





|
>







 







|







 







>




|












<







 







|


<
<

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
...
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
...
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595

596
597
598
599
600
601
602
...
635
636
637
638
639
640
641
642
643
644


645
#
#***********************************************************************
#
# This file contains tests to ensure that the library handles malloc() failures
# correctly. The emphasis of these tests are the _prepare(), _step() and
# _finalize() calls.
#
# $Id: malloc3.test,v 1.11 2007/08/22 22:04:37 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
ifcapable !memdebug {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

#--------------------------------------------------------------------------
# NOTES ON RECOVERING FROM A MALLOC FAILURE
................................................................................
        incr pc
      }

      -sql {
        set ::rollback_hook_count 0

        set ac [sqlite3_get_autocommit $::DB]        ;# Auto-Commit
        sqlite3_memdebug_fail $iFail 1
        set rc [catch {db eval [lindex $v 1]} msg]   ;# True error occurs
        set nac [sqlite3_get_autocommit $::DB]       ;# New Auto-Commit 


        if {$rc != 0 && $nac && !$ac} {
          # Before [db eval] the auto-commit flag was clear. Now it
          # is set. Since an error occured we assume this was not a
................................................................................
	  # commit - therefore a rollback occured. Check that the
	  # rollback-hook was invoked.
          do_test malloc3-rollback_hook.$iterid {
            set ::rollback_hook_count
          } {1}
        }

        set nFail [sqlite3_memdebug_fail -1 -1]
        if {$rc == 0} {
            # Successful execution of sql. Our "mallocs-until-failure" 
            # count should be greater than 0. Otherwise a malloc() failed
            # and the error was not reported.
            if {$nFail>0} {
              error "Unreported malloc() failure"
            }

            if {$ac && !$nac} {
              # Before the [db eval] the auto-commit flag was set, now it
              # is clear. We can deduce that a "BEGIN" statement has just
              # been successfully executed.
              set begin_pc $pc
            } 

            incr pc
            set iFail 1

            integrity_check "malloc3-(integrity).$iterid"
        } elseif {[regexp {.*out of memory} $msg]} {
            # Out of memory error, as expected
            integrity_check "malloc3-(integrity).$iterid"
            incr iFail
            if {$nac && !$ac} {

................................................................................
}

# Turn of the Tcl interface's prepared statement caching facility.
db cache size 0

run_test $::run_test_script 9 1
# run_test [lrange $::run_test_script 0 3] 0 63
sqlite3_memdebug_fail -1 -1
db close



finish_test

Changes to test/malloc4.test.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
..
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
..
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
...
185
186
187
188
189
190
191
192
193
194
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains tests to ensure that the library handles malloc() failures
# correctly. The emphasis in this file is on sqlite3_column_XXX() APIs.
#
# $Id: malloc4.test,v 1.3 2006/01/23 07:52:41 danielk1977 Exp $

#---------------------------------------------------------------------------
# NOTES ON EXPECTED BEHAVIOUR
#
# [193] When a memory allocation failure occurs during sqlite3_column_name(),
#       sqlite3_column_name16(), sqlite3_column_decltype(), or
#       sqlite3_column_decltype16() the function shall return NULL.
................................................................................
#
#---------------------------------------------------------------------------

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

ifcapable !utf16 {
  finish_test
................................................................................
  return
}

proc do_stmt_test {id sql} {
  set ::sql $sql
  set go 1
  for {set n 1} {$go} {incr n} {
    set testid "malloc4-$id.(iFail $n)"

    # Prepare the statement
    do_test ${testid}.1 {
      set ::STMT [sqlite3_prepare $::DB $sql -1 TAIL]
      expr [string length $::STMT] > 0
    } {1}

    # Set the Nth malloc() to fail.
    sqlite_malloc_fail $n

    # Test malloc failure in the _name(), _name16(), decltype() and
    # decltype16() APIs. Calls that occur after the malloc() failure should
    # return NULL. No error is raised though.
    #
    # ${testid}.2.1 - Call _name()
    # ${testid}.2.2 - Call _name16()
................................................................................
    #                 malloc() failures.
    #
    # Because the code that implements the _decltype() and _decltype16() APIs
    # is the same as the _name() and _name16() implementations, we don't worry
    # about explicitly testing them.
    #
    do_test ${testid}.2.1 {
      set mf1 [expr [lindex [sqlite_malloc_stat] 2] <= 0]
      set ::name8  [sqlite3_column_name $::STMT 0]
      set mf2 [expr [lindex [sqlite_malloc_stat] 2] <= 0]
      expr {$mf1 == $mf2 || $::name8 == ""}
    } {1}
    do_test ${testid}.2.2 {
      set mf1 [expr [lindex [sqlite_malloc_stat] 2] <= 0]
      set ::name16 [sqlite3_column_name16 $::STMT 0]
      set ::name16 [encoding convertfrom unicode $::name16]
      set ::name16 [string range $::name16 0 end-1]
      set mf2 [expr [lindex [sqlite_malloc_stat] 2] <= 0]
      expr {$mf1 == $mf2 || $::name16 == ""}
    } {1}
    do_test ${testid}.2.3 {
      set mf1 [expr [lindex [sqlite_malloc_stat] 2] <= 0]
      set ::name8_2 [sqlite3_column_name $::STMT 0]
      set mf2 [expr [lindex [sqlite_malloc_stat] 2] <= 0]
      expr {$mf1 == $mf2 || $::name8_2 == ""}
    } {1}
    set ::mallocFailed [expr [lindex [sqlite_malloc_stat] 2] <= 0]
    do_test ${testid}.2.4 {
      expr {
        $::name8 == $::name8_2 && $::name16 == $::name8 && !$::mallocFailed ||
        $::name8 == $::name8_2 && $::name16 == "" &&        $::mallocFailed ||
        $::name8 == $::name16 && $::name8_2 == "" &&        $::mallocFailed ||
        $::name8_2 == $::name16 && $::name8 == "" &&        $::mallocFailed
      }
    } {1}

    # Step the statement so that we can call _text() and _text16().  Before
    # running sqlite3_step(), make sure that malloc() is not about to fail.
    # Memory allocation failures that occur within sqlite3_step() are tested
    # elsewhere.
    set mf [lindex [sqlite_malloc_stat] 2]
    sqlite_malloc_fail 0
    do_test ${testid}.3 {
      sqlite3_step $::STMT
    } {SQLITE_ROW}
    sqlite_malloc_fail $mf

    # Test for malloc() failures within _text() and _text16().
    #
    do_test ${testid}.4.1 {
      set ::text8 [sqlite3_column_text $::STMT 0]
      set mf [expr [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed]
      expr {$mf==0 || $::text8 == ""}
    } {1}
    do_test ${testid}.4.2 {
      set ::text16 [sqlite3_column_text16 $::STMT 0]
      set ::text16 [encoding convertfrom unicode $::text16]
      set ::text16 [string range $::text16 0 end-1]
      set mf [expr [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed]
      expr {$mf==0 || $::text16 == ""}
    } {1}
    do_test ${testid}.4.3 {
      set ::text8_2 [sqlite3_column_text $::STMT 0]
      set mf [expr [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed]
      expr {$mf==0 || $::text8_2 == "" || ($::text16 == "" && $::text8 != "")}
    } {1}

    # Test for malloc() failures within _int(), _int64() and _real(). The only
    # way this can occur is if the string has to be translated from UTF-16 to
    # UTF-8 before being converted to a numeric value.
    do_test ${testid}.4.4.1 {
      set mf [lindex [sqlite_malloc_stat] 2]
      sqlite_malloc_fail 0
      sqlite3_column_text16 $::STMT 0
      sqlite_malloc_fail $mf
      sqlite3_column_int $::STMT 0
    } {0}
    do_test ${testid}.4.5 {
      set mf [lindex [sqlite_malloc_stat] 2]
      sqlite_malloc_fail 0
      sqlite3_column_text16 $::STMT 0
      sqlite_malloc_fail $mf
      sqlite3_column_int64 $::STMT 0
    } {0}

    do_test ${testid}.4.6 {
      set mf [lindex [sqlite_malloc_stat] 2]
      sqlite_malloc_fail 0
      sqlite3_column_text16 $::STMT 0
      sqlite_malloc_fail $mf
      sqlite3_column_double $::STMT 0
    } {0.0}

    set mallocFailedAfterStep [expr \
      [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed
    ]

    sqlite_malloc_fail 0
    # Test that if a malloc() failed the next call to sqlite3_step() returns
    # SQLITE_ERROR. If malloc() did not fail, it should return SQLITE_DONE.
    #
    do_test ${testid}.5 {
      sqlite3_step $::STMT
    } [expr {$mallocFailedAfterStep ? "SQLITE_ERROR" : "SQLITE_DONE"}]

    do_test ${testid}.6 {
      sqlite3_finalize $::STMT
    } [expr {$mallocFailedAfterStep ? "SQLITE_NOMEM" : "SQLITE_OK"}]

    if {$::mallocFailed == 0 && $mallocFailedAfterStep == 0} {
      sqlite_malloc_fail 0
      set go 0
    }
  }
}

execsql {
  CREATE TABLE tbl(
................................................................................
  INSERT INTO tbl VALUES(
    'An extra long string. Far too long to be stored in NBFS bytes.'
  );
}

do_stmt_test 1 "SELECT * FROM tbl"

sqlite_malloc_fail 0
finish_test








|







 







|







 







|








|







 







|

|



|



|



|

|


|













|
|



|





|






|




|







|
|

|



|
|

|




|
|

|




|


|












|







 







|

<
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
..
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
..
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
...
185
186
187
188
189
190
191
192
193

#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains tests to ensure that the library handles malloc() failures
# correctly. The emphasis in this file is on sqlite3_column_XXX() APIs.
#
# $Id: malloc4.test,v 1.4 2007/08/22 22:04:37 drh Exp $

#---------------------------------------------------------------------------
# NOTES ON EXPECTED BEHAVIOUR
#
# [193] When a memory allocation failure occurs during sqlite3_column_name(),
#       sqlite3_column_name16(), sqlite3_column_decltype(), or
#       sqlite3_column_decltype16() the function shall return NULL.
................................................................................
#
#---------------------------------------------------------------------------

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
if {[info command sqlite3_memdebug_pending]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

ifcapable !utf16 {
  finish_test
................................................................................
  return
}

proc do_stmt_test {id sql} {
  set ::sql $sql
  set go 1
  for {set n 1} {$go} {incr n} {
    set testid "malloc4-$id.$n"

    # Prepare the statement
    do_test ${testid}.1 {
      set ::STMT [sqlite3_prepare $::DB $sql -1 TAIL]
      expr [string length $::STMT] > 0
    } {1}

    # Set the Nth malloc() to fail.
    sqlite3_memdebug_fail $n 1

    # Test malloc failure in the _name(), _name16(), decltype() and
    # decltype16() APIs. Calls that occur after the malloc() failure should
    # return NULL. No error is raised though.
    #
    # ${testid}.2.1 - Call _name()
    # ${testid}.2.2 - Call _name16()
................................................................................
    #                 malloc() failures.
    #
    # Because the code that implements the _decltype() and _decltype16() APIs
    # is the same as the _name() and _name16() implementations, we don't worry
    # about explicitly testing them.
    #
    do_test ${testid}.2.1 {
      set mf1 [expr [sqlite3_memdebug_pending] <= 0]
      set ::name8  [sqlite3_column_name $::STMT 0]
      set mf2 [expr [sqlite3_memdebug_pending] <= 0]
      expr {$mf1 == $mf2 || $::name8 == ""}
    } {1}
    do_test ${testid}.2.2 {
      set mf1 [expr [sqlite3_memdebug_pending] <= 0]
      set ::name16 [sqlite3_column_name16 $::STMT 0]
      set ::name16 [encoding convertfrom unicode $::name16]
      set ::name16 [string range $::name16 0 end-1]
      set mf2 [expr [sqlite3_memdebug_pending] <= 0]
      expr {$mf1 == $mf2 || $::name16 == ""}
    } {1}
    do_test ${testid}.2.3 {
      set mf1 [expr [sqlite3_memdebug_pending] <= 0]
      set ::name8_2 [sqlite3_column_name $::STMT 0]
      set mf2 [expr [sqlite3_memdebug_pending] <= 0]
      expr {$mf1 == $mf2 || $::name8_2 == ""}
    } {1}
    set ::mallocFailed [expr [sqlite3_memdebug_pending] <= 0]
    do_test ${testid}.2.4 {
      expr {
        $::name8 == $::name8_2 && $::name16 == $::name8 && !$::mallocFailed ||
        $::name8 == $::name8_2 && $::name16 == "" &&        $::mallocFailed ||
        $::name8 == $::name16 && $::name8_2 == "" &&        $::mallocFailed ||
        $::name8_2 == $::name16 && $::name8 == "" &&        $::mallocFailed
      }
    } {1}

    # Step the statement so that we can call _text() and _text16().  Before
    # running sqlite3_step(), make sure that malloc() is not about to fail.
    # Memory allocation failures that occur within sqlite3_step() are tested
    # elsewhere.
    set mf [sqlite3_memdebug_pending]
    sqlite3_memdebug_fail -1
    do_test ${testid}.3 {
      sqlite3_step $::STMT
    } {SQLITE_ROW}
    sqlite3_memdebug_fail $mf

    # Test for malloc() failures within _text() and _text16().
    #
    do_test ${testid}.4.1 {
      set ::text8 [sqlite3_column_text $::STMT 0]
      set mf [expr [sqlite3_memdebug_pending] <= 0 && !$::mallocFailed]
      expr {$mf==0 || $::text8 == ""}
    } {1}
    do_test ${testid}.4.2 {
      set ::text16 [sqlite3_column_text16 $::STMT 0]
      set ::text16 [encoding convertfrom unicode $::text16]
      set ::text16 [string range $::text16 0 end-1]
      set mf [expr [sqlite3_memdebug_pending] <= 0 && !$::mallocFailed]
      expr {$mf==0 || $::text16 == ""}
    } {1}
    do_test ${testid}.4.3 {
      set ::text8_2 [sqlite3_column_text $::STMT 0]
      set mf [expr [sqlite3_memdebug_pending] <= 0 && !$::mallocFailed]
      expr {$mf==0 || $::text8_2 == "" || ($::text16 == "" && $::text8 != "")}
    } {1}

    # Test for malloc() failures within _int(), _int64() and _real(). The only
    # way this can occur is if the string has to be translated from UTF-16 to
    # UTF-8 before being converted to a numeric value.
    do_test ${testid}.4.4.1 {
      set mf [sqlite3_memdebug_pending]
      sqlite3_memdebug_fail -1
      sqlite3_column_text16 $::STMT 0
      sqlite3_memdebug_fail $mf
      sqlite3_column_int $::STMT 0
    } {0}
    do_test ${testid}.4.5 {
      set mf [sqlite3_memdebug_pending]
      sqlite3_memdebug_fail -1
      sqlite3_column_text16 $::STMT 0
      sqlite3_memdebug_fail $mf
      sqlite3_column_int64 $::STMT 0
    } {0}

    do_test ${testid}.4.6 {
      set mf [sqlite3_memdebug_pending]
      sqlite3_memdebug_fail -1
      sqlite3_column_text16 $::STMT 0
      sqlite3_memdebug_fail $mf
      sqlite3_column_double $::STMT 0
    } {0.0}

    set mallocFailedAfterStep [expr \
      [sqlite3_memdebug_pending] <= 0 && !$::mallocFailed
    ]

    sqlite3_memdebug_fail -1
    # Test that if a malloc() failed the next call to sqlite3_step() returns
    # SQLITE_ERROR. If malloc() did not fail, it should return SQLITE_DONE.
    #
    do_test ${testid}.5 {
      sqlite3_step $::STMT
    } [expr {$mallocFailedAfterStep ? "SQLITE_ERROR" : "SQLITE_DONE"}]

    do_test ${testid}.6 {
      sqlite3_finalize $::STMT
    } [expr {$mallocFailedAfterStep ? "SQLITE_NOMEM" : "SQLITE_OK"}]

    if {$::mallocFailed == 0 && $mallocFailedAfterStep == 0} {
      sqlite3_memdebug_fail -1
      set go 0
    }
  }
}

execsql {
  CREATE TABLE tbl(
................................................................................
  INSERT INTO tbl VALUES(
    'An extra long string. Far too long to be stored in NBFS bytes.'
  );
}

do_stmt_test 1 "SELECT * FROM tbl"

sqlite3_memdebug_fail -1
finish_test

Changes to test/malloc5.test.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
...
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
...
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains test cases focused on the two memory-management APIs, 
# sqlite3_soft_heap_limit() and sqlite3_release_memory().
#
# $Id: malloc5.test,v 1.12 2007/08/12 20:07:59 drh Exp $

#---------------------------------------------------------------------------
# NOTES ON EXPECTED BEHAVIOUR
#
#---------------------------------------------------------------------------


set testdir [file dirname $argv0]
source $testdir/tester.tcl
db close

# Only run these tests if memory debugging is turned on.
if {[info command sqlite_malloc_stat]==""} {

   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

# Skip these tests if OMIT_MEMORY_MANAGEMENT was defined at compile time.
ifcapable !memorymanage {
................................................................................
do_test malloc5-3.2 {
  concat \
    [execsql {SELECT * FROM abc; COMMIT}] \
    [execsql {SELECT * FROM def; COMMIT} db2]
} {1 2 3 4 5 6 7 8 9 10 11 12}

db2 close
sqlite_malloc_outstanding -clearmaxbytes

# The following two test cases each execute a transaction in which 
# 10000 rows are inserted into table abc. The first test case is used
# to ensure that more than 1MB of dynamic memory is used to perform
# the transaction. 
#
# The second test case sets the "soft-heap-limit" to 100,000 bytes (0.1 MB)
................................................................................
do_test malloc5-4.1 {
  execsql {BEGIN;}
  execsql {DELETE FROM abc;}
  for {set i 0} {$i < 10000} {incr i} {
    execsql "INSERT INTO abc VALUES($i, $i, '[string repeat X 100]');"
  }
  execsql {COMMIT;}
  set ::nMaxBytes [sqlite_malloc_outstanding -maxbytes]
  if {$::nMaxBytes==""} {set ::nMaxBytes 1000001}
  expr $::nMaxBytes > 1000000
} {1}
do_test malloc5-4.2 {
  sqlite3_release_memory
  sqlite_malloc_outstanding -clearmaxbytes
  sqlite3_soft_heap_limit 100000

  execsql {BEGIN;}
  for {set i 0} {$i < 10000} {incr i} {
    execsql "INSERT INTO abc VALUES($i, $i, '[string repeat X 100]');"
  }
  execsql {COMMIT;}
  set ::nMaxBytes [sqlite_malloc_outstanding -maxbytes]
  if {$::nMaxBytes==""} {set ::nMaxBytes 0}
  expr $::nMaxBytes <= 100000
} {1}
do_test malloc5-4.3 {
  # Check that the content of table abc is at least roughly as expected.
  execsql {
    SELECT count(*), sum(a), sum(b) FROM abc;
  }
} [list 20000 [expr int(20000.0 * 4999.5)] [expr int(20000.0 * 4999.5)]]







|












|
>







 







|







 







|
|
|



<

>





|
|
|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
...
195
196
197
198
199
200
201
202
203
204
205
206
207

208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains test cases focused on the two memory-management APIs, 
# sqlite3_soft_heap_limit() and sqlite3_release_memory().
#
# $Id: malloc5.test,v 1.13 2007/08/22 22:04:37 drh Exp $

#---------------------------------------------------------------------------
# NOTES ON EXPECTED BEHAVIOUR
#
#---------------------------------------------------------------------------


set testdir [file dirname $argv0]
source $testdir/tester.tcl
db close

# Only run these tests if memory debugging is turned on.
#
ifcapable !memdebug {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

# Skip these tests if OMIT_MEMORY_MANAGEMENT was defined at compile time.
ifcapable !memorymanage {
................................................................................
do_test malloc5-3.2 {
  concat \
    [execsql {SELECT * FROM abc; COMMIT}] \
    [execsql {SELECT * FROM def; COMMIT} db2]
} {1 2 3 4 5 6 7 8 9 10 11 12}

db2 close
puts "Highwater mark: [sqlite3_memory_highwater]"

# The following two test cases each execute a transaction in which 
# 10000 rows are inserted into table abc. The first test case is used
# to ensure that more than 1MB of dynamic memory is used to perform
# the transaction. 
#
# The second test case sets the "soft-heap-limit" to 100,000 bytes (0.1 MB)
................................................................................
do_test malloc5-4.1 {
  execsql {BEGIN;}
  execsql {DELETE FROM abc;}
  for {set i 0} {$i < 10000} {incr i} {
    execsql "INSERT INTO abc VALUES($i, $i, '[string repeat X 100]');"
  }
  execsql {COMMIT;}
  set nMaxBytes [sqlite3_memory_highwater 1]
  puts -nonewline " (Highwater mark: $nMaxBytes) "
  expr $nMaxBytes > 1000000
} {1}
do_test malloc5-4.2 {
  sqlite3_release_memory

  sqlite3_soft_heap_limit 100000
  sqlite3_memory_highwater 1
  execsql {BEGIN;}
  for {set i 0} {$i < 10000} {incr i} {
    execsql "INSERT INTO abc VALUES($i, $i, '[string repeat X 100]');"
  }
  execsql {COMMIT;}
  set nMaxBytes [sqlite3_memory_highwater 1]
  puts -nonewline " (Highwater mark: $nMaxBytes) "
  expr $nMaxBytes <= 100000
} {1}
do_test malloc5-4.3 {
  # Check that the content of table abc is at least roughly as expected.
  execsql {
    SELECT count(*), sum(a), sum(b) FROM abc;
  }
} [list 20000 [expr int(20000.0 * 4999.5)] [expr int(20000.0 * 4999.5)]]

Changes to test/malloc6.test.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
...
145
146
147
148
149
150
151
152
153
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file attempts to check the library in an out-of-memory situation.
# When compiled with -DSQLITE_DEBUG=1, the SQLite library accepts a special
# command (sqlite_malloc_fail N) which causes the N-th malloc to fail.  This
# special feature is used to see what happens in the library if a malloc
# were to really fail due to an out-of-memory situation.
#
# $Id: malloc6.test,v 1.1 2006/06/26 12:50:09 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}


# Usage: do_malloc_test <test number> <options...>
#
# The first argument, <test number>, is an integer used to name the
# tests executed by this proc. Options are as follows:
#
#     -tclprep          TCL script to run to prepare test.
#     -sqlprep          SQL script to run to prepare test.
#     -tclbody          TCL script to run with malloc failure simulation.
#     -sqlbody          TCL script to run with malloc failure simulation.
#     -cleanup          TCL script to run after the test.
#
# This command runs a series of tests to verify SQLite's ability
# to handle an out-of-memory condition gracefully. It is assumed
# that if this condition occurs a malloc() call will return a
# NULL pointer. Linux, for example, doesn't do that by default. See
# the "BUGS" section of malloc(3).
#
# Each iteration of a loop, the TCL commands in any argument passed
# to the -tclbody switch, followed by the SQL commands in any argument
# passed to the -sqlbody switch are executed. Each iteration the
# Nth call to sqliteMalloc() is made to fail, where N is increased
# each time the loop runs starting from 1. When all commands execute
# successfully, the loop ends.
#
proc do_malloc_test {tn args} {
  array unset ::mallocopts 
  array set ::mallocopts $args

  set ::go 1
  for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
    do_test malloc6-$tn.$::n {

      # Remove all traces of database files test.db and test2.db from the files
      # system. Then open (empty database) "test.db" with the handle [db].
      # 
      sqlite_malloc_fail 0
      catch {db close} 
      catch {file delete -force test.db}
      catch {file delete -force test.db-journal}
      catch {file delete -force test2.db}
      catch {file delete -force test2.db-journal}
      catch {sqlite3 db test.db} 
      set ::DB [sqlite3_connection_pointer db]

      # Execute any -tclprep and -sqlprep scripts.
      #
      if {[info exists ::mallocopts(-tclprep)]} {
        eval $::mallocopts(-tclprep)
      }
      if {[info exists ::mallocopts(-sqlprep)]} {
        execsql $::mallocopts(-sqlprep)
      }

      # Now set the ${::n}th malloc() to fail and execute the -tclbody and
      # -sqlbody scripts.
      #
      sqlite_malloc_fail $::n
      set ::mallocbody {}
      if {[info exists ::mallocopts(-tclbody)]} {
        append ::mallocbody "$::mallocopts(-tclbody)\n"
      }
      if {[info exists ::mallocopts(-sqlbody)]} {
        append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
      }
      set v [catch $::mallocbody msg]

      # If the test fails (if $v!=0) and the database connection actually
      # exists, make sure the failure code is SQLITE_NOMEM.
      if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)]
              && [db errorcode]!=7} {
        set v 999
      }

      set leftover [lindex [sqlite_malloc_stat] 2]
      if {$leftover>0} {
        if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v  Message=$msg"}
        set ::go 0
        if {$v} {
          puts "\nError message returned: $msg"
        } else {
          set v {1 1}
        }
      } else {
        set v2 [expr {$msg=="" || $msg=="out of memory"}]
        if {!$v2} {puts "\nError message returned: $msg"}
        lappend v $v2
      }
    } {1 1}

    if {[info exists ::mallocopts(-cleanup)]} {
      catch [list uplevel #0 $::mallocopts(-cleanup)] msg
    }
  }
  unset ::mallocopts
}

set sqlite_os_trace 0
do_malloc_test 1 -tclprep {
  db close
} -tclbody {
  if {[catch {sqlite3 db test.db}]} {
    error "out of memory"
  }
} -sqlbody {
  DROP TABLE IF EXISTS t1;
................................................................................

# Ensure that no file descriptors were leaked.
do_test malloc6-1.X {
  catch {db close}
  set sqlite_open_file_count
} {0}

sqlite_malloc_fail 0
finish_test







<
<
<
<

|






|




>

<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|







 







<

5
6
7
8
9
10
11




12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

27






























































































28
29
30
31
32
33
34
35
36
..
47
48
49
50
51
52
53

54
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file attempts to check the library in an out-of-memory situation.




#
# $Id: malloc6.test,v 1.2 2007/08/22 22:04:37 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
ifcapable !memdebug {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}
source $testdir/malloc_common.tcl

































































































set sqlite_os_trace 0
do_malloc_test malloc6-1 -tclprep {
  db close
} -tclbody {
  if {[catch {sqlite3 db test.db}]} {
    error "out of memory"
  }
} -sqlbody {
  DROP TABLE IF EXISTS t1;
................................................................................

# Ensure that no file descriptors were leaked.
do_test malloc6-1.X {
  catch {db close}
  set sqlite_open_file_count
} {0}


finish_test

Changes to test/malloc7.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file contains additional out-of-memory checks (see malloc.tcl)
# added to expose a bug in out-of-memory handling for sqlite3_prepare16().
#
# $Id: malloc7.test,v 1.2 2006/07/26 14:57:30 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}


# Usage: do_malloc_test <test number> <options...>
#
# The first argument, <test number>, is an integer used to name the
# tests executed by this proc. Options are as follows:
#
#     -tclprep          TCL script to run to prepare test.
#     -sqlprep          SQL script to run to prepare test.
#     -tclbody          TCL script to run with malloc failure simulation.
#     -sqlbody          TCL script to run with malloc failure simulation.
#     -cleanup          TCL script to run after the test.
#
# This command runs a series of tests to verify SQLite's ability
# to handle an out-of-memory condition gracefully. It is assumed
# that if this condition occurs a malloc() call will return a
# NULL pointer. Linux, for example, doesn't do that by default. See
# the "BUGS" section of malloc(3).
#
# Each iteration of a loop, the TCL commands in any argument passed
# to the -tclbody switch, followed by the SQL commands in any argument
# passed to the -sqlbody switch are executed. Each iteration the
# Nth call to sqliteMalloc() is made to fail, where N is increased
# each time the loop runs starting from 1. When all commands execute
# successfully, the loop ends.
#
proc do_malloc_test {tn args} {
  array unset ::mallocopts 
  array set ::mallocopts $args

  set ::go 1
  for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
    do_test malloc7-$tn.$::n {

      # Remove all traces of database files test.db and test2.db from the files
      # system. Then open (empty database) "test.db" with the handle [db].
      # 
      sqlite_malloc_fail 0
      catch {db close} 
      catch {file delete -force test.db}
      catch {file delete -force test.db-journal}
      catch {file delete -force test2.db}
      catch {file delete -force test2.db-journal}
      catch {sqlite3 db test.db} 
      set ::DB [sqlite3_connection_pointer db]

      # Execute any -tclprep and -sqlprep scripts.
      #
      if {[info exists ::mallocopts(-tclprep)]} {
        eval $::mallocopts(-tclprep)
      }
      if {[info exists ::mallocopts(-sqlprep)]} {
        execsql $::mallocopts(-sqlprep)
      }

      # Now set the ${::n}th malloc() to fail and execute the -tclbody and
      # -sqlbody scripts.
      #
      sqlite_malloc_fail $::n
      set ::mallocbody {}
      if {[info exists ::mallocopts(-tclbody)]} {
        append ::mallocbody "$::mallocopts(-tclbody)\n"
      }
      if {[info exists ::mallocopts(-sqlbody)]} {
        append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
      }
      set v [catch $::mallocbody msg]

      # If the test fails (if $v!=0) and the database connection actually
      # exists, make sure the failure code is SQLITE_NOMEM.
      if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)]
              && [db errorcode]!=7} {
        set v 999
      }

      set leftover [lindex [sqlite_malloc_stat] 2]
      if {$leftover>0} {
        if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v  Message=$msg"}
        set ::go 0
        if {$v} {
          puts "\nError message returned: $msg"
        } else {
          set v {1 1}
        }
      } else {
        set v2 [expr {$msg=="" || $msg=="out of memory"}]
        if {!$v2} {puts "\nError message returned: $msg"}
        lappend v $v2
      }
    } {1 1}

    if {[info exists ::mallocopts(-cleanup)]} {
      catch [list uplevel #0 $::mallocopts(-cleanup)] msg
    }
  }
  unset ::mallocopts
}

db eval {
  CREATE TABLE t1(a,b,c,d);
  CREATE INDEX i1 ON t1(b,c);
}

do_malloc_test 1 -tclbody {
  set sql16 [encoding convertto unicode "SELECT * FROM sqlite_master"]
  append sql16 "\00\00"
  set nbyte [string length $sql16]
  set ::STMT [sqlite3_prepare16 $::DB $sql16 $nbyte DUMMY]
  sqlite3_finalize $::STMT
} 



# Ensure that no file descriptors were leaked.
do_test malloc-99.X {
  catch {db close}
  set sqlite_open_file_count
} {0}

puts open-file-count=$sqlite_open_file_count
sqlite_malloc_fail 0
finish_test







|






|




>

<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


<
<
|



|


<









<

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

28






















29








































































30
31


32
33
34
35
36
37
38

39
40
41
42
43
44
45
46
47

48
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file contains additional out-of-memory checks (see malloc.tcl)
# added to expose a bug in out-of-memory handling for sqlite3_prepare16().
#
# $Id: malloc7.test,v 1.3 2007/08/22 22:04:37 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
ifcapable !memdebug {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}
source $testdir/malloc_common.tcl

























do_malloc_test malloc7-1 -sqlprep {








































































  CREATE TABLE t1(a,b,c,d);
  CREATE INDEX i1 ON t1(b,c);


} -tclbody {
  set sql16 [encoding convertto unicode "SELECT * FROM sqlite_master"]
  append sql16 "\00\00"
  set nbyte [string length $sql16]
  set ::STMT [sqlite3_prepare16 db $sql16 $nbyte DUMMY]
  sqlite3_finalize $::STMT
} 



# Ensure that no file descriptors were leaked.
do_test malloc-99.X {
  catch {db close}
  set sqlite_open_file_count
} {0}

puts open-file-count=$sqlite_open_file_count

finish_test

Changes to test/malloc8.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126


127
128
129
130
131
132
133
134
135
136





137
138
139
140





141
142
143





144
145
146





147
148
149





150
151
152
153
154
155
156
157
158
159
160
161
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file contains additional out-of-memory checks (see malloc.tcl)
# added to expose a bug in out-of-memory handling for sqlite3_value_text()
#
# $Id: malloc8.test,v 1.3 2007/05/07 19:31:17 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

# Usage: do_malloc_test <test number> <options...>
#
# The first argument, <test number>, is an integer used to name the
# tests executed by this proc. Options are as follows:
#
#     -tclprep          TCL script to run to prepare test.
#     -sqlprep          SQL script to run to prepare test.
#     -tclbody          TCL script to run with malloc failure simulation.
#     -sqlbody          TCL script to run with malloc failure simulation.
#     -cleanup          TCL script to run after the test.
#
# This command runs a series of tests to verify SQLite's ability
# to handle an out-of-memory condition gracefully. It is assumed
# that if this condition occurs a malloc() call will return a
# NULL pointer. Linux, for example, doesn't do that by default. See
# the "BUGS" section of malloc(3).
#
# Each iteration of a loop, the TCL commands in any argument passed
# to the -tclbody switch, followed by the SQL commands in any argument
# passed to the -sqlbody switch are executed. Each iteration the
# Nth call to sqliteMalloc() is made to fail, where N is increased
# each time the loop runs starting from 1. When all commands execute
# successfully, the loop ends.
#
proc do_malloc_test {tn args} {
  array unset ::mallocopts 
  array set ::mallocopts $args

  set ::go 1
  for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
    do_test malloc8-$tn.$::n {

      sqlite_malloc_fail 0
      catch {db close}
      sqlite3 db test.db
      set ::DB [sqlite3_connection_pointer db]

      # Execute any -tclprep and -sqlprep scripts.
      #
      if {[info exists ::mallocopts(-tclprep)]} {
        eval $::mallocopts(-tclprep)
      }
      if {[info exists ::mallocopts(-sqlprep)]} {
        execsql $::mallocopts(-sqlprep)
      }

      # Now set the ${::n}th malloc() to fail and execute the -tclbody and
      # -sqlbody scripts.
      #
      sqlite_malloc_fail $::n
      set ::mallocbody {}
      if {[info exists ::mallocopts(-tclbody)]} {
        append ::mallocbody "$::mallocopts(-tclbody)\n"
      }
      if {[info exists ::mallocopts(-sqlbody)]} {
        append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
      }
      set v [catch $::mallocbody msg]

      # If the test fails (if $v!=0) and the database connection actually
      # exists, make sure the failure code is SQLITE_NOMEM.
      if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)]
              && [db errorcode]!=7} {
        set v 999
      }

      set leftover [lindex [sqlite_malloc_stat] 2]
      if {$leftover>0} {
        if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v  Message=$msg"}
        set ::go 0
        if {$v} {
          puts "\nError message returned: $msg"
        } else {
          set v {1 1}
        }
      } else {
        set v2 [expr {$msg=="" || $msg=="out of memory"}]
        if {!$v2} {puts "\nError message returned: $msg"}
        lappend v $v2
      }
    } {1 1}

    if {[info exists ::mallocopts(-cleanup)]} {
      catch [list uplevel #0 $::mallocopts(-cleanup)] msg
    }
  }
  unset ::mallocopts
}

# The setup is a database with UTF-16 encoding that contains a single
# large string.  We will be running lots of queries against this 
# database.  Because we will be extracting the string as UTF-8, there
# is a type conversion that occurs and thus an opportunity for malloc()
# to fail and for sqlite3_value_text() to return 0 even though
# sqlite3_value_type() returns SQLITE_TEXT.
#
db close
file delete -force test.db test.db-journal
sqlite3 db test.db
db eval {


  PRAGMA encoding='UTF-16';
  CREATE TABLE t1(a);
  INSERT INTO t1 
  VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
}


do_malloc_test 1 -sqlbody {
  SELECT lower(a), upper(a), quote(a), trim(a), trim('x',a) FROM t1;
}





do_malloc_test 2 -sqlbody {
  SELECT replace(a,'x','y'), replace('x',a,'y'), replace('x','y',a)
    FROM t1;
}





do_malloc_test 3 -sqlbody {
  SELECT length(a), substr(a, 4, 4) FROM t1;
}





do_malloc_test 4 -sqlbody {
  SELECT julianday(a,a) FROM t1;
}





do_malloc_test 5 -sqlbody {
  SELECT 1 FROM t1 WHERE a LIKE 'hello' ESCAPE NULL;
}





do_malloc_test 6 -sqlbody {
  SELECT hex(randomblob(100));
}

# Ensure that no file descriptors were leaked.
do_test malloc-99.X {
  catch {db close}
  set sqlite_open_file_count
} {0}

sqlite_malloc_fail 0
finish_test







|






|





|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







<
<
<
<
>
>




<
<
<
|


>
>
>
>
>
|



>
>
>
>
>
|


>
>
>
>
>
|


>
>
>
>
>
|


>
>
>
>
>
|









<

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28























































































29
30
31
32
33
34
35




36
37
38
39
40
41



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

93
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file contains additional out-of-memory checks (see malloc.tcl)
# added to expose a bug in out-of-memory handling for sqlite3_value_text()
#
# $Id: malloc8.test,v 1.4 2007/08/22 22:04:37 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
ifcapable !memdebug {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

source $testdir/malloc_common.tcl
























































































# The setup is a database with UTF-16 encoding that contains a single
# large string.  We will be running lots of queries against this 
# database.  Because we will be extracting the string as UTF-8, there
# is a type conversion that occurs and thus an opportunity for malloc()
# to fail and for sqlite3_value_text() to return 0 even though
# sqlite3_value_type() returns SQLITE_TEXT.
#





do_malloc_test malloc8-1 -sqlprep {
  PRAGMA encoding='UTF-16';
  CREATE TABLE t1(a);
  INSERT INTO t1 
  VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');



} -sqlbody {
  SELECT lower(a), upper(a), quote(a), trim(a), trim('x',a) FROM t1;
}
do_malloc_test malloc8-2 -sqlprep {
  PRAGMA encoding='UTF-16';
  CREATE TABLE t1(a);
  INSERT INTO t1 
  VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
} -sqlbody {
  SELECT replace(a,'x','y'), replace('x',a,'y'), replace('x','y',a)
    FROM t1;
}
do_malloc_test malloc8-3 -sqlprep {
  PRAGMA encoding='UTF-16';
  CREATE TABLE t1(a);
  INSERT INTO t1 
  VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
} -sqlbody {
  SELECT length(a), substr(a, 4, 4) FROM t1;
}
do_malloc_test malloc8-4 -sqlprep {
  PRAGMA encoding='UTF-16';
  CREATE TABLE t1(a);
  INSERT INTO t1 
  VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
} -sqlbody {
  SELECT julianday(a,a) FROM t1;
}
do_malloc_test malloc8-5 -sqlprep {
  PRAGMA encoding='UTF-16';
  CREATE TABLE t1(a);
  INSERT INTO t1 
  VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
} -sqlbody {
  SELECT 1 FROM t1 WHERE a LIKE 'hello' ESCAPE NULL;
}
do_malloc_test malloc8-6 -sqlprep {
  PRAGMA encoding='UTF-16';
  CREATE TABLE t1(a);
  INSERT INTO t1 
  VALUES('0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ');
} -sqlbody {
  SELECT hex(randomblob(100));
}

# Ensure that no file descriptors were leaked.
do_test malloc-99.X {
  catch {db close}
  set sqlite_open_file_count
} {0}


finish_test

Changes to test/malloc9.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
...
134
135
136
137
138
139
140
141
142
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file contains additional out-of-memory checks (see malloc.tcl)
# added to expose a bug in out-of-memory handling for sqlite3_prepare().
#
# $Id: malloc9.test,v 1.1 2007/04/30 21:39:16 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

# Usage: do_malloc_test <test number> <options...>
#
# The first argument, <test number>, is an integer used to name the
# tests executed by this proc. Options are as follows:
#
#     -tclprep          TCL script to run to prepare test.
#     -sqlprep          SQL script to run to prepare test.
#     -tclbody          TCL script to run with malloc failure simulation.
#     -sqlbody          TCL script to run with malloc failure simulation.
#     -cleanup          TCL script to run after the test.
#
# This command runs a series of tests to verify SQLite's ability
# to handle an out-of-memory condition gracefully. It is assumed
# that if this condition occurs a malloc() call will return a
# NULL pointer. Linux, for example, doesn't do that by default. See
# the "BUGS" section of malloc(3).
#
# Each iteration of a loop, the TCL commands in any argument passed
# to the -tclbody switch, followed by the SQL commands in any argument
# passed to the -sqlbody switch are executed. Each iteration the
# Nth call to sqliteMalloc() is made to fail, where N is increased
# each time the loop runs starting from 1. When all commands execute
# successfully, the loop ends.
#
proc do_malloc_test {tn args} {
  array unset ::mallocopts 
  array set ::mallocopts $args

  set ::go 1
  for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
    do_test malloc9-$tn.$::n {

      sqlite_malloc_fail 0
      catch {db close}
      catch {file delete -force test.db}
      catch {file delete -force test.db-journal}
      sqlite3 db test.db
      set ::DB [sqlite3_connection_pointer db]

      # Execute any -tclprep and -sqlprep scripts.
      #
      if {[info exists ::mallocopts(-tclprep)]} {
        eval $::mallocopts(-tclprep)
      }
      if {[info exists ::mallocopts(-sqlprep)]} {
        execsql $::mallocopts(-sqlprep)
      }

      # Now set the ${::n}th malloc() to fail and execute the -tclbody and
      # -sqlbody scripts.
      #
      sqlite_malloc_fail $::n
      set ::mallocbody {}
      if {[info exists ::mallocopts(-tclbody)]} {
        append ::mallocbody "$::mallocopts(-tclbody)\n"
      }
      if {[info exists ::mallocopts(-sqlbody)]} {
        append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
      }
      set v [catch $::mallocbody msg]

      # If the test fails (if $v!=0) and the database connection actually
      # exists, make sure the failure code is SQLITE_NOMEM.
      if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)]
              && [db errorcode]!=7} {
        set v 999
      }

      set leftover [lindex [sqlite_malloc_stat] 2]
      if {$leftover>0} {
        if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v  Message=$msg"}
        set ::go 0
        if {$v} {
          puts "\nError message returned: $msg"
        } else {
          set v {1 1}
        }
      } else {
        set v2 [expr {$msg=="" || [regexp {out of memory} $msg]}]
        if {!$v2} {puts "\nError message returned: $msg"}
        lappend v $v2
      }
    } {1 1}

    if {[info exists ::mallocopts(-cleanup)]} {
      catch [list uplevel #0 $::mallocopts(-cleanup)] msg
    }
  }
  unset ::mallocopts
}


do_malloc_test 1 -tclprep {
  set sql {CREATE TABLE t1(x)}
  set sqlbytes [string length $sql]
  append sql {; INSERT INTO t1 VALUES(1)}
} -tclbody {
  if {[catch {sqlite3_prepare db $sql $sqlbytes TAIL} STMT]} {
................................................................................

# Ensure that no file descriptors were leaked.
do_test malloc-99.X {
  catch {db close}
  set sqlite_open_file_count
} {0}

sqlite_malloc_fail 0
finish_test







|






|





|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


























































































28
29
30
31
32
33
34
..
44
45
46
47
48
49
50

51
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file contains additional out-of-memory checks (see malloc.tcl)
# added to expose a bug in out-of-memory handling for sqlite3_prepare().
#
# $Id: malloc9.test,v 1.2 2007/08/22 22:04:37 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
ifcapable !memdebug {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

source $testdir/malloc_common.tcl



























































































do_malloc_test 1 -tclprep {
  set sql {CREATE TABLE t1(x)}
  set sqlbytes [string length $sql]
  append sql {; INSERT INTO t1 VALUES(1)}
} -tclbody {
  if {[catch {sqlite3_prepare db $sql $sqlbytes TAIL} STMT]} {
................................................................................

# Ensure that no file descriptors were leaked.
do_test malloc-99.X {
  catch {db close}
  set sqlite_open_file_count
} {0}


finish_test

Changes to test/mallocA.test.

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file contains additional out-of-memory checks (see malloc.tcl).
#
# $Id: mallocA.test,v 1.2 2007/05/12 15:00:15 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

# Usage: do_malloc_test <test number> <options...>
#
# The first argument, <test number>, is an integer used to name the
# tests executed by this proc. Options are as follows:
#
#     -tclprep          TCL script to run to prepare test.
#     -sqlprep          SQL script to run to prepare test.
#     -tclbody          TCL script to run with malloc failure simulation.
#     -sqlbody          TCL script to run with malloc failure simulation.
#     -cleanup          TCL script to run after the test.
#
# This command runs a series of tests to verify SQLite's ability
# to handle an out-of-memory condition gracefully. It is assumed
# that if this condition occurs a malloc() call will return a
# NULL pointer. Linux, for example, doesn't do that by default. See
# the "BUGS" section of malloc(3).
#
# Each iteration of a loop, the TCL commands in any argument passed
# to the -tclbody switch, followed by the SQL commands in any argument
# passed to the -sqlbody switch are executed. Each iteration the
# Nth call to sqliteMalloc() is made to fail, where N is increased
# each time the loop runs starting from 1. When all commands execute
# successfully, the loop ends.
#
proc do_malloc_test {tn args} {
  array unset ::mallocopts 
  array set ::mallocopts $args

  set ::go 1
  for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
    do_test mallocA-$tn.$::n {

      sqlite_malloc_fail 0
      catch {db close}
      catch {file delete -force test.db test.db-journal}
      catch {file copy test.db.bu test.db}
      sqlite3 db test.db
      set ::DB [sqlite3_connection_pointer db]

      # Execute any -tclprep and -sqlprep scripts.
      #
      if {[info exists ::mallocopts(-tclprep)]} {
        eval $::mallocopts(-tclprep)
      }
      if {[info exists ::mallocopts(-sqlprep)]} {
        execsql $::mallocopts(-sqlprep)
      }

      # Now set the ${::n}th malloc() to fail and execute the -tclbody and
      # -sqlbody scripts.
      #
      sqlite_malloc_fail $::n
      set ::mallocbody {}
      if {[info exists ::mallocopts(-tclbody)]} {
        append ::mallocbody "$::mallocopts(-tclbody)\n"
      }
      if {[info exists ::mallocopts(-sqlbody)]} {
        append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
      }
      set v [catch $::mallocbody msg]

      # If the test fails (if $v!=0) and the database connection actually
      # exists, make sure the failure code is SQLITE_NOMEM.
      if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)]
              && [db errorcode]!=7} {
        set v 999
      }

      set leftover [lindex [sqlite_malloc_stat] 2]
      if {$leftover>0} {
        if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v  Message=$msg"}
        set ::go 0
        if {$v} {
          puts "\nError message returned: $msg"
        } else {
          set v {1 1}
        }
      } else {
        set v2 [expr {$msg=="" || [regexp {out of memory} $msg]}]
        if {!$v2} {puts "\nError message returned: $msg"}
        lappend v $v2
      }
    } {1 1}

    if {[info exists ::mallocopts(-cleanup)]} {
      catch [list uplevel #0 $::mallocopts(-cleanup)] msg
    }
  }
  unset ::mallocopts
}

# Construct a test database
#
file delete -force test.db.bu
db eval {
  CREATE TABLE t1(a COLLATE NOCASE,b,c);
  INSERT INTO t1 VALUES(1,2,3);
................................................................................
  INSERT INTO t1 VALUES(2,3,4);
  CREATE INDEX t1i1 ON t1(a);
  CREATE INDEX t1i2 ON t1(b,c);
  CREATE TABLE t2(x,y,z);
}
db close
file copy test.db test.db.bu
sqlite3 db test.db


do_malloc_test 1 -sqlbody {
  ANALYZE
}
do_malloc_test 2 -sqlbody {
  REINDEX;
}
do_malloc_test 3 -sqlbody {
  REINDEX t1;
}
do_malloc_test 4 -sqlbody {
  REINDEX main.t1;
}
do_malloc_test 5 -sqlbody {
  REINDEX nocase;
}

# Ensure that no file descriptors were leaked.
do_test malloc-99.X {
  catch {db close}
  set sqlite_open_file_count
} {0}

file delete -force test.db.bu
sqlite_malloc_fail 0
finish_test







|






|





|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<


|


|


|


|


|










<

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

























































































27
28
29
30
31
32
33
..
35
36
37
38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

67
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file contains additional out-of-memory checks (see malloc.tcl).
#
# $Id: mallocA.test,v 1.3 2007/08/22 22:04:37 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
ifcapable !memdebug {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

source $testdir/malloc_common.tcl


























































































# Construct a test database
#
file delete -force test.db.bu
db eval {
  CREATE TABLE t1(a COLLATE NOCASE,b,c);
  INSERT INTO t1 VALUES(1,2,3);
................................................................................
  INSERT INTO t1 VALUES(2,3,4);
  CREATE INDEX t1i1 ON t1(a);
  CREATE INDEX t1i2 ON t1(b,c);
  CREATE TABLE t2(x,y,z);
}
db close
file copy test.db test.db.bu



do_malloc_test 1 -testdb test.db.bu -sqlbody {
  ANALYZE
}
do_malloc_test 2 -testdb test.db.bu -sqlbody {
  REINDEX;
}
do_malloc_test 3 -testdb test.db.bu -sqlbody {
  REINDEX t1;
}
do_malloc_test 4 -testdb test.db.bu -sqlbody {
  REINDEX main.t1;
}
do_malloc_test 5 -testdb test.db.bu -sqlbody {
  REINDEX nocase;
}

# Ensure that no file descriptors were leaked.
do_test malloc-99.X {
  catch {db close}
  set sqlite_open_file_count
} {0}

file delete -force test.db.bu

finish_test

Changes to test/mallocB.test.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
..
37
38
39
40
41
42
43
44
45
#
#***********************************************************************
# This file contains additional out-of-memory checks (see malloc.tcl).
# These were all discovered by fuzzy generation of SQL. Apart from
# that they have little in common.
#
#
# $Id: mallocB.test,v 1.3 2007/07/26 06:50:06 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl

# Only run these tests if memory debugging is turned on.
#
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}


do_malloc_test mallocB-1 -sqlbody {SELECT - 456}
do_malloc_test mallocB-2 -sqlbody {SELECT - 456.1}
do_malloc_test mallocB-3 -sqlbody {SELECT random()}
do_malloc_test mallocB-4 -sqlbody {SELECT zeroblob(1000)}
do_malloc_test mallocB-5 -sqlbody {SELECT * FROM (SELECT 1) GROUP BY 1;}

................................................................................
# malloc() failure in sqlite3_set_auxdata().
#
# Note: This problem was not discovered by fuzzy generation of SQL. Not
# that it really matters.
#
do_malloc_test mallocB-6 -sqlbody { SELECT test_auxdata('hello world'); }

sqlite_malloc_fail 0
finish_test







|







|




>







 







<

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
..
38
39
40
41
42
43
44

45
#
#***********************************************************************
# This file contains additional out-of-memory checks (see malloc.tcl).
# These were all discovered by fuzzy generation of SQL. Apart from
# that they have little in common.
#
#
# $Id: mallocB.test,v 1.4 2007/08/22 22:04:37 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl

# Only run these tests if memory debugging is turned on.
#
ifcapable !memdebug {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}
source $testdir/malloc_common.tcl

do_malloc_test mallocB-1 -sqlbody {SELECT - 456}
do_malloc_test mallocB-2 -sqlbody {SELECT - 456.1}
do_malloc_test mallocB-3 -sqlbody {SELECT random()}
do_malloc_test mallocB-4 -sqlbody {SELECT zeroblob(1000)}
do_malloc_test mallocB-5 -sqlbody {SELECT * FROM (SELECT 1) GROUP BY 1;}

................................................................................
# malloc() failure in sqlite3_set_auxdata().
#
# Note: This problem was not discovered by fuzzy generation of SQL. Not
# that it really matters.
#
do_malloc_test mallocB-6 -sqlbody { SELECT test_auxdata('hello world'); }


finish_test

Changes to test/mallocC.test.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
..
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
..
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# 
# This file tests aspects of the malloc failure while parsing
# CREATE TABLE statements in auto_vacuum mode.
#
# $Id: mallocC.test,v 1.2 2007/08/13 12:58:18 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
if {[info command sqlite_malloc_stat]==""} {
  puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG=1"
  finish_test
  return
}

# Generate a checksum based on the contents of the database. If the
# checksum of two databases is the same, and the integrity-check passes
# for both, the two databases are identical.
#
proc cksum {db} {
................................................................................
  array set ::mallocopts $args
  set sum [cksum db]

  for {set ::n 1} {true} {incr ::n} {

    # Run the SQL. Malloc number $::n is set to fail. A malloc() failure
    # may or may not be reported.
    sqlite_malloc_fail $::n
    do_test mallocC-$tn.$::n.1 {
      set res [catchsql [string trim $::mallocopts(-sql)]]
      set rc [expr { 
        0==[string compare $res {1 {out of memory}}] ||
        0==[lindex $res 0]
      }]
      if {$rc!=1} {
................................................................................
        puts "Error: $res"
      }
      set rc
    } {1}

    # If $::n is greater than the number of malloc() calls required to
    # execute the SQL, then this test is finished. Break out of the loop.
    if {[lindex [sqlite_malloc_stat] 2]>0} {
      sqlite_malloc_fail -1
      break
    }

    # Recover from the malloc failure.
    #
    # Update: The new malloc() failure handling means that a transaction may
    # still be active even if a malloc() has failed. But when these tests were







|






|
|
|
|







 







|







 







|
|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
..
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
..
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# 
# This file tests aspects of the malloc failure while parsing
# CREATE TABLE statements in auto_vacuum mode.
#
# $Id: mallocC.test,v 1.3 2007/08/22 22:04:37 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
#
ifcapable !memdebug {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
   finish_test
   return
}

# Generate a checksum based on the contents of the database. If the
# checksum of two databases is the same, and the integrity-check passes
# for both, the two databases are identical.
#
proc cksum {db} {
................................................................................
  array set ::mallocopts $args
  set sum [cksum db]

  for {set ::n 1} {true} {incr ::n} {

    # Run the SQL. Malloc number $::n is set to fail. A malloc() failure
    # may or may not be reported.
    sqlite3_memdebug_fail $::n 1
    do_test mallocC-$tn.$::n.1 {
      set res [catchsql [string trim $::mallocopts(-sql)]]
      set rc [expr { 
        0==[string compare $res {1 {out of memory}}] ||
        0==[lindex $res 0]
      }]
      if {$rc!=1} {
................................................................................
        puts "Error: $res"
      }
      set rc
    } {1}

    # If $::n is greater than the number of malloc() calls required to
    # execute the SQL, then this test is finished. Break out of the loop.
    set nFail [sqlite3_memdebug_fail -1 -1]
    if {$nFail==0} {
      break
    }

    # Recover from the malloc failure.
    #
    # Update: The new malloc() failure handling means that a transaction may
    # still be active even if a malloc() has failed. But when these tests were

Changes to test/malloc_common.tcl.

44
45
46
47
48
49
50



51
52
53
54
55
56
57
      # system. Then open (empty database) "test.db" with the handle [db].
      # 
      catch {db close} 
      catch {file delete -force test.db}
      catch {file delete -force test.db-journal}
      catch {file delete -force test2.db}
      catch {file delete -force test2.db-journal}



      catch {sqlite3 db test.db} 

      # Execute any -tclprep and -sqlprep scripts.
      #
      if {[info exists ::mallocopts(-tclprep)]} {
        eval $::mallocopts(-tclprep)
      }







>
>
>







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
      # system. Then open (empty database) "test.db" with the handle [db].
      # 
      catch {db close} 
      catch {file delete -force test.db}
      catch {file delete -force test.db-journal}
      catch {file delete -force test2.db}
      catch {file delete -force test2.db-journal}
      if {[info exists ::mallocopts(-testdb)]} {
        file copy $::mallocopts(-testdb) test.db
      }
      catch {sqlite3 db test.db} 

      # Execute any -tclprep and -sqlprep scripts.
      #
      if {[info exists ::mallocopts(-tclprep)]} {
        eval $::mallocopts(-tclprep)
      }

Changes to test/tester.tcl.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
...
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
# $Id: tester.tcl,v 1.85 2007/08/22 20:18:22 drh Exp $

# Make sure tclsqlite3 was compiled correctly.  Abort now with an
# error message if not.
#
if {[sqlite3 -tcl-uses-utf]} {
  if {"\u1234"=="u1234"} {
    puts stderr "***** BUILD PROBLEM *****"
................................................................................
  global nTest nErr sqlite_open_file_count
  if {$nErr==0} memleak_check

  catch {db close}
  catch {db2 close}
  catch {db3 close}

  catch {
    pp_check_for_leaks
  }
  sqlite3 db {}
  # sqlite3_clear_tsd_memdebug
  db close
  set heaplimit [sqlite3_soft_heap_limit]
  if {$heaplimit!=$::soft_limit} {
    puts "soft-heap-limit changed by this script\
          from $::soft_limit to $heaplimit"
................................................................................
    fconfigure $t -translation binary
    puts -nonewline $t [read $f [file size $from]]
    close $t
    close $f
  }
}

# This command checks for outstanding calls to sqlite3_malloc()
# A list is returned with one entry for each outstanding
# malloc. Each list entry is itself a list of 5 items, as follows:
#
#     { <number-bytes> <file-name> <line-number> <test-case> <stack-dump> }
#
proc check_for_leaks {} {
  set ret [list]
  set cnt 0
  foreach alloc [sqlite_malloc_outstanding] {
    foreach {nBytes file iLine userstring backtrace} $alloc {}
    set stack [list]
    set skip 0

    # The first command in this block will probably fail on windows. This
    # means there will be no stack dump available.
    if {$cnt < 25 && $backtrace!=""} {
      catch {
        set stuff [eval "exec addr2line -e ./testfixture -f $backtrace"]
        foreach {func line} $stuff {
          if {$func != "??" || $line != "??:0"} {
            regexp {.*/(.*)} $line dummy line
            lappend stack "${func}() $line"
          } else {
            if {[lindex $stack end] != "..."} {
              lappend stack "..."
            }
          }
        }
      }
      incr cnt
    }

    if {!$skip} {
      lappend ret [list $nBytes $file $iLine $userstring $stack]
    }
  }
  return $ret
}

# Pretty print a report based on the return value of [check_for_leaks] to
# stdout.
proc pp_check_for_leaks {} {
  set l [check_for_leaks]
  set n 0
  foreach leak $l {
    foreach {nBytes file iLine userstring stack} $leak {}
    puts "$nBytes bytes leaked at $file:$iLine ($userstring)"
    foreach frame $stack {
      puts "        $frame"
    }
    incr n $nBytes
  }
  puts "Memory leaked: $n bytes in [llength $l] allocations"
  puts ""
}

# If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set
# to non-zero, then set the global variable $AUTOVACUUM to 1.
set AUTOVACUUM $sqlite_options(default_autovacuum)







|







 







<
<
<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
188
189
190
191
192
193
194



195
196
197
198
199
200
201
...
564
565
566
567
568
569
570

























































571
572
573
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
# $Id: tester.tcl,v 1.86 2007/08/22 22:04:37 drh Exp $

# Make sure tclsqlite3 was compiled correctly.  Abort now with an
# error message if not.
#
if {[sqlite3 -tcl-uses-utf]} {
  if {"\u1234"=="u1234"} {
    puts stderr "***** BUILD PROBLEM *****"
................................................................................
  global nTest nErr sqlite_open_file_count
  if {$nErr==0} memleak_check

  catch {db close}
  catch {db2 close}
  catch {db3 close}




  sqlite3 db {}
  # sqlite3_clear_tsd_memdebug
  db close
  set heaplimit [sqlite3_soft_heap_limit]
  if {$heaplimit!=$::soft_limit} {
    puts "soft-heap-limit changed by this script\
          from $::soft_limit to $heaplimit"
................................................................................
    fconfigure $t -translation binary
    puts -nonewline $t [read $f [file size $from]]
    close $t
    close $f
  }
}


























































# If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set
# to non-zero, then set the global variable $AUTOVACUUM to 1.
set AUTOVACUUM $sqlite_options(default_autovacuum)