SQLite

Check-in [a628d84d31]
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
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: a628d84d3185fb7742cc929e758bfd59c811ca0b
User & Date: danielk1977 2006-01-19 07:18:14.000
Context
2006-01-19
08:43
Account for read-uncommitted cursors in sqlite3BtreeClearTable(). (CVS 2977) (check-in: 9507983268 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: a628d84d31 user: danielk1977 tags: trunk)
2006-01-18
18:33
Omit thread2.test if memory-management is enabled at compile time. (CVS 2975) (check-in: df91f685ca user: danielk1977 tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btree.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
** 2004 April 6
**
** 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: 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.











|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
** 2004 April 6
**
** 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: 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.
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
#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 







|

|


<
|

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

|

|
|
<







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
#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 
581
582
583
584
585
586
587

588
589
590
591
592
593
594
595
596
597
598
** 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;







>


<
<







579
580
581
582
583
584
585
586
587
588


589
590
591
592
593
594
595
** 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
**    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>








|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
**    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>

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
  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
**
*/







|

<

>
>


|
|
>
>
|
|









<







>
>
>







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
  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
**
*************************************************************************
** 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>

/*







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
**
*************************************************************************
** 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>

/*
388
389
390
391
392
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
**     * 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;







|





>







388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
**     * 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;
439
440
441
442
443
444
445
446
447

448
449
450
451
452
453
454
#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++;







<
|
>







440
441
442
443
444
445
446

447
448
449
450
451
452
453
454
455
#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++;
481
482
483
484
485
486
487
488
489
490
491
492

493
494
495
496
497
498
499
}

/*
** 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);







<
<
<
<
|
>







482
483
484
485
486
487
488




489
490
491
492
493
494
495
496
497
}

/*
** 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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2005 December 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: 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











|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2005 December 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: 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
36
37
38
39
40
41
42





43
44
45
46
47
48
49
} 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 







>
>
>
>
>







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
} 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 
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219

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







|







|







202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224

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