/ Check-in [e44995de]
Login

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

Overview
Comment:Detect database file changes using a 128-bit segment of the file header that includes the change counter. Ticket #2303. (CVS 3844)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:e44995debf2456e55b502783849e93a045a527c8
User & Date: drh 2007-04-16 15:02:19
Context
2007-04-16
15:06
Ensure sqlite3_finalize() can be called from within the xDisconnect() method of virtual tables. (CVS 3845) check-in: 8d6c3bfc user: danielk1977 tags: trunk
15:02
Detect database file changes using a 128-bit segment of the file header that includes the change counter. Ticket #2303. (CVS 3844) check-in: e44995de user: drh tags: trunk
2007-04-14
12:04
Update the whentouse.html document to mention that less bitmap memory is used for larger page sizes. (CVS 3843) check-in: 2c8e2a5b user: drh 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
...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
....
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136

1137
1138

1139



1140
1141
1142
1143
1144
1145
1146
....
2437
2438
2439
2440
2441
2442
2443



2444
2445
2446
2447
2448
2449
2450
....
2676
2677
2678
2679
2680
2681
2682




2683
2684
2685
2686
2687
2688
2689
....
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789






2790
2791
2792

2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809

2810
2811
2812

2813
2814
2815
2816
2817
2818
2819
2820
2821
....
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
....
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
** 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.328 2007/04/13 04:01:59 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include <assert.h>
#include <string.h>
................................................................................
#endif
  int nHash;                  /* Size of the pager hash table */
  PgHdr **aHash;              /* Hash table to map page number to PgHdr */
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  Pager *pNext;               /* Linked list of pagers in this thread */
#endif
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  u32 iChangeCount;           /* Db change-counter for which cache is valid */
};

/*
** The following global variables hold counters used for
** testing purposes only.  These variables do not exist in
** a non-testing build.  These variables are not thread-safe.
*/
................................................................................
    memcpy(pData, aData, pPager->pageSize);
    if( pPager->xReiniter ){
      pPager->xReiniter(pPg, pPager->pageSize);
    }
#ifdef SQLITE_CHECK_PAGES
    pPg->pageHash = pager_pagehash(pPg);
#endif
    CODEC1(pPager, pData, pPg->pgno, 3);

    /* If this was page 1, then restore the value of Pager.iChangeCount */

    if( pgno==1 ){
      pPager->iChangeCount = retrieve32bits(pPg, 24);

    }



  }
  return rc;
}

/*
** Parameter zMaster is the name of a master journal file. A single journal
** file that referred to the master journal file has just been rolled back.
................................................................................
    if( pList->pgno<=pPager->dbSize ){
      char *pData = CODEC2(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
      PAGERTRACE3("STORE %d page %d\n", PAGERID(pPager), pList->pgno);
      IOTRACE(("PGOUT %p %d\n", pPager, pList->pgno));
      rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize);
      PAGER_INCR(sqlite3_pager_writedb_count);
      PAGER_INCR(pPager->nWrite);



    }
#ifndef NDEBUG
    else{
      PAGERTRACE3("NOSTORE %d page %d\n", PAGERID(pPager), pList->pgno);
    }
#endif
    if( rc ) return rc;
................................................................................
    rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg),
                          pPager->pageSize);
  }
  PAGER_INCR(sqlite3_pager_readdb_count);
  PAGER_INCR(pPager->nRead);
  IOTRACE(("PGIN %p %d\n", pPager, pgno));
  PAGERTRACE3("FETCH %d page %d\n", PAGERID(pPager), pPg->pgno);




  CODEC1(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3);
  return rc;
}


/*
** This function is called to obtain the shared lock required before
................................................................................
            (pPager->exclusiveMode && pPager->state>PAGER_SHARED)
        );
      }

      if( pPager->pAll ){
        /* The shared-lock has just been acquired on the database file
        ** and there are already pages in the cache (from a previous
        ** read or write transaction). If the value of the change-counter
        ** stored in Pager.iChangeCount matches that found on page 1 of
        ** the database file, then no database changes have occured since
        ** the cache was last valid and it is safe to retain the cached
        ** pages. Otherwise, if Pager.iChangeCount does not match the
        ** change-counter on page 1 of the file, the current cache contents
        ** must be discarded.






        */
        u8 zC[4];
        u32 iChangeCounter = 0;

        sqlite3PagerPagecount(pPager);

        if( pPager->errCode ){
          return pPager->errCode;
        }

        if( pPager->dbSize>0 ){
          /* Read the 4-byte change counter directly from the file. */
          rc = sqlite3OsSeek(pPager->fd, 24);
          if( rc!=SQLITE_OK ){
            return rc;
          }
          rc = sqlite3OsRead(pPager->fd, zC, 4);
          if( rc!=SQLITE_OK ){
            return rc;
          }
          iChangeCounter = (zC[0]<<24) + (zC[1]<<16) + (zC[2]<<8) + zC[3];

        }

        if( iChangeCounter!=pPager->iChangeCount ){

          pager_reset(pPager);
          pPager->iChangeCount = iChangeCounter;
        }
      }
    }
    assert( pPager->exclusiveMode || pPager->state<=PAGER_SHARED );
    if( pPager->state==PAGER_UNLOCK ){
      pPager->state = PAGER_SHARED;
    }
................................................................................
      rc = readDbPage(pPager, pPg, pgno);
      if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){
        pPg->pgno = 0;
        sqlite3PagerUnref(pPg);
        return rc;
      }
    }
    /* If this was page 1, then restore the value of Pager.iChangeCount */
    if( pgno==1 ){
      pPager->iChangeCount = retrieve32bits(pPg, 24);
    }

    /* Link the page into the page hash table */
    h = pgno & (pPager->nHash-1);
    assert( pgno!=0 );
    pPg->pNextHash = pPager->aHash[h];
    pPager->aHash[h] = pPg;
    if( pPg->pNextHash ){
................................................................................
  
    /* Read the current value at byte 24. */
    change_counter = retrieve32bits(pPgHdr, 24);
  
    /* Increment the value just read and write it back to byte 24. */
    change_counter++;
    put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter);
    pPager->iChangeCount = change_counter;
  
    /* Release the page reference. */
    sqlite3PagerUnref(pPgHdr);
    pPager->changeCountDone = 1;
  }
  return SQLITE_OK;
}







|







 







|







 







<
<
|
>

<
>

>
>
>







 







>
>
>







 







>
>
>
>







 







|
|
|
|
|
|
|
>
>
>
>
>
>

<
<
>







<




|



|
>


<
>

<







 







<
<
<
<







 







<







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
....
1127
1128
1129
1130
1131
1132
1133


1134
1135
1136

1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
....
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
....
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
....
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805


2806
2807
2808
2809
2810
2811
2812
2813

2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825

2826
2827

2828
2829
2830
2831
2832
2833
2834
....
3038
3039
3040
3041
3042
3043
3044




3045
3046
3047
3048
3049
3050
3051
....
3727
3728
3729
3730
3731
3732
3733

3734
3735
3736
3737
3738
3739
3740
** 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.329 2007/04/16 15:02:19 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include <assert.h>
#include <string.h>
................................................................................
#endif
  int nHash;                  /* Size of the pager hash table */
  PgHdr **aHash;              /* Hash table to map page number to PgHdr */
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  Pager *pNext;               /* Linked list of pagers in this thread */
#endif
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  char dbFileVers[16];        /* Changes whenever database file changes */
};

/*
** The following global variables hold counters used for
** testing purposes only.  These variables do not exist in
** a non-testing build.  These variables are not thread-safe.
*/
................................................................................
    memcpy(pData, aData, pPager->pageSize);
    if( pPager->xReiniter ){
      pPager->xReiniter(pPg, pPager->pageSize);
    }
#ifdef SQLITE_CHECK_PAGES
    pPg->pageHash = pager_pagehash(pPg);
#endif


    /* If this was page 1, then restore the value of Pager.dbFileVers.
    ** Do this before any decoding. */
    if( pgno==1 ){

      memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers));
    }

    /* Decode the page just read from disk */
    CODEC1(pPager, pData, pPg->pgno, 3);
  }
  return rc;
}

/*
** Parameter zMaster is the name of a master journal file. A single journal
** file that referred to the master journal file has just been rolled back.
................................................................................
    if( pList->pgno<=pPager->dbSize ){
      char *pData = CODEC2(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
      PAGERTRACE3("STORE %d page %d\n", PAGERID(pPager), pList->pgno);
      IOTRACE(("PGOUT %p %d\n", pPager, pList->pgno));
      rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize);
      PAGER_INCR(sqlite3_pager_writedb_count);
      PAGER_INCR(pPager->nWrite);
      if( pList->pgno==1 ){
        memcpy(&pPager->dbFileVers, &pData[24], sizeof(pPager->dbFileVers));
      }
    }
#ifndef NDEBUG
    else{
      PAGERTRACE3("NOSTORE %d page %d\n", PAGERID(pPager), pList->pgno);
    }
#endif
    if( rc ) return rc;
................................................................................
    rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg),
                          pPager->pageSize);
  }
  PAGER_INCR(sqlite3_pager_readdb_count);
  PAGER_INCR(pPager->nRead);
  IOTRACE(("PGIN %p %d\n", pPager, pgno));
  PAGERTRACE3("FETCH %d page %d\n", PAGERID(pPager), pPg->pgno);
  if( pgno==1 ){
    memcpy(&pPager->dbFileVers, &((u8*)PGHDR_TO_DATA(pPg))[24],
                                              sizeof(pPager->dbFileVers));
  }
  CODEC1(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3);
  return rc;
}


/*
** This function is called to obtain the shared lock required before
................................................................................
            (pPager->exclusiveMode && pPager->state>PAGER_SHARED)
        );
      }

      if( pPager->pAll ){
        /* The shared-lock has just been acquired on the database file
        ** and there are already pages in the cache (from a previous
        ** read or write transaction).  Check to see if the database
        ** has been modified.  If the database has changed, flush the
        ** cache.
        **
        ** Database changes is detected by looking at 15 bytes beginning
        ** at offset 24 into the file.  The first 4 of these 16 bytes are
        ** a 32-bit counter that is incremented with each change.  The
        ** other bytes change randomly with each file change when
        ** a codec is in use.
        ** 
        ** There is a vanishingly small chance that a change will not be 
        ** deteched.  The chance of an undetected change is so small that
        ** it can be neglected.
        */


        char dbFileVers[sizeof(pPager->dbFileVers)];
        sqlite3PagerPagecount(pPager);

        if( pPager->errCode ){
          return pPager->errCode;
        }

        if( pPager->dbSize>0 ){

          rc = sqlite3OsSeek(pPager->fd, 24);
          if( rc!=SQLITE_OK ){
            return rc;
          }
          rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers));
          if( rc!=SQLITE_OK ){
            return rc;
          }
        }else{
          memset(dbFileVers, 0, sizeof(dbFileVers));
        }


        if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
          pager_reset(pPager);

        }
      }
    }
    assert( pPager->exclusiveMode || pPager->state<=PAGER_SHARED );
    if( pPager->state==PAGER_UNLOCK ){
      pPager->state = PAGER_SHARED;
    }
................................................................................
      rc = readDbPage(pPager, pPg, pgno);
      if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){
        pPg->pgno = 0;
        sqlite3PagerUnref(pPg);
        return rc;
      }
    }





    /* Link the page into the page hash table */
    h = pgno & (pPager->nHash-1);
    assert( pgno!=0 );
    pPg->pNextHash = pPager->aHash[h];
    pPager->aHash[h] = pPg;
    if( pPg->pNextHash ){
................................................................................
  
    /* Read the current value at byte 24. */
    change_counter = retrieve32bits(pPgHdr, 24);
  
    /* Increment the value just read and write it back to byte 24. */
    change_counter++;
    put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter);

  
    /* Release the page reference. */
    sqlite3PagerUnref(pPgHdr);
    pPager->changeCountDone = 1;
  }
  return SQLITE_OK;
}

Changes to test/exclusive2.test.

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
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
..
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
131
132
133
134
135
136
137

138
139
140
141
142
143
144
145
146
147
148
149
150
151
#    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 implements regression tests for SQLite library.
#
# $Id: exclusive2.test,v 1.3 2007/04/08 16:52:22 drh Exp $

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

ifcapable {!pager_pragmas} {
  finish_test
  return
................................................................................
  pagerChangeCounter test.db
} {0}

#-----------------------------------------------------------------------
# The following tests - exclusive2-1.X - check that:
#
# 1-3:   Build a database with connection 1, calculate a signature.
# 4-9:   Modify the database using a second connection, then reset

#        the pager change-counter to the value it had before the modifications.
# 8:     Check that using the first connection, the database signature
#        is still the same. This is because it uses the in-memory cache.
#        It can't tell the db has changed because we reset the change-counter.
# 9:     Increment the change-counter.
# 10:    Ensure that the first connection now sees the updated database. It
#        sees the change-counter has been incremented and discards the 
#        invalid in-memory cache.
#
do_test exclusive2-1.1 {
  execsql {
    BEGIN;
    CREATE TABLE t1(a UNIQUE);
    INSERT INTO t1 VALUES(randstr(10, 400));
    INSERT INTO t1 VALUES(randstr(10, 400));
    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
    COMMIT;
    SELECT count(*) FROM t1;
  }
} {64}
do_test exclusive2-1.2 {
  set ::sig [t1sig]
  pagerChangeCounter test.db
................................................................................
} $::sig
do_test exclusive2-1.4 {
  sqlite3 db2 test.db
  t1sig db2
} $::sig
do_test exclusive2-1.5 {
  execsql {
    DELETE FROM t1;
  } db2
  expr {[t1sig db2] eq $::sig}
} 0
do_test exclusive2-1.6 {
  pagerChangeCounter test.db
} {2}
do_test exclusive2-1.7 {
................................................................................
#        SQLite detects the modified change-counter and discards the
#        in-memory cache. Then it finds the corruption caused in step 4....
#
do_test exclusive2-2.1 {
  execsql {PRAGMA locking_mode = exclusive;}
  execsql {
    BEGIN;

    INSERT INTO t1 VALUES(randstr(10, 400));
    INSERT INTO t1 VALUES(randstr(10, 400));
    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1 SELECT randstr(10, 400) FROM t1;
    COMMIT;
    SELECT count(*) FROM t1;
  }
} {64}
do_test exclusive2-2.2 {
  set ::sig [t1sig]
  pagerChangeCounter test.db







|







 







|
>
|











|
|
|
|
|
|
|
|







 







|







 







>
|
|
|
|
|
|
|







6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
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
..
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
...
132
133
134
135
136
137
138
139
140
141
142
143
144
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 implements regression tests for SQLite library.
#
# $Id: exclusive2.test,v 1.4 2007/04/16 15:02:20 drh Exp $

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

ifcapable {!pager_pragmas} {
  finish_test
  return
................................................................................
  pagerChangeCounter test.db
} {0}

#-----------------------------------------------------------------------
# The following tests - exclusive2-1.X - check that:
#
# 1-3:   Build a database with connection 1, calculate a signature.
# 4-9:   Modify the database using a second connection in a way that
#        does not modify the freelist, then reset the pager change-counter
#        to the value it had before the modifications.
# 8:     Check that using the first connection, the database signature
#        is still the same. This is because it uses the in-memory cache.
#        It can't tell the db has changed because we reset the change-counter.
# 9:     Increment the change-counter.
# 10:    Ensure that the first connection now sees the updated database. It
#        sees the change-counter has been incremented and discards the 
#        invalid in-memory cache.
#
do_test exclusive2-1.1 {
  execsql {
    BEGIN;
    CREATE TABLE t1(a, b);
    INSERT INTO t1(a) VALUES(randstr(10, 400));
    INSERT INTO t1(a) VALUES(randstr(10, 400));
    INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    COMMIT;
    SELECT count(*) FROM t1;
  }
} {64}
do_test exclusive2-1.2 {
  set ::sig [t1sig]
  pagerChangeCounter test.db
................................................................................
} $::sig
do_test exclusive2-1.4 {
  sqlite3 db2 test.db
  t1sig db2
} $::sig
do_test exclusive2-1.5 {
  execsql {
    UPDATE t1 SET b=a, a=NULL;
  } db2
  expr {[t1sig db2] eq $::sig}
} 0
do_test exclusive2-1.6 {
  pagerChangeCounter test.db
} {2}
do_test exclusive2-1.7 {
................................................................................
#        SQLite detects the modified change-counter and discards the
#        in-memory cache. Then it finds the corruption caused in step 4....
#
do_test exclusive2-2.1 {
  execsql {PRAGMA locking_mode = exclusive;}
  execsql {
    BEGIN;
    DELETE FROM t1;
    INSERT INTO t1(a) VALUES(randstr(10, 400));
    INSERT INTO t1(a) VALUES(randstr(10, 400));
    INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
    COMMIT;
    SELECT count(*) FROM t1;
  }
} {64}
do_test exclusive2-2.2 {
  set ::sig [t1sig]
  pagerChangeCounter test.db

Changes to test/speed2.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#*************************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is measuring executing speed.
#
# $Id: speed2.test,v 1.6 2007/03/31 22:34:16 drh Exp $
#

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

# Set a uniform random seed
................................................................................

# Create a database schema.
#
do_test speed2-1.0 {
  execsql {
    PRAGMA page_size=1024;
    PRAGMA cache_size=8192;
--    PRAGMA locking_mode=EXCLUSIVE;
    CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
    CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT);
    CREATE INDEX i2a ON t2(a);
    CREATE INDEX i2b ON t2(b);
  }
  execsql {
    SELECT name FROM sqlite_master ORDER BY 1;







|







 







|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#*************************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is measuring executing speed.
#
# $Id: speed2.test,v 1.7 2007/04/16 15:02:20 drh Exp $
#

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

# Set a uniform random seed
................................................................................

# Create a database schema.
#
do_test speed2-1.0 {
  execsql {
    PRAGMA page_size=1024;
    PRAGMA cache_size=8192;
    PRAGMA locking_mode=EXCLUSIVE;
    CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
    CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT);
    CREATE INDEX i2a ON t2(a);
    CREATE INDEX i2b ON t2(b);
  }
  execsql {
    SELECT name FROM sqlite_master ORDER BY 1;