/ Check-in [154282fc]
Login

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

Overview
Comment:Add some very simple test cases (and resulting bug fixes) for release_memory(). (CVS 2826)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:154282fca54bf03d310d6931660f99805bb5477f
User & Date: danielk1977 2005-12-19 14:18:11
Context
2005-12-19
16:15
Tentative fix for ticket #1567: disable the sqlite3pager_dont_write() optimization when a statement transaction is active. We continue to look for a better fix. (CVS 2827) check-in: e6106cc1 user: drh tags: trunk
14:18
Add some very simple test cases (and resulting bug fixes) for release_memory(). (CVS 2826) check-in: 154282fc user: danielk1977 tags: trunk
2005-12-18
08:51
Add the (untested) sqlite3_release_memory() function. (CVS 2825) check-in: 345addaa user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
....
2389
2390
2391
2392
2393
2394
2395
2396

2397
2398

2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431

2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
....
2452
2453
2454
2455
2456
2457
2458

2459
2460
2461
2462
2463
2464
2465
** 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.225 2005/12/18 08:51:23 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include <assert.h>
#include <string.h>
................................................................................
  pPg = pPager->pFirstSynced;

  /* If we could not find a page that does not require an fsync()
  ** on the journal file then fsync the journal file.  This is a
  ** very slow operation, so we work hard to avoid it.  But sometimes
  ** it can't be helped.
  */
  if( pPg==0 && syncOk ){
    int rc = syncJournal(pPager);
    if( rc!=0 ){
      sqlite3pager_rollback(pPager);
      return SQLITE_IOERR;
    }
    if( pPager->fullSync ){
      /* If in full-sync mode, write a new journal header into the
................................................................................

/*
** This function is called to free superfluous dynamically allocated memory
** held by the pager system. Memory in use by any SQLite pager allocated
** by the current thread may be sqliteFree()ed.
**
** nReq is the number of bytes of memory required. Once this much has
** been released, the function returns. The return value is the total number 

** of bytes of memory released.
*/

int sqlite3pager_release_memory(int nReq){
  SqliteTsd *pTsd = sqlite3Tsd();
  Pager *pPager;
  int nReleased = 0;
  int i;

  /* Outermost loop runs for at most two iterations. First iteration we
  ** try to find memory that can be released without calling fsync(). Second
  ** iteration (which only runs if the first failed to free nReq bytes of
  ** memory) is permitted to call fsync(). This is of course much more 
  ** expensive.
  */
  for(i=0; i==0 || i==1; i++){

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

      /* 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.
        */
        PgHdr *pTmp;

        if( pPg==pPager->pAll ){
           pPager->pAll = pPg->pNextAll;
        }else{
          for( pTmp=pPager->pAll; pTmp->pNextAll!=pPg; pTmp=pTmp->pNextAll );
          pTmp->pNextAll = pPg->pNextAll;
        }
        nReleased += sqliteAllocSize(pPg);
        sqliteFree(pPg);
      }

      if( rc!=SQLITE_OK ){
................................................................................
        assert(0);
      }
    }
  }
  
  return nReleased;
}


/*
** Acquire a page.
**
** A read lock on the disk file is obtained when the first page is acquired. 
** This read lock is dropped when the last page is released.
**







|







 







|







 







|
>


>


|












|







|









>
|
|

|







 







>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
....
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
....
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
** 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.226 2005/12/19 14:18:11 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include <assert.h>
#include <string.h>
................................................................................
  pPg = pPager->pFirstSynced;

  /* If we could not find a page that does not require an fsync()
  ** on the journal file then fsync the journal file.  This is a
  ** very slow operation, so we work hard to avoid it.  But sometimes
  ** it can't be helped.
  */
  if( pPg==0 && pPager->pFirst && syncOk ){
    int rc = syncJournal(pPager);
    if( rc!=0 ){
      sqlite3pager_rollback(pPager);
      return SQLITE_IOERR;
    }
    if( pPager->fullSync ){
      /* If in full-sync mode, write a new journal header into the
................................................................................

/*
** This function is called to free superfluous dynamically allocated memory
** held by the pager system. Memory in use by any SQLite pager allocated
** by the current thread may be sqliteFree()ed.
**
** nReq is the number of bytes of memory required. Once this much has
** been released, the function returns. A negative value for nReq means
** free as much memory as possible. The return value is the total number 
** of bytes of memory released.
*/
#ifndef SQLITE_OMIT_MEMORY_MANAGEMENT
int sqlite3pager_release_memory(int nReq){
  SqliteTsd *pTsd = sqlite3Tsd();
  Pager *p;
  int nReleased = 0;
  int i;

  /* Outermost loop runs for at most two iterations. First iteration we
  ** try to find memory that can be released without calling fsync(). Second
  ** iteration (which only runs if the first failed to free nReq bytes of
  ** memory) is permitted to call fsync(). This is of course much more 
  ** expensive.
  */
  for(i=0; i==0 || i==1; i++){

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

      /* 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(p, 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.
        */
        PgHdr *pTmp;
        assert( pPg );
        if( pPg==p->pAll ){
           p->pAll = pPg->pNextAll;
        }else{
          for( pTmp=p->pAll; pTmp->pNextAll!=pPg; pTmp=pTmp->pNextAll );
          pTmp->pNextAll = pPg->pNextAll;
        }
        nReleased += sqliteAllocSize(pPg);
        sqliteFree(pPg);
      }

      if( rc!=SQLITE_OK ){
................................................................................
        assert(0);
      }
    }
  }
  
  return nReleased;
}
#endif

/*
** Acquire a page.
**
** A read lock on the disk file is obtained when the first page is acquired. 
** This read lock is dropped when the last page is released.
**

Changes to src/pager.h.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
104
105
106
107
108
109
110

111
112
113
114
115
116
117
118
119
120
121
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite page cache
** subsystem.  The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
** @(#) $Id: pager.h,v 1.47 2005/12/18 08:51:24 danielk1977 Exp $
*/

#ifndef _PAGER_H_
#define _PAGER_H_

/*
** The default size of a database page.
................................................................................
const char *sqlite3pager_dirname(Pager*);
const char *sqlite3pager_journalname(Pager*);
int sqlite3pager_nosync(Pager*);
int sqlite3pager_rename(Pager*, const char *zNewName);
void sqlite3pager_set_codec(Pager*,void(*)(void*,void*,Pgno,int),void*);
int sqlite3pager_movepage(Pager*,void*,Pgno);
int sqlite3pager_reset(Pager*);


#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
int sqlite3pager_lockstate(Pager*);
#endif

#ifdef SQLITE_TEST
void sqlite3pager_refdump(Pager*);
int pager3_refinfo_enable;
#endif

#endif /* _PAGER_H_ */







|







 







>











9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite page cache
** subsystem.  The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
** @(#) $Id: pager.h,v 1.48 2005/12/19 14:18:11 danielk1977 Exp $
*/

#ifndef _PAGER_H_
#define _PAGER_H_

/*
** The default size of a database page.
................................................................................
const char *sqlite3pager_dirname(Pager*);
const char *sqlite3pager_journalname(Pager*);
int sqlite3pager_nosync(Pager*);
int sqlite3pager_rename(Pager*, const char *zNewName);
void sqlite3pager_set_codec(Pager*,void(*)(void*,void*,Pgno,int),void*);
int sqlite3pager_movepage(Pager*,void*,Pgno);
int sqlite3pager_reset(Pager*);
int sqlite3pager_release_memory(int);

#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
int sqlite3pager_lockstate(Pager*);
#endif

#ifdef SQLITE_TEST
void sqlite3pager_refdump(Pager*);
int pager3_refinfo_enable;
#endif

#endif /* _PAGER_H_ */

Changes to src/sqlite.h.in.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1331
1332
1333
1334
1335
1336
1337


1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.149 2005/12/18 08:51:24 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
** For the purposes of this API, a transaction is said to have been 
** rolled back if an explicit "ROLLBACK" statement is executed, or
** an error or constraint causes an implicit rollback to occur. The 
** callback is not invoked if a transaction is automatically rolled
** back because the database connection is closed.
*/
void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);



/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE_OMIT_FLOATING_POINT
# undef double
#endif

#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
#endif







|







 







>
>













8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
....
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.150 2005/12/19 14:18:11 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
** For the purposes of this API, a transaction is said to have been 
** rolled back if an explicit "ROLLBACK" statement is executed, or
** an error or constraint causes an implicit rollback to occur. The 
** callback is not invoked if a transaction is automatically rolled
** back because the database connection is closed.
*/
void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);

int sqlite3_release_memory(int);

/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE_OMIT_FLOATING_POINT
# undef double
#endif

#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
#endif

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
**    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.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.440 2005/12/18 08:51:24 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Many people are failing to set -DNDEBUG=1 when compiling SQLite.
** Setting NDEBUG makes the code smaller and run faster.  So the following
................................................................................
/*
** An instance of this structure is allocated for each thread that uses SQLite.
*/
typedef struct SqliteTsd SqliteTsd;
struct SqliteTsd {
  int isInit;                     /* True if structure has been initialised */
  int mallocFailed;               /* True after a malloc() has failed */

#ifndef SQLITE_OMIT_MEMORY_MANAGEMENT
  unsigned int nSoftHeapLimit;    /* (uint)-1 for unlimited */
  unsigned int nAlloc;            /* Number of bytes currently allocated */
  Pager *pPager;                  /* Linked list of all pagers in this thread */
#endif

#ifndef NDEBUG







|







 







<







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
268
269
270
271
272
273
274

275
276
277
278
279
280
281
**    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.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.441 2005/12/19 14:18:11 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Many people are failing to set -DNDEBUG=1 when compiling SQLite.
** Setting NDEBUG makes the code smaller and run faster.  So the following
................................................................................
/*
** An instance of this structure is allocated for each thread that uses SQLite.
*/
typedef struct SqliteTsd SqliteTsd;
struct SqliteTsd {
  int isInit;                     /* True if structure has been initialised */
  int mallocFailed;               /* True after a malloc() has failed */

#ifndef SQLITE_OMIT_MEMORY_MANAGEMENT
  unsigned int nSoftHeapLimit;    /* (uint)-1 for unlimited */
  unsigned int nAlloc;            /* Number of bytes currently allocated */
  Pager *pPager;                  /* Linked list of all pagers in this thread */
#endif

#ifndef NDEBUG

Changes to src/tclsqlite.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
....
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759

1760
1761
1762
1763
1764
1765
1766
1767
1768
1769




























1770
1771
1772
1773
1774
1775
1776
**    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.
**
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.140 2005/12/16 06:54:03 danielk1977 Exp $
*/
#ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */

#include "sqliteInt.h"
#include "hash.h"
#include "tcl.h"
#include <stdlib.h>
................................................................................
    "authorizer",         "busy",              "cache",
    "changes",            "close",             "collate",
    "collation_needed",   "commit_hook",       "complete",
    "copy",               "errorcode",         "eval",
    "exists",             "function",          "last_insert_rowid",
    "nullvalue",          "onecolumn",         "profile",
    "progress",           "rekey",             "rollback_hook",
    "soft_heap_limit",    "timeout",           "total_changes",
    "trace",              "transaction",       "update_hook",       
    "version",            0                    
  };
  enum DB_enum {
    DB_AUTHORIZER,        DB_BUSY,             DB_CACHE,
    DB_CHANGES,           DB_CLOSE,            DB_COLLATE,
    DB_COLLATION_NEEDED,  DB_COMMIT_HOOK,      DB_COMPLETE,
    DB_COPY,              DB_ERRORCODE,        DB_EVAL,
    DB_EXISTS,            DB_FUNCTION,         DB_LAST_INSERT_ROWID,
    DB_NULLVALUE,         DB_ONECOLUMN,        DB_PROFILE,
    DB_PROGRESS,          DB_REKEY,            DB_ROLLBACK_HOOK,
    DB_SOFT_HEAP_LIMIT,   DB_TIMEOUT,          DB_TOTAL_CHANGES,    
    DB_TRACE,             DB_TRANSACTION,      DB_UPDATE_HOOK,      
    DB_VERSION
  };
  /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */

  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
    return TCL_ERROR;
  }
................................................................................
    break;
  }

  /*
  **     $db soft_heap_limit N
  **
  ** Set the soft-heap-limit for this thread. Note that the limit is 
  ** per-thread, not per-database.
  */
  case DB_SOFT_HEAP_LIMIT: {

    int n;
    if( objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "BYTES");
      return TCL_ERROR;
    }
    if( Tcl_GetIntFromObj(interp, objv[2], &n) ){
      return TCL_ERROR;
    }
    sqlite3_soft_heap_limit(n);
    Tcl_ResetResult(interp);




























    break;
  }
  
  /*
  **     $db total_changes
  **
  ** Return the number of rows that were modified, inserted, or deleted 







|







 







|
|
|









|
|
|







 







|


>










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







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
....
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
**    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.
**
*************************************************************************
** A TCL Interface to SQLite
**
** $Id: tclsqlite.c,v 1.141 2005/12/19 14:18:11 danielk1977 Exp $
*/
#ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */

#include "sqliteInt.h"
#include "hash.h"
#include "tcl.h"
#include <stdlib.h>
................................................................................
    "authorizer",         "busy",              "cache",
    "changes",            "close",             "collate",
    "collation_needed",   "commit_hook",       "complete",
    "copy",               "errorcode",         "eval",
    "exists",             "function",          "last_insert_rowid",
    "nullvalue",          "onecolumn",         "profile",
    "progress",           "rekey",             "rollback_hook",
    "release_memory",     "soft_heap_limit",   "timeout",
    "total_changes",      "trace",             "transaction",
    "update_hook",        "version",           0                    
  };
  enum DB_enum {
    DB_AUTHORIZER,        DB_BUSY,             DB_CACHE,
    DB_CHANGES,           DB_CLOSE,            DB_COLLATE,
    DB_COLLATION_NEEDED,  DB_COMMIT_HOOK,      DB_COMPLETE,
    DB_COPY,              DB_ERRORCODE,        DB_EVAL,
    DB_EXISTS,            DB_FUNCTION,         DB_LAST_INSERT_ROWID,
    DB_NULLVALUE,         DB_ONECOLUMN,        DB_PROFILE,
    DB_PROGRESS,          DB_REKEY,            DB_ROLLBACK_HOOK,
    DB_RELEASE_MEMORY,    DB_SOFT_HEAP_LIMIT,  DB_TIMEOUT,
    DB_TOTAL_CHANGES,     DB_TRACE,            DB_TRANSACTION,
    DB_UPDATE_HOOK,       DB_VERSION
  };
  /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */

  if( objc<2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
    return TCL_ERROR;
  }
................................................................................
    break;
  }

  /*
  **     $db soft_heap_limit N
  **
  ** Set the soft-heap-limit for this thread. Note that the limit is 
  ** per-thread, not per-database. An empty string is returned.
  */
  case DB_SOFT_HEAP_LIMIT: {
#ifndef SQLITE_OMIT_MEMORY_MANAGEMENT
    int n;
    if( objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "BYTES");
      return TCL_ERROR;
    }
    if( Tcl_GetIntFromObj(interp, objv[2], &n) ){
      return TCL_ERROR;
    }
    sqlite3_soft_heap_limit(n);
    Tcl_ResetResult(interp);
#endif
    break;
  }

  /*
  **     $db release_memory ?N?
  **
  ** Try to release memory currently held (but not really required) by 
  ** SQLite database connections opened by the current thread. If an
  ** integer argument is supplied, then SQLite stops trying to free memory
  ** after N bytes have been freed.
  **
  ** The value returned is the number of bytes actually freed.
  **/
  case DB_RELEASE_MEMORY: {
    int nRelease = 0;
    int N = -1;
    if( objc!=2 && objc!=3 ){
      Tcl_WrongNumArgs(interp, 2, objv, "?N?");
      return TCL_ERROR;
    }
#ifndef SQLITE_OMIT_MEMORY_MANAGEMENT
    if( objc==3 && TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &N) ){
      return TCL_ERROR;
    }
    nRelease = sqlite3_release_memory(N);
#endif
    Tcl_SetObjResult(interp, Tcl_NewIntObj(nRelease));
    break;
  }
  
  /*
  **     $db total_changes
  **
  ** Return the number of rows that were modified, inserted, or deleted 

Changes to src/util.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
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
...
152
153
154
155
156
157
158































159
160
161
162
163
164
165
...
448
449
450
451
452
453
454

455
456
457
458
459
460
461
462
....
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
**
*************************************************************************
** Utility functions used throughout sqlite.
**
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
** $Id: util.c,v 1.155 2005/12/18 08:51:24 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <stdarg.h>
#include <ctype.h>

/*
................................................................................
**     sqlite3MallocRaw()
**     sqlite3Realloc()
**     sqlite3ReallocOrFree()
**     sqlite3Free()
**     sqlite3AllocSize()
**
** The function sqlite3FreeX performs the same task as sqlite3Free and is
** guaranteed to be a real function.
**
** The above APIs are implemented in terms of the functions provided at the Os
** level (not in this file). The Os level interface is never accessed directly
** by code outside of this file.
**
**     sqlite3Os.xMalloc()
**     sqlite3Os.xRealloc()
**     sqlite3Os.xFree()
**     sqlite3Os.xAllocationSize()
**
** Functions sqlite3MallocRaw() and sqlite3Realloc() may invoke 
** sqlite3_release_memory() if a call to sqlite3Os.xMalloc() or

** sqlite3Os.xRealloc() fails. Function sqlite3Malloc() usually invokes
** sqlite3MallocRaw().
**
** MALLOC TEST WRAPPER ARCHITECTURE
**
** The test wrapper provides extra test facilities to ensure the library 
** does not leak memory and handles the failure of the underlying (Os level)
** allocation system correctly. It is only present if the library is 
................................................................................
** compiled with the SQLITE_MEMDEBUG macro set.
**
**     * Guardposts to detect overwrites.
**     * Ability to cause a specific Malloc() or Realloc() to fail.
**     * Audit outstanding memory allocations (i.e check for leaks).
*/

/*
** TODO!
*/
#define sqlite3_release_memory(x) 0

#ifdef SQLITE_MEMDEBUG
/*--------------------------------------------------------------------------
** Begin code for memory allocation system test layer.
**
** Memory debugging is turned on by defining the SQLITE_MEMDEBUG macro.
**
** SQLITE_MEMDEBUG==1    -> Fence-posting only (thread safe) 
................................................................................
  sizeof(void *)*2 +                   /* pPrev and pNext pointers */   \
  TESTALLOC_NGUARD*sizeof(u32)*2 +              /* Guard words */       \
  sizeof(u32) + TESTALLOC_FILESIZE +   /* File and line number */       \
  TESTALLOC_USERSIZE +                 /* User string */                \
  TESTALLOC_STACKSIZE                  /* backtrace() stack */          \
)
































/*
** For keeping track of the number of mallocs and frees.   This
** is used to check for memory leaks.  The iMallocFail and iMallocReset
** values are used to simulate malloc() failures during testing in 
** order to verify that the library correctly handles an out-of-memory
** condition.
*/
................................................................................

void OSMALLOC_FAILED(){
  sqlite3Tsd()->isFail = 0;
}

int OSSIZEOF(void *p){
  if( p ){

    return sqlite3Os.xAllocationSize(p) - TESTALLOC_OVERHEAD;
  }
  return 0;
}

#else
/* Define macros to call the sqlite3Os.xXXX interface directly if 
** the SQLITE_MEMDEBUG macro is not defined.
................................................................................
** Clear the "mallocFailed" flag. This should be invoked before exiting any
** entry points that may have called sqliteMalloc().
*/
void sqlite3MallocClearFailed(){
  sqlite3Tsd()->mallocFailed = 0;
}

#ifndef SQLITE_OMIT_MEMORY_MANAGEMENT
/*
** Set the soft heap-size limit for the current thread.
*/
void sqlite3_soft_heap_limit(int n){
  unsigned int N;
  if( n<0 ){
    /* No limit */
    N = 0xFFFFFFFF;
  }else{
    N = n;
  }
  sqlite3Tsd()->nSoftHeapLimit = N;
}
#endif

#ifndef NDEBUG
/*
** This function sets a flag in the thread-specific-data structure that will
** cause an assert to fail if sqliteMalloc() or sqliteRealloc() is called.
*/
void sqlite3MallocDisallow(){







|







 







|












>
|







 







<
<
<
<
<







 







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







 







>
|







 







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







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
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
...
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
183
184
185
186
187
188
189
190
191
192
...
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
....
1288
1289
1290
1291
1292
1293
1294















1295
1296
1297
1298
1299
1300
1301
**
*************************************************************************
** Utility functions used throughout sqlite.
**
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
** $Id: util.c,v 1.156 2005/12/19 14:18:11 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <stdarg.h>
#include <ctype.h>

/*
................................................................................
**     sqlite3MallocRaw()
**     sqlite3Realloc()
**     sqlite3ReallocOrFree()
**     sqlite3Free()
**     sqlite3AllocSize()
**
** The function sqlite3FreeX performs the same task as sqlite3Free and is
** guaranteed to be a real function. The same holds for sqlite3MallocX
**
** The above APIs are implemented in terms of the functions provided at the Os
** level (not in this file). The Os level interface is never accessed directly
** by code outside of this file.
**
**     sqlite3Os.xMalloc()
**     sqlite3Os.xRealloc()
**     sqlite3Os.xFree()
**     sqlite3Os.xAllocationSize()
**
** Functions sqlite3MallocRaw() and sqlite3Realloc() may invoke 
** sqlite3_release_memory() if a call to sqlite3Os.xMalloc() or
** sqlite3Os.xRealloc() fails (or if the soft-heap-limit for the thread is
** exceeded). Function sqlite3Malloc() usually invokes
** sqlite3MallocRaw().
**
** MALLOC TEST WRAPPER ARCHITECTURE
**
** The test wrapper provides extra test facilities to ensure the library 
** does not leak memory and handles the failure of the underlying (Os level)
** allocation system correctly. It is only present if the library is 
................................................................................
** compiled with the SQLITE_MEMDEBUG macro set.
**
**     * Guardposts to detect overwrites.
**     * Ability to cause a specific Malloc() or Realloc() to fail.
**     * Audit outstanding memory allocations (i.e check for leaks).
*/






#ifdef SQLITE_MEMDEBUG
/*--------------------------------------------------------------------------
** Begin code for memory allocation system test layer.
**
** Memory debugging is turned on by defining the SQLITE_MEMDEBUG macro.
**
** SQLITE_MEMDEBUG==1    -> Fence-posting only (thread safe) 
................................................................................
  sizeof(void *)*2 +                   /* pPrev and pNext pointers */   \
  TESTALLOC_NGUARD*sizeof(u32)*2 +              /* Guard words */       \
  sizeof(u32) + TESTALLOC_FILESIZE +   /* File and line number */       \
  TESTALLOC_USERSIZE +                 /* User string */                \
  TESTALLOC_STACKSIZE                  /* backtrace() stack */          \
)


#ifndef SQLITE_OMIT_MEMORY_MANAGEMENT
/*
** Set the soft heap-size limit for the current thread.
*/
void sqlite3_soft_heap_limit(int n){
  unsigned int N;
  if( n<0 ){
    /* No limit */
    N = 0xFFFFFFFF;
  }else{
    N = n;
  }
  sqlite3Tsd()->nSoftHeapLimit = N;
}

/*
** Release memory held by SQLite instances created by the current thread.
*/
int sqlite3_release_memory(int n){
  return sqlite3pager_release_memory(n);
}
#else
/* If SQLITE_OMIT_MEMORY_MANAGEMENT is defined, then define a version
** of sqlite3_release_memory() to be used by other code in this file.
** This is done for no better reason than to reduce the number of 
** pre-processor #ifndef statements.
*/
#define sqlite3_release_memory(x) 0    /* 0 == no memory freed */
#endif

/*
** For keeping track of the number of mallocs and frees.   This
** is used to check for memory leaks.  The iMallocFail and iMallocReset
** values are used to simulate malloc() failures during testing in 
** order to verify that the library correctly handles an out-of-memory
** condition.
*/
................................................................................

void OSMALLOC_FAILED(){
  sqlite3Tsd()->isFail = 0;
}

int OSSIZEOF(void *p){
  if( p ){
    u32 *pOs = (u32 *)getOsPointer(p);
    return sqlite3Os.xAllocationSize(pOs) - TESTALLOC_OVERHEAD;
  }
  return 0;
}

#else
/* Define macros to call the sqlite3Os.xXXX interface directly if 
** the SQLITE_MEMDEBUG macro is not defined.
................................................................................
** Clear the "mallocFailed" flag. This should be invoked before exiting any
** entry points that may have called sqliteMalloc().
*/
void sqlite3MallocClearFailed(){
  sqlite3Tsd()->mallocFailed = 0;
}

















#ifndef NDEBUG
/*
** This function sets a flag in the thread-specific-data structure that will
** cause an assert to fail if sqliteMalloc() or sqliteRealloc() is called.
*/
void sqlite3MallocDisallow(){

Added test/malloc5.test.

































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
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
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
# 2005 November 30
#
# The author disclaims copyright to this source code.  In place of
# 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.
#
#***********************************************************************
#
# $Id: malloc5.test,v 1.1 2005/12/19 14:18:12 danielk1977 Exp $

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

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

do_test malloc5-1.1 {
  # Simplest possible test. Call [db release_memory] when there is exactly
  # one unused page in a single pager cache. This test case set's the 
  # value of the ::pgalloc variable, which is used in subsequent tests.
  #
  # Note: Even though executing this statement on an empty database 
  # modifies 2 pages (the root of sqlite_master and the new root page), 
  # the sqlite_master root (page 1) is never freed because the btree layer
  # retains a reference to it for the entire transaction. 
  execsql {
    BEGIN;
    CREATE TABLE abc(a, b, c);
  }
  set ::pgalloc [db release_memory]
  expr $::pgalloc > 0
} {1}
do_test malloc5-1.2 {
  # Test that the transaction started in the above test is still active.
  # Because the page freed had been written to, freeing it required a
  # journal sync and exclusive lock on the database file. Test the file
  # appears to be locked.
  sqlite3 db2 test.db
  catchsql {
    SELECT * FROM abc;
  } db2
} {1 {database is locked}}
do_test malloc5-1.3 {
  # Again call [db release_memory] when there is exactly one unused page 
  # in the cache. The same amount of memory is required, but no journal-sync
  # or exclusive lock should be established.
  execsql {
    COMMIT;
    BEGIN;
    SELECT * FROM abc;
  }
  db release_memory
} $::pgalloc
do_test malloc5-1.4 {
  # Database should not be locked this time.
  catchsql {
    SELECT * FROM abc;
  } db2
} {0 {}}
do_test malloc5-1.5 {
  # Manipulate the cache so that it contains two unused pages. One requires 
  # a journal-sync to free, the other does not.
  execsql {
    SELECT * FROM abc;
    CREATE TABLE def(d, e, f);
  }
  db release_memory 500
} $::pgalloc
do_test malloc5-1.6 {
  # Database should not be locked this time. The above test case only
  # requested 500 bytes of memory, which can be obtained by freeing the page
  # that does not require an fsync().
  catchsql {
    SELECT * FROM abc;
  } db2
} {0 {}}
do_test malloc5-1.7 {
  # Release another 500 bytes of memory. This time we require a sync(), 
  # so the database file will be locked afterwards.
  db release_memory 500
} $::pgalloc
do_test malloc5-1.8 {
  catchsql {
    SELECT * FROM abc;
  } db2
} {1 {database is locked}}
do_test malloc5-1.9 {
  execsql {
    COMMIT;
  }
} {}


do_test malloc5-2.1 {
  # Put some data in tables abc and def. Both tables are still wholly 
  # contained within their root pages.
  execsql {
    INSERT INTO abc VALUES(1, 2, 3);
    INSERT INTO abc VALUES(4, 5, 6);
    INSERT INTO def VALUES(7, 8, 9);
    INSERT INTO def VALUES(10,11,12);
  }
} {}
do_test malloc5-2.2 {
  # Load the root-page for table def into the cache. Then query table abc. 
  # Halfway through the query call sqlite3_release_memory(). The goal of this
  # test is to make sure we don't free pages that are in use (specifically, 
  # the root of table abc).
  set nRelease 0
  execsql { 
    BEGIN;
    SELECT * FROM def;
  }
  db eval {SELECT * FROM abc} {
    incr nRelease [db release_memory]
    lappend data $a $b $c
  }
  list $nRelease $data
} [list $pgalloc [list 1 2 3 4 5 6]]

finish_test