/ Check-in [34b56600]
Login

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

Overview
Comment:Add the savepoint feature. This feature is largely untested at this point. (CVS 6036)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 34b56600ec0c5cd7b5faab265750252bc9850e3e
User & Date: danielk1977 2008-12-17 17:30:26
Context
2008-12-17
19:22
Update the WHERE clause processing infrastructure in preparation for adding multi-index OR evaluation. (CVS 6037) check-in: 78401b33 user: drh tags: trunk
17:30
Add the savepoint feature. This feature is largely untested at this point. (CVS 6036) check-in: 34b56600 user: danielk1977 tags: trunk
15:49
Fix some strict-aliasing problems in fts3_expr.c. (CVS 6035) check-in: 20a4ca5d 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
....
2055
2056
2057
2058
2059
2060
2061



2062
2063
2064
2065
2066
2067
2068
....
2725
2726
2727
2728
2729
2730
2731
2732










2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750

2751
2752
2753
2754
2755
2756
2757
....
2766
2767
2768
2769
2770
2771
2772
2773




2774
2775
2776
2777
2778





















2779
2780
2781
2782
2783
2784
2785
** 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.548 2008/12/16 13:46:30 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

................................................................................
      pBt->pExclusive = p;
    }
#endif
  }


trans_begun:



  btreeIntegrity(p);
  sqlite3BtreeLeave(p);
  return rc;
}

#ifndef SQLITE_OMIT_AUTOVACUUM

................................................................................
  BtShared *pBt = p->pBt;
  sqlite3BtreeEnter(p);
  pBt->db = p->db;
  if( (p->inTrans!=TRANS_WRITE) || pBt->inStmt ){
    rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  }else{
    assert( pBt->inTransaction==TRANS_WRITE );
    rc = pBt->readOnly ? SQLITE_OK : sqlite3PagerStmtBegin(pBt->pPager);










    pBt->inStmt = 1;
  }
  sqlite3BtreeLeave(p);
  return rc;
}


/*
** Commit the statment subtransaction currently in progress.  If no
** subtransaction is active, this is a no-op.
*/
int sqlite3BtreeCommitStmt(Btree *p){
  int rc;
  BtShared *pBt = p->pBt;
  sqlite3BtreeEnter(p);
  pBt->db = p->db;
  if( pBt->inStmt && !pBt->readOnly ){
    rc = sqlite3PagerStmtCommit(pBt->pPager);

  }else{
    rc = SQLITE_OK;
  }
  pBt->inStmt = 0;
  sqlite3BtreeLeave(p);
  return rc;
}
................................................................................
*/
int sqlite3BtreeRollbackStmt(Btree *p){
  int rc = SQLITE_OK;
  BtShared *pBt = p->pBt;
  sqlite3BtreeEnter(p);
  pBt->db = p->db;
  if( pBt->inStmt && !pBt->readOnly ){
    rc = sqlite3PagerStmtRollback(pBt->pPager);




    pBt->inStmt = 0;
  }
  sqlite3BtreeLeave(p);
  return rc;
}






















/*
** Create a new cursor for the BTree whose root is on the page
** iTable.  The act of acquiring a cursor gets a read lock on 
** the database file.
**
** If wrFlag==0, then the cursor can only be used for reading.







|







 







>
>
>







 







|
>
>
>
>
>
>
>
>
>
>





<











|
>







 







|
>
>
>
>





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







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
....
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750

2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
....
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
2822
2823
** 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.549 2008/12/17 17:30:26 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

................................................................................
      pBt->pExclusive = p;
    }
#endif
  }


trans_begun:
  if( rc==SQLITE_OK && wrflag ){
    rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint);
  }
  btreeIntegrity(p);
  sqlite3BtreeLeave(p);
  return rc;
}

#ifndef SQLITE_OMIT_AUTOVACUUM

................................................................................
  BtShared *pBt = p->pBt;
  sqlite3BtreeEnter(p);
  pBt->db = p->db;
  if( (p->inTrans!=TRANS_WRITE) || pBt->inStmt ){
    rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  }else{
    assert( pBt->inTransaction==TRANS_WRITE );
    if( pBt->readOnly ){
      rc = SQLITE_OK;
    }else{
      /* At the pager level, a statement transaction is a savepoint with
      ** an index greater than all savepoints created explicitly using
      ** SQL statements. It is illegal to open, release or rollback any
      ** such savepoints while the statement transaction savepoint is active.
      */
      int iStmtpoint = p->db->nSavepoint + 1;
      rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStmtpoint);
    }
    pBt->inStmt = 1;
  }
  sqlite3BtreeLeave(p);
  return rc;
}


/*
** Commit the statment subtransaction currently in progress.  If no
** subtransaction is active, this is a no-op.
*/
int sqlite3BtreeCommitStmt(Btree *p){
  int rc;
  BtShared *pBt = p->pBt;
  sqlite3BtreeEnter(p);
  pBt->db = p->db;
  if( pBt->inStmt && !pBt->readOnly ){
    int iStmtpoint = p->db->nSavepoint;
    rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_RELEASE, iStmtpoint);
  }else{
    rc = SQLITE_OK;
  }
  pBt->inStmt = 0;
  sqlite3BtreeLeave(p);
  return rc;
}
................................................................................
*/
int sqlite3BtreeRollbackStmt(Btree *p){
  int rc = SQLITE_OK;
  BtShared *pBt = p->pBt;
  sqlite3BtreeEnter(p);
  pBt->db = p->db;
  if( pBt->inStmt && !pBt->readOnly ){
    int iStmtpoint = p->db->nSavepoint;
    rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_ROLLBACK, iStmtpoint);
    if( rc==SQLITE_OK ){
      rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_RELEASE, iStmtpoint);
    }
    pBt->inStmt = 0;
  }
  sqlite3BtreeLeave(p);
  return rc;
}

/*
** The second argument to this function, op, is always SAVEPOINT_ROLLBACK
** or SAVEPOINT_RELEASE. This function either releases or rolls back the
** savepoint identified by parameter iSavepoint, depending on the value of
** op.
*/
int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
  int rc = SQLITE_OK;
  if( p && p->inTrans==TRANS_WRITE ){
    BtShared *pBt = p->pBt;
    assert( pBt->inStmt==0 );
    assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
    assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) );
    sqlite3BtreeEnter(p);
    pBt->db = p->db;
    rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint);
    sqlite3BtreeLeave(p);
  }
  return rc;
}

/*
** Create a new cursor for the BTree whose root is on the page
** iTable.  The act of acquiring a cursor gets a read lock on 
** the database file.
**
** If wrFlag==0, then the cursor can only be used for reading.

Changes to src/btree.h.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
97
98
99
100
101
102
103

104
105
106
107
108
109
110
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite B-Tree file
** subsystem.  See comments in the source code for a detailed description
** of what each interface routine does.
**
** @(#) $Id: btree.h,v 1.105 2008/10/27 13:59:34 danielk1977 Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_

/* TODO: This definition is just included so other modules compile. It
** needs to be revisited.
*/
................................................................................
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
int sqlite3BtreeIsInTrans(Btree*);
int sqlite3BtreeIsInStmt(Btree*);
int sqlite3BtreeIsInReadTrans(Btree*);
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
int sqlite3BtreeSchemaLocked(Btree *);
int sqlite3BtreeLockTable(Btree *, int, u8);


const char *sqlite3BtreeGetFilename(Btree *);
const char *sqlite3BtreeGetDirname(Btree *);
const char *sqlite3BtreeGetJournalname(Btree *);
int sqlite3BtreeCopyFile(Btree *, Btree *);

int sqlite3BtreeIncrVacuum(Btree *);







|







 







>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite B-Tree file
** subsystem.  See comments in the source code for a detailed description
** of what each interface routine does.
**
** @(#) $Id: btree.h,v 1.106 2008/12/17 17:30:26 danielk1977 Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_

/* TODO: This definition is just included so other modules compile. It
** needs to be revisited.
*/
................................................................................
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
int sqlite3BtreeIsInTrans(Btree*);
int sqlite3BtreeIsInStmt(Btree*);
int sqlite3BtreeIsInReadTrans(Btree*);
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
int sqlite3BtreeSchemaLocked(Btree *);
int sqlite3BtreeLockTable(Btree *, int, u8);
int sqlite3BtreeSavepoint(Btree *, int, int);

const char *sqlite3BtreeGetFilename(Btree *);
const char *sqlite3BtreeGetDirname(Btree *);
const char *sqlite3BtreeGetJournalname(Btree *);
int sqlite3BtreeCopyFile(Btree *, Btree *);

int sqlite3BtreeIncrVacuum(Btree *);

Changes to src/build.c.

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
....
3306
3307
3308
3309
3310
3311
3312

















3313
3314
3315
3316
3317
3318
3319
**     CREATE INDEX
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**
** $Id: build.c,v 1.508 2008/12/10 22:30:25 shane Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Initialize the pParse structure as needed.
................................................................................
  if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ) return;

  v = sqlite3GetVdbe(pParse);
  if( v ){
    sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, 1);
  }
}


















/*
** Make sure the TEMP database is open and available for use.  Return
** the number of errors.  Leave any error messages in the pParse structure.
*/
int sqlite3OpenTempDatabase(Parse *pParse){
  sqlite3 *db = pParse->db;







|







 







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







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
....
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
**     CREATE INDEX
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**
** $Id: build.c,v 1.509 2008/12/17 17:30:26 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Initialize the pParse structure as needed.
................................................................................
  if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ) return;

  v = sqlite3GetVdbe(pParse);
  if( v ){
    sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, 1);
  }
}

/*
** This function is called by the parser when it parses a command to create,
** release or rollback an SQL savepoint. 
*/
void sqlite3Savepoint(Parse *pParse, int op, Token *pName){
  Vdbe *v;
  if( pParse->nErr || pParse->db->mallocFailed ) return;

  /* TODO: Invoke the authorization callback */

  v = sqlite3GetVdbe(pParse);
  if( v ){
    const char *zName = (const char *)pName->z;
    sqlite3VdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, pName->n);
  }
}

/*
** Make sure the TEMP database is open and available for use.  Return
** the number of errors.  Leave any error messages in the pParse structure.
*/
int sqlite3OpenTempDatabase(Parse *pParse){
  sqlite3 *db = pParse->db;

Changes to src/main.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
540
541
542
543
544
545
546















547
548
549
550
551
552
553
...
582
583
584
585
586
587
588



589
590
591
592
593
594
595
**
*************************************************************************
** Main file for the SQLite library.  The routines in this file
** implement the programmer interface to the library.  Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.519 2008/12/10 23:04:13 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

#ifdef SQLITE_ENABLE_FTS3
# include "fts3.h"
#endif
................................................................................

/*
** Return the number of changes since the database handle was opened.
*/
int sqlite3_total_changes(sqlite3 *db){
  return db->nTotalChange;
}
















/*
** Close an existing SQLite database
*/
int sqlite3_close(sqlite3 *db){
  HashElem *i;
  int j;
................................................................................
  if( db->pVdbe ){
    sqlite3Error(db, SQLITE_BUSY, 
        "Unable to close due to unfinalised statements");
    sqlite3_mutex_leave(db->mutex);
    return SQLITE_BUSY;
  }
  assert( sqlite3SafetyCheckSickOrOk(db) );




  for(j=0; j<db->nDb; j++){
    struct Db *pDb = &db->aDb[j];
    if( pDb->pBt ){
      sqlite3BtreeClose(pDb->pBt);
      pDb->pBt = 0;
      if( j!=1 ){







|







 







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







 







>
>
>







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
...
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
**
*************************************************************************
** Main file for the SQLite library.  The routines in this file
** implement the programmer interface to the library.  Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.520 2008/12/17 17:30:26 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

#ifdef SQLITE_ENABLE_FTS3
# include "fts3.h"
#endif
................................................................................

/*
** Return the number of changes since the database handle was opened.
*/
int sqlite3_total_changes(sqlite3 *db){
  return db->nTotalChange;
}

/*
** Close all open savepoints. This function only manipulates fields of the
** database handle object, it does not close any savepoints that may be open
** at the b-tree/pager level.
*/
void sqlite3CloseSavepoints(sqlite3 *db){
  while( db->pSavepoint ){
    Savepoint *pTmp = db->pSavepoint;
    db->pSavepoint = pTmp->pNext;
    sqlite3DbFree(db, pTmp);
  }
  db->nSavepoint = 0;
  db->isTransactionSavepoint = 0;
}

/*
** Close an existing SQLite database
*/
int sqlite3_close(sqlite3 *db){
  HashElem *i;
  int j;
................................................................................
  if( db->pVdbe ){
    sqlite3Error(db, SQLITE_BUSY, 
        "Unable to close due to unfinalised statements");
    sqlite3_mutex_leave(db->mutex);
    return SQLITE_BUSY;
  }
  assert( sqlite3SafetyCheckSickOrOk(db) );

  /* Free any outstanding Savepoint structures. */
  sqlite3CloseSavepoints(db);

  for(j=0; j<db->nDb; j++){
    struct Db *pDb = &db->aDb[j];
    if( pDb->pBt ){
      sqlite3BtreeClose(pDb->pBt);
      pDb->pBt = 0;
      if( j!=1 ){

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
136
137
138
139
140
141
142






















143
144
145
146
147
148
149
...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
...
215
216
217
218
219
220
221



222
223
224
225
226
227
228
...
301
302
303
304
305
306
307
308
309
310








311
312
313



314


315
316
317
318
319
320
321
...
627
628
629
630
631
632
633

634
635
636
637
638
639





640

641
642
643
644
645
646
647
...
880
881
882
883
884
885
886

























887
888
889
890
891
892
893
...
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
...
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
....
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091

1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
....
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122



1123
1124
1125
1126
1127
1128
1129
....
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
....
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
....
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
....
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
....
2944
2945
2946
2947
2948
2949
2950












2951
2952
2953
2954
2955
2956
2957
....
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
....
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
....
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
....
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
....
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
....
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
....
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878




3879

3880
3881
3882
3883
3884
3885
3886
3887





3888
3889
3890
3891
3892
3893
3894
3895



3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906









3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917


3918
3919
3920
3921
3922
3923
3924
3925
3926
3927








3928

3929
3930
3931
3932







3933
3934
3935
3936
3937
3938
3939

3940
3941
3942




3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955


3956
3957

3958
3959
3960
3961
3962
3963
3964
** 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.514 2008/12/10 22:15:00 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"

/*
** Macros for troubleshooting.  Normally turned off
*/
................................................................................
# define CODEC1(P,D,N,X) if( P->xCodec!=0 ){ P->xCodec(P->pCodecArg,D,N,X); }
# define CODEC2(P,D,N,X) ((char*)(P->xCodec!=0?P->xCodec(P->pCodecArg,D,N,X):D))
#else
# define CODEC1(P,D,N,X) /* NO-OP */
# define CODEC2(P,D,N,X) ((char*)D)
#endif























/*
** A open page cache is an instance of the following structure.
**
** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or
** or SQLITE_FULL. Once one of the first three errors occurs, it persists
** and is returned as the result of every major pager API call.  The
** SQLITE_FULL return code is slightly different. It persists only until the
................................................................................
*/
struct Pager {
  sqlite3_vfs *pVfs;          /* OS functions to use for IO */
  u8 journalOpen;             /* True if journal file descriptors is valid */
  u8 journalStarted;          /* True if header of journal is synced */
  u8 useJournal;              /* Use a rollback journal on this file */
  u8 noReadlock;              /* Do not bother to obtain readlocks */
  u8 stmtOpen;                /* True if the statement subjournal is open */
  u8 stmtInUse;               /* True we are in a statement subtransaction */
  u8 stmtAutoopen;            /* Open stmt journal when main journal is opened*/
  u8 noSync;                  /* Do not sync the journal if true */
  u8 fullSync;                /* Do extra syncs of the journal for robustness */
  u8 sync_flags;              /* One of SYNC_NORMAL or SYNC_FULL */
  u8 state;                   /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
  u8 tempFile;                /* zFilename is a temporary file */
  u8 readOnly;                /* True for a read-only database */
  u8 needSync;                /* True if an fsync() is needed on the journal */
................................................................................
  u8 dbModified;              /* True if there are any changes to the Db */
  u8 changeCountDone;         /* Set after incrementing the change-counter */
  u8 dbSizeValid;             /* Set when dbSize is correct */
  u32 vfsFlags;               /* Flags for sqlite3_vfs.xOpen() */
  int errCode;                /* One of several kinds of errors */
  Pgno dbSize;                /* Number of pages in the file */
  Pgno origDbSize;            /* dbSize before the current change */
  Pgno stmtSize;              /* Size of database (in pages) at stmt_begin() */
  int nRec;                   /* Number of pages written to the journal */
  u32 cksumInit;              /* Quasi-random value added to every checksum */
  int stmtNRec;               /* Number of records in stmt subjournal */
  int nExtra;                 /* Add this many bytes to each in-memory page */
  int pageSize;               /* Number of bytes in a page */
  int nPage;                  /* Total number of in-memory pages */
  int mxPage;                 /* Maximum number of pages to hold in cache */
  Pgno mxPgno;                /* Maximum allowed size of the database */
  Bitvec *pInJournal;         /* One bit for each page in the database file */
  Bitvec *pInStmt;            /* One bit for each page in the database */
  Bitvec *pAlwaysRollback;    /* One bit for each page marked always-rollback */
  char *zFilename;            /* Name of the database file */
  char *zJournal;             /* Name of the journal file */
  char *zDirectory;           /* Directory hold database and journal files */
  sqlite3_file *fd, *jfd;     /* File descriptors for database and journal */
  sqlite3_file *stfd;         /* File descriptor for the statement subjournal*/
  int (*xBusyHandler)(void*); /* Function to call when busy */
  void *pBusyHandlerArg;      /* Context argument for xBusyHandler */
  i64 journalOff;             /* Current byte offset in the journal file */
  i64 journalHdr;             /* Byte offset to previous journal header */
  i64 stmtHdrOff;             /* First journal header written this statement */
  i64 stmtCksum;              /* cksumInit when statement was started */
  i64 stmtJSize;              /* Size of journal at stmt_begin() */
  u32 sectorSize;             /* Assumed sector size during rollback */
#ifdef SQLITE_TEST
  int nHit, nMiss;            /* Cache hits and missing */
  int nRead, nWrite;          /* Database pages read/written */
#endif
  void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
#ifdef SQLITE_HAS_CODEC
................................................................................
  void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */
  void *pCodecArg;            /* First argument to xCodec() */
#endif
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  char dbFileVers[16];        /* Changes whenever database file changes */
  i64 journalSizeLimit;       /* Size limit for persistent journal files */
  PCache *pPCache;            /* Pointer to page cache object */



};

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

/*
** The maximum legal page number is (2^31 - 1).
*/
#define PAGER_MAX_PGNO 2147483647

/*
** Return true if page *pPg has already been written to the statement
** journal (or statement snapshot has been created, if *pPg is part
** of an in-memory database).








*/
static int pageInStatement(PgHdr *pPg){
  Pager *pPager = pPg->pPager;



  return sqlite3BitvecTest(pPager->pInStmt, pPg->pgno);


}

static int pageInJournal(PgHdr *pPg){
  return sqlite3BitvecTest(pPg->pPager->pInJournal, pPg->pgno);
}

/*
................................................................................
** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
*/
static int writeJournalHdr(Pager *pPager){
  int rc = SQLITE_OK;
  char *zHeader = pPager->pTmpSpace;
  u32 nHeader = pPager->pageSize;
  u32 nWrite;


  if( nHeader>JOURNAL_HDR_SZ(pPager) ){
    nHeader = JOURNAL_HDR_SZ(pPager);
  }

  if( pPager->stmtHdrOff==0 ){





    pPager->stmtHdrOff = pPager->journalOff;

  }

  seekJournalHdr(pPager);
  pPager->journalHdr = pPager->journalOff;

  memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));

................................................................................
** opened.  Any outstanding pages are invalidated and subsequent attempts
** to access those pages will likely result in a coredump.
*/
static void pager_reset(Pager *pPager){
  if( pPager->errCode ) return;
  sqlite3PcacheClear(pPager->pPCache);
}


























/*
** Unlock the database file. 
**
** If the pager is currently in error state, discard the contents of 
** the cache and reset the Pager structure internal state. If there is
** an open journal-file, then the next time a shared-lock is obtained
................................................................................
    /* If Pager.errCode is set, the contents of the pager cache cannot be
    ** trusted. Now that the pager file is unlocked, the contents of the
    ** cache can be discarded and the error code safely cleared.
    */
    if( pPager->errCode ){
      if( rc==SQLITE_OK ) pPager->errCode = SQLITE_OK;
      pager_reset(pPager);
      if( pPager->stmtOpen ){
        sqlite3OsClose(pPager->stfd);
        sqlite3BitvecDestroy(pPager->pInStmt);
        pPager->pInStmt = 0;
      }
      pPager->stmtOpen = 0;
      pPager->stmtInUse = 0;
      pPager->journalOff = 0;
      pPager->journalStarted = 0;
      pPager->stmtAutoopen = 0;
      pPager->origDbSize = 0;
    }

    pPager->state = PAGER_UNLOCK;
    pPager->changeCountDone = 0;
  }
}
................................................................................
*/
static int pager_end_transaction(Pager *pPager, int hasMaster){
  int rc = SQLITE_OK;
  int rc2 = SQLITE_OK;
  if( pPager->state<PAGER_RESERVED ){
    return SQLITE_OK;
  }
  sqlite3PagerStmtCommit(pPager);
  if( pPager->stmtOpen && !pPager->exclusiveMode ){
    sqlite3OsClose(pPager->stfd);
    pPager->stmtOpen = 0;
  }
  if( pPager->journalOpen ){
    if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
      int isMemoryJournal = sqlite3IsMemJournal(pPager->jfd);
      sqlite3OsClose(pPager->jfd);
      pPager->journalOpen = 0;
      if( !isMemoryJournal ){
        rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
................................................................................
** jfd.  Playback this one page.
**
** The isMainJrnl flag is true if this is the main rollback journal and
** false for the statement journal.  The main rollback journal uses
** checksums - the statement journal does not.
*/
static int pager_playback_one_page(
  Pager *pPager,       /* The pager being played back */
  sqlite3_file *jfd,   /* The file that is the journal being rolled back */
  i64 offset,          /* Offset of the page within the journal */
  int isMainJrnl       /* True for main rollback journal. False for Stmt jrnl */
){
  int rc;
  PgHdr *pPg;                   /* An existing page in the cache */
  Pgno pgno;                    /* The page number of a page in journal */
  u32 cksum;                    /* Checksum used for sanity checking */
  u8 *aData = (u8 *)pPager->pTmpSpace;   /* Temp storage for a page */


  /* isMainJrnl should be true for the main journal and false for
  ** statement journals.  Verify that this is always the case
  */
  assert( jfd == (isMainJrnl ? pPager->jfd : pPager->stfd) );
  assert( aData );

  rc = read32bits(jfd, offset, &pgno);
  if( rc!=SQLITE_OK ) return rc;
  rc = sqlite3OsRead(jfd, aData, pPager->pageSize, offset+4);
  if( rc!=SQLITE_OK ) return rc;
  pPager->journalOff += pPager->pageSize + 4;
................................................................................
  ** thought.  If a power failure occurs while the journal is being written,
  ** it could cause invalid data to be written into the journal.  We need to
  ** detect this invalid data (with high probability) and ignore it.
  */
  if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){
    return SQLITE_DONE;
  }
  if( pgno>(unsigned)pPager->dbSize ){
    return SQLITE_OK;
  }
  if( isMainJrnl ){
    rc = read32bits(jfd, offset+pPager->pageSize+4, &cksum);
    if( rc ) return rc;
    pPager->journalOff += 4;
    if( pager_cksum(pPager, aData)!=cksum ){
      return SQLITE_DONE;
    }



  }

  assert( pPager->state==PAGER_RESERVED || pPager->state>=PAGER_EXCLUSIVE );

  /* If the pager is in RESERVED state, then there must be a copy of this
  ** page in the pager cache. In this case just update the pager cache,
  ** not the database file. The page is left marked dirty in this case.
................................................................................
        goto end_playback;
      }
    }

    /* Copy original pages out of the journal and back into the database file.
    */
    for(u=0; u<nRec; u++){
      rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1);
      if( rc!=SQLITE_OK ){
        if( rc==SQLITE_DONE ){
          rc = SQLITE_OK;
          pPager->journalOff = szJ;
          break;
        }else{
          /* If we are unable to rollback, then the database is probably
................................................................................
  ** value. Reset it to the correct value for this process.
  */
  setSectorSize(pPager);
  return rc;
}

/*
** Playback the statement journal.
**
** This is similar to playing back the transaction journal but with
** a few extra twists.
**
**    (1)  The number of pages in the database file at the start of
**         the statement is stored in pPager->stmtSize, not in the
**         journal file itself.
**
**    (2)  In addition to playing back the statement journal, also
**         playback all pages of the transaction journal beginning
**         at offset pPager->stmtJSize.
*/
static int pager_stmt_playback(Pager *pPager){
  i64 szJ;                 /* Size of the full journal */
  i64 hdrOff;
  int nRec;                /* Number of Records */
  int i;                   /* Loop counter */
  int rc;

  szJ = pPager->journalOff;

  /* Set hdrOff to be the offset just after the end of the last journal
  ** page written before the first journal-header for this statement
  ** transaction was written, or the end of the file if no journal
  ** header was written.
  */
  hdrOff = pPager->stmtHdrOff;
  assert( pPager->fullSync || !hdrOff );
  if( !hdrOff ){
    hdrOff = szJ;
  }
  
  /* Truncate the database back to its original size.
  */
  rc = pager_truncate(pPager, pPager->stmtSize);
  assert( pPager->state>=PAGER_SHARED );

  /* Figure out how many records are in the statement journal.
  */
  assert( pPager->stmtInUse && pPager->journalOpen );
  nRec = pPager->stmtNRec;
  
  /* Copy original pages out of the statement journal and back into the
  ** database file.  Note that the statement journal omits checksums from
  ** each record since power-failure recovery is not important to statement
  ** journals.
  */
  for(i=0; i<nRec; i++){
    i64 offset = i*(4+pPager->pageSize);
    rc = pager_playback_one_page(pPager, pPager->stfd, offset, 0);
    assert( rc!=SQLITE_DONE );
    if( rc!=SQLITE_OK ) goto end_stmt_playback;
  }

  /* Now roll some pages back from the transaction journal. Pager.stmtJSize
  ** was the size of the journal file when this statement was started, so
  ** everything after that needs to be rolled back, either into the
  ** database, the memory cache, or both.
  **
  ** If it is not zero, then Pager.stmtHdrOff is the offset to the start
  ** of the first journal header written during this statement transaction.
  */
  pPager->journalOff = pPager->stmtJSize;
  pPager->cksumInit = (int)(pPager->stmtCksum & 0xffffffff);
  while( pPager->journalOff < hdrOff ){
    rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1);
    assert( rc!=SQLITE_DONE );
    if( rc!=SQLITE_OK ) goto end_stmt_playback;
  }

  while( pPager->journalOff < szJ ){
    u32 nJRec;         /* Number of Journal Records */
    u32 dummy;
    rc = readJournalHdr(pPager, szJ, &nJRec, &dummy);
    if( rc!=SQLITE_OK ){
      assert( rc!=SQLITE_DONE );
      goto end_stmt_playback;
    }
    if( nJRec==0 ){
      nJRec = (int)((szJ - pPager->journalOff) / (pPager->pageSize+8));
    }
    for(i=nJRec-1; i>=0 && pPager->journalOff < szJ; i--){
      rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1);
      assert( rc!=SQLITE_DONE );
      if( rc!=SQLITE_OK ) goto end_stmt_playback;
    }
  }

  pPager->journalOff = szJ;
  
end_stmt_playback:
  if( rc==SQLITE_OK) {
    pPager->journalOff = szJ;
    /* pager_reload_cache(pPager); */
  }
  return rc;
}

/*
** Change the maximum number of in-memory pages that are allowed.
*/
................................................................................
    sqlite3_free(zPathname);
    return SQLITE_NOMEM;
  }
  pPager->pPCache = (PCache *)&pPager[1];
  pPtr = ((u8 *)&pPager[1]) + pcacheSize;
  pPager->vfsFlags = vfsFlags;
  pPager->fd = (sqlite3_file*)&pPtr[pVfs->szOsFile*0];
  pPager->stfd = (sqlite3_file*)&pPtr[pVfs->szOsFile];
  pPager->jfd = (sqlite3_file*)&pPtr[pVfs->szOsFile+journalFileSize];
  pPager->zFilename = (char*)&pPtr[pVfs->szOsFile+2*journalFileSize];
  pPager->zDirectory = &pPager->zFilename[nPathname+1];
  pPager->zJournal = &pPager->zDirectory[nPathname+1];
  pPager->pVfs = pVfs;
  if( zPathname ){
    memcpy(pPager->zFilename, zPathname, nPathname+1);
................................................................................
  PAGERTRACE2("CLOSE %d\n", PAGERID(pPager));
  IOTRACE(("CLOSE %p\n", pPager))
  if( pPager->journalOpen ){
    sqlite3OsClose(pPager->jfd);
  }
  sqlite3BitvecDestroy(pPager->pInJournal);
  sqlite3BitvecDestroy(pPager->pAlwaysRollback);
  if( pPager->stmtOpen ){
    sqlite3OsClose(pPager->stfd);
  }
  sqlite3OsClose(pPager->fd);
  /* Temp files are automatically deleted by the OS
  ** if( pPager->tempFile ){
  **   sqlite3OsDelete(pPager->zFilename);
  ** }
  */

................................................................................
  if( pPg ){
    Pager *pPager = pPg->pPager;
    sqlite3PcacheRelease(pPg);
    pagerUnlockIfUnused(pPager);
  }
  return SQLITE_OK;
}













/*
** Create a journal file for pPager.  There should already be a RESERVED
** or EXCLUSIVE lock on the database file when this routine is called.
**
** Return SQLITE_OK if everything.  Return an error code and release the
** write lock if anything goes wrong.
................................................................................
    rc = pPager->errCode;
    goto failed_to_open_journal;
  }
  pPager->origDbSize = pPager->dbSize;

  rc = writeJournalHdr(pPager);

  if( pPager->stmtAutoopen && rc==SQLITE_OK ){
    rc = sqlite3PagerStmtBegin(pPager);
  }
  if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && rc!=SQLITE_IOERR_NOMEM ){
    rc = pager_end_transaction(pPager, 0);
    if( rc==SQLITE_OK ){
      rc = SQLITE_FULL;
    }
  }
................................................................................
    return rc;
  }

  /* Mark the page as dirty.  If the page has already been written
  ** to the journal then we can return right away.
  */
  sqlite3PcacheMakeDirty(pPg);
  if( pageInJournal(pPg) && (pageInStatement(pPg) || pPager->stmtInUse==0) ){
    pPager->dirtyCache = 1;
    pPager->dbModified = 1;
  }else{

    /* If we get this far, it means that the page needs to be
    ** written to the transaction journal or the ckeckpoint journal
    ** or both.
................................................................................

        pPager->nRec++;
        assert( pPager->pInJournal!=0 );
        sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
        if( !pPager->noSync ){
          pPg->flags |= PGHDR_NEED_SYNC;
        }
        if( pPager->stmtInUse ){
          sqlite3BitvecSet(pPager->pInStmt, pPg->pgno);
        }
      }else{
        if( !pPager->journalStarted && !pPager->noSync ){
          pPg->flags |= PGHDR_NEED_SYNC;
        }
        PAGERTRACE4("APPEND %d page %d needSync=%d\n",
                PAGERID(pPager), pPg->pgno,
               ((pPg->flags&PGHDR_NEED_SYNC)?1:0));
................................................................................
    }
  
    /* If the statement journal is open and the page is not in it,
    ** then write the current page to the statement journal.  Note that
    ** the statement journal format differs from the standard journal format
    ** in that it omits the checksums and the header.
    */
    if( pPager->stmtInUse 
     && !pageInStatement(pPg) 
     && pPg->pgno<=pPager->stmtSize 
    ){
      i64 offset = pPager->stmtNRec*(4+pPager->pageSize);
      char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7);
      assert( pageInJournal(pPg) || pPg->pgno>pPager->origDbSize );
      rc = write32bits(pPager->stfd, offset, pPg->pgno);
      if( rc==SQLITE_OK ){
        rc = sqlite3OsWrite(pPager->stfd, pData2, pPager->pageSize, offset+4);
      }
      PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
      if( rc!=SQLITE_OK ){
        return rc;
      }
      pPager->stmtNRec++;
      assert( pPager->pInStmt!=0 );
      sqlite3BitvecSet(pPager->pInStmt, pPg->pgno);
    }
  }

  /* Update the database size and return.
  */
  assert( pPager->state>=PAGER_SHARED );
  if( pPager->dbSize<pPg->pgno ){
................................................................................
    pPager->pAlwaysRollback = sqlite3BitvecCreate(pPager->origDbSize);
    if( !pPager->pAlwaysRollback ){
      return SQLITE_NOMEM;
    }
  }
  rc = sqlite3BitvecSet(pPager->pAlwaysRollback, pPg->pgno);

  if( rc==SQLITE_OK && (pPg->flags&PGHDR_DIRTY) && !pPager->stmtInUse ){
    assert( pPager->state>=PAGER_SHARED );
    if( pPager->dbSize==pPg->pgno && pPager->origDbSize<pPager->dbSize ){
      /* If this pages is the last page in the file and the file has grown
      ** during the current transaction, then do NOT mark the page as clean.
      ** When the database file grows, we must make sure that the last page
      ** gets written at least once so that the disk file will be the correct
      ** size. If you do not write this page and the size of the file
................................................................................
  ** necessarily true:
  */
  /* assert( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ); */

  assert( pPager->pInJournal!=0 );
  sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
  pPg->flags &= ~PGHDR_NEED_READ;
  if( pPager->stmtInUse ){
    assert( pPager->stmtSize >= pPager->origDbSize );
    sqlite3BitvecSet(pPager->pInStmt, pPg->pgno);
  }
  PAGERTRACE3("DONT_ROLLBACK page %d of %d\n", pPg->pgno, PAGERID(pPager));
  IOTRACE(("GARBAGE %p %d\n", pPager, pPg->pgno))
}


/*
** This routine is called to increment the database file change-counter,
................................................................................
}
int sqlite3PagerIsMemdb(Pager *pPager){
  return MEMDB;
}
#endif

/*
** Set the statement rollback point.
**
** This routine should be called with the transaction journal already
** open.  A new statement journal is created that can be used to rollback
** changes of a single SQL command within a larger transaction.
*/
static int pagerStmtBegin(Pager *pPager){




  int rc;

  assert( !pPager->stmtInUse );
  assert( pPager->state>=PAGER_SHARED );
  assert( pPager->dbSizeValid );
  PAGERTRACE2("STMT-BEGIN %d\n", PAGERID(pPager));
  if( !pPager->journalOpen ){
    pPager->stmtAutoopen = 1;
    return SQLITE_OK;
  }





  assert( pPager->journalOpen );
  assert( pPager->pInStmt==0 );
  pPager->pInStmt = sqlite3BitvecCreate(pPager->dbSize);
  if( pPager->pInStmt==0 ){
    /* sqlite3OsLock(pPager->fd, SHARED_LOCK); */
    return SQLITE_NOMEM;
  }
  pPager->stmtJSize = pPager->journalOff;



  pPager->stmtSize = pPager->dbSize;
  pPager->stmtHdrOff = 0;
  pPager->stmtCksum = pPager->cksumInit;
  if( !pPager->stmtOpen ){
    if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
      sqlite3MemJournalOpen(pPager->stfd);
    }else{
      rc = sqlite3PagerOpentemp(pPager, pPager->stfd, SQLITE_OPEN_SUBJOURNAL);
      if( rc ){
        goto stmt_begin_failed;
      }









    }
    pPager->stmtOpen = 1;
    pPager->stmtNRec = 0;
  }
  pPager->stmtInUse = 1;
  return SQLITE_OK;
 
stmt_begin_failed:
  if( pPager->pInStmt ){
    sqlite3BitvecDestroy(pPager->pInStmt);
    pPager->pInStmt = 0;


  }
  return rc;
}
int sqlite3PagerStmtBegin(Pager *pPager){
  int rc;
  rc = pagerStmtBegin(pPager);
  return rc;
}

/*








** Commit a statement.

*/
int sqlite3PagerStmtCommit(Pager *pPager){
  if( pPager->stmtInUse ){
    PAGERTRACE2("STMT-COMMIT %d\n", PAGERID(pPager));







    sqlite3BitvecDestroy(pPager->pInStmt);
    pPager->pInStmt = 0;
    pPager->stmtNRec = 0;
    pPager->stmtInUse = 0;
    if( sqlite3IsMemJournal(pPager->stfd) ){
      sqlite3OsTruncate(pPager->stfd, 0);
    }

  }
  pPager->stmtAutoopen = 0;
  return SQLITE_OK;




}

/*
** Rollback a statement.
*/
int sqlite3PagerStmtRollback(Pager *pPager){
  int rc;
  if( pPager->stmtInUse ){
    PAGERTRACE2("STMT-ROLLBACK %d\n", PAGERID(pPager));
    rc = pager_stmt_playback(pPager);
    sqlite3PagerStmtCommit(pPager);
  }else{
    rc = SQLITE_OK;


  }
  pPager->stmtAutoopen = 0;

  return rc;
}

/*
** Return the full pathname of the database file.
*/
const char *sqlite3PagerFilename(Pager *pPager){







|







 







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







 







<
<
<







 







<









<





|




<
<
<







 







>
>
>







 







|
|
|
>
>
>
>
>
>
>
>

|

>
>
>
|
>
>







 







>





|
>
>
>
>
>
|
>







 







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







 







|
<
<
<
<
<
<


<







 







|
<
<
<
<







 







|
|
|
|






>

<
|
<
<







 







|






|


>
>
>







 







|







 







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



|
<
<
|

<







 







|







 







|
<
<







 







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







 







|
|







 







|







 







|
<
<







 







|
<
<
<



|

|






|
|







 







|







 







|
<
<
<







 







|
<
<
<
<

<
>
>
>
>
|
>
|
<
|
<
<
<
<
|
>
>
>
>
>
|
|
|
<
<
|
|
<
>
>
>
|
|
|
<
<
<
<
<
<
<
|
>
>
>
>
>
>
>
>
>
|
<
<
|
<
<
|
<
<
<
<
>
>

<
|
<
<
<




>
>
>
>
>
>
>
>
|
>
|
<
|
|
>
>
>
>
>
>
>
|
<
<
<
<
<

>
|
<
<
>
>
>
>
|

<
<
<
<
<
|
|
|
<
<
|
>
>
|
<
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
...
175
176
177
178
179
180
181



182
183
184
185
186
187
188
...
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210
211

212
213
214
215
216
217
218
219
220
221



222
223
224
225
226
227
228
...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
...
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
...
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
951
952
953
954
955
...
979
980
981
982
983
984
985
986






987
988

989
990
991
992
993
994
995
....
1027
1028
1029
1030
1031
1032
1033
1034




1035
1036
1037
1038
1039
1040
1041
....
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144

1145


1146
1147
1148
1149
1150
1151
1152
....
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
....
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
....
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664

























1665
1666
1667
1668


1669
1670

1671
1672
1673
1674
1675
1676
1677
....
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
....
2267
2268
2269
2270
2271
2272
2273
2274


2275
2276
2277
2278
2279
2280
2281
....
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
....
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
....
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
....
3256
3257
3258
3259
3260
3261
3262
3263


3264
3265
3266
3267
3268
3269
3270
....
3275
3276
3277
3278
3279
3280
3281
3282



3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
....
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
....
3526
3527
3528
3529
3530
3531
3532
3533



3534
3535
3536
3537
3538
3539
3540
....
3891
3892
3893
3894
3895
3896
3897
3898




3899

3900
3901
3902
3903
3904
3905
3906

3907




3908
3909
3910
3911
3912
3913
3914
3915
3916


3917
3918

3919
3920
3921
3922
3923
3924







3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935


3936


3937




3938
3939
3940

3941



3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956

3957
3958
3959
3960
3961
3962
3963
3964
3965
3966





3967
3968
3969


3970
3971
3972
3973
3974
3975





3976
3977
3978


3979
3980
3981
3982

3983
3984
3985
3986
3987
3988
3989
3990
** 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.515 2008/12/17 17:30:26 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"

/*
** Macros for troubleshooting.  Normally turned off
*/
................................................................................
# define CODEC1(P,D,N,X) if( P->xCodec!=0 ){ P->xCodec(P->pCodecArg,D,N,X); }
# define CODEC2(P,D,N,X) ((char*)(P->xCodec!=0?P->xCodec(P->pCodecArg,D,N,X):D))
#else
# define CODEC1(P,D,N,X) /* NO-OP */
# define CODEC2(P,D,N,X) ((char*)D)
#endif

/*
** An instance of the following structure is allocated for each active
** savepoint and statement transaction in the system. All such structures
** are stored in the Pager.aSavepoint[] array, which is allocated and
** resized using sqlite3Realloc().
**
** When a savepoint is created, the PagerSavepoint.iHdrOffset field is
** set to 0. If a journal-header is written into the main journal while
** the savepoint is active, then iHdrOffset is set to the byte offset 
** immediately following the last journal record written into the main
** journal before the journal-header. This is required during savepoint
** rollback (see pagerPlaybackSavepoint()).
*/
typedef struct PagerSavepoint PagerSavepoint;
struct PagerSavepoint {
  i64 iOffset;                 /* Starting offset in main journal */
  i64 iHdrOffset;              /* See above */
  Bitvec *pInSavepoint;        /* Set of pages in this savepoint */
  Pgno nOrig;                  /* Original number of pages in file */
  Pgno iSubRec;                /* Index of first record in sub-journal */
};

/*
** A open page cache is an instance of the following structure.
**
** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or
** or SQLITE_FULL. Once one of the first three errors occurs, it persists
** and is returned as the result of every major pager API call.  The
** SQLITE_FULL return code is slightly different. It persists only until the
................................................................................
*/
struct Pager {
  sqlite3_vfs *pVfs;          /* OS functions to use for IO */
  u8 journalOpen;             /* True if journal file descriptors is valid */
  u8 journalStarted;          /* True if header of journal is synced */
  u8 useJournal;              /* Use a rollback journal on this file */
  u8 noReadlock;              /* Do not bother to obtain readlocks */



  u8 noSync;                  /* Do not sync the journal if true */
  u8 fullSync;                /* Do extra syncs of the journal for robustness */
  u8 sync_flags;              /* One of SYNC_NORMAL or SYNC_FULL */
  u8 state;                   /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
  u8 tempFile;                /* zFilename is a temporary file */
  u8 readOnly;                /* True for a read-only database */
  u8 needSync;                /* True if an fsync() is needed on the journal */
................................................................................
  u8 dbModified;              /* True if there are any changes to the Db */
  u8 changeCountDone;         /* Set after incrementing the change-counter */
  u8 dbSizeValid;             /* Set when dbSize is correct */
  u32 vfsFlags;               /* Flags for sqlite3_vfs.xOpen() */
  int errCode;                /* One of several kinds of errors */
  Pgno dbSize;                /* Number of pages in the file */
  Pgno origDbSize;            /* dbSize before the current change */

  int nRec;                   /* Number of pages written to the journal */
  u32 cksumInit;              /* Quasi-random value added to every checksum */
  int stmtNRec;               /* Number of records in stmt subjournal */
  int nExtra;                 /* Add this many bytes to each in-memory page */
  int pageSize;               /* Number of bytes in a page */
  int nPage;                  /* Total number of in-memory pages */
  int mxPage;                 /* Maximum number of pages to hold in cache */
  Pgno mxPgno;                /* Maximum allowed size of the database */
  Bitvec *pInJournal;         /* One bit for each page in the database file */

  Bitvec *pAlwaysRollback;    /* One bit for each page marked always-rollback */
  char *zFilename;            /* Name of the database file */
  char *zJournal;             /* Name of the journal file */
  char *zDirectory;           /* Directory hold database and journal files */
  sqlite3_file *fd, *jfd;     /* File descriptors for database and journal */
  sqlite3_file *sjfd;         /* File descriptor for the sub-journal*/
  int (*xBusyHandler)(void*); /* Function to call when busy */
  void *pBusyHandlerArg;      /* Context argument for xBusyHandler */
  i64 journalOff;             /* Current byte offset in the journal file */
  i64 journalHdr;             /* Byte offset to previous journal header */



  u32 sectorSize;             /* Assumed sector size during rollback */
#ifdef SQLITE_TEST
  int nHit, nMiss;            /* Cache hits and missing */
  int nRead, nWrite;          /* Database pages read/written */
#endif
  void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
#ifdef SQLITE_HAS_CODEC
................................................................................
  void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */
  void *pCodecArg;            /* First argument to xCodec() */
#endif
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  char dbFileVers[16];        /* Changes whenever database file changes */
  i64 journalSizeLimit;       /* Size limit for persistent journal files */
  PCache *pPCache;            /* Pointer to page cache object */

  PagerSavepoint *aSavepoint;
  int nSavepoint;
};

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

/*
** The maximum legal page number is (2^31 - 1).
*/
#define PAGER_MAX_PGNO 2147483647

/*
** Return false if it is necessary to write page *pPg into the sub-journal.
** More accurately, true is returned if either:
**
**   * No savepoints are open, or
**   * The page has been saved to the sub-journal since the most recent
**     savepoint was opened.
**
** TODO: There's a bug here. To do with PagerSavepoint.nOrig. Also consider
**       the idea that the page may not be required by the outermost savepoint
**       but may be required by some earlier savepoint, due to an incremental
**       vacuum operation.
*/
static int pageInSavepoint(PgHdr *pPg){
  Pager *pPager = pPg->pPager;
  if( pPager->nSavepoint==0 ){
    return 1;
  }
  return sqlite3BitvecTest(
      pPager->aSavepoint[pPager->nSavepoint-1].pInSavepoint, pPg->pgno
  );
}

static int pageInJournal(PgHdr *pPg){
  return sqlite3BitvecTest(pPg->pPager->pInJournal, pPg->pgno);
}

/*
................................................................................
** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
*/
static int writeJournalHdr(Pager *pPager){
  int rc = SQLITE_OK;
  char *zHeader = pPager->pTmpSpace;
  u32 nHeader = pPager->pageSize;
  u32 nWrite;
  int ii;

  if( nHeader>JOURNAL_HDR_SZ(pPager) ){
    nHeader = JOURNAL_HDR_SZ(pPager);
  }

  /* If there are active savepoints and any of them were created since the
  ** most recent journal header was written, update the PagerSavepoint.iHdrOff
  ** fields now.
  */
  for(ii=0; ii<pPager->nSavepoint; ii++){
    if( pPager->aSavepoint[ii].iHdrOffset==0 ){
      pPager->aSavepoint[ii].iHdrOffset = pPager->journalOff;
    }
  }

  seekJournalHdr(pPager);
  pPager->journalHdr = pPager->journalOff;

  memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));

................................................................................
** opened.  Any outstanding pages are invalidated and subsequent attempts
** to access those pages will likely result in a coredump.
*/
static void pager_reset(Pager *pPager){
  if( pPager->errCode ) return;
  sqlite3PcacheClear(pPager->pPCache);
}

static void releaseAllSavepoint(Pager *pPager){
  int ii;
  for(ii=0; ii<pPager->nSavepoint; ii++){
    sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint);
  }
  if( !pPager->exclusiveMode ){
    sqlite3OsClose(pPager->sjfd);
  }
  sqlite3_free(pPager->aSavepoint);
  pPager->aSavepoint = 0;
  pPager->nSavepoint = 0;
}

static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){
  int ii;
  for(ii=0; ii<pPager->nSavepoint; ii++){
    PagerSavepoint *p = &pPager->aSavepoint[ii];
    if( pgno<=p->nOrig ){
      /* TODO: malloc() failure handling */
      sqlite3BitvecSet(p->pInSavepoint, pgno);
    }
  }
  return SQLITE_OK;
}

/*
** Unlock the database file. 
**
** If the pager is currently in error state, discard the contents of 
** the cache and reset the Pager structure internal state. If there is
** an open journal-file, then the next time a shared-lock is obtained
................................................................................
    /* If Pager.errCode is set, the contents of the pager cache cannot be
    ** trusted. Now that the pager file is unlocked, the contents of the
    ** cache can be discarded and the error code safely cleared.
    */
    if( pPager->errCode ){
      if( rc==SQLITE_OK ) pPager->errCode = SQLITE_OK;
      pager_reset(pPager);
      releaseAllSavepoint(pPager);






      pPager->journalOff = 0;
      pPager->journalStarted = 0;

      pPager->origDbSize = 0;
    }

    pPager->state = PAGER_UNLOCK;
    pPager->changeCountDone = 0;
  }
}
................................................................................
*/
static int pager_end_transaction(Pager *pPager, int hasMaster){
  int rc = SQLITE_OK;
  int rc2 = SQLITE_OK;
  if( pPager->state<PAGER_RESERVED ){
    return SQLITE_OK;
  }
  releaseAllSavepoint(pPager);




  if( pPager->journalOpen ){
    if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
      int isMemoryJournal = sqlite3IsMemJournal(pPager->jfd);
      sqlite3OsClose(pPager->jfd);
      pPager->journalOpen = 0;
      if( !isMemoryJournal ){
        rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
................................................................................
** jfd.  Playback this one page.
**
** The isMainJrnl flag is true if this is the main rollback journal and
** false for the statement journal.  The main rollback journal uses
** checksums - the statement journal does not.
*/
static int pager_playback_one_page(
  Pager *pPager,                /* The pager being played back */
  int isMainJrnl,               /* 1 -> main journal. 0 -> sub-journal. */
  i64 offset,                   /* Offset of record to playback */
  Bitvec *pDone                 /* Bitvec of pages already played back */
){
  int rc;
  PgHdr *pPg;                   /* An existing page in the cache */
  Pgno pgno;                    /* The page number of a page in journal */
  u32 cksum;                    /* Checksum used for sanity checking */
  u8 *aData = (u8 *)pPager->pTmpSpace;   /* Temp storage for a page */
  sqlite3_file *jfd = (isMainJrnl ? pPager->jfd : pPager->sjfd);


  /* The temp storage must be allocated at this point */


  assert( aData );

  rc = read32bits(jfd, offset, &pgno);
  if( rc!=SQLITE_OK ) return rc;
  rc = sqlite3OsRead(jfd, aData, pPager->pageSize, offset+4);
  if( rc!=SQLITE_OK ) return rc;
  pPager->journalOff += pPager->pageSize + 4;
................................................................................
  ** thought.  If a power failure occurs while the journal is being written,
  ** it could cause invalid data to be written into the journal.  We need to
  ** detect this invalid data (with high probability) and ignore it.
  */
  if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){
    return SQLITE_DONE;
  }
  if( pgno>(Pgno)pPager->dbSize || sqlite3BitvecTest(pDone, pgno) ){
    return SQLITE_OK;
  }
  if( isMainJrnl ){
    rc = read32bits(jfd, offset+pPager->pageSize+4, &cksum);
    if( rc ) return rc;
    pPager->journalOff += 4;
    if( !pDone && pager_cksum(pPager, aData)!=cksum ){
      return SQLITE_DONE;
    }
  }
  if( pDone && (rc = sqlite3BitvecSet(pDone, pgno)) ){
    return rc;
  }

  assert( pPager->state==PAGER_RESERVED || pPager->state>=PAGER_EXCLUSIVE );

  /* If the pager is in RESERVED state, then there must be a copy of this
  ** page in the pager cache. In this case just update the pager cache,
  ** not the database file. The page is left marked dirty in this case.
................................................................................
        goto end_playback;
      }
    }

    /* Copy original pages out of the journal and back into the database file.
    */
    for(u=0; u<nRec; u++){
      rc = pager_playback_one_page(pPager, 1, pPager->journalOff, 0);
      if( rc!=SQLITE_OK ){
        if( rc==SQLITE_DONE ){
          rc = SQLITE_OK;
          pPager->journalOff = szJ;
          break;
        }else{
          /* If we are unable to rollback, then the database is probably
................................................................................
  ** value. Reset it to the correct value for this process.
  */
  setSectorSize(pPager);
  return rc;
}

/*
** Playback a savepoint.
*/
static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
  i64 szJ;                 /* Size of the full journal */
  i64 iHdrOff;             /* End of first segment of main-journal records */
  Pgno ii;                 /* Loop counter */
  int rc;                  /* Return code */
  Bitvec *pDone = 0;       /* Bitvec to ensure pages played back only once */

  /* Allocate a bitvec to use to store the set of pages rolled back */
  if( pSavepoint ){
    pDone = sqlite3BitvecCreate(pSavepoint->nOrig);
    if( !pDone ){
      return SQLITE_NOMEM;
    }
  }

  /* Truncate the database back to the size it was before the 
  ** savepoint being reverted was opened.
  */
  rc = pager_truncate(pPager, pSavepoint?pSavepoint->nOrig:pPager->origDbSize);
  assert( pPager->state>=PAGER_SHARED );

  /* Now roll back all main journal file records that occur after byte
  ** byte offset PagerSavepoint.iOffset that have a page number less than
  ** or equal to PagerSavepoint.nOrig. As each record is played back,
  ** the corresponding bit in bitvec PagerSavepoint.pInSavepoint is 
  ** cleared.
  */
  szJ = pPager->journalOff;
  if( pSavepoint ){
    iHdrOff = pSavepoint->iHdrOffset ? pSavepoint->iHdrOffset : szJ;
    pPager->journalOff = pSavepoint->iOffset;
    while( rc==SQLITE_OK && pPager->journalOff<iHdrOff ){
      rc = pager_playback_one_page(pPager, 1, pPager->journalOff, pDone);
      assert( rc!=SQLITE_DONE );
    }
  }else{
    pPager->journalOff = 0;
  }
  while( rc==SQLITE_OK && pPager->journalOff<szJ ){
    u32 nJRec;         /* Number of Journal Records */
    u32 dummy;
    rc = readJournalHdr(pPager, szJ, &nJRec, &dummy);
    assert( rc!=SQLITE_DONE );
    if( nJRec==0 ){
      nJRec = (szJ - pPager->journalOff) / (pPager->pageSize+8);
    }
    for(ii=0; rc==SQLITE_OK && ii<nJRec; ii++){
      rc = pager_playback_one_page(pPager, 1, pPager->journalOff, pDone);
      assert( rc!=SQLITE_DONE );
    }
  }
  assert( rc!=SQLITE_OK || pPager->journalOff==szJ );

  /* Now roll back pages from the sub-journal. */
  if( pSavepoint ){
    for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && ii<pPager->stmtNRec; ii++){
      i64 offset = ii*(4+pPager->pageSize);
      rc = pager_playback_one_page(pPager, 0, offset, pDone);
      assert( rc!=SQLITE_DONE );

























    }
  }

  sqlite3BitvecDestroy(pDone);


  if( rc==SQLITE_OK ){
    pPager->journalOff = szJ;

  }
  return rc;
}

/*
** Change the maximum number of in-memory pages that are allowed.
*/
................................................................................
    sqlite3_free(zPathname);
    return SQLITE_NOMEM;
  }
  pPager->pPCache = (PCache *)&pPager[1];
  pPtr = ((u8 *)&pPager[1]) + pcacheSize;
  pPager->vfsFlags = vfsFlags;
  pPager->fd = (sqlite3_file*)&pPtr[pVfs->szOsFile*0];
  pPager->sjfd = (sqlite3_file*)&pPtr[pVfs->szOsFile];
  pPager->jfd = (sqlite3_file*)&pPtr[pVfs->szOsFile+journalFileSize];
  pPager->zFilename = (char*)&pPtr[pVfs->szOsFile+2*journalFileSize];
  pPager->zDirectory = &pPager->zFilename[nPathname+1];
  pPager->zJournal = &pPager->zDirectory[nPathname+1];
  pPager->pVfs = pVfs;
  if( zPathname ){
    memcpy(pPager->zFilename, zPathname, nPathname+1);
................................................................................
  PAGERTRACE2("CLOSE %d\n", PAGERID(pPager));
  IOTRACE(("CLOSE %p\n", pPager))
  if( pPager->journalOpen ){
    sqlite3OsClose(pPager->jfd);
  }
  sqlite3BitvecDestroy(pPager->pInJournal);
  sqlite3BitvecDestroy(pPager->pAlwaysRollback);
  releaseAllSavepoint(pPager);


  sqlite3OsClose(pPager->fd);
  /* Temp files are automatically deleted by the OS
  ** if( pPager->tempFile ){
  **   sqlite3OsDelete(pPager->zFilename);
  ** }
  */

................................................................................
  if( pPg ){
    Pager *pPager = pPg->pPager;
    sqlite3PcacheRelease(pPg);
    pagerUnlockIfUnused(pPager);
  }
  return SQLITE_OK;
}

static int openSubJournal(Pager *pPager){
  int rc = SQLITE_OK;
  if( pPager->journalOpen && !pPager->sjfd->pMethods ){
    if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
      sqlite3MemJournalOpen(pPager->sjfd);
    }else{
      rc = sqlite3PagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL);
    }
  }
  return rc;
}

/*
** Create a journal file for pPager.  There should already be a RESERVED
** or EXCLUSIVE lock on the database file when this routine is called.
**
** Return SQLITE_OK if everything.  Return an error code and release the
** write lock if anything goes wrong.
................................................................................
    rc = pPager->errCode;
    goto failed_to_open_journal;
  }
  pPager->origDbSize = pPager->dbSize;

  rc = writeJournalHdr(pPager);

  if( pPager->nSavepoint && rc==SQLITE_OK ){
    rc = openSubJournal(pPager);
  }
  if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && rc!=SQLITE_IOERR_NOMEM ){
    rc = pager_end_transaction(pPager, 0);
    if( rc==SQLITE_OK ){
      rc = SQLITE_FULL;
    }
  }
................................................................................
    return rc;
  }

  /* Mark the page as dirty.  If the page has already been written
  ** to the journal then we can return right away.
  */
  sqlite3PcacheMakeDirty(pPg);
  if( pageInJournal(pPg) && pageInSavepoint(pPg) ){
    pPager->dirtyCache = 1;
    pPager->dbModified = 1;
  }else{

    /* If we get this far, it means that the page needs to be
    ** written to the transaction journal or the ckeckpoint journal
    ** or both.
................................................................................

        pPager->nRec++;
        assert( pPager->pInJournal!=0 );
        sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
        if( !pPager->noSync ){
          pPg->flags |= PGHDR_NEED_SYNC;
        }
        addToSavepointBitvecs(pPager, pPg->pgno);


      }else{
        if( !pPager->journalStarted && !pPager->noSync ){
          pPg->flags |= PGHDR_NEED_SYNC;
        }
        PAGERTRACE4("APPEND %d page %d needSync=%d\n",
                PAGERID(pPager), pPg->pgno,
               ((pPg->flags&PGHDR_NEED_SYNC)?1:0));
................................................................................
    }
  
    /* If the statement journal is open and the page is not in it,
    ** then write the current page to the statement journal.  Note that
    ** the statement journal format differs from the standard journal format
    ** in that it omits the checksums and the header.
    */
    if( !pageInSavepoint(pPg) ){



      i64 offset = pPager->stmtNRec*(4+pPager->pageSize);
      char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7);
      assert( pageInJournal(pPg) || pPg->pgno>pPager->origDbSize );
      rc = write32bits(pPager->sjfd, offset, pPg->pgno);
      if( rc==SQLITE_OK ){
        rc = sqlite3OsWrite(pPager->sjfd, pData2, pPager->pageSize, offset+4);
      }
      PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
      if( rc!=SQLITE_OK ){
        return rc;
      }
      pPager->stmtNRec++;
      assert( pPager->nSavepoint>0 );
      addToSavepointBitvecs(pPager, pPg->pgno);
    }
  }

  /* Update the database size and return.
  */
  assert( pPager->state>=PAGER_SHARED );
  if( pPager->dbSize<pPg->pgno ){
................................................................................
    pPager->pAlwaysRollback = sqlite3BitvecCreate(pPager->origDbSize);
    if( !pPager->pAlwaysRollback ){
      return SQLITE_NOMEM;
    }
  }
  rc = sqlite3BitvecSet(pPager->pAlwaysRollback, pPg->pgno);

  if( rc==SQLITE_OK && (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
    assert( pPager->state>=PAGER_SHARED );
    if( pPager->dbSize==pPg->pgno && pPager->origDbSize<pPager->dbSize ){
      /* If this pages is the last page in the file and the file has grown
      ** during the current transaction, then do NOT mark the page as clean.
      ** When the database file grows, we must make sure that the last page
      ** gets written at least once so that the disk file will be the correct
      ** size. If you do not write this page and the size of the file
................................................................................
  ** necessarily true:
  */
  /* assert( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ); */

  assert( pPager->pInJournal!=0 );
  sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
  pPg->flags &= ~PGHDR_NEED_READ;
  addToSavepointBitvecs(pPager, pPg->pgno);



  PAGERTRACE3("DONT_ROLLBACK page %d of %d\n", pPg->pgno, PAGERID(pPager));
  IOTRACE(("GARBAGE %p %d\n", pPager, pPg->pgno))
}


/*
** This routine is called to increment the database file change-counter,
................................................................................
}
int sqlite3PagerIsMemdb(Pager *pPager){
  return MEMDB;
}
#endif

/*
** Ensure that there are at least nSavepoint savepoints open.




*/

int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
  int rc = SQLITE_OK;

  if( nSavepoint>pPager->nSavepoint ){
    int ii;

    /* Either the sub-journal is open or there are no active savepoints. */

    assert( pPager->nSavepoint==0 || pPager->sjfd->pMethods );





    /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
    ** if the allocation fails. Otherwise, zero the new portion in case a 
    ** malloc failure occurs while populating it in the for(...) loop below.
    */
    PagerSavepoint *aNew = (PagerSavepoint *)sqlite3Realloc(
        pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint
    );
    if( !aNew ){


      return SQLITE_NOMEM;
    }

    memset(&aNew[pPager->nSavepoint], 0,
        (nSavepoint - pPager->nSavepoint) * sizeof(PagerSavepoint)
    );
    pPager->aSavepoint = aNew;
    ii = pPager->nSavepoint;
    pPager->nSavepoint = nSavepoint;








    /* Populate the PagerSavepoint structures just allocated. */
    for(/* no-op */; ii<nSavepoint; ii++){
      assert( pPager->dbSize>=0 );
      aNew[ii].nOrig = pPager->dbSize;
      aNew[ii].iOffset = (pPager->journalOpen ? pPager->journalOff : 0);
      aNew[ii].iSubRec = pPager->stmtNRec;
      aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
      if( !aNew[ii].pInSavepoint ){
        return SQLITE_NOMEM;
      }


    }







    /* Open the sub-journal, if it is not already opened. */
    rc = openSubJournal(pPager);
  }





  return rc;
}

/*
** Parameter op is always either SAVEPOINT_ROLLBACK or SAVEPOINT_RELEASE.
** If it is SAVEPOINT_RELEASE, then release and destroy the savepoint with
** index iSavepoint. If it is SAVEPOINT_ROLLBACK, then rollback all changes
** that have occured since savepoint iSavepoint was created.
**
** In either case, all savepoints with an index greater than iSavepoint 
** are destroyed.
**
** If there are less than (iSavepoint+1) active savepoints when this 
** function is called it is a no-op.
*/ 

int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
  int rc = SQLITE_OK;

  assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );

  if( iSavepoint<pPager->nSavepoint ){
    int ii;
    int nNew = iSavepoint + (op==SAVEPOINT_ROLLBACK);
    for(ii=nNew; ii<pPager->nSavepoint; ii++){
      sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint);





    }
    pPager->nSavepoint = nNew;



    if( op==SAVEPOINT_ROLLBACK ){
      PagerSavepoint *pSavepoint = (nNew==0) ? 0 : &pPager->aSavepoint[nNew-1];
      rc = pagerPlaybackSavepoint(pPager, pSavepoint);
      assert(rc!=SQLITE_DONE);
    }






    /* If this is a release of the outermost savepoint, truncate 
    ** the sub-journal. */
    if( nNew==0 && op==SAVEPOINT_RELEASE && pPager->sjfd->pMethods ){


      assert( rc==SQLITE_OK );
      rc = sqlite3OsTruncate(pPager->sjfd, 0);
      pPager->stmtNRec = 0;
    }

  }
  return rc;
}

/*
** Return the full pathname of the database file.
*/
const char *sqlite3PagerFilename(Pager *pPager){

Changes to src/pager.h.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
112
113
114
115
116
117
118



119
120
121
122
123
124
125
**    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.88 2008/12/10 16:45:51 drh Exp $
*/

#ifndef _PAGER_H_
#define _PAGER_H_

/*
** If defined as non-zero, auto-vacuum is enabled by default. Otherwise
................................................................................
void *sqlite3PagerGetExtra(DbPage *); 
int sqlite3PagerLockingMode(Pager *, int);
int sqlite3PagerJournalMode(Pager *, int);
i64 sqlite3PagerJournalSizeLimit(Pager *, i64);
void *sqlite3PagerTempSpace(Pager*);
int sqlite3PagerSync(Pager *pPager);




#ifdef SQLITE_HAS_CODEC
  void sqlite3PagerSetCodec(Pager*,void*(*)(void*,void*,Pgno,int),void*);
#endif

#if !defined(NDEBUG) || defined(SQLITE_TEST)
  Pgno sqlite3PagerPagenumber(DbPage*);
  int sqlite3PagerIswriteable(DbPage*);







|







 







>
>
>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
**    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.89 2008/12/17 17:30:26 danielk1977 Exp $
*/

#ifndef _PAGER_H_
#define _PAGER_H_

/*
** If defined as non-zero, auto-vacuum is enabled by default. Otherwise
................................................................................
void *sqlite3PagerGetExtra(DbPage *); 
int sqlite3PagerLockingMode(Pager *, int);
int sqlite3PagerJournalMode(Pager *, int);
i64 sqlite3PagerJournalSizeLimit(Pager *, i64);
void *sqlite3PagerTempSpace(Pager*);
int sqlite3PagerSync(Pager *pPager);

int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);

#ifdef SQLITE_HAS_CODEC
  void sqlite3PagerSetCodec(Pager*,void*(*)(void*,void*,Pgno,int),void*);
#endif

#if !defined(NDEBUG) || defined(SQLITE_TEST)
  Pgno sqlite3PagerPagenumber(DbPage*);
  int sqlite3PagerIswriteable(DbPage*);

Changes to src/parse.y.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
113
114
115
116
117
118
119












120
121
122
123
124
125
126
**
*************************************************************************
** This file contains SQLite's grammar for SQL.  Process this file
** using the lemon parser generator to generate C code that runs
** the parser.  Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.265 2008/12/10 18:03:46 drh Exp $
*/

// All token codes are small integers with #defines that begin with "TK_"
%token_prefix TK_

// The type of the data attached to each token is Token.  This is also the
// default type for non-terminals.
................................................................................
transtype(A) ::= .             {A = TK_DEFERRED;}
transtype(A) ::= DEFERRED(X).  {A = @X;}
transtype(A) ::= IMMEDIATE(X). {A = @X;}
transtype(A) ::= EXCLUSIVE(X). {A = @X;}
cmd ::= COMMIT trans_opt.      {sqlite3CommitTransaction(pParse);}
cmd ::= END trans_opt.         {sqlite3CommitTransaction(pParse);}
cmd ::= ROLLBACK trans_opt.    {sqlite3RollbackTransaction(pParse);}













///////////////////// The CREATE TABLE statement ////////////////////////////
//
cmd ::= create_table create_table_args.
create_table ::= CREATE temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z). {
   sqlite3StartTable(pParse,&Y,&Z,T,0,0,E);
}







|







 







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







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
**
*************************************************************************
** This file contains SQLite's grammar for SQL.  Process this file
** using the lemon parser generator to generate C code that runs
** the parser.  Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.266 2008/12/17 17:30:26 danielk1977 Exp $
*/

// All token codes are small integers with #defines that begin with "TK_"
%token_prefix TK_

// The type of the data attached to each token is Token.  This is also the
// default type for non-terminals.
................................................................................
transtype(A) ::= .             {A = TK_DEFERRED;}
transtype(A) ::= DEFERRED(X).  {A = @X;}
transtype(A) ::= IMMEDIATE(X). {A = @X;}
transtype(A) ::= EXCLUSIVE(X). {A = @X;}
cmd ::= COMMIT trans_opt.      {sqlite3CommitTransaction(pParse);}
cmd ::= END trans_opt.         {sqlite3CommitTransaction(pParse);}
cmd ::= ROLLBACK trans_opt.    {sqlite3RollbackTransaction(pParse);}

savepoint_opt ::= SAVEPOINT.
savepoint_opt ::= .
cmd ::= SAVEPOINT nm(X). {
  sqlite3Savepoint(pParse, SAVEPOINT_BEGIN, &X);
}
cmd ::= RELEASE savepoint_opt nm(X). {
  sqlite3Savepoint(pParse, SAVEPOINT_RELEASE, &X);
}
cmd ::= ROLLBACK trans_opt TO savepoint_opt nm(X). {
  sqlite3Savepoint(pParse, SAVEPOINT_ROLLBACK, &X);
}

///////////////////// The CREATE TABLE statement ////////////////////////////
//
cmd ::= create_table create_table_args.
create_table ::= CREATE temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z). {
   sqlite3StartTable(pParse,&Y,&Z,T,0,0,E);
}

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
518
519
520
521
522
523
524

525
526
527
528
529
530
531
...
761
762
763
764
765
766
767



768
769
770
771
772
773
774
...
872
873
874
875
876
877
878



















879
880
881
882
883
884
885
....
2245
2246
2247
2248
2249
2250
2251


2252
2253
2254
2255
2256
2257
2258
**    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.809 2008/12/10 21:19:57 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Include the configuration header output by 'configure' if we're using the
** autoconf-based build
................................................................................
typedef struct KeyClass KeyClass;
typedef struct KeyInfo KeyInfo;
typedef struct Lookaside Lookaside;
typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
typedef struct Parse Parse;

typedef struct Select Select;
typedef struct SrcList SrcList;
typedef struct StrAccum StrAccum;
typedef struct Table Table;
typedef struct TableLock TableLock;
typedef struct Token Token;
typedef struct TriggerStack TriggerStack;
................................................................................
  Hash aCollSeq;                /* All collating sequences */
  BusyHandler busyHandler;      /* Busy callback */
  int busyTimeout;              /* Busy handler timeout, in msec */
  Db aDbStatic[2];              /* Static space for the 2 default backends */
#ifdef SQLITE_SSE
  sqlite3_stmt *pFetch;         /* Used by SSE to fetch stored statements */
#endif



};

/*
** A macro to discover the encoding of a database.
*/
#define ENC(db) ((db)->aDb[0].pSchema->enc)

................................................................................
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
  {nArg, SQLITE_UTF8, bNC*8, pArg, 0, xFunc, 0, 0, #zName, 0}
#define LIKEFUNC(zName, nArg, arg, flags) \
  {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0}
#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
  {nArg, SQLITE_UTF8, nc*8, SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0}





















/*
** Each SQLite module (virtual table definition) is defined by an
** instance of the following structure, stored in the sqlite3.aModule
** hash table.
*/
struct Module {
................................................................................
void sqlite3PrngRestoreState(void);
void sqlite3PrngResetState(void);
void sqlite3RollbackAll(sqlite3*);
void sqlite3CodeVerifySchema(Parse*, int);
void sqlite3BeginTransaction(Parse*, int);
void sqlite3CommitTransaction(Parse*);
void sqlite3RollbackTransaction(Parse*);


int sqlite3ExprIsConstant(Expr*);
int sqlite3ExprIsConstantNotJoin(Expr*);
int sqlite3ExprIsConstantOrFunction(Expr*);
int sqlite3ExprIsInteger(Expr*, int*);
int sqlite3IsRowid(const char*);
void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int);
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*);







|







 







>







 







>
>
>







 







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







 







>
>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
...
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
...
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
....
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
**    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.810 2008/12/17 17:30:26 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Include the configuration header output by 'configure' if we're using the
** autoconf-based build
................................................................................
typedef struct KeyClass KeyClass;
typedef struct KeyInfo KeyInfo;
typedef struct Lookaside Lookaside;
typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
typedef struct Parse Parse;
typedef struct Savepoint Savepoint;
typedef struct Select Select;
typedef struct SrcList SrcList;
typedef struct StrAccum StrAccum;
typedef struct Table Table;
typedef struct TableLock TableLock;
typedef struct Token Token;
typedef struct TriggerStack TriggerStack;
................................................................................
  Hash aCollSeq;                /* All collating sequences */
  BusyHandler busyHandler;      /* Busy callback */
  int busyTimeout;              /* Busy handler timeout, in msec */
  Db aDbStatic[2];              /* Static space for the 2 default backends */
#ifdef SQLITE_SSE
  sqlite3_stmt *pFetch;         /* Used by SSE to fetch stored statements */
#endif
  Savepoint *pSavepoint;        /* List of active savepoints */
  int nSavepoint;               /* Number of non-transaction savepoints */
  u8 isTransactionSavepoint;    /* True if the outermost savepoint is a TS */
};

/*
** A macro to discover the encoding of a database.
*/
#define ENC(db) ((db)->aDb[0].pSchema->enc)

................................................................................
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
  {nArg, SQLITE_UTF8, bNC*8, pArg, 0, xFunc, 0, 0, #zName, 0}
#define LIKEFUNC(zName, nArg, arg, flags) \
  {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0}
#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
  {nArg, SQLITE_UTF8, nc*8, SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0}

/*
** All current savepoints are stored in a linked list starting at
** sqlite3.pSavepoint. The first element in the list is the most recently
** opened savepoint. Savepoints are added to the list by the vdbe
** OP_Savepoint instruction.
*/
struct Savepoint {
  char *zName;                        /* Savepoint name (nul-terminated) */
  Savepoint *pNext;                   /* Parent savepoint (if any) */
};

/*
** The following are used as the second parameter to sqlite3Savepoint(),
** and as the P1 argument to the OP_Savepoint instruction.
*/
#define SAVEPOINT_BEGIN      0
#define SAVEPOINT_RELEASE    1
#define SAVEPOINT_ROLLBACK   2


/*
** Each SQLite module (virtual table definition) is defined by an
** instance of the following structure, stored in the sqlite3.aModule
** hash table.
*/
struct Module {
................................................................................
void sqlite3PrngRestoreState(void);
void sqlite3PrngResetState(void);
void sqlite3RollbackAll(sqlite3*);
void sqlite3CodeVerifySchema(Parse*, int);
void sqlite3BeginTransaction(Parse*, int);
void sqlite3CommitTransaction(Parse*);
void sqlite3RollbackTransaction(Parse*);
void sqlite3Savepoint(Parse*, int, Token*);
void sqlite3CloseSavepoints(sqlite3 *);
int sqlite3ExprIsConstant(Expr*);
int sqlite3ExprIsConstantNotJoin(Expr*);
int sqlite3ExprIsConstantOrFunction(Expr*);
int sqlite3ExprIsInteger(Expr*, int*);
int sqlite3IsRowid(const char*);
void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int);
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*);

Changes to src/test2.c.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
...
220
221
222
223
224
225
226
227

228
229
230
231
232
233
234
...
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the pager.c module in SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test2.c,v 1.62 2008/09/29 11:49:48 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

................................................................................
  int rc;
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID\"", 0);
    return TCL_ERROR;
  }
  pPager = sqlite3TestTextToPtr(argv[1]);
  rc = sqlite3PagerStmtBegin(pPager);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  return TCL_OK;
}

................................................................................
  int rc;
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID\"", 0);
    return TCL_ERROR;
  }
  pPager = sqlite3TestTextToPtr(argv[1]);
  rc = sqlite3PagerStmtRollback(pPager);

  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  return TCL_OK;
}

................................................................................
  int rc;
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID\"", 0);
    return TCL_ERROR;
  }
  pPager = sqlite3TestTextToPtr(argv[1]);
  rc = sqlite3PagerStmtCommit(pPager);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  return TCL_OK;
}








|







 







|







 







|
>







 







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
...
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
...
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the pager.c module in SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test2.c,v 1.63 2008/12/17 17:30:26 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

................................................................................
  int rc;
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID\"", 0);
    return TCL_ERROR;
  }
  pPager = sqlite3TestTextToPtr(argv[1]);
  rc = sqlite3PagerOpenSavepoint(pPager, 1);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  return TCL_OK;
}

................................................................................
  int rc;
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID\"", 0);
    return TCL_ERROR;
  }
  pPager = sqlite3TestTextToPtr(argv[1]);
  rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, 0);
  sqlite3PagerSavepoint(pPager, SAVEPOINT_RELEASE, 0);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  return TCL_OK;
}

................................................................................
  int rc;
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID\"", 0);
    return TCL_ERROR;
  }
  pPager = sqlite3TestTextToPtr(argv[1]);
  rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_RELEASE, 0);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  return TCL_OK;
}

Changes to src/vdbe.c.

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
...
494
495
496
497
498
499
500




















501
502
503
504
505
506
507
....
2351
2352
2353
2354
2355
2356
2357







































































































































2358
2359
2360
2361
2362
2363
2364
....
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405

2406
2407
2408
2409
2410
2411
2412
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.803 2008/12/15 15:27:52 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
#include "vdbeInt.h"

/*
** The following global variable is incremented every time a cursor
................................................................................
  extern int sqlite3_io_error_pending;
  if( sqlite3_io_error_pending<=0 )
#endif
    rc = sqlite3OsAccess(db->pVfs, zFile, SQLITE_ACCESS_EXISTS, &res);
  return (res && rc==SQLITE_OK);
}
#endif





















/*
** Execute as much of a VDBE program as we can then return.
**
** sqlite3VdbeMakeReady() must be called before this routine in order to
** close the program with a final OP_Halt and to set up the callbacks
** and the error message pointer.
................................................................................
    if( !sqlite3BtreeIsInStmt(pBt) ){
      rc = sqlite3BtreeBeginStmt(pBt);
      p->openedStatement = 1;
    }
  }
  break;
}








































































































































/* Opcode: AutoCommit P1 P2 * * *
**
** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
** back any currently active btree transactions. If there are any active
** VMs (apart from this one), then the COMMIT or ROLLBACK statement fails.
**
................................................................................
    /* If this instruction implements a COMMIT and other VMs are writing
    ** return an error indicating that the other VMs must complete first. 
    */
    sqlite3SetString(&p->zErrMsg, db, "cannot commit transaction - "
        "SQL statements in progress");
    rc = SQLITE_BUSY;
  }else if( desiredAutoCommit!=db->autoCommit ){
    if( pOp->p2 ){
      assert( desiredAutoCommit==1 );
      sqlite3RollbackAll(db);
      db->autoCommit = 1;
    }else{
      db->autoCommit = (u8)desiredAutoCommit;
      if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
        p->pc = pc;
        db->autoCommit = (u8)(1-desiredAutoCommit);
        p->rc = rc = SQLITE_BUSY;
        goto vdbe_return;
      }
    }

    if( p->rc==SQLITE_OK ){
      rc = SQLITE_DONE;
    }else{
      rc = SQLITE_ERROR;
    }
    goto vdbe_return;
  }else{







|







 







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







 







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







 







|












>







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
...
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
....
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
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
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
....
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** in this file for details.  If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.804 2008/12/17 17:30:26 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
#include "vdbeInt.h"

/*
** The following global variable is incremented every time a cursor
................................................................................
  extern int sqlite3_io_error_pending;
  if( sqlite3_io_error_pending<=0 )
#endif
    rc = sqlite3OsAccess(db->pVfs, zFile, SQLITE_ACCESS_EXISTS, &res);
  return (res && rc==SQLITE_OK);
}
#endif

#ifndef NDEBUG
/*
** This function is only called from within an assert() expression. It
** checks that the sqlite3.nTransaction variable is correctly set to
** the number of non-transaction savepoints currently in the 
** linked list starting at sqlite3.pSavepoint.
** 
** Usage:
**
**     assert( checkSavepointCount(db) );
*/
static int checkSavepointCount(sqlite3 *db){
  int n = 0;
  Savepoint *p;
  for(p=db->pSavepoint; p; p=p->pNext) n++;
  assert( n==(db->nSavepoint + db->isTransactionSavepoint) );
  return 1;
}
#endif

/*
** Execute as much of a VDBE program as we can then return.
**
** sqlite3VdbeMakeReady() must be called before this routine in order to
** close the program with a final OP_Halt and to set up the callbacks
** and the error message pointer.
................................................................................
    if( !sqlite3BtreeIsInStmt(pBt) ){
      rc = sqlite3BtreeBeginStmt(pBt);
      p->openedStatement = 1;
    }
  }
  break;
}

/* Opcode: Savepoint P1 * * P4 *
**
** Open, release or rollback the savepoint named by parameter P4, depending
** on the value of P1. To open a new savepoint, P1==0. To release (commit) an
** existing savepoint, P1==1, or to rollback an existing savepoint P1==2.
*/
case OP_Savepoint: {
  int p1 = pOp->p1;
  char *zName = pOp->p4.z;         /* Name of savepoint */

  /* Assert that the p1 parameter is valid. Also that if there is no open
  ** transaction, then there cannot be any savepoints. 
  */
  assert( db->pSavepoint==0 || db->autoCommit==0 );
  assert( p1==SAVEPOINT_BEGIN||p1==SAVEPOINT_RELEASE||p1==SAVEPOINT_ROLLBACK );
  assert( db->pSavepoint || db->isTransactionSavepoint==0 );
  assert( checkSavepointCount(db) );

  if( p1==SAVEPOINT_BEGIN ){
    if( db->writeVdbeCnt>1 ){
      /* A new savepoint cannot be created if there are active write 
      ** statements (i.e. open read/write incremental blob handles).
      */
      sqlite3SetString(&p->zErrMsg, db, "cannot open savepoint - "
        "SQL statements in progress");
      rc = SQLITE_BUSY;
    }else{
      int nName = sqlite3Strlen30(zName);
      Savepoint *pNew;

      /* Create a new savepoint structure. */
      pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+nName+1);
      if( pNew ){
        pNew->zName = (char *)&pNew[1];
        memcpy(pNew->zName, zName, nName+1);
    
        /* If there is no open transaction, then mark this as a special
        ** "transaction savepoint". */
        if( db->autoCommit ){
          db->autoCommit = 0;
          db->isTransactionSavepoint = 1;
        }else{
          db->nSavepoint++;
	}
    
        /* Link the new savepoint into the database handle's list. */
        pNew->pNext = db->pSavepoint;
        db->pSavepoint = pNew;
      }
    }
  }else{
    Savepoint *pSavepoint;
    int iSavepoint = 0;

    /* Find the named savepoint. If there is no such savepoint, then an
    ** an error is returned to the user.  */
    for(
      pSavepoint=db->pSavepoint; 
      pSavepoint && sqlite3StrICmp(pSavepoint->zName, zName);
      pSavepoint=pSavepoint->pNext
    ){
      iSavepoint++;
    }
    if( !pSavepoint ){
      sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", zName);
      rc = SQLITE_ERROR;
    }else if( 
        db->writeVdbeCnt>0 || (p1==SAVEPOINT_ROLLBACK && db->activeVdbeCnt>1) 
    ){
      /* It is not possible to release (commit) a savepoint if there are 
      ** active write statements. It is not possible to rollback a savepoint
      ** if there are any active statements at all.
      */
      sqlite3SetString(&p->zErrMsg, db, 
        "cannot %s savepoint - SQL statements in progress",
        (p1==SAVEPOINT_ROLLBACK ? "rollback": "release")
      );
      rc = SQLITE_BUSY;
    }else{

      /* Determine whether or not this is a transaction savepoint. If so,
      ** operate on the currently open transaction. If this is a RELEASE 
      ** command, then the transaction is committed. If it is a ROLLBACK 
      ** command, then all changes made by the current transaction are 
      ** reverted, but the transaction is not actually closed.
      */
      int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
      if( isTransaction && p1==SAVEPOINT_RELEASE ){
        db->isTransactionSavepoint = 0;
        db->autoCommit = 1;
        if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
          p->pc = pc;
          db->autoCommit = 0;
          p->rc = rc = SQLITE_BUSY;
          goto vdbe_return;
        }
      }else{
        int ii;
        iSavepoint = db->nSavepoint - iSavepoint - 1;
        for(ii=0; ii<db->nDb; ii++){
          rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
          if( rc!=SQLITE_OK ){
            goto abort_due_to_error;
	  }
        }
        if( p1==SAVEPOINT_ROLLBACK && db->flags&SQLITE_InternChanges ){
          sqlite3ExpirePreparedStatements(db);
          sqlite3ResetInternalSchema(db, 0);
        }
      }
  
      /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all 
      ** savepoints nested inside of the savepoint being operated on. */
      while( db->pSavepoint!=pSavepoint ){
        Savepoint *pTmp = db->pSavepoint;
        db->pSavepoint = pTmp->pNext;
        sqlite3DbFree(db, pTmp);
        db->nSavepoint--;
      }

      /* If it is a RELEASE, then destroy the savepoint being operated on too */
      if( p1==SAVEPOINT_RELEASE ){
        assert( pSavepoint==db->pSavepoint );
        db->pSavepoint = pSavepoint->pNext;
        sqlite3DbFree(db, pSavepoint);
        if( !isTransaction ){
          db->nSavepoint--;
        }
      }
    }
  }

  break;
}

/* Opcode: AutoCommit P1 P2 * * *
**
** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
** back any currently active btree transactions. If there are any active
** VMs (apart from this one), then the COMMIT or ROLLBACK statement fails.
**
................................................................................
    /* If this instruction implements a COMMIT and other VMs are writing
    ** return an error indicating that the other VMs must complete first. 
    */
    sqlite3SetString(&p->zErrMsg, db, "cannot commit transaction - "
        "SQL statements in progress");
    rc = SQLITE_BUSY;
  }else if( desiredAutoCommit!=db->autoCommit ){
    if( rollback ){
      assert( desiredAutoCommit==1 );
      sqlite3RollbackAll(db);
      db->autoCommit = 1;
    }else{
      db->autoCommit = (u8)desiredAutoCommit;
      if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
        p->pc = pc;
        db->autoCommit = (u8)(1-desiredAutoCommit);
        p->rc = rc = SQLITE_BUSY;
        goto vdbe_return;
      }
    }
    sqlite3CloseSavepoints(db);
    if( p->rc==SQLITE_OK ){
      rc = SQLITE_DONE;
    }else{
      rc = SQLITE_ERROR;
    }
    goto vdbe_return;
  }else{

Added test/savepoint.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# 2008 December 15
#
# 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: savepoint.test,v 1.1 2008/12/17 17:30:26 danielk1977 Exp $

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


#----------------------------------------------------------------------
# The following tests - savepoint-1.* - test that the SAVEPOINT, RELEASE
# and ROLLBACK TO comands are correctly parsed, and that the auto-commit
# flag is correctly set and unset as a result.
#
do_test savepoint-1.1 {
  execsql {
    SAVEPOINT sp1;
    RELEASE sp1;
  }
} {}
do_test savepoint-1.2 {
  execsql {
    SAVEPOINT sp1;
    ROLLBACK TO sp1;
  }
} {}
do_test savepoint-1.3 {
  execsql { SAVEPOINT sp1 }
  db close
} {}
sqlite3 db test.db
do_test savepoint-1.4.1 {
  execsql {
    SAVEPOINT sp1;
    SAVEPOINT sp2;
    RELEASE sp1;
  }
  sqlite3_get_autocommit db
} {1}
do_test savepoint-1.4.2 {
  execsql {
    SAVEPOINT sp1;
    SAVEPOINT sp2;
    RELEASE sp2;
  }
  sqlite3_get_autocommit db
} {0}
do_test savepoint-1.4.3 {
  execsql { RELEASE sp1 }
  sqlite3_get_autocommit db
} {1}
do_test savepoint-1.4.4 {
  execsql {
    SAVEPOINT sp1;
    SAVEPOINT sp2;
    ROLLBACK TO sp1;
  }
  sqlite3_get_autocommit db
} {0}
do_test savepoint-1.4.5 {
  execsql { RELEASE SAVEPOINT sp1 }
  sqlite3_get_autocommit db
} {1}
do_test savepoint-1.4.6 {
  execsql {
    SAVEPOINT sp1;
    SAVEPOINT sp2;
    SAVEPOINT sp3;
    ROLLBACK TO SAVEPOINT sp3;
    ROLLBACK TRANSACTION TO sp2;
    ROLLBACK TRANSACTION TO SAVEPOINT sp1;
  }
  sqlite3_get_autocommit db
} {0}
do_test savepoint-1.4.7 {
  execsql { RELEASE SAVEPOINT SP1 }
  sqlite3_get_autocommit db
} {1}
do_test savepoint-1.5 {
  execsql {
    SAVEPOINT sp1;
    ROLLBACK TO sp1;
  }
} {}
do_test savepoint-1.6 {
  execsql COMMIT
} {}

#------------------------------------------------------------------------
# These tests - savepoint-2.* - test rollbacks and releases of savepoints
# with a very simple data set.
# 

do_test savepoint-2.1 {
  execsql {
    CREATE TABLE t1(a, b, c);
    BEGIN;
    INSERT INTO t1 VALUES(1, 2, 3);
    SAVEPOINT one;
    UPDATE t1 SET a = 2, b = 3, c = 4;
  }
  execsql { SELECT * FROM t1 }
} {2 3 4}
do_test savepoint-2.2 {
  execsql {
    ROLLBACK TO one;
  }
  execsql { SELECT * FROM t1 }
} {1 2 3}
do_test savepoint-2.3 {
  execsql {
    INSERT INTO t1 VALUES(4, 5, 6);
  }
  execsql { SELECT * FROM t1 }
} {1 2 3 4 5 6}
do_test savepoint-2.4 {
  execsql {
    ROLLBACK TO one;
  }
  execsql { SELECT * FROM t1 }
} {1 2 3}


do_test savepoint-2.5 {
  execsql {
    INSERT INTO t1 VALUES(7, 8, 9);
    SAVEPOINT two;
    INSERT INTO t1 VALUES(10, 11, 12);
  }
  execsql { SELECT * FROM t1 }
} {1 2 3 7 8 9 10 11 12}
do_test savepoint-2.6 {
  execsql {
    ROLLBACK TO two;
  }
  execsql { SELECT * FROM t1 }
} {1 2 3 7 8 9}
do_test savepoint-2.7 {
  execsql {
    INSERT INTO t1 VALUES(10, 11, 12);
  }
  execsql { SELECT * FROM t1 }
} {1 2 3 7 8 9 10 11 12}
do_test savepoint-2.8 {
  execsql {
    ROLLBACK TO one;
  }
  execsql { SELECT * FROM t1 }
} {1 2 3}
do_test savepoint-2.9 {
  execsql {
    INSERT INTO t1 VALUES('a', 'b', 'c');
    SAVEPOINT two;
    INSERT INTO t1 VALUES('d', 'e', 'f');
  }
  execsql { SELECT * FROM t1 }
} {1 2 3 a b c d e f}
do_test savepoint-2.10 {
  execsql {
    RELEASE two;
  }
  execsql { SELECT * FROM t1 }
} {1 2 3 a b c d e f}
do_test savepoint-2.11 {
  execsql {
    ROLLBACK;
  }
  execsql { SELECT * FROM t1 }
} {}

#------------------------------------------------------------------------
# This block of tests - savepoint-3.* - test that when a transaction
# savepoint is rolled back, locks are not released from database files.
# And that when a transaction savepoint is released, they are released.
# 
do_test savepoint-3.1 {
  execsql { SAVEPOINT "transaction" }
  execsql { PRAGMA lock_status }
} {main unlocked temp closed}

do_test savepoint-3.2 {
  execsql { INSERT INTO t1 VALUES(1, 2, 3) }
  execsql { PRAGMA lock_status }
} {main reserved temp closed}

do_test savepoint-3.3 {
  execsql { ROLLBACK TO "transaction" }
  execsql { PRAGMA lock_status }
} {main reserved temp closed}

do_test savepoint-3.4 {
  execsql { INSERT INTO t1 VALUES(1, 2, 3) }
  execsql { PRAGMA lock_status }
} {main reserved temp closed}

do_test savepoint-3.5 {
  execsql { RELEASE "transaction" }
  execsql { PRAGMA lock_status }
} {main unlocked temp closed}

#------------------------------------------------------------------------
# Test that savepoints that include schema modifications are handled
# correctly. Test cases savepoint-4.*.
# 
do_test savepoint-4.1 {
  execsql {
    CREATE TABLE t2(d, e, f);
    SELECT sql FROM sqlite_master;
  }
} {{CREATE TABLE t1(a, b, c)} {CREATE TABLE t2(d, e, f)}}
do_test savepoint-4.2 {
  execsql {
    BEGIN;
    CREATE TABLE t3(g,h);
    INSERT INTO t3 VALUES('I', 'II');
    SAVEPOINT one;
    DROP TABLE t3;
  }
} {}
do_test savepoint-4.3 {
  execsql {
    CREATE TABLE t3(g, h, i);
    INSERT INTO t3 VALUES('III', 'IV', 'V');
  }
  execsql {SELECT * FROM t3}
} {III IV V}
do_test savepoint-4.4 {
  execsql { ROLLBACK TO one; }
  execsql {SELECT * FROM t3}
} {I II}
do_test savepoint-4.5 {
  execsql {
    ROLLBACK;
    SELECT sql FROM sqlite_master;
  }
} {{CREATE TABLE t1(a, b, c)} {CREATE TABLE t2(d, e, f)}}

do_test savepoint-4.6 {
  execsql {
    BEGIN;
    INSERT INTO t1 VALUES('o', 't', 't');
    SAVEPOINT sp1;
    CREATE TABLE t3(a, b, c);
    INSERT INTO t3 VALUES('z', 'y', 'x');
  }
  execsql {SELECT * FROM t3}
} {z y x}
do_test savepoint-4.7 {
  execsql {
    ROLLBACK TO sp1;
    CREATE TABLE t3(a);
    INSERT INTO t3 VALUES('value');
  }
  execsql {SELECT * FROM t3}
} {value}
do_test savepoint-4.8 {
  execsql COMMIT
} {}

finish_test

Changes to tool/mkkeywordhash.c.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
229
230
231
232
233
234
235

236
237
238
239
240
241

242
243
244
245
246
247
248
** A header comment placed at the beginning of generated code.
*/
static const char zHdr[] = 
  "/***** This file contains automatically generated code ******\n"
  "**\n"
  "** The code in this file has been automatically generated by\n"
  "**\n"
  "**     $Header: /home/drh/sqlite/trans/cvs/sqlite/sqlite/tool/mkkeywordhash.c,v 1.34 2008/12/10 20:11:01 shane Exp $\n"
  "**\n"
  "** The code in this file implements a function that determines whether\n"
  "** or not a given identifier is really an SQL keyword.  The same thing\n"
  "** might be implemented more directly using a hand-written hash table.\n"
  "** But by using this automatically generated code, the size of the code\n"
  "** is substantially reduced.  This is important for embedded applications\n"
  "** on platforms with limited memory.\n"
................................................................................
  { "PRAGMA",           "TK_PRAGMA",       PRAGMA                 },
  { "PRIMARY",          "TK_PRIMARY",      ALWAYS                 },
  { "QUERY",            "TK_QUERY",        EXPLAIN                },
  { "RAISE",            "TK_RAISE",        TRIGGER                },
  { "REFERENCES",       "TK_REFERENCES",   FKEY                   },
  { "REGEXP",           "TK_LIKE_KW",      ALWAYS                 },
  { "REINDEX",          "TK_REINDEX",      REINDEX                },

  { "RENAME",           "TK_RENAME",       ALTER                  },
  { "REPLACE",          "TK_REPLACE",      CONFLICT               },
  { "RESTRICT",         "TK_RESTRICT",     FKEY                   },
  { "RIGHT",            "TK_JOIN_KW",      ALWAYS                 },
  { "ROLLBACK",         "TK_ROLLBACK",     ALWAYS                 },
  { "ROW",              "TK_ROW",          TRIGGER                },

  { "SELECT",           "TK_SELECT",       ALWAYS                 },
  { "SET",              "TK_SET",          ALWAYS                 },
  { "TABLE",            "TK_TABLE",        ALWAYS                 },
  { "TEMP",             "TK_TEMP",         ALWAYS                 },
  { "TEMPORARY",        "TK_TEMP",         ALWAYS                 },
  { "THEN",             "TK_THEN",         ALWAYS                 },
  { "TO",               "TK_TO",           ALTER                  },







|







 







>






>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
** A header comment placed at the beginning of generated code.
*/
static const char zHdr[] = 
  "/***** This file contains automatically generated code ******\n"
  "**\n"
  "** The code in this file has been automatically generated by\n"
  "**\n"
  "**     $Header: /home/drh/sqlite/trans/cvs/sqlite/sqlite/tool/mkkeywordhash.c,v 1.35 2008/12/17 17:30:26 danielk1977 Exp $\n"
  "**\n"
  "** The code in this file implements a function that determines whether\n"
  "** or not a given identifier is really an SQL keyword.  The same thing\n"
  "** might be implemented more directly using a hand-written hash table.\n"
  "** But by using this automatically generated code, the size of the code\n"
  "** is substantially reduced.  This is important for embedded applications\n"
  "** on platforms with limited memory.\n"
................................................................................
  { "PRAGMA",           "TK_PRAGMA",       PRAGMA                 },
  { "PRIMARY",          "TK_PRIMARY",      ALWAYS                 },
  { "QUERY",            "TK_QUERY",        EXPLAIN                },
  { "RAISE",            "TK_RAISE",        TRIGGER                },
  { "REFERENCES",       "TK_REFERENCES",   FKEY                   },
  { "REGEXP",           "TK_LIKE_KW",      ALWAYS                 },
  { "REINDEX",          "TK_REINDEX",      REINDEX                },
  { "RELEASE",          "TK_RELEASE",      ALWAYS                 },
  { "RENAME",           "TK_RENAME",       ALTER                  },
  { "REPLACE",          "TK_REPLACE",      CONFLICT               },
  { "RESTRICT",         "TK_RESTRICT",     FKEY                   },
  { "RIGHT",            "TK_JOIN_KW",      ALWAYS                 },
  { "ROLLBACK",         "TK_ROLLBACK",     ALWAYS                 },
  { "ROW",              "TK_ROW",          TRIGGER                },
  { "SAVEPOINT",        "TK_SAVEPOINT",    ALWAYS                 },
  { "SELECT",           "TK_SELECT",       ALWAYS                 },
  { "SET",              "TK_SET",          ALWAYS                 },
  { "TABLE",            "TK_TABLE",        ALWAYS                 },
  { "TEMP",             "TK_TEMP",         ALWAYS                 },
  { "TEMPORARY",        "TK_TEMP",         ALWAYS                 },
  { "THEN",             "TK_THEN",         ALWAYS                 },
  { "TO",               "TK_TO",           ALTER                  },