/ Check-in [a628d84d]
Login

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

Overview
Comment:Fix a bug with internally saving cursors open on index tables. Also increase coverage of util.c and btree.c. (CVS 2976)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:a628d84d3185fb7742cc929e758bfd59c811ca0b
User & Date: danielk1977 2006-01-19 07:18:14
Context
2006-01-19
08:43
Account for read-uncommitted cursors in sqlite3BtreeClearTable(). (CVS 2977) check-in: 95079832 user: danielk1977 tags: trunk
07:18
Fix a bug with internally saving cursors open on index tables. Also increase coverage of util.c and btree.c. (CVS 2976) check-in: a628d84d user: danielk1977 tags: trunk
2006-01-18
18:33
Omit thread2.test if memory-management is enabled at compile time. (CVS 2975) check-in: df91f685 user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
...
581
582
583
584
585
586
587

588
589
590
591
592
593
594
595
596
597
598
** 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: btree.c,v 1.300 2006/01/18 15:25:17 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
#else

/*
** Save the current cursor position in the variables BtCursor.nKey 
** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
*/
static int saveCursorPosition(BtCursor *pCur){
  int rc = SQLITE_OK;

  assert( CURSOR_VALID==pCur->eState|| CURSOR_INVALID==pCur->eState );
  assert( 0==pCur->pKey );

  if( pCur->eState==CURSOR_VALID ){
    rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);

    /* If this is an intKey table, then the above call to BtreeKeySize()
    ** stores the integer key in pCur->nKey. In this case this value is
    ** all that is required. Otherwise, if pCur is not open on an intKey
    ** table, then malloc space for and store the pCur->nKey bytes of key 
    ** data.
    */
    if( rc==SQLITE_OK && 0==pCur->pPage->intKey){
      void *pKey = sqliteMalloc(pCur->nKey);
      if( pKey ){
        rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
        if( rc==SQLITE_OK ){
          pCur->pKey = pKey;
        }else{
          sqliteFree(pKey);
        }
      }else{
        rc = SQLITE_NOMEM;
      }
    }
    assert( !pCur->pPage->intKey || !pCur->pKey );

    /* Todo: Should we drop the reference to pCur->pPage here? */

    if( rc==SQLITE_OK ){
      pCur->eState = CURSOR_REQUIRESEEK;
    }
  }

  return rc;
}

/*
** Save the positions of all cursors except pExcept open on the table 
................................................................................
** returning the cursor to it's saved position, any saved position is deleted
** and the cursor state set to CURSOR_INVALID.
*/
static int restoreOrClearCursorPositionX(BtCursor *pCur, int doSeek){
  int rc = SQLITE_OK;
  assert( sqlite3ThreadDataReadOnly()->useSharedData );
  assert( pCur->eState==CURSOR_REQUIRESEEK );

  if( doSeek ){
    rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip);
  }else{
    pCur->eState = CURSOR_INVALID;
  }
  if( rc==SQLITE_OK ){
    sqliteFree(pCur->pKey);
    pCur->pKey = 0;
    assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState );
  }
  return rc;







|







 







|

|


<
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|

|
|
<







 







>


<
<







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
507
508
509
510
511
512
513
514
515
516
517
518

519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545

546
547
548
549
550
551
552
...
579
580
581
582
583
584
585
586
587
588


589
590
591
592
593
594
595
** 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: btree.c,v 1.301 2006/01/19 07:18:14 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
#else

/*
** Save the current cursor position in the variables BtCursor.nKey 
** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
*/
static int saveCursorPosition(BtCursor *pCur){
  int rc;

  assert( CURSOR_VALID==pCur->eState );
  assert( 0==pCur->pKey );


  rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);

  /* If this is an intKey table, then the above call to BtreeKeySize()
  ** stores the integer key in pCur->nKey. In this case this value is
  ** all that is required. Otherwise, if pCur is not open on an intKey
  ** table, then malloc space for and store the pCur->nKey bytes of key 
  ** data.
  */
  if( rc==SQLITE_OK && 0==pCur->pPage->intKey){
    void *pKey = sqliteMalloc(pCur->nKey);
    if( pKey ){
      rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
      if( rc==SQLITE_OK ){
        pCur->pKey = pKey;
      }else{
        sqliteFree(pKey);
      }
    }else{
      rc = SQLITE_NOMEM;
    }
  }
  assert( !pCur->pPage->intKey || !pCur->pKey );

  /* Todo: Should we drop the reference to pCur->pPage here? */

  if( rc==SQLITE_OK ){
    pCur->eState = CURSOR_REQUIRESEEK;

  }

  return rc;
}

/*
** Save the positions of all cursors except pExcept open on the table 
................................................................................
** returning the cursor to it's saved position, any saved position is deleted
** and the cursor state set to CURSOR_INVALID.
*/
static int restoreOrClearCursorPositionX(BtCursor *pCur, int doSeek){
  int rc = SQLITE_OK;
  assert( sqlite3ThreadDataReadOnly()->useSharedData );
  assert( pCur->eState==CURSOR_REQUIRESEEK );
  pCur->eState = CURSOR_INVALID;
  if( doSeek ){
    rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip);


  }
  if( rc==SQLITE_OK ){
    sqliteFree(pCur->pKey);
    pCur->pKey = 0;
    assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState );
  }
  return rc;

Changes to src/test1.c.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
905
906
907
908
909
910
911
912
913
914
915


916
917
918
919
920
921




922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938



939
940
941
942
943
944
945
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the printf() interface to SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test1.c,v 1.197 2006/01/18 18:22:43 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include "os.h"
#include <stdlib.h>
#include <string.h>

................................................................................
  ClientData clientData,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int objc,              /* Number of arguments */
  Tcl_Obj *CONST objv[]  /* Command arguments */
){
  extern int sqlite3OutstandingMallocs(Tcl_Interp *interp);

#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  if( objc==2 ){
    ThreadData const *pTd = sqlite3ThreadDataReadOnly();
    const char *zArg = Tcl_GetString(objv[1]);


    if( 0==strcmp(zArg, "-bytes") ){
      Tcl_SetObjResult(interp, Tcl_NewIntObj(pTd->nAlloc));
    }else if( 0==strcmp(zArg, "-maxbytes") ){
      Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sqlite3_nMaxAlloc));
    }else if( 0==strcmp(zArg, "-clearmaxbytes") ){
      sqlite3_nMaxAlloc = pTd->nAlloc;




    }else{
      Tcl_AppendResult(interp, "bad option \"", zArg, 
        "\": must be -bytes, -maxbytes or -clearmaxbytes", 0
      );
      return TCL_ERROR;
    }

    return TCL_OK;
  }
#endif

  if( objc!=1 ){
    Tcl_WrongNumArgs(interp, 1, objv, "?-bytes?");
    return TCL_ERROR;
  }

  return sqlite3OutstandingMallocs(interp);



}
#endif

/*
** Usage: sqlite3_enable_shared_cache      BOOLEAN
**
*/







|







 







|

<

>
>


<
<


>
>
>
>









<







>
>
>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
905
906
907
908
909
910
911
912
913

914
915
916
917
918


919
920
921
922
923
924
925
926
927
928
929
930
931
932
933

934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the printf() interface to SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test1.c,v 1.198 2006/01/19 07:18:14 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include "os.h"
#include <stdlib.h>
#include <string.h>

................................................................................
  ClientData clientData,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int objc,              /* Number of arguments */
  Tcl_Obj *CONST objv[]  /* Command arguments */
){
  extern int sqlite3OutstandingMallocs(Tcl_Interp *interp);

#if defined(SQLITE_DEBUG) && defined(SQLITE_MEMDEBUG)
  if( objc==2 ){

    const char *zArg = Tcl_GetString(objv[1]);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
    ThreadData const *pTd = sqlite3ThreadDataReadOnly();
    if( 0==strcmp(zArg, "-bytes") ){
      Tcl_SetObjResult(interp, Tcl_NewIntObj(pTd->nAlloc));


    }else if( 0==strcmp(zArg, "-clearmaxbytes") ){
      sqlite3_nMaxAlloc = pTd->nAlloc;
    }else 
#endif
    if( 0==strcmp(zArg, "-maxbytes") ){
      Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sqlite3_nMaxAlloc));
    }else{
      Tcl_AppendResult(interp, "bad option \"", zArg, 
        "\": must be -bytes, -maxbytes or -clearmaxbytes", 0
      );
      return TCL_ERROR;
    }

    return TCL_OK;
  }


  if( objc!=1 ){
    Tcl_WrongNumArgs(interp, 1, objv, "?-bytes?");
    return TCL_ERROR;
  }

  return sqlite3OutstandingMallocs(interp);
#else
  return TCL_OK;
#endif
}
#endif

/*
** Usage: sqlite3_enable_shared_cache      BOOLEAN
**
*/

Changes to src/util.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
388
389
390
391
392
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
...
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
...
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
**
*************************************************************************
** Utility functions used throughout sqlite.
**
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
** $Id: util.c,v 1.178 2006/01/18 18:22:43 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <stdarg.h>
#include <ctype.h>

/*
................................................................................
**     * The __LINE__ macro ...
**     * The value of the sqlite3_malloc_id variable ...
**     * The output of backtrace() (if available) ...
**
** Todo: We could have a version of this function that outputs to stdout, 
** to debug memory leaks when Tcl is not available.
*/
#ifdef TCLSH
#include <tcl.h>
int sqlite3OutstandingMallocs(Tcl_Interp *interp){
  void *p;
  Tcl_Obj *pRes = Tcl_NewObj();
  Tcl_IncrRefCount(pRes);


  for(p=sqlite3_pFirst; p; p=((void **)p)[1]){
    Tcl_Obj *pEntry = Tcl_NewObj();
    Tcl_Obj *pStack = Tcl_NewObj();
    char *z;
    u32 iLine;
    int nBytes = sqlite3OsAllocationSize(p) - TESTALLOC_OVERHEAD;
................................................................................
#endif

/*
** This is the test layer's wrapper around sqlite3OsMalloc().
*/
static void * OSMALLOC(int n){
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  ThreadData const *pTsd = sqlite3ThreadDataReadOnly();
  sqlite3_nMaxAlloc = MAX(sqlite3_nMaxAlloc, pTsd->nAlloc);
#endif
  assert( !sqlite3_mallocDisallowed );
  if( !sqlite3TestMallocFail() ){
    u32 *p;
    p = (u32 *)sqlite3OsMalloc(n + TESTALLOC_OVERHEAD);
    assert(p);
    sqlite3_nMalloc++;
................................................................................
}

/*
** This is the test layer's wrapper around sqlite3OsRealloc().
*/
static void * OSREALLOC(void *pRealloc, int n){
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  ThreadData *pTsd = sqlite3ThreadData();
  if( !pTsd ){
    return 0;
  }
  sqlite3_nMaxAlloc = MAX(sqlite3_nMaxAlloc, pTsd->nAlloc);
#endif
  assert( !sqlite3_mallocDisallowed );
  if( !sqlite3TestMallocFail() ){
    u32 *p = (u32 *)getOsPointer(pRealloc);
    checkGuards(p);
    p = sqlite3OsRealloc(p, n + TESTALLOC_OVERHEAD);
    applyGuards(p);







|







 







|





>







 







|
|







 







|
<
<
<
|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
...
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
...
482
483
484
485
486
487
488
489



490
491
492
493
494
495
496
497
**
*************************************************************************
** Utility functions used throughout sqlite.
**
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
** $Id: util.c,v 1.179 2006/01/19 07:18:14 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <stdarg.h>
#include <ctype.h>

/*
................................................................................
**     * The __LINE__ macro ...
**     * The value of the sqlite3_malloc_id variable ...
**     * The output of backtrace() (if available) ...
**
** Todo: We could have a version of this function that outputs to stdout, 
** to debug memory leaks when Tcl is not available.
*/
#if defined(TCLSH) && defined(SQLITE_DEBUG) && SQLITE_MEMDEBUG>1
#include <tcl.h>
int sqlite3OutstandingMallocs(Tcl_Interp *interp){
  void *p;
  Tcl_Obj *pRes = Tcl_NewObj();
  Tcl_IncrRefCount(pRes);


  for(p=sqlite3_pFirst; p; p=((void **)p)[1]){
    Tcl_Obj *pEntry = Tcl_NewObj();
    Tcl_Obj *pStack = Tcl_NewObj();
    char *z;
    u32 iLine;
    int nBytes = sqlite3OsAllocationSize(p) - TESTALLOC_OVERHEAD;
................................................................................
#endif

/*
** This is the test layer's wrapper around sqlite3OsMalloc().
*/
static void * OSMALLOC(int n){
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  sqlite3_nMaxAlloc = 
      MAX(sqlite3_nMaxAlloc, sqlite3ThreadDataReadOnly()->nAlloc);
#endif
  assert( !sqlite3_mallocDisallowed );
  if( !sqlite3TestMallocFail() ){
    u32 *p;
    p = (u32 *)sqlite3OsMalloc(n + TESTALLOC_OVERHEAD);
    assert(p);
    sqlite3_nMalloc++;
................................................................................
}

/*
** This is the test layer's wrapper around sqlite3OsRealloc().
*/
static void * OSREALLOC(void *pRealloc, int n){
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  sqlite3_nMaxAlloc = 



      MAX(sqlite3_nMaxAlloc, sqlite3ThreadDataReadOnly()->nAlloc);
#endif
  assert( !sqlite3_mallocDisallowed );
  if( !sqlite3TestMallocFail() ){
    u32 *p = (u32 *)getOsPointer(pRealloc);
    checkGuards(p);
    p = sqlite3OsRealloc(p, n + TESTALLOC_OVERHEAD);
    applyGuards(p);

Changes to test/shared.test.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
36
37
38
39
40
41
42





43
44
45
46
47
48
49
...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#
#    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: shared.test,v 1.17 2006/01/17 09:35:02 danielk1977 Exp $

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

ifcapable !shared_cache {
  finish_test
................................................................................
} else {
  if {$av} {
    db close
    break
  }
}






incr av

# Test organization:
#
# shared-1.*: Simple test to verify basic sanity of table level locking when
#             two connections share a pager cache.
# shared-2.*: Test that a read transaction can co-exist with a 
................................................................................

do_test shared-$av.3.1.1 {
  # This test case starts a linear scan of table 'seq' using a 
  # read-uncommitted connection. In the middle of the scan, rows are added
  # to the end of the seq table (ahead of the current cursor position).
  # The uncommitted rows should be included in the results of the scan.
  execsql "
    CREATE TABLE seq(i, x);
    INSERT INTO seq VALUES(1, '[string repeat X 500]');
    INSERT INTO seq VALUES(2, '[string repeat X 500]');
  "
  execsql {SELECT * FROM sqlite_master} db2
  execsql {PRAGMA read_uncommitted = 1} db2

  set ret [list]
  db2 eval {SELECT i FROM seq} {
    if {$i < 4} {
      set max [execsql {SELECT max(i) FROM seq}]
      db eval {
        INSERT INTO seq SELECT i + :max, x FROM seq;
      }
    }
    lappend ret $i







|







 







>
>
>
>
>







 







|







|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
...
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 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: shared.test,v 1.18 2006/01/19 07:18:15 danielk1977 Exp $

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

ifcapable !shared_cache {
  finish_test
................................................................................
} else {
  if {$av} {
    db close
    break
  }
}

# $av is currently 0 if this loop iteration is to test with auto-vacuum turned
# off, and 1 if it is turned on. Increment it so that (1 -> no auto-vacuum) 
# and (2 -> auto-vacuum). The sole reason for this is so that it looks nicer
# when we use this variable as part of test-case names.
#
incr av

# Test organization:
#
# shared-1.*: Simple test to verify basic sanity of table level locking when
#             two connections share a pager cache.
# shared-2.*: Test that a read transaction can co-exist with a 
................................................................................

do_test shared-$av.3.1.1 {
  # This test case starts a linear scan of table 'seq' using a 
  # read-uncommitted connection. In the middle of the scan, rows are added
  # to the end of the seq table (ahead of the current cursor position).
  # The uncommitted rows should be included in the results of the scan.
  execsql "
    CREATE TABLE seq(i PRIMARY KEY, x);
    INSERT INTO seq VALUES(1, '[string repeat X 500]');
    INSERT INTO seq VALUES(2, '[string repeat X 500]');
  "
  execsql {SELECT * FROM sqlite_master} db2
  execsql {PRAGMA read_uncommitted = 1} db2

  set ret [list]
  db2 eval {SELECT i FROM seq ORDER BY i} {
    if {$i < 4} {
      set max [execsql {SELECT max(i) FROM seq}]
      db eval {
        INSERT INTO seq SELECT i + :max, x FROM seq;
      }
    }
    lappend ret $i