SQLite

Check-in [83c8ae5bee]
Login

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

Overview
Comment:Verify that the rollback-hook is invoked correctly when a malloc() failure occurs. (CVS 2824)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 83c8ae5bee3b6bdb556d2e85fa260ba855742601
User & Date: danielk1977 2005-12-16 15:24:29.000
Context
2005-12-18
08:51
Add the (untested) sqlite3_release_memory() function. (CVS 2825) (check-in: 345addaa03 user: danielk1977 tags: trunk)
2005-12-16
15:24
Verify that the rollback-hook is invoked correctly when a malloc() failure occurs. (CVS 2824) (check-in: 83c8ae5bee user: danielk1977 tags: trunk)
06:54
Add the sqlite3_rollback_hook() API. Still requires further testing. (CVS 2823) (check-in: 3baa3ff324 user: danielk1977 tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btree.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
** 2004 April 6
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.274 2005/12/16 06:54:02 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.











|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
** 2004 April 6
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.275 2005/12/16 15:24:29 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.
5814
5815
5816
5817
5818
5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
    return sqlite3pager_sync(pBt->pPager, zMaster, nTrunc);
#endif
    return sqlite3pager_sync(pBt->pPager, zMaster, 0);
  }
  return SQLITE_OK;
}

#ifndef SQLITE_OMIT_GLOBALRECOVER
/*
** Reset the btree and underlying pager after a malloc() failure. Any
** transaction that was active when malloc() failed is rolled back.
*/
int sqlite3BtreeReset(Btree *pBt){
  if( pBt->pCursor ) return SQLITE_BUSY;
  pBt->inTrans = TRANS_NONE;
  unlockBtreeIfUnused(pBt);
  return sqlite3pager_reset(pBt->pPager);
}
#endif







<
<
<
<
<
<
<
<
<
<
<
<
5814
5815
5816
5817
5818
5819
5820












    return sqlite3pager_sync(pBt->pPager, zMaster, nTrunc);
#endif
    return sqlite3pager_sync(pBt->pPager, zMaster, 0);
  }
  return SQLITE_OK;
}













Changes to src/btree.h.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
**    May you share freely, never taking more than you give.
**
*************************************************************************
** 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.64 2005/08/27 16:36:49 drh Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_

/* TODO: This definition is just included so other modules compile. It
** needs to be revisited.
*/







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
**    May you share freely, never taking more than you give.
**
*************************************************************************
** 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.65 2005/12/16 15:24:29 danielk1977 Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_

/* TODO: This definition is just included so other modules compile. It
** needs to be revisited.
*/
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
int sqlite3BtreeBeginStmt(Btree*);
int sqlite3BtreeCommitStmt(Btree*);
int sqlite3BtreeRollbackStmt(Btree*);
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
int sqlite3BtreeIsInTrans(Btree*);
int sqlite3BtreeIsInStmt(Btree*);
int sqlite3BtreeSync(Btree*, const char *zMaster);
int sqlite3BtreeReset(Btree *);

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

/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR







<







70
71
72
73
74
75
76

77
78
79
80
81
82
83
int sqlite3BtreeBeginStmt(Btree*);
int sqlite3BtreeCommitStmt(Btree*);
int sqlite3BtreeRollbackStmt(Btree*);
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
int sqlite3BtreeIsInTrans(Btree*);
int sqlite3BtreeIsInStmt(Btree*);
int sqlite3BtreeSync(Btree*, const char *zMaster);


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

/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR
Changes to src/main.c.
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
**
*************************************************************************
** 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.311 2005/12/16 06:54:02 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** The following constant value is used by the SQLITE_BIGENDIAN and







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
**
*************************************************************************
** 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.312 2005/12/16 15:24:29 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** The following constant value is used by the SQLITE_BIGENDIAN and
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
}

/*
** Rollback all database files.
*/
void sqlite3RollbackAll(sqlite3 *db){
  int i;

  for(i=0; i<db->nDb; i++){
    if( db->aDb[i].pBt ){



      sqlite3BtreeRollback(db->aDb[i].pBt);
      db->aDb[i].inTrans = 0;
    }
  }
  if( db->flags&SQLITE_InternChanges ){
    sqlite3ResetInternalSchema(db, 0);
  }

  /* If one has been configured, invoke the rollback-hook callback */
  if( db->xRollbackCallback ){
    db->xRollbackCallback(db->pRollbackArg);
  }
}

/*
** Return a static string that describes the kind of error specified in the
** argument.







>


>
>
>









|







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
}

/*
** Rollback all database files.
*/
void sqlite3RollbackAll(sqlite3 *db){
  int i;
  int inTrans = 0;
  for(i=0; i<db->nDb; i++){
    if( db->aDb[i].pBt ){
      if( sqlite3BtreeIsInTrans(db->aDb[i].pBt) ){
        inTrans = 1;
      }
      sqlite3BtreeRollback(db->aDb[i].pBt);
      db->aDb[i].inTrans = 0;
    }
  }
  if( db->flags&SQLITE_InternChanges ){
    sqlite3ResetInternalSchema(db, 0);
  }

  /* If one has been configured, invoke the rollback-hook callback */
  if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){
    db->xRollbackCallback(db->pRollbackArg);
  }
}

/*
** Return a static string that describes the kind of error specified in the
** argument.
Changes to src/sqlite.h.in.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.147 2005/12/16 06:54:02 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the SQLite library
** presents to client programs.
**
** @(#) $Id: sqlite.h.in,v 1.148 2005/12/16 15:24:29 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
1315
1316
1317
1318
1319
1320
1321















1322
1323
1324
1325
1326
1327
1328
*/
void *sqlite3_update_hook(
  sqlite3*, 
  void(*)(void *,int ,char const *,char const *,sqlite_int64),
  void*
);
















void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);

/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE_OMIT_FLOATING_POINT







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







1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
*/
void *sqlite3_update_hook(
  sqlite3*, 
  void(*)(void *,int ,char const *,char const *,sqlite_int64),
  void*
);

/*
** Register a callback to be invoked whenever a transaction is rolled
** back. 
**
** The new callback function overrides any existing rollback-hook
** callback. If there was an existing callback, then it's pArg value 
** (the third argument to sqlite3_rollback_hook() when it was registered) 
** is returned. Otherwise, NULL is returned.
**
** For the purposes of this API, a transaction is said to have been 
** rolled back if an explicit "ROLLBACK" statement is executed, or
** an error or constraint causes an implicit rollback to occur. The 
** callback is not invoked if a transaction is automatically rolled
** back because the database connection is closed.
*/
void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);

/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
#ifdef SQLITE_OMIT_FLOATING_POINT
Changes to src/vdbe.c.
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
**
** 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.505 2005/12/15 15:22:10 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>
#include "vdbeInt.h"

/*







|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
**
** 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.506 2005/12/16 15:24:29 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>
#include "vdbeInt.h"

/*
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305



2306
2307
2308
2309
2310
2311

2312
2313
2314
2315
2316
2317
2318
    ** still running, and a transaction is active, return an error indicating
    ** that the other VMs must complete first. 
    */
    sqlite3SetString(&p->zErrMsg, "cannot ", rollback?"rollback":"commit", 
        " transaction - SQL statements in progress", 0);
    rc = SQLITE_ERROR;
  }else if( i!=db->autoCommit ){
    db->autoCommit = i;
    if( pOp->p2 ){
      assert( i==1 );
      sqlite3RollbackAll(db);



    }else if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
      p->pTos = pTos;
      p->pc = pc;
      db->autoCommit = 1-i;
      p->rc = SQLITE_BUSY;
      return SQLITE_BUSY;

    }
    return SQLITE_DONE;
  }else{
    sqlite3SetString(&p->zErrMsg,
        (!i)?"cannot start a transaction within a transaction":(
        (rollback)?"cannot rollback - no transaction is active":
                   "cannot commit - no transaction is active"), 0);







<



>
>
>
|
|
|
|
|
|
>







2295
2296
2297
2298
2299
2300
2301

2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
    ** still running, and a transaction is active, return an error indicating
    ** that the other VMs must complete first. 
    */
    sqlite3SetString(&p->zErrMsg, "cannot ", rollback?"rollback":"commit", 
        " transaction - SQL statements in progress", 0);
    rc = SQLITE_ERROR;
  }else if( i!=db->autoCommit ){

    if( pOp->p2 ){
      assert( i==1 );
      sqlite3RollbackAll(db);
      db->autoCommit = 1;
    }else{
      db->autoCommit = i;
      if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
        p->pTos = pTos;
        p->pc = pc;
        db->autoCommit = 1-i;
        p->rc = SQLITE_BUSY;
        return SQLITE_BUSY;
      }
    }
    return SQLITE_DONE;
  }else{
    sqlite3SetString(&p->zErrMsg,
        (!i)?"cannot start a transaction within a transaction":(
        (rollback)?"cannot rollback - no transaction is active":
                   "cannot commit - no transaction is active"), 0);
Changes to src/vdbeaux.c.
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
      ** 'OR FAIL' constraint. This means a commit is required.
      */
      int rc = vdbeCommit(db);
      if( rc==SQLITE_BUSY ){
        return SQLITE_BUSY;
      }else if( rc!=SQLITE_OK ){
        p->rc = rc;
        xFunc = sqlite3BtreeRollback;
      }else{
        sqlite3CommitInternalChanges(db);
      }
    }else{
      xFunc = sqlite3BtreeRollback;
    }
  }else{

    if( p->rc==SQLITE_NOMEM ){
      /* This loop does static analysis of the query to see which of the
      ** following three categories it falls into:
      **







|




|







1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
      ** 'OR FAIL' constraint. This means a commit is required.
      */
      int rc = vdbeCommit(db);
      if( rc==SQLITE_BUSY ){
        return SQLITE_BUSY;
      }else if( rc!=SQLITE_OK ){
        p->rc = rc;
        sqlite3RollbackAll(db);
      }else{
        sqlite3CommitInternalChanges(db);
      }
    }else{
      sqlite3RollbackAll(db);
    }
  }else{

    if( p->rc==SQLITE_NOMEM ){
      /* This loop does static analysis of the query to see which of the
      ** following three categories it falls into:
      **
1223
1224
1225
1226
1227
1228
1229

1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
    }

    if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
      xFunc = sqlite3BtreeCommitStmt;
    }else if( p->errorAction==OE_Abort ){
      xFunc = sqlite3BtreeRollbackStmt;
    }else{

      xFunc = sqlite3BtreeRollback;
      db->autoCommit = 1;
      abortOtherActiveVdbes(p);
    }
  }

  /* 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 ||
    xFunc==sqlite3BtreeRollback
  );
  if( xFunc==sqlite3BtreeRollback ){
    assert( p->rc!=SQLITE_OK );
    sqlite3RollbackAll(db);
  }else{
    for(i=0; xFunc && i<db->nDb; i++){ 
      int rc;
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        rc = xFunc(pBt);
        if( p->rc==SQLITE_OK ) p->rc = rc;
      }
    }
  }

  /* If this was an INSERT, UPDATE or DELETE, 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;
  }

  /* Rollback or commit any schema changes that occurred. */
  if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){
    sqlite3ResetInternalSchema(db, 0);
    if( xFunc!=sqlite3BtreeRollback ){
      db->flags = (db->flags | SQLITE_InternChanges);
    }
  }

  /* We have successfully halted and closed the VM.  Record this fact. */
  if( p->pc>=0 ){
    db->activeVdbeCnt--;
  }
  p->magic = VDBE_MAGIC_HALT;







>
|

<










|
<

<
<
<
<
|
|
|
|
|
|
<
















<
|
<







1223
1224
1225
1226
1227
1228
1229
1230
1231
1232

1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243

1244




1245
1246
1247
1248
1249
1250

1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266

1267

1268
1269
1270
1271
1272
1273
1274
    }

    if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
      xFunc = sqlite3BtreeCommitStmt;
    }else if( p->errorAction==OE_Abort ){
      xFunc = sqlite3BtreeRollbackStmt;
    }else{
      abortOtherActiveVdbes(p);
      sqlite3RollbackAll(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( p->rc==SQLITE_OK ) p->rc = rc;

    }
  }

  /* If this was an INSERT, UPDATE or DELETE, 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;
  }

  /* Rollback or commit any schema changes that occurred. */
  if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){
    sqlite3ResetInternalSchema(db, 0);

    db->flags = (db->flags | SQLITE_InternChanges);

  }

  /* We have successfully halted and closed the VM.  Record this fact. */
  if( p->pc>=0 ){
    db->activeVdbeCnt--;
  }
  p->magic = VDBE_MAGIC_HALT;
Changes to test/hook.test.
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#
# The focus of the tests in this file is the  following interface:
#
#      sqlite_commit_hook    (tests hook-1..hook-3 inclusive)
#      sqlite_update_hook    (tests hook-4-*)
#      sqlite_rollback_hook  (tests hook-5.*)
#
# $Id: hook.test,v 1.7 2005/12/16 06:54:03 danielk1977 Exp $

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

do_test hook-1.2 {
  db commit_hook
} {}







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#
# The focus of the tests in this file is the  following interface:
#
#      sqlite_commit_hook    (tests hook-1..hook-3 inclusive)
#      sqlite_update_hook    (tests hook-4-*)
#      sqlite_rollback_hook  (tests hook-5.*)
#
# $Id: hook.test,v 1.8 2005/12/16 15:24:30 danielk1977 Exp $

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

do_test hook-1.2 {
  db commit_hook
} {}
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
270
271
272
273

274
275
276
277
#----------------------------------------------------------------------------
# Test the rollback-hook. The rollback-hook is a bit more complicated than
# either the commit or update hooks because a rollback can happen 
# explicitly (an sql ROLLBACK statement) or implicitly (a constraint or 
# error condition).
#
# hook-5.1.* - Test explicit rollbacks.
# hook-5.2.* - Test implicit rollbacks.



# hook-5.3.* - Test hot-journal rollbacks.

#

do_test hook-5.0 {
  # Configure the rollback hook to increment global variable 
  # $::rollback_hook each time it is invoked.
  set ::rollback_hook 0
  db rollback_hook [list incr ::rollback_hook]
} {}

# Test explicit rollbacks. Not much can really go wrong here.

do_test hook-5.1.1 {
  set ::rollback_hook 0
  execsql {
    BEGIN;
    ROLLBACK;
  }
  set ::rollback_hook
} {1}

# Test implicit rollbacks caused by constraints.

do_test hook-5.2.1 {
  set ::rollback_hook 0
  catchsql {
    DROP TABLE t1;
    CREATE TABLE t1(a PRIMARY KEY, b);
    INSERT INTO t1 VALUES('one', 'I');
    INSERT INTO t1 VALUES('one', 'I');
  }
  set ::rollback_hook
} {1}
do_test hook-5.2.2 {
  # Check that the INSERT transaction above really was rolled back.
  execsql {
    SELECT count(*) FROM t1;
  }
} {1}

#

#----------------------------------------------------------------------------

finish_test








|
>
>
>
|
>










>










>


















>




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
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
#----------------------------------------------------------------------------
# Test the rollback-hook. The rollback-hook is a bit more complicated than
# either the commit or update hooks because a rollback can happen 
# explicitly (an sql ROLLBACK statement) or implicitly (a constraint or 
# error condition).
#
# hook-5.1.* - Test explicit rollbacks.
# hook-5.2.* - Test implicit rollbacks caused by constraint failure.
#
# hook-5.3.* - Test implicit rollbacks caused by IO errors.
# hook-5.4.* - Test implicit rollbacks caused by malloc() failure.
# hook-5.5.* - Test hot-journal rollbacks. Or should the rollback hook 
#              not be called for these?
#

do_test hook-5.0 {
  # Configure the rollback hook to increment global variable 
  # $::rollback_hook each time it is invoked.
  set ::rollback_hook 0
  db rollback_hook [list incr ::rollback_hook]
} {}

# Test explicit rollbacks. Not much can really go wrong here.
#
do_test hook-5.1.1 {
  set ::rollback_hook 0
  execsql {
    BEGIN;
    ROLLBACK;
  }
  set ::rollback_hook
} {1}

# Test implicit rollbacks caused by constraints.
#
do_test hook-5.2.1 {
  set ::rollback_hook 0
  catchsql {
    DROP TABLE t1;
    CREATE TABLE t1(a PRIMARY KEY, b);
    INSERT INTO t1 VALUES('one', 'I');
    INSERT INTO t1 VALUES('one', 'I');
  }
  set ::rollback_hook
} {1}
do_test hook-5.2.2 {
  # Check that the INSERT transaction above really was rolled back.
  execsql {
    SELECT count(*) FROM t1;
  }
} {1}

#
# End rollback-hook testing.
#----------------------------------------------------------------------------

finish_test

Changes to test/malloc3.test.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#
#***********************************************************************
#
# This file contains tests to ensure that the library handles malloc() failures
# correctly. The emphasis of these tests are the _prepare(), _step() and
# _finalize() calls.
#
# $Id: malloc3.test,v 1.4 2005/12/09 14:25:12 danielk1977 Exp $

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

# Only run these tests if memory debugging is turned on.
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#
#***********************************************************************
#
# This file contains tests to ensure that the library handles malloc() failures
# correctly. The emphasis of these tests are the _prepare(), _step() and
# _finalize() calls.
#
# $Id: malloc3.test,v 1.5 2005/12/16 15:24:30 danielk1977 Exp $

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

# Only run these tests if memory debugging is turned on.
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
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
528
529
530
531
532











533
534


535
536
537

538



539
540

541
542
543
544
545
546
547
548
549

550
551
552

553
554
555
556
557
558
559
      -sql  {db eval [lindex $v2 1]}
      -prep {db eval $v2}
    }
    set nac [sqlite3_get_autocommit $::DB]       ;# New Auto-Commit 
    if {$ac && !$nac} {set begin_pc $i}
  }



  set iFail $iFailStart
  set pc $pcstart
  while {$pc*2 < [llength $arglist]} {

    # Id of this iteration:
    set iterid "(pc $pc).(iFail $iFail)"

    set k [lindex $arglist [expr 2 * $pc]]
    set v [lindex $arglist [expr 2 * $pc + 1]]

    switch -- $k {

      -test { 
        foreach {id script} $v {}
        set testid "malloc3-(test $id).$iterid"
        eval $script
        incr pc
      }

      -sql {


        set ac [sqlite3_get_autocommit $::DB]        ;# Auto-Commit
        sqlite_malloc_fail $iFail
# puts "SQL $iterid [lindex $v 1]"
        set rc [catch {db eval [lindex $v 1]} msg]   ;# True error occurs
# puts "rc = $rc msg = \"$msg\""
        set nac [sqlite3_get_autocommit $::DB]       ;# New Auto-Commit 












        if {$rc == 0} {
            # Successful execution of sql.


            if {[lindex [sqlite_malloc_stat] 2] <= 0} {
              error "Unreported malloc() failure"
            }

            if {$ac && !$nac} {



              set begin_pc $pc
            } 

            incr pc
            set iFail 1
            sqlite_malloc_fail 0
            integrity_check "malloc3-(integrity).$iterid"
        } elseif {[regexp {.*out of memory} $msg]} {
            # Out of memory error, as expected
            integrity_check "malloc3-(integrity).$iterid"
            incr iFail
            if {$nac && !$ac} {

              if {![lindex $v 0]} {
                error "Statement \"[lindex $v 1]\" caused a rollback"
              }

# puts "Statement \"[lindex $v 1]\" caused a rollback"
              for {set i $begin_pc} {$i < $pc} {incr i} {
                set k2 [lindex $arglist [expr 2 * $i]]
                set v2 [lindex $arglist [expr 2 * $i + 1]]
                set catchupsql ""
                switch -- $k2 {
                  -sql  {set catchupsql [lindex $v2 1]}







>
>




















>
>







>
>
>
>
>
>
>
>
>
>
>

|
>
>



>

>
>
>


>









>



>







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
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
      -sql  {db eval [lindex $v2 1]}
      -prep {db eval $v2}
    }
    set nac [sqlite3_get_autocommit $::DB]       ;# New Auto-Commit 
    if {$ac && !$nac} {set begin_pc $i}
  }

  db rollback_hook [list incr ::rollback_hook_count]

  set iFail $iFailStart
  set pc $pcstart
  while {$pc*2 < [llength $arglist]} {

    # Id of this iteration:
    set iterid "(pc $pc).(iFail $iFail)"

    set k [lindex $arglist [expr 2 * $pc]]
    set v [lindex $arglist [expr 2 * $pc + 1]]

    switch -- $k {

      -test { 
        foreach {id script} $v {}
        set testid "malloc3-(test $id).$iterid"
        eval $script
        incr pc
      }

      -sql {
        set ::rollback_hook_count 0

        set ac [sqlite3_get_autocommit $::DB]        ;# Auto-Commit
        sqlite_malloc_fail $iFail
# puts "SQL $iterid [lindex $v 1]"
        set rc [catch {db eval [lindex $v 1]} msg]   ;# True error occurs
# puts "rc = $rc msg = \"$msg\""
        set nac [sqlite3_get_autocommit $::DB]       ;# New Auto-Commit 


        if {$rc != 0 && $nac && !$ac} {
          # Before [db eval] the auto-commit flag was clear. Now it
          # is set. Since an error occured we assume this was not a
	  # commit - therefore a rollback occured. Check that the
	  # rollback-hook was invoked.
          do_test malloc3-rollback_hook.$iterid {
            set ::rollback_hook_count
          } {1}
        }

        if {$rc == 0} {
            # Successful execution of sql. Our "mallocs-until-failure" 
            # count should be greater than 0. Otherwise a malloc() failed
            # and the error was not reported.
            if {[lindex [sqlite_malloc_stat] 2] <= 0} {
              error "Unreported malloc() failure"
            }

            if {$ac && !$nac} {
              # Before the [db eval] the auto-commit flag was set, now it
              # is clear. We can deduce that a "BEGIN" statement has just
              # been successfully executed.
              set begin_pc $pc
            } 

            incr pc
            set iFail 1
            sqlite_malloc_fail 0
            integrity_check "malloc3-(integrity).$iterid"
        } elseif {[regexp {.*out of memory} $msg]} {
            # Out of memory error, as expected
            integrity_check "malloc3-(integrity).$iterid"
            incr iFail
            if {$nac && !$ac} {

              if {![lindex $v 0]} {
                error "Statement \"[lindex $v 1]\" caused a rollback"
              }

# puts "Statement \"[lindex $v 1]\" caused a rollback"
              for {set i $begin_pc} {$i < $pc} {incr i} {
                set k2 [lindex $arglist [expr 2 * $i]]
                set v2 [lindex $arglist [expr 2 * $i + 1]]
                set catchupsql ""
                switch -- $k2 {
                  -sql  {set catchupsql [lindex $v2 1]}
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
# if {$iFail > ($iFailStart+1)} return
  }
}

# Turn of the Tcl interface's prepared statement caching facility.
db cache size 0

run_test $::run_test_script 
# run_test [lrange $::run_test_script 0 3] 0 63
sqlite_malloc_fail 0
db close

pp_check_for_leaks

finish_test








|








607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
# if {$iFail > ($iFailStart+1)} return
  }
}

# Turn of the Tcl interface's prepared statement caching facility.
db cache size 0

run_test $::run_test_script
# run_test [lrange $::run_test_script 0 3] 0 63
sqlite_malloc_fail 0
db close

pp_check_for_leaks

finish_test