/ Check-in [a60f4191]
Login

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

Overview
Comment:Fix some cases where executing SQL from within a user-function callback could cause problems related to statement-transactions. (CVS 6355)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: a60f4191791dd7bb49d5c95b350a9924845b59a8
User & Date: danielk1977 2009-03-18 10:33:01
Context
2009-03-18
10:36
Add comments and testcase() macros to the fix for shared-cache schema default value problem of check-in (6353). (CVS 6356) check-in: 05d8607d user: drh tags: trunk
10:33
Fix some cases where executing SQL from within a user-function callback could cause problems related to statement-transactions. (CVS 6355) check-in: a60f4191 user: danielk1977 tags: trunk
2009-03-17
22:33
Move the rowid cache out of VdbeCursor and into BtCursor. When multiple BtCursors are open on the same table, set their rowid cache all at the same time. Ticket #3731. (CVS 6354) check-in: 18978583 user: drh 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
....
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
....
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
....
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
....
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824







2825
2826
2827
2828
2829
2830
2831
2832
2833


2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
....
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
....
7397
7398
7399
7400
7401
7402
7403
7404
7405
7406
7407
7408
7409
7410
7411
7412
7413
7414
7415
7416
7417
7418
** 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.574 2009/03/17 22:33:01 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"

................................................................................
        pPage->pBt = pBt;
        pPage->pgno = 1;
      }
#endif
      releasePage(pBt->pPage1);
    }
    pBt->pPage1 = 0;
    pBt->inStmt = 0;
  }
}

/*
** Create a new database by initializing the first page of the
** file.
*/
................................................................................
        rc = sqlite3PagerBegin(pBt->pPager, wrflag>1);
        if( rc==SQLITE_OK ){
          rc = newDatabase(pBt);
        }
      }
    }
  
    if( rc==SQLITE_OK ){
      if( wrflag ) pBt->inStmt = 0;
    }else{
      unlockBtreeIfUnused(pBt);
    }
  }while( rc==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
          btreeInvokeBusyHandler(pBt) );

  if( rc==SQLITE_OK ){
    if( p->inTrans==TRANS_NONE ){
................................................................................
    assert( pBt->nTransaction>0 );
    rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
    if( rc!=SQLITE_OK ){
      sqlite3BtreeLeave(p);
      return rc;
    }
    pBt->inTransaction = TRANS_READ;
    pBt->inStmt = 0;
  }
  clearAllSharedCacheTableLocks(p);

  /* If the handle has any kind of transaction open, decrement the transaction
  ** count of the shared btree. If the transaction count reaches 0, set
  ** the shared state to TRANS_NONE. The unlockBtreeIfUnused() call below
  ** will unlock the pager.
................................................................................
    if( 0==pBt->nTransaction ){
      pBt->inTransaction = TRANS_NONE;
    }
  }

  btreeClearHasContent(pBt);
  p->inTrans = TRANS_NONE;
  pBt->inStmt = 0;
  unlockBtreeIfUnused(pBt);

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

/*
** Start a statement subtransaction.  The subtransaction can
** can be rolled back independently of the main transaction.
** You must start a transaction before starting a subtransaction.
** The subtransaction is ended automatically if the main transaction
** commits or rolls back.
**
** Only one subtransaction may be active at a time.  It is an error to try
** to start a new subtransaction if another subtransaction is already active.
**
** Statement subtransactions are used around individual SQL statements
** that are contained within a BEGIN...COMMIT block.  If a constraint
** error occurs within the statement, the effect of that one statement
** can be rolled back without having to rollback the entire transaction.







*/
int sqlite3BtreeBeginStmt(Btree *p){
  int rc;
  BtShared *pBt = p->pBt;
  sqlite3BtreeEnter(p);
  pBt->db = p->db;
  assert( p->inTrans==TRANS_WRITE );
  assert( !pBt->inStmt );
  assert( pBt->readOnly==0 );


  if( NEVER(p->inTrans!=TRANS_WRITE || pBt->inStmt || pBt->readOnly) ){
    rc = SQLITE_INTERNAL;
  }else{
    assert( pBt->inTransaction==TRANS_WRITE );
    /* 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.
    */
    rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint+1);
    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 = SQLITE_OK;
  BtShared *pBt = p->pBt;
  sqlite3BtreeEnter(p);
  pBt->db = p->db;
  if( p->inTrans==TRANS_WRITE && pBt->inStmt ){
    int iStmtpoint = p->db->nSavepoint;
    assert( pBt->readOnly==0 );
    rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_RELEASE, iStmtpoint);
    pBt->inStmt = 0;
  }
  sqlite3BtreeLeave(p);
  return rc;
}

/*
** Rollback the active statement subtransaction.  If no subtransaction
** is active this routine is a no-op.
**
** All cursors will be invalidated by this operation.  Any attempt
** to use a cursor that was open at the beginning of this operation
** will result in an error.
*/
int sqlite3BtreeRollbackStmt(Btree *p){
  int rc = SQLITE_OK;
  BtShared *pBt = p->pBt;
  sqlite3BtreeEnter(p);
  pBt->db = p->db;
  if( p->inTrans==TRANS_WRITE && pBt->inStmt ){
    int iStmtpoint = p->db->nSavepoint;
    assert( pBt->readOnly==0 );
    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
................................................................................
** from a normal transaction rollback, as no locks are released and the
** transaction remains open.
*/
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);
    if( rc==SQLITE_OK ){
      rc = newDatabase(pBt);
................................................................................
** Return non-zero if a transaction is active.
*/
int sqlite3BtreeIsInTrans(Btree *p){
  assert( p==0 || sqlite3_mutex_held(p->db->mutex) );
  return (p && (p->inTrans==TRANS_WRITE));
}

/*
** Return non-zero if a statement transaction is active.
*/
int sqlite3BtreeIsInStmt(Btree *p){
  assert( sqlite3BtreeHoldsMutex(p) );
  return ALWAYS(p->pBt) && p->pBt->inStmt;
}

/*
** Return non-zero if a read (or write) transaction is active.
*/
int sqlite3BtreeIsInReadTrans(Btree *p){
  assert( p );
  assert( sqlite3_mutex_held(p->db->mutex) );
  return p->inTrans!=TRANS_NONE;







|







 







<







 







|
<
<







 







<







 







<








|
|
|
|
<
<
<
<





>
>
>
>
>
>
>

|





<

>
>
|








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







 







<







 







<
<
<
<
<
<
<
<







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1992
1993
1994
1995
1996
1997
1998

1999
2000
2001
2002
2003
2004
2005
....
2136
2137
2138
2139
2140
2141
2142
2143


2144
2145
2146
2147
2148
2149
2150
....
2636
2637
2638
2639
2640
2641
2642

2643
2644
2645
2646
2647
2648
2649
....
2792
2793
2794
2795
2796
2797
2798

2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810




2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829

2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842














































2843
2844
2845
2846
2847
2848
2849
....
2857
2858
2859
2860
2861
2862
2863

2864
2865
2866
2867
2868
2869
2870
....
7349
7350
7351
7352
7353
7354
7355








7356
7357
7358
7359
7360
7361
7362
** 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.575 2009/03/18 10:33:01 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"

................................................................................
        pPage->pBt = pBt;
        pPage->pgno = 1;
      }
#endif
      releasePage(pBt->pPage1);
    }
    pBt->pPage1 = 0;

  }
}

/*
** Create a new database by initializing the first page of the
** file.
*/
................................................................................
        rc = sqlite3PagerBegin(pBt->pPager, wrflag>1);
        if( rc==SQLITE_OK ){
          rc = newDatabase(pBt);
        }
      }
    }
  
    if( rc!=SQLITE_OK ){


      unlockBtreeIfUnused(pBt);
    }
  }while( rc==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
          btreeInvokeBusyHandler(pBt) );

  if( rc==SQLITE_OK ){
    if( p->inTrans==TRANS_NONE ){
................................................................................
    assert( pBt->nTransaction>0 );
    rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
    if( rc!=SQLITE_OK ){
      sqlite3BtreeLeave(p);
      return rc;
    }
    pBt->inTransaction = TRANS_READ;

  }
  clearAllSharedCacheTableLocks(p);

  /* If the handle has any kind of transaction open, decrement the transaction
  ** count of the shared btree. If the transaction count reaches 0, set
  ** the shared state to TRANS_NONE. The unlockBtreeIfUnused() call below
  ** will unlock the pager.
................................................................................
    if( 0==pBt->nTransaction ){
      pBt->inTransaction = TRANS_NONE;
    }
  }

  btreeClearHasContent(pBt);
  p->inTrans = TRANS_NONE;

  unlockBtreeIfUnused(pBt);

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

/*
** Start a statement subtransaction. The subtransaction can can be rolled
** back independently of the main transaction. You must start a transaction 
** before starting a subtransaction. The subtransaction is ended automatically 
** if the main transaction commits or rolls back.




**
** Statement subtransactions are used around individual SQL statements
** that are contained within a BEGIN...COMMIT block.  If a constraint
** error occurs within the statement, the effect of that one statement
** can be rolled back without having to rollback the entire transaction.
**
** A statement sub-transaction is implemented as an anonymous savepoint. The
** value passed as the second parameter is the total number of savepoints,
** including the new anonymous savepoint, open on the B-Tree. i.e. if there
** are no active savepoints and no other statement-transactions open,
** iStatement is 1. This anonymous savepoint can be released or rolled back
** using the sqlite3BtreeSavepoint() function.
*/
int sqlite3BtreeBeginStmt(Btree *p, int iStatement){
  int rc;
  BtShared *pBt = p->pBt;
  sqlite3BtreeEnter(p);
  pBt->db = p->db;
  assert( p->inTrans==TRANS_WRITE );

  assert( pBt->readOnly==0 );
  assert( iStatement>0 );
  assert( iStatement>p->db->nSavepoint );
  if( NEVER(p->inTrans!=TRANS_WRITE || pBt->readOnly) ){
    rc = SQLITE_INTERNAL;
  }else{
    assert( pBt->inTransaction==TRANS_WRITE );
    /* 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.
    */
    rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStatement);














































  }
  sqlite3BtreeLeave(p);
  return rc;
}

/*
** The second argument to this function, op, is always SAVEPOINT_ROLLBACK
................................................................................
** from a normal transaction rollback, as no locks are released and the
** transaction remains open.
*/
int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
  int rc = SQLITE_OK;
  if( p && p->inTrans==TRANS_WRITE ){
    BtShared *pBt = p->pBt;

    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);
    if( rc==SQLITE_OK ){
      rc = newDatabase(pBt);
................................................................................
** Return non-zero if a transaction is active.
*/
int sqlite3BtreeIsInTrans(Btree *p){
  assert( p==0 || sqlite3_mutex_held(p->db->mutex) );
  return (p && (p->inTrans==TRANS_WRITE));
}









/*
** Return non-zero if a read (or write) transaction is active.
*/
int sqlite3BtreeIsInReadTrans(Btree *p){
  assert( p );
  assert( sqlite3_mutex_held(p->db->mutex) );
  return p->inTrans!=TRANS_NONE;

Changes to src/btree.h.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
**    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.110 2009/03/17 22:33:01 drh Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_

/* TODO: This definition is just included so other modules compile. It
** needs to be revisited.
*/
................................................................................
int sqlite3BtreeSetAutoVacuum(Btree *, int);
int sqlite3BtreeGetAutoVacuum(Btree *);
int sqlite3BtreeBeginTrans(Btree*,int);
int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
int sqlite3BtreeCommitPhaseTwo(Btree*);
int sqlite3BtreeCommit(Btree*);
int sqlite3BtreeRollback(Btree*);
int sqlite3BtreeBeginStmt(Btree*);
int sqlite3BtreeCommitStmt(Btree*);
int sqlite3BtreeRollbackStmt(Btree*);
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
int sqlite3BtreeIsInTrans(Btree*);
int sqlite3BtreeIsInStmt(Btree*);
int sqlite3BtreeIsInReadTrans(Btree*);
int sqlite3BtreeIsInBackup(Btree*);
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
int sqlite3BtreeSchemaLocked(Btree *);
int sqlite3BtreeLockTable(Btree *, int, u8);
int sqlite3BtreeSavepoint(Btree *, int, int);








|







 







|
<
<


<







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
87
88
89
90
91
92
93
94


95
96

97
98
99
100
101
102
103
**    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.111 2009/03/18 10:33:01 danielk1977 Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_

/* TODO: This definition is just included so other modules compile. It
** needs to be revisited.
*/
................................................................................
int sqlite3BtreeSetAutoVacuum(Btree *, int);
int sqlite3BtreeGetAutoVacuum(Btree *);
int sqlite3BtreeBeginTrans(Btree*,int);
int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
int sqlite3BtreeCommitPhaseTwo(Btree*);
int sqlite3BtreeCommit(Btree*);
int sqlite3BtreeRollback(Btree*);
int sqlite3BtreeBeginStmt(Btree*,int);


int sqlite3BtreeCreateTable(Btree*, int*, int flags);
int sqlite3BtreeIsInTrans(Btree*);

int sqlite3BtreeIsInReadTrans(Btree*);
int sqlite3BtreeIsInBackup(Btree*);
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
int sqlite3BtreeSchemaLocked(Btree *);
int sqlite3BtreeLockTable(Btree *, int, u8);
int sqlite3BtreeSavepoint(Btree *, int, int);

Changes to src/btreeInt.h.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
** 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: btreeInt.h,v 1.44 2009/03/17 22:33:01 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
**   This feature is included to help prevent writer-starvation.
*/
struct BtShared {
  Pager *pPager;        /* The page cache */
  sqlite3 *db;          /* Database connection currently using this Btree */
  BtCursor *pCursor;    /* A list of all open cursors */
  MemPage *pPage1;      /* First page of the database */
  u8 inStmt;            /* True if we are in a statement subtransaction */
  u8 readOnly;          /* True if the underlying file is readonly */
  u8 pageSizeFixed;     /* True if the page size can no longer be changed */
#ifndef SQLITE_OMIT_AUTOVACUUM
  u8 autoVacuum;        /* True if auto-vacuum is enabled */
  u8 incrVacuum;        /* True if incr-vacuum is enabled */
#endif
  u16 pageSize;         /* Total number of bytes on a page */







|







 







<







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
376
377
378
379
380
381
382

383
384
385
386
387
388
389
** 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: btreeInt.h,v 1.45 2009/03/18 10:33:01 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
**   This feature is included to help prevent writer-starvation.
*/
struct BtShared {
  Pager *pPager;        /* The page cache */
  sqlite3 *db;          /* Database connection currently using this Btree */
  BtCursor *pCursor;    /* A list of all open cursors */
  MemPage *pPage1;      /* First page of the database */

  u8 readOnly;          /* True if the underlying file is readonly */
  u8 pageSizeFixed;     /* True if the page size can no longer be changed */
#ifndef SQLITE_OMIT_AUTOVACUUM
  u8 autoVacuum;        /* True if auto-vacuum is enabled */
  u8 incrVacuum;        /* True if incr-vacuum is enabled */
#endif
  u16 pageSize;         /* Total number of bytes on a page */

Changes to src/main.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
556
557
558
559
560
561
562

563
564
565
566
567
568
569
**
*************************************************************************
** 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.531 2009/03/16 13:19:36 danielk1977 Exp $
*/
#include "sqliteInt.h"

#ifdef SQLITE_ENABLE_FTS3
# include "fts3.h"
#endif
#ifdef SQLITE_ENABLE_RTREE
................................................................................
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){







|







 







>







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
**
*************************************************************************
** 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.532 2009/03/18 10:33:01 danielk1977 Exp $
*/
#include "sqliteInt.h"

#ifdef SQLITE_ENABLE_FTS3
# include "fts3.h"
#endif
#ifdef SQLITE_ENABLE_RTREE
................................................................................
void sqlite3CloseSavepoints(sqlite3 *db){
  while( db->pSavepoint ){
    Savepoint *pTmp = db->pSavepoint;
    db->pSavepoint = pTmp->pNext;
    sqlite3DbFree(db, pTmp);
  }
  db->nSavepoint = 0;
  db->nStatement = 0;
  db->isTransactionSavepoint = 0;
}

/*
** Close an existing SQLite database
*/
int sqlite3_close(sqlite3 *db){

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
803
804
805
806
807
808
809

810
811
812
813
814
815
816
**    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.842 2009/03/17 17:49:00 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Include the configuration header output by 'configure' if we're using the
** autoconf-based build
................................................................................
  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 */

#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
  /* The following variables are all protected by the STATIC_MASTER 
  ** mutex, not by sqlite3.mutex. They are used by code in notify.c. 
  */
  sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */







|







 







>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
**    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.843 2009/03/18 10:33:01 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Include the configuration header output by 'configure' if we're using the
** autoconf-based build
................................................................................
  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 */
  int nStatement;               /* Number of nested statement-transactions  */
  u8 isTransactionSavepoint;    /* True if the outermost savepoint is a TS */

#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
  /* The following variables are all protected by the STATIC_MASTER 
  ** mutex, not by sqlite3.mutex. They are used by code in notify.c. 
  */
  sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */

Changes to src/test3.c.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
...
260
261
262
263
264
265
266


267

268
269
270
271
272
273
274
...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the btree.c module in SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test3.c,v 1.102 2008/10/27 13:59:34 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "btreeInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>

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


  rc = sqlite3BtreeRollbackStmt(pBt);

  sqlite3BtreeLeave(pBt);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  return TCL_OK;
}
................................................................................
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID\"", 0);
    return TCL_ERROR;
  }
  pBt = sqlite3TestTextToPtr(argv[1]);
  sqlite3BtreeEnter(pBt);
  rc = sqlite3BtreeCommitStmt(pBt);
  sqlite3BtreeLeave(pBt);
  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
...
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
...
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the btree.c module in SQLite.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test3.c,v 1.103 2009/03/18 10:33:02 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "btreeInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>

................................................................................
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID\"", 0);
    return TCL_ERROR;
  }
  pBt = sqlite3TestTextToPtr(argv[1]);
  sqlite3BtreeEnter(pBt);
  rc = sqlite3BtreeBeginStmt(pBt, 1);
  sqlite3BtreeLeave(pBt);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  return TCL_OK;
}
................................................................................
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID\"", 0);
    return TCL_ERROR;
  }
  pBt = sqlite3TestTextToPtr(argv[1]);
  sqlite3BtreeEnter(pBt);
  rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, 0);
  if( rc==SQLITE_OK ){
    rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, 0);
  }
  sqlite3BtreeLeave(pBt);
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, errorName(rc), 0);
    return TCL_ERROR;
  }
  return TCL_OK;
}
................................................................................
  if( argc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID\"", 0);
    return TCL_ERROR;
  }
  pBt = sqlite3TestTextToPtr(argv[1]);
  sqlite3BtreeEnter(pBt);
  rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, 0);
  sqlite3BtreeLeave(pBt);
  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
....
1081
1082
1083
1084
1085
1086
1087

















1088
1089
1090
1091
1092
1093
1094
....
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391

2392

2393
2394
2395
2396
2397
2398
2399
....
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
....
2572
2573
2574
2575
2576
2577
2578

2579
2580
2581
2582
2583
2584
2585
**
** 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.826 2009/03/17 22:33:01 drh Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"

/*
** The following global variable is incremented every time a cursor
** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes.  The test
................................................................................
*/
case OP_ResultRow: {
  Mem *pMem;
  int i;
  assert( p->nResColumn==pOp->p2 );
  assert( pOp->p1>0 );
  assert( pOp->p1+pOp->p2<=p->nMem+1 );


















  /* Invalidate all ephemeral cursor row caches */
  p->cacheCtr = (p->cacheCtr + 2)|1;

  /* Make sure the results of the current row are \000 terminated
  ** and have an assigned type.  The results are de-ephemeralized as
  ** as side effect.
................................................................................
    int i = pOp->p1;
    Btree *pBt;
    assert( i>=0 && i<db->nDb );
    assert( db->aDb[i].pBt!=0 );
    pBt = db->aDb[i].pBt;
    assert( sqlite3BtreeIsInTrans(pBt) );
    assert( (p->btreeMask & (1<<i))!=0 );
    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
................................................................................
      }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)!=0 ){
          sqlite3ExpirePreparedStatements(db);
          sqlite3ResetInternalSchema(db, 0);
        }
      }
  
................................................................................
      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;







|







 







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







 







|
|
|
>

>







 







|







 







>







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
....
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
....
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
....
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
....
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
**
** 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.827 2009/03/18 10:33:02 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"

/*
** The following global variable is incremented every time a cursor
** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes.  The test
................................................................................
*/
case OP_ResultRow: {
  Mem *pMem;
  int i;
  assert( p->nResColumn==pOp->p2 );
  assert( pOp->p1>0 );
  assert( pOp->p1+pOp->p2<=p->nMem+1 );

  /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then 
  ** DML statements invoke this opcode to return the number of rows 
  ** modified to the user. This is the only way that a VM that
  ** opens a statement transaction may invoke this opcode.
  **
  ** In case this is such a statement, close any statement transaction
  ** opened by this VM before returning control to the user. This is to
  ** ensure that statement-transactions are always nested, not overlapping.
  ** If the open statement-transaction is not closed here, then the user
  ** may step another VM that opens its own statement transaction. This
  ** may lead to overlapping statement transactions.
  */
  assert( p->iStatement==0 || db->flags&SQLITE_CountRows );
  if( SQLITE_OK!=(rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE)) ){
    break;
  }

  /* Invalidate all ephemeral cursor row caches */
  p->cacheCtr = (p->cacheCtr + 2)|1;

  /* Make sure the results of the current row are \000 terminated
  ** and have an assigned type.  The results are de-ephemeralized as
  ** as side effect.
................................................................................
    int i = pOp->p1;
    Btree *pBt;
    assert( i>=0 && i<db->nDb );
    assert( db->aDb[i].pBt!=0 );
    pBt = db->aDb[i].pBt;
    assert( sqlite3BtreeIsInTrans(pBt) );
    assert( (p->btreeMask & (1<<i))!=0 );
    if( p->iStatement==0 ){
      assert( db->nStatement>=0 && db->nSavepoint>=0 );
      db->nStatement++; 
      p->iStatement = db->nSavepoint + db->nStatement;
    }
    rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
  }
  break;
}

/* Opcode: Savepoint P1 * * P4 *
**
** Open, release or rollback the savepoint named by parameter P4, depending
................................................................................
      }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)!=0 ){
          sqlite3ExpirePreparedStatements(db);
          sqlite3ResetInternalSchema(db, 0);
        }
      }
  
................................................................................
      if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
        p->pc = pc;
        db->autoCommit = (u8)(1-desiredAutoCommit);
        p->rc = rc = SQLITE_BUSY;
        goto vdbe_return;
      }
    }
    assert( db->nStatement==0 );
    sqlite3CloseSavepoints(db);
    if( p->rc==SQLITE_OK ){
      rc = SQLITE_DONE;
    }else{
      rc = SQLITE_ERROR;
    }
    goto vdbe_return;

Changes to src/vdbeInt.h.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
...
370
371
372
373
374
375
376

377
378
379
380
381
382
383
*************************************************************************
** This is the header file for information that is private to the
** VDBE.  This information used to all be at the top of the single
** source code file "vdbe.c".  When that file became too big (over
** 6000 lines long) it was split up into several smaller files and
** this header information was factored out.
**
** $Id: vdbeInt.h,v 1.165 2009/03/17 22:33:01 drh Exp $
*/
#ifndef _VDBEINT_H_
#define _VDBEINT_H_

/*
** intToKey() and keyToInt() used to transform the rowid.  But with
** the latest versions of the design they are no-ops.
................................................................................
  BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */
  int aCounter[2];        /* Counters used by sqlite3_stmt_status() */
  char *zSql;           /* Text of the SQL statement that generated this */
  void *pFree;            /* Free this when deleting the vdbe */
#ifdef SQLITE_DEBUG
  FILE *trace;          /* Write an execution trace here, if not NULL */
#endif
  int openedStatement;  /* True if this VM has opened a statement journal */
#ifdef SQLITE_SSE
  int fetchId;          /* Statement number used by sqlite3_fetch_statement */
  int lru;              /* Counter used for LRU cache replacement */
#endif
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  Vdbe *pLruPrev;
  Vdbe *pLruNext;
................................................................................
int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
void sqlite3VdbeMemRelease(Mem *p);
void sqlite3VdbeMemReleaseExternal(Mem *p);
int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
const char *sqlite3OpcodeName(int);
int sqlite3VdbeOpcodeHasProperty(int, int);
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);

#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
int sqlite3VdbeReleaseBuffers(Vdbe *p);
#endif

int sqlite3VdbeMemTranslate(Mem*, u8);
#ifdef SQLITE_DEBUG
  void sqlite3VdbePrintSql(Vdbe*);







|







 







|







 







>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
...
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
*************************************************************************
** This is the header file for information that is private to the
** VDBE.  This information used to all be at the top of the single
** source code file "vdbe.c".  When that file became too big (over
** 6000 lines long) it was split up into several smaller files and
** this header information was factored out.
**
** $Id: vdbeInt.h,v 1.166 2009/03/18 10:33:02 danielk1977 Exp $
*/
#ifndef _VDBEINT_H_
#define _VDBEINT_H_

/*
** intToKey() and keyToInt() used to transform the rowid.  But with
** the latest versions of the design they are no-ops.
................................................................................
  BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */
  int aCounter[2];        /* Counters used by sqlite3_stmt_status() */
  char *zSql;           /* Text of the SQL statement that generated this */
  void *pFree;            /* Free this when deleting the vdbe */
#ifdef SQLITE_DEBUG
  FILE *trace;          /* Write an execution trace here, if not NULL */
#endif
  int iStatement;         /* Statement number (or 0 if has not opened stmt) */
#ifdef SQLITE_SSE
  int fetchId;          /* Statement number used by sqlite3_fetch_statement */
  int lru;              /* Counter used for LRU cache replacement */
#endif
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  Vdbe *pLruPrev;
  Vdbe *pLruNext;
................................................................................
int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
void sqlite3VdbeMemRelease(Mem *p);
void sqlite3VdbeMemReleaseExternal(Mem *p);
int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
const char *sqlite3OpcodeName(int);
int sqlite3VdbeOpcodeHasProperty(int, int);
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
int sqlite3VdbeCloseStatement(Vdbe *, int);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
int sqlite3VdbeReleaseBuffers(Vdbe *p);
#endif

int sqlite3VdbeMemTranslate(Mem*, u8);
#ifdef SQLITE_DEBUG
  void sqlite3VdbePrintSql(Vdbe*);

Changes to src/vdbeaux.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
....
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
....
1559
1560
1561
1562
1563
1564
1565










































1566
1567
1568
1569
1570
1571
1572
....
1574
1575
1576
1577
1578
1579
1580

1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
....
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
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692

1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712

1713
1714
1715

1716
1717
1718
1719
1720
1721
1722
....
1748
1749
1750
1751
1752
1753
1754

1755
1756
1757
1758
1759
1760
1761
**
*************************************************************************
** This file contains code used for creating, destroying, and populating
** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.)  Prior
** to version 2.8.7, all this code was combined into the vdbe.c source file.
** But that file was getting too big so this subroutines were split out.
**
** $Id: vdbeaux.c,v 1.442 2009/03/16 13:19:36 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"



/*
................................................................................
  p->rc = SQLITE_OK;
  p->errorAction = OE_Abort;
  p->explain |= isExplain;
  p->magic = VDBE_MAGIC_RUN;
  p->nChange = 0;
  p->cacheCtr = 1;
  p->minWriteFileFormat = 255;
  p->openedStatement = 0;
#ifdef VDBE_PROFILE
  {
    int i;
    for(i=0; i<p->nOp; i++){
      p->aOp[i].cnt = 0;
      p->aOp[i].cycles = 0;
    }
................................................................................
  for(i=0; i<db->nDb; i++){
    Btree *p = db->aDb[i].pBt;
    if( p && sqlite3BtreeIsInTrans(p) ){
      sqlite3BtreeTripAllCursors(p, SQLITE_ABORT);
    }
  }
}











































/*
** This routine is called the when a VDBE tries to halt.  If the VDBE
** has made changes and is in autocommit mode, then commit those
** changes.  If a rollback is needed, then do the rollback.
**
** This routine is the only way to move the state of a VM from
................................................................................
** call this on a VM that is in the SQLITE_MAGIC_HALT state.
**
** Return an error code.  If the commit could not complete because of
** lock contention, return SQLITE_BUSY.  If SQLITE_BUSY is returned, it
** means the close did not happen and needs to be repeated.
*/
int sqlite3VdbeHalt(Vdbe *p){

  sqlite3 *db = p->db;
  int i;
  int (*xFunc)(Btree *pBt) = 0;  /* Function to call on each btree backend */
  int isSpecialError;            /* Set to true if SQLITE_NOMEM or IOERR */

  /* This function contains the logic that determines if a statement or
  ** transaction will be committed or rolled back as a result of the
  ** execution of this virtual machine. 
  **
  ** If any of the following errors occur:
  **
................................................................................
    return SQLITE_OK;
  }
  checkActiveVdbeCnt(db);

  /* No commit or rollback needed if the program never started */
  if( p->pc>=0 ){
    int mrc;   /* Primary error code from p->rc */



    /* Lock all btrees used by the statement */
    sqlite3BtreeMutexArrayEnter(&p->aMutex);

    /* Check for one of the special errors */
    mrc = p->rc & 0xff;
    isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
................................................................................
                     || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
    if( isSpecialError ){
      /* If the query was read-only, we need do no rollback at all. Otherwise,
      ** proceed with the special handling.
      */
      if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
        if( p->rc==SQLITE_IOERR_BLOCKED && p->usesStmtJournal ){
          xFunc = sqlite3BtreeRollbackStmt;
          p->rc = SQLITE_BUSY;
        }else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL)
                   && p->usesStmtJournal ){
          xFunc = sqlite3BtreeRollbackStmt;
        }else{
          /* We are forced to roll back the active transaction. Before doing
          ** so, abort any other statements this handle currently has active.
          */
          invalidateCursorsOnModifiedBtrees(db);
          sqlite3RollbackAll(db);
          sqlite3CloseSavepoints(db);
          db->autoCommit = 1;
        }
      }
    }
  
    /* If the auto-commit flag is set and this is the only active vdbe, then
    ** we do either a commit or rollback of the current transaction. 
    **
    ** Note: This block also runs if one of the special errors handled 
    ** above has occurred. 
    */
    if( !sqlite3VtabInSync(db) 
     && db->autoCommit 
     && db->writeVdbeCnt==(p->readOnly==0) 
................................................................................
     && (db->flags & SQLITE_CommitBusy)==0
    ){
      if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
        /* The auto-commit flag is true, and the vdbe program was 
        ** successful or hit an 'OR FAIL' constraint. This means a commit 
        ** is required.
        */
        int rc = vdbeCommit(db, p);
        if( rc==SQLITE_BUSY ){
          sqlite3BtreeMutexArrayLeave(&p->aMutex);
          return SQLITE_BUSY;
        }else if( rc!=SQLITE_OK ){
          p->rc = rc;
          sqlite3RollbackAll(db);
        }else{
          sqlite3CommitInternalChanges(db);
        }
      }else{
        sqlite3RollbackAll(db);
      }
    }else if( !xFunc ){
      if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
        if( p->openedStatement ){
          xFunc = sqlite3BtreeCommitStmt;
        } 
      }else if( p->errorAction==OE_Abort ){
        xFunc = sqlite3BtreeRollbackStmt;
      }else{
        invalidateCursorsOnModifiedBtrees(db);
        sqlite3RollbackAll(db);
        sqlite3CloseSavepoints(db);
        db->autoCommit = 1;
      }
    }
  
    /* If xFunc is not NULL, then it is one of sqlite3BtreeRollbackStmt or
    ** sqlite3BtreeCommitStmt. Call it once on each backend. If an error occurs
    ** and the return code is still SQLITE_OK, set the return code to the new
    ** error value.

    */
    assert(!xFunc ||
      xFunc==sqlite3BtreeCommitStmt ||
      xFunc==sqlite3BtreeRollbackStmt
    );
    for(i=0; xFunc && i<db->nDb; i++){ 
      int rc;
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        rc = xFunc(pBt);
        if( rc && (p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT) ){
          p->rc = rc;
          sqlite3DbFree(db, p->zErrMsg);
          p->zErrMsg = 0;
        }
      }
    }
  
    /* If this was an INSERT, UPDATE or DELETE and the statement was committed, 
    ** set the change counter. 

    */
    if( p->changeCntOn && p->pc>=0 ){
      if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){

        sqlite3VdbeSetChanges(db, p->nChange);
      }else{
        sqlite3VdbeSetChanges(db, 0);
      }
      p->nChange = 0;
    }
  
................................................................................
  ** by connection db have now been released. Call sqlite3ConnectionUnlocked() 
  ** to invoke any required unlock-notify callbacks.
  */
  if( db->autoCommit ){
    sqlite3ConnectionUnlocked(db);
  }


  return SQLITE_OK;
}


/*
** Each VDBE holds the result of the most recent sqlite3_step() call
** in p->rc.  This routine sets that result back to SQLITE_OK.







|







 







|







 







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







 







>

<
<
<







 







>
>







 







|



|












|
|







 







|












|
|
|
|
|
|
<








|
|
|
|
>

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


<
>







 







>







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
....
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
....
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
....
1616
1617
1618
1619
1620
1621
1622
1623
1624



1625
1626
1627
1628
1629
1630
1631
....
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
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
....
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721

1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735

1736
1737






1738
1739
1740
1741
1742
1743
1744

1745

1746
1747
1748

1749
1750
1751
1752
1753
1754
1755
1756
....
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
**
*************************************************************************
** This file contains code used for creating, destroying, and populating
** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.)  Prior
** to version 2.8.7, all this code was combined into the vdbe.c source file.
** But that file was getting too big so this subroutines were split out.
**
** $Id: vdbeaux.c,v 1.443 2009/03/18 10:33:02 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"



/*
................................................................................
  p->rc = SQLITE_OK;
  p->errorAction = OE_Abort;
  p->explain |= isExplain;
  p->magic = VDBE_MAGIC_RUN;
  p->nChange = 0;
  p->cacheCtr = 1;
  p->minWriteFileFormat = 255;
  p->iStatement = 0;
#ifdef VDBE_PROFILE
  {
    int i;
    for(i=0; i<p->nOp; i++){
      p->aOp[i].cnt = 0;
      p->aOp[i].cycles = 0;
    }
................................................................................
  for(i=0; i<db->nDb; i++){
    Btree *p = db->aDb[i].pBt;
    if( p && sqlite3BtreeIsInTrans(p) ){
      sqlite3BtreeTripAllCursors(p, SQLITE_ABORT);
    }
  }
}

/*
** If the Vdbe passed as the first argument opened a statement-transaction,
** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or
** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement
** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the 
** statement transaction is commtted.
**
** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned. 
** Otherwise SQLITE_OK.
*/
int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
  int rc = SQLITE_OK;
  if( p->iStatement ){
    int i;
    const int iSavepoint = p->iStatement-1;
    sqlite3 *const db = p->db;

    assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE);
    assert( db->nStatement>0 );
    assert( p->iStatement==(db->nStatement+db->nSavepoint) );

    for(i=0; i<db->nDb; i++){ 
      int rc2 = SQLITE_OK;
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        if( eOp==SAVEPOINT_ROLLBACK ){
          rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, iSavepoint);
        }
        if( rc2==SQLITE_OK ){
          rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, iSavepoint);
        }
        if( rc==SQLITE_OK ){
          rc = rc2;
        }
      }
    }
    db->nStatement--;
    p->iStatement = 0;
  }
  return rc;
}

/*
** This routine is called the when a VDBE tries to halt.  If the VDBE
** has made changes and is in autocommit mode, then commit those
** changes.  If a rollback is needed, then do the rollback.
**
** This routine is the only way to move the state of a VM from
................................................................................
** call this on a VM that is in the SQLITE_MAGIC_HALT state.
**
** Return an error code.  If the commit could not complete because of
** lock contention, return SQLITE_BUSY.  If SQLITE_BUSY is returned, it
** means the close did not happen and needs to be repeated.
*/
int sqlite3VdbeHalt(Vdbe *p){
  int rc;                         /* Used to store transient return codes */
  sqlite3 *db = p->db;




  /* This function contains the logic that determines if a statement or
  ** transaction will be committed or rolled back as a result of the
  ** execution of this virtual machine. 
  **
  ** If any of the following errors occur:
  **
................................................................................
    return SQLITE_OK;
  }
  checkActiveVdbeCnt(db);

  /* No commit or rollback needed if the program never started */
  if( p->pc>=0 ){
    int mrc;   /* Primary error code from p->rc */
    int eStatementOp = 0;
    int isSpecialError;            /* Set to true if a 'special' error */

    /* Lock all btrees used by the statement */
    sqlite3BtreeMutexArrayEnter(&p->aMutex);

    /* Check for one of the special errors */
    mrc = p->rc & 0xff;
    isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
................................................................................
                     || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
    if( isSpecialError ){
      /* If the query was read-only, we need do no rollback at all. Otherwise,
      ** proceed with the special handling.
      */
      if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
        if( p->rc==SQLITE_IOERR_BLOCKED && p->usesStmtJournal ){
          eStatementOp = SAVEPOINT_ROLLBACK;
          p->rc = SQLITE_BUSY;
        }else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL)
                   && p->usesStmtJournal ){
          eStatementOp = SAVEPOINT_ROLLBACK;
        }else{
          /* We are forced to roll back the active transaction. Before doing
          ** so, abort any other statements this handle currently has active.
          */
          invalidateCursorsOnModifiedBtrees(db);
          sqlite3RollbackAll(db);
          sqlite3CloseSavepoints(db);
          db->autoCommit = 1;
        }
      }
    }
  
    /* If the auto-commit flag is set and this is the only active writer 
    ** VM, then we do either a commit or rollback of the current transaction. 
    **
    ** Note: This block also runs if one of the special errors handled 
    ** above has occurred. 
    */
    if( !sqlite3VtabInSync(db) 
     && db->autoCommit 
     && db->writeVdbeCnt==(p->readOnly==0) 
................................................................................
     && (db->flags & SQLITE_CommitBusy)==0
    ){
      if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
        /* The auto-commit flag is true, and the vdbe program was 
        ** successful or hit an 'OR FAIL' constraint. This means a commit 
        ** is required.
        */
        rc = vdbeCommit(db, p);
        if( rc==SQLITE_BUSY ){
          sqlite3BtreeMutexArrayLeave(&p->aMutex);
          return SQLITE_BUSY;
        }else if( rc!=SQLITE_OK ){
          p->rc = rc;
          sqlite3RollbackAll(db);
        }else{
          sqlite3CommitInternalChanges(db);
        }
      }else{
        sqlite3RollbackAll(db);
      }
      db->nStatement = 0;
    }else if( eStatementOp==0 ){
      if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
        eStatementOp = SAVEPOINT_RELEASE;
      }else if( p->errorAction==OE_Abort ){
        eStatementOp = SAVEPOINT_ROLLBACK;

      }else{
        invalidateCursorsOnModifiedBtrees(db);
        sqlite3RollbackAll(db);
        sqlite3CloseSavepoints(db);
        db->autoCommit = 1;
      }
    }
  
    /* If eStatementOp is non-zero, then a statement transaction needs to
    ** be committed or rolled back. Call sqlite3VdbeCloseStatement() to
    ** do so. If this operation returns an error, and the current statement
    ** error code is SQLITE_OK or SQLITE_CONSTRAINT, then set the error
    ** code to the new value.
    */

    if( eStatementOp ){
      rc = sqlite3VdbeCloseStatement(p, eStatementOp);






      if( rc && (p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT) ){
        p->rc = rc;
        sqlite3DbFree(db, p->zErrMsg);
        p->zErrMsg = 0;
      }
    }
  

    /* If this was an INSERT, UPDATE or DELETE and no statement transaction

    ** has been rolled back, update the database connection change-counter. 
    */
    if( p->changeCntOn && p->pc>=0 ){

      if( eStatementOp!=SAVEPOINT_ROLLBACK ){
        sqlite3VdbeSetChanges(db, p->nChange);
      }else{
        sqlite3VdbeSetChanges(db, 0);
      }
      p->nChange = 0;
    }
  
................................................................................
  ** by connection db have now been released. Call sqlite3ConnectionUnlocked() 
  ** to invoke any required unlock-notify callbacks.
  */
  if( db->autoCommit ){
    sqlite3ConnectionUnlocked(db);
  }

  assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 );
  return SQLITE_OK;
}


/*
** Each VDBE holds the result of the most recent sqlite3_step() call
** in p->rc.  This routine sets that result back to SQLITE_OK.

Changes to test/memdb.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is in-memory database backend.
#
# $Id: memdb.test,v 1.17 2009/01/09 14:29:35 drh Exp $


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

ifcapable memorydb {

................................................................................
    if {$i>1} break
  }

  if {$t0} {set t1 {column a is not unique}}
  do_test memdb-5.$i {
    if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
    if {$conf2!=""} {set conf2 "ON CONFLICT $conf2"}
    set r0 [catch {execsql [subst {
      DROP TABLE t1;
      CREATE TABLE t1(a,b,c, UNIQUE(a) $conf1);
      INSERT INTO t1 SELECT * FROM t2;
      UPDATE t3 SET x=0;
      BEGIN $conf2;
      $cmd t3 SET x=1;
      $cmd t1 SET b=b*2;
      $cmd t1 SET a=c+5;
    }]} r1]
    catch {execsql {COMMIT}}
    if {!$r0} {set r1 [execsql {SELECT a FROM t1 ORDER BY b}]}
    set r2 [execsql {SELECT x FROM t3}]
    list $r0 $r1 $r2
  } [list $t0 $t1 $t2]
}








|







 







|








|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is in-memory database backend.
#
# $Id: memdb.test,v 1.18 2009/03/18 10:33:02 danielk1977 Exp $


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

ifcapable memorydb {

................................................................................
    if {$i>1} break
  }

  if {$t0} {set t1 {column a is not unique}}
  do_test memdb-5.$i {
    if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
    if {$conf2!=""} {set conf2 "ON CONFLICT $conf2"}
    set r0 [catch {execsql "
      DROP TABLE t1;
      CREATE TABLE t1(a,b,c, UNIQUE(a) $conf1);
      INSERT INTO t1 SELECT * FROM t2;
      UPDATE t3 SET x=0;
      BEGIN $conf2;
      $cmd t3 SET x=1;
      $cmd t1 SET b=b*2;
      $cmd t1 SET a=c+5;
    "} r1]
    catch {execsql {COMMIT}}
    if {!$r0} {set r1 [execsql {SELECT a FROM t1 ORDER BY b}]}
    set r2 [execsql {SELECT x FROM t3}]
    list $r0 $r1 $r2
  } [list $t0 $t1 $t2]
}

Added test/tkt3718.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
# 2001 September 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.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the execution of SQL statements from
# within callbacks generated by VMs that themselves open statement 
# transactions.
#
# $Id: tkt3718.test,v 1.1 2009/03/18 10:33:02 danielk1977 Exp $

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

do_test tkt3718-1.1 {
  execsql {
    CREATE TABLE t1(a PRIMARY KEY, b);
    INSERT INTO t1 VALUES(1, 'one');
    INSERT INTO t1 VALUES(2, 'two');
    INSERT INTO t1 VALUES(3, 'three');
    INSERT INTO t1 VALUES(4, 'four');
    INSERT INTO t1 VALUES(5, 'five');
    CREATE TABLE t2(a PRIMARY KEY, b);
  }
} {}

# SQL scalar function:
#
#   f1(<arg>)
#
# Uses database handle [db] to execute "SELECT f2(<arg>)". Returns either
# the results or error message from the "SELECT f2(<arg>)" query to the
# caller.
#
proc f1 {args} {
  set a [lindex $args 0]
  catch { db eval {SELECT f2($a)} } msg
  set msg
}

# SQL scalar function:
#
#   f2(<arg>)
#
# Return the value of <arg>. Unless <arg> is "three", in which case throw
# an exception.
#
proc f2 {args} {
  set a [lindex $args 0]
  if {$a == "three"} { error "Three!!" }
  return $a
}

db func f1 f1
db func f2 f2

# The second INSERT statement below uses the f1 user function such that
# half-way through the INSERT operation f1() will run an SQL statement
# that throws an exception. At one point, before #3718 was fixed, this
# caused the statement transaction belonging to the INSERT statement to
# be rolled back. The result was that some (but not all) of the rows that 
# should have been inserted went missing.
#
do_test tkt3718-1.2 {
  execsql {
    BEGIN;
    INSERT INTO t2 SELECT a, b FROM t1;
    INSERT INTO t2 SELECT a+5, f1(b) FROM t1;
    COMMIT;
  }
  execsql {
    SELECT a FROM t2;
  }
} {1 2 3 4 5 6 7 8 9 10}

# This test turns on the count_changes pragma (causing DML statements to
# return SQLITE_ROW once, with a single integer result value reporting the
# number of rows affected by the statement). It then executes an INSERT
# statement that requires a statement journal. After stepping the statement
# once, so that it returns SQLITE_ROW, a second SQL statement that throws an
# exception is run. At one point, before #3718 was fixed, this caused the
# statement transaction belonging to the INSERT statement to be rolled back.
# The result was that none of the rows were actually inserted.
# 
#
do_test tkt3718-1.3 {
  execsql { 
    DELETE FROM t2 WHERE a > 5;
    PRAGMA count_changes = 1;
    BEGIN;
  }
  db eval {INSERT INTO t2 SELECT a+5, b||'+5' FROM t1} {
    catch { db eval {SELECT f2('three')} } msg
  }
  execsql {
    COMMIT;
    SELECT a FROM t2;
  }
} {1 2 3 4 5 6 7 8 9 10}

do_test tkt3718-1.4 {
  execsql {pragma count_changes=0}
} {}

# This SQL function executes the SQL specified as an argument against
# database [db].
#
proc sql {doit zSql} {
  if {$doit} { catchsql $zSql }
}
db func sql [list sql]

# The following tests, tkt3718-2.*, test that a nested statement 
# transaction can be successfully committed or reverted without 
# affecting the parent statement transaction.
#
do_test tkt3718-2.1 {
  execsql { SELECT sql(1, 'DELETE FROM t2 WHERE a = '||a ) FROM t2 WHERE a>5 }
  execsql { SELECT a from t2 }
} {1 2 3 4 5}
do_test tkt3718-2.2 {
  execsql {
    DELETE FROM t2 WHERE a > 5;
    BEGIN;
    INSERT INTO t2 SELECT a+5, sql(a==3,
        'INSERT INTO t2 SELECT a+10, f2(b) FROM t1'
    ) FROM t1;
  }
  execsql {
    COMMIT;
    SELECT a FROM t2;
  }
} {1 2 3 4 5 6 7 8 9 10}
do_test tkt3718-2.3 {
  execsql {
    DELETE FROM t2 WHERE a > 5;
    BEGIN;
    INSERT INTO t2 SELECT a+5, sql(a==3,
        'INSERT INTO t2 SELECT a+10, b FROM t1'
    ) FROM t1;
    COMMIT;
  }
  execsql { SELECT a FROM t2 ORDER BY a+0}
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15}
integrity_check tkt3718.2-4

# The next set of tests, tkt3718-3.*, test that a statement transaction
# that has a committed statement transaction nested inside of it can
# be committed or reverted.
#
foreach {tn io ii results} {
  1 0 10 {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
  2 1 10 {6 7 8 9 10 16 17 18 19 20}
  3 0 11 {1 2 3 4 5 6 7 8 9 10 16 17 18 19 20}
  4 1 11 {6 7 8 9 10 16 17 18 19 20}
} {
  do_test tkt3718-3.$tn {
    execsql { 
      DELETE FROM t2;
      INSERT INTO t2 SELECT a+5, b FROM t1;
      INSERT INTO t2 SELECT a+15, b FROM t1;
    }

    catchsql "
      BEGIN;
      INSERT INTO t2 SELECT a+$io, sql(a==3,
          'INSERT INTO t2 SELECT a+$ii, b FROM t1'
      ) FROM t1;
    "

    execsql { COMMIT }

    execsql { SELECT a FROM t2 ORDER BY a+0}
  } $results

  integrity_check tkt3718-3.$tn.integrity
}

# This is the same test as tkt3718-3.*, but with 3 levels of nesting.
#
foreach {tn i1 i2 i3 results} {
  1   0 10 20   {5 10 15 20 25 30}
  2   0 10 21   {5 10 15 20 30}
  3   0 11 20   {5 10 20 30}
  4   0 11 21   {5 10 20 30}
  5   1 10 20   {10 20 30}
  6   1 10 21   {10 20 30}
  7   1 11 20   {10 20 30}
  8   1 11 21   {10 20 30}
} {
  do_test tkt3718-4.$tn {
    execsql { 
      DELETE FROM t2;
      INSERT INTO t2 SELECT a+5, b FROM t1;
      INSERT INTO t2 SELECT a+15, b FROM t1;
      INSERT INTO t2 SELECT a+25, b FROM t1;
    }

    catchsql "
      BEGIN;
      INSERT INTO t2 SELECT a+$i1, sql(a==3,
          'INSERT INTO t2 SELECT a+$i2, sql(a==3, 
             ''INSERT INTO t2 SELECT a+$i3, b FROM t1''
           ) FROM t1'
      ) FROM t1;
    "

    execsql { COMMIT }

    execsql { SELECT a FROM t2 WHERE (a%5)==0 ORDER BY a+0}
  } $results

  do_test tkt3718-4.$tn.extra {
    execsql {
      SELECT 
        (SELECT sum(a) FROM t2)==(SELECT sum(a*5-10) FROM t2 WHERE (a%5)==0)
    }
  } {1}

  integrity_check tkt3718-4.$tn.integrity
}


finish_test