Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Handle some of the IO error conditions that may occur in a shared-cache context. (CVS 2980) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
97491d4eb5fc24d8f5cc7605db844359 |
User & Date: | danielk1977 2006-01-20 10:55:05.000 |
Context
2006-01-20
| ||
15:45 | Fix and test the processing of sqlite3_result_error() withing aggregate functions. Allow errors to come from the step function (a new capability). Ticket #1632. (CVS 2981) (check-in: fd4a6bb1ac user: drh tags: trunk) | |
10:55 | Handle some of the IO error conditions that may occur in a shared-cache context. (CVS 2980) (check-in: 97491d4eb5 user: danielk1977 tags: trunk) | |
2006-01-19
| ||
17:42 | Make sure sqlite3_thread_cleanup() does not try to allocate memory. (CVS 2979) (check-in: 0208e4221a user: drh tags: trunk) | |
Changes
Changes to src/btree.c.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 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. ** ************************************************************************* | | | 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.303 2006/01/20 10:55:05 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. |
︙ | ︙ | |||
2494 2495 2496 2497 2498 2499 2500 | for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ if( pCur->wrFlag ) r++; } return r; } #endif | | | 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 | for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ if( pCur->wrFlag ) r++; } return r; } #endif #if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) /* ** Print debugging information about all cursors to standard output. */ void sqlite3BtreeCursorList(Btree *p){ BtCursor *pCur; BtShared *pBt = p->pBt; for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ |
︙ | ︙ | |||
4429 4430 4431 4432 4433 4434 4435 | /* ** Find the parent page. */ assert( pPage->isInit ); assert( sqlite3pager_iswriteable(pPage->aData) ); pBt = pPage->pBt; pParent = pPage->pParent; | < > > > | 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 | /* ** Find the parent page. */ assert( pPage->isInit ); assert( sqlite3pager_iswriteable(pPage->aData) ); pBt = pPage->pBt; pParent = pPage->pParent; assert( pParent ); if( SQLITE_OK!=(rc = sqlite3pager_write(pParent->aData)) ){ return rc; } TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno)); #ifndef SQLITE_OMIT_QUICKBALANCE /* ** A special case: If a new entry has just been inserted into a ** table (that is, a btree with integer keys and all data at the leaves) ** and the new entry is the right-most entry in the tree (it has the |
︙ | ︙ | |||
5869 5870 5871 5872 5873 5874 5875 | return SQLITE_OK; } int sqlite3BtreePageDump(Btree *p, int pgno, int recursive){ return btreePageDump(p->pBt, pgno, recursive, 0); } #endif | | | 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 | return SQLITE_OK; } int sqlite3BtreePageDump(Btree *p, int pgno, int recursive){ return btreePageDump(p->pBt, pgno, recursive, 0); } #endif #if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) /* ** Fill aResult[] with information about the entry and page that the ** cursor is pointing to. ** ** aResult[0] = The page number ** aResult[1] = The entry number ** aResult[2] = Total number of entries on this page |
︙ | ︙ | |||
6542 6543 6544 6545 6546 6547 6548 | } /* ** The following debugging interface has to be in this file (rather ** than in, for example, test1.c) so that it can get access to ** the definition of BtShared. */ | | | 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 | } /* ** The following debugging interface has to be in this file (rather ** than in, for example, test1.c) so that it can get access to ** the definition of BtShared. */ #if defined(SQLITE_DEBUG) && defined(TCLSH) #include <tcl.h> int sqlite3_shared_cache_report( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** The pager is used to access a database disk file. It implements ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** The pager is used to access a database disk file. It implements ** atomic commit and rollback through the use of a journal file that ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** ** @(#) $Id: pager.c,v 1.246 2006/01/20 10:55:05 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" #include "os.h" #include "pager.h" #include <assert.h> #include <string.h> |
︙ | ︙ | |||
464 465 466 467 468 469 470 | ** If the second argument is SQLITE_IOERR, SQLITE_CORRUPT or SQLITE_PROTOCOL, ** the error becomes persistent. All subsequent API calls on this Pager ** will immediately return the same error code. */ static int pager_error(Pager *pPager, int rc){ assert( pPager->errCode==SQLITE_FULL || pPager->errCode==SQLITE_OK ); if( | | | | 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 | ** If the second argument is SQLITE_IOERR, SQLITE_CORRUPT or SQLITE_PROTOCOL, ** the error becomes persistent. All subsequent API calls on this Pager ** will immediately return the same error code. */ static int pager_error(Pager *pPager, int rc){ assert( pPager->errCode==SQLITE_FULL || pPager->errCode==SQLITE_OK ); if( rc==SQLITE_FULL || rc==SQLITE_IOERR || rc==SQLITE_CORRUPT || rc==SQLITE_PROTOCOL ){ pPager->errCode = rc; } return rc; } |
︙ | ︙ | |||
3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 | assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); CODEC(pPager, pData, pPg->pgno, 7); cksum = pager_cksum(pPager, pPg->pgno, pData); saved = *(u32*)PGHDR_TO_EXTRA(pPg, pPager); store32bits(cksum, pPg, pPager->pageSize); szPg = pPager->pageSize+8; store32bits(pPg->pgno, pPg, -4); rc = sqlite3OsWrite(pPager->jfd, &((char*)pData)[-4], szPg); pPager->journalOff += szPg; TRACE4("JOURNAL %d page %d needSync=%d\n", PAGERID(pPager), pPg->pgno, pPg->needSync); CODEC(pPager, pData, pPg->pgno, 0); *(u32*)PGHDR_TO_EXTRA(pPg, pPager) = saved; if( rc!=SQLITE_OK ){ sqlite3pager_rollback(pPager); if( !pPager->errCode ){ pager_error(pPager, SQLITE_FULL); } return rc; } pPager->nRec++; assert( pPager->aInJournal!=0 ); pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7); pPg->needSync = !pPager->noSync; if( pPager->stmtInUse ){ pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7); page_add_to_stmt_list(pPg); | > > > > > > > > | 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 | assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); CODEC(pPager, pData, pPg->pgno, 7); cksum = pager_cksum(pPager, pPg->pgno, pData); saved = *(u32*)PGHDR_TO_EXTRA(pPg, pPager); store32bits(cksum, pPg, pPager->pageSize); szPg = pPager->pageSize+8; store32bits(pPg->pgno, pPg, -4); rc = sqlite3OsWrite(pPager->jfd, &((char*)pData)[-4], szPg); pPager->journalOff += szPg; TRACE4("JOURNAL %d page %d needSync=%d\n", PAGERID(pPager), pPg->pgno, pPg->needSync); CODEC(pPager, pData, pPg->pgno, 0); *(u32*)PGHDR_TO_EXTRA(pPg, pPager) = saved; /* An error has occured writing to the journal file. The ** transaction will be rolled back by the layer above. */ if( rc!=SQLITE_OK ){ #if 0 sqlite3pager_rollback(pPager); if( !pPager->errCode ){ pager_error(pPager, SQLITE_FULL); } #endif return rc; } pPager->nRec++; assert( pPager->aInJournal!=0 ); pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7); pPg->needSync = !pPager->noSync; if( pPager->stmtInUse ){ pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7); page_add_to_stmt_list(pPg); |
︙ | ︙ | |||
3367 3368 3369 3370 3371 3372 3373 3374 3375 | rc2 = pager_unwritelock(pPager); if( rc==SQLITE_OK ){ rc = rc2; } }else{ rc = pager_playback(pPager); } if( rc!=SQLITE_OK ){ rc = SQLITE_CORRUPT_BKPT; | > < > > > > > > | | 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 | rc2 = pager_unwritelock(pPager); if( rc==SQLITE_OK ){ rc = rc2; } }else{ rc = pager_playback(pPager); } #if 0 if( rc!=SQLITE_OK ){ rc = SQLITE_CORRUPT_BKPT; } #endif pPager->dbSize = -1; /* If an error occurs during a ROLLBACK, we can no longer trust the pager ** cache. So call pager_error() on the way out to make any error ** persistent. */ return pager_error(pPager, rc); } /* ** Return TRUE if the database file is opened read-only. Return FALSE ** if the database is (in theory) writable. */ int sqlite3pager_isreadonly(Pager *pPager){ |
︙ | ︙ |
Changes to src/test1.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** Code for testing the printf() interface to SQLite. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** Code for testing the printf() interface to SQLite. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** ** $Id: test1.c,v 1.200 2006/01/20 10:55:05 danielk1977 Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include "os.h" #include <stdlib.h> #include <string.h> |
︙ | ︙ | |||
3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 | (char*)&sqlite3_sync_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_fullsync_count", (char*)&sqlite3_fullsync_count, TCL_LINK_INT); #endif /* OS_UNIX */ set_options(interp); { extern int sqlite3_shared_cache_report(void *, Tcl_Interp *, int, Tcl_Obj *CONST[]); Tcl_CreateObjCommand(interp, "sqlite_shared_cache_report", sqlite3_shared_cache_report, 0, 0); } return TCL_OK; } | > > | 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 | (char*)&sqlite3_sync_count, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_fullsync_count", (char*)&sqlite3_fullsync_count, TCL_LINK_INT); #endif /* OS_UNIX */ set_options(interp); { #ifdef SQLITE_DEBUG extern int sqlite3_shared_cache_report(void *, Tcl_Interp *, int, Tcl_Obj *CONST[]); Tcl_CreateObjCommand(interp, "sqlite_shared_cache_report", sqlite3_shared_cache_report, 0, 0); #endif } return TCL_OK; } |
Changes to src/test3.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 | ** 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. ** | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** Code for testing the 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.65 2006/01/20 10:55:05 danielk1977 Exp $ */ #include "sqliteInt.h" #include "pager.h" #include "btree.h" #include "tcl.h" #include <stdlib.h> #include <string.h> |
︙ | ︙ | |||
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 | } /* ** Usage: btree_cursor_list ID ** ** Print information about all cursors to standard output for debugging. */ static int btree_cursor_list( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ const char **argv /* Text of each argument */ ){ Btree *pBt; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } pBt = sqlite3TextToPtr(argv[1]); sqlite3BtreeCursorList(pBt); return SQLITE_OK; } /* ** Usage: btree_cursor ID TABLENUM WRITEABLE ** ** Create a new cursor. Return the ID for the cursor. */ static int btree_cursor( | > > | 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 | } /* ** Usage: btree_cursor_list ID ** ** Print information about all cursors to standard output for debugging. */ #ifdef SQLITE_DEBUG static int btree_cursor_list( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ const char **argv /* Text of each argument */ ){ Btree *pBt; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID\"", 0); return TCL_ERROR; } pBt = sqlite3TextToPtr(argv[1]); sqlite3BtreeCursorList(pBt); return SQLITE_OK; } #endif /* ** Usage: btree_cursor ID TABLENUM WRITEABLE ** ** Create a new cursor. Return the ID for the cursor. */ static int btree_cursor( |
︙ | ︙ | |||
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 | ** aResult[4] = Number of free bytes on this page ** aResult[5] = Number of free blocks on the page ** aResult[6] = Total payload size (local + overflow) ** aResult[7] = Header size in bytes ** aResult[8] = Local payload size ** aResult[9] = Parent page number */ static int btree_cursor_info( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ const char **argv /* Text of each argument */ ){ BtCursor *pCur; | > | 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 | ** aResult[4] = Number of free bytes on this page ** aResult[5] = Number of free blocks on the page ** aResult[6] = Total payload size (local + overflow) ** aResult[7] = Header size in bytes ** aResult[8] = Local payload size ** aResult[9] = Parent page number */ #ifdef SQLITE_DEBUG static int btree_cursor_info( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ const char **argv /* Text of each argument */ ){ BtCursor *pCur; |
︙ | ︙ | |||
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 | for(i=0; i<sizeof(aResult)/sizeof(aResult[0]); i++){ sqlite3_snprintf(40,&zBuf[j]," %d", aResult[i]); j += strlen(&zBuf[j]); } Tcl_AppendResult(interp, &zBuf[1], 0); return SQLITE_OK; } /* ** The command is provided for the purpose of setting breakpoints. ** in regression test scripts. ** ** By setting a GDB breakpoint on this procedure and executing the ** btree_breakpoint command in a test script, we can stop GDB at | > | 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 | for(i=0; i<sizeof(aResult)/sizeof(aResult[0]); i++){ sqlite3_snprintf(40,&zBuf[j]," %d", aResult[i]); j += strlen(&zBuf[j]); } Tcl_AppendResult(interp, &zBuf[1], 0); return SQLITE_OK; } #endif /* ** The command is provided for the purpose of setting breakpoints. ** in regression test scripts. ** ** By setting a GDB breakpoint on this procedure and executing the ** btree_breakpoint command in a test script, we can stop GDB at |
︙ | ︙ | |||
1424 1425 1426 1427 1428 1429 1430 | { "btree_key", (Tcl_CmdProc*)btree_key }, { "btree_data", (Tcl_CmdProc*)btree_data }, { "btree_fetch_key", (Tcl_CmdProc*)btree_fetch_key }, { "btree_fetch_data", (Tcl_CmdProc*)btree_fetch_data }, { "btree_payload_size", (Tcl_CmdProc*)btree_payload_size }, { "btree_first", (Tcl_CmdProc*)btree_first }, { "btree_last", (Tcl_CmdProc*)btree_last }, | < < > > > > | 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 | { "btree_key", (Tcl_CmdProc*)btree_key }, { "btree_data", (Tcl_CmdProc*)btree_data }, { "btree_fetch_key", (Tcl_CmdProc*)btree_fetch_key }, { "btree_fetch_data", (Tcl_CmdProc*)btree_fetch_data }, { "btree_payload_size", (Tcl_CmdProc*)btree_payload_size }, { "btree_first", (Tcl_CmdProc*)btree_first }, { "btree_last", (Tcl_CmdProc*)btree_last }, { "btree_integrity_check", (Tcl_CmdProc*)btree_integrity_check }, { "btree_breakpoint", (Tcl_CmdProc*)btree_breakpoint }, { "btree_varint_test", (Tcl_CmdProc*)btree_varint_test }, { "btree_begin_statement", (Tcl_CmdProc*)btree_begin_statement }, { "btree_commit_statement", (Tcl_CmdProc*)btree_commit_statement }, { "btree_rollback_statement", (Tcl_CmdProc*)btree_rollback_statement }, { "btree_from_db", (Tcl_CmdProc*)btree_from_db }, { "btree_set_cache_size", (Tcl_CmdProc*)btree_set_cache_size }, #ifdef SQLITE_DEBUG { "btree_cursor_info", (Tcl_CmdProc*)btree_cursor_info }, { "btree_cursor_list", (Tcl_CmdProc*)btree_cursor_list }, #endif }; int i; for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); } Tcl_LinkVar(interp, "pager_refinfo_enable", (char*)&pager3_refinfo_enable, |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 | for(pOther=pVdbe->db->pVdbe; pOther; pOther=pOther->pNext){ if( pOther==pVdbe ) continue; if( pOther->magic!=VDBE_MAGIC_RUN || pOther->pc<0 ) continue; closeAllCursors(pOther); pOther->aborted = 1; } } /* ** This routine checks that the sqlite3.activeVdbeCnt count variable ** matches the number of vdbe's in the list sqlite3.pVdbe that are ** currently active. An assertion fails if the two counts do not match. ** This is an internal self-check only - it is not an essential processing ** step. | > > > > > | 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 | for(pOther=pVdbe->db->pVdbe; pOther; pOther=pOther->pNext){ if( pOther==pVdbe ) continue; if( pOther->magic!=VDBE_MAGIC_RUN || pOther->pc<0 ) continue; closeAllCursors(pOther); pOther->aborted = 1; } } static void rollbackAll(sqlite3 *db, Vdbe *pVdbe){ abortOtherActiveVdbes(pVdbe); sqlite3RollbackAll(db); } /* ** This routine checks that the sqlite3.activeVdbeCnt count variable ** matches the number of vdbe's in the list sqlite3.pVdbe that are ** currently active. An assertion fails if the two counts do not match. ** This is an internal self-check only - it is not an essential processing ** step. |
︙ | ︙ | |||
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 | ** 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 */ if( sqlite3MallocFailed() ){ p->rc = SQLITE_NOMEM; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | | < | < < < < < < < < < < < < < | < < < < | | > | | | | < < | | > > > > > | | > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | > > | | | | | | | | | | | | | > | 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 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 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 | ** 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. ** ** Special errors: ** ** If an SQLITE_NOMEM error has occured in a statement that writes to ** the database, then either a statement or transaction must be rolled ** back to ensure the tree-structures are in a consistent state. A ** statement transaction is rolled back if one is open, otherwise the ** entire transaction must be rolled back. ** ** If an SQLITE_IOERR error has occured in a statement that writes to ** the database, then the entire transaction must be rolled back. The ** I/O error may have caused garbage to be written to the journal ** file. Were the transaction to continue and eventually be rolled ** back that garbage might end up in the database file. ** ** In both of the above cases, the Vdbe.errorAction variable is ** ignored. If the sqlite3.autoCommit flag is false and a transaction ** is rolled back, it will be set to true. ** ** Other errors: ** ** No error: ** */ if( sqlite3MallocFailed() ){ p->rc = SQLITE_NOMEM; } if( p->magic!=VDBE_MAGIC_RUN ){ /* Already halted. Nothing to do. */ assert( p->magic==VDBE_MAGIC_HALT ); return SQLITE_OK; } closeAllCursors(p); checkActiveVdbeCnt(db); /* No commit or rollback needed if the program never started */ if( p->pc>=0 ){ /* Check for one of the special errors - SQLITE_NOMEM or SQLITE_IOERR */ isSpecialError = ((p->rc==SQLITE_NOMEM || p->rc==SQLITE_IOERR)?1:0); if( isSpecialError ){ /* This loop does static analysis of the query to see which of the ** following three categories it falls into: ** ** Read-only ** Query with statement journal ** Query without statement journal ** ** We could do something more elegant than this static analysis (i.e. ** store the type of query as part of the compliation phase), but ** handling malloc() or IO failure is a fairly obscure edge case so ** this is probably easier. Todo: Might be an opportunity to reduce ** code size a very small amount though... */ int isReadOnly = 1; int isStatement = 0; assert(p->aOp || p->nOp==0); for(i=0; i<p->nOp; i++){ switch( p->aOp[i].opcode ){ case OP_Transaction: isReadOnly = 0; break; case OP_Statement: isStatement = 1; break; } } /* If the query was read-only, we need do no rollback at all. Otherwise, ** proceed with the special handling. */ if( !isReadOnly ){ if( p->rc==SQLITE_NOMEM && isStatement ){ xFunc = sqlite3BtreeRollbackStmt; }else{ /* We are forced to roll back the active transaction. Before doing ** so, abort any other statements this handle currently has active. */ 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 occured. */ if( db->autoCommit && db->activeVdbeCnt==1 ){ 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); if( rc==SQLITE_BUSY ){ return SQLITE_BUSY; }else if( rc!=SQLITE_OK ){ p->rc = rc; rollbackAll(db, p); }else{ sqlite3CommitInternalChanges(db); } }else{ rollbackAll(db, p); } }else if( !xFunc ){ if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ xFunc = sqlite3BtreeCommitStmt; }else if( p->errorAction==OE_Abort ){ xFunc = sqlite3BtreeRollbackStmt; }else{ rollbackAll(db, p); 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 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; } /* 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/malloc3.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # # 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. # | | | 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.7 2006/01/20 10:55:05 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..." |
︙ | ︙ | |||
478 479 480 481 482 483 484 485 486 487 488 489 490 491 | do_test $testid { execsql { SELECT * FROM tbl2, def WHERE d = x; } } {1 2 3 1 2 3} } # # End of test program declaration #-------------------------------------------------------------------------- proc run_test {arglist {pcstart 0} {iFailStart 1}} { if {[llength $arglist] %2} { error "Uneven number of arguments to TEST" | > > > > > > > > > > > > > > > > > > | 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | do_test $testid { execsql { SELECT * FROM tbl2, def WHERE d = x; } } {1 2 3 1 2 3} } # Test what happens when a malloc() fails while there are other active # statements. This changes the way sqlite3VdbeHalt() works. TEST 32 { if {![info exists ::STMT32]} { set sql "SELECT name FROM sqlite_master" set ::STMT32 [sqlite3_prepare $::DB $sql -1 DUMMY] do_test $testid { sqlite3_step $::STMT32 } {SQLITE_ROW} } puts [execsql {SELECT * FROM ghi}] } SQL { BEGIN; INSERT INTO ghi SELECT * FROM ghi; COMMIT; } # # End of test program declaration #-------------------------------------------------------------------------- proc run_test {arglist {pcstart 0} {iFailStart 1}} { if {[llength $arglist] %2} { error "Uneven number of arguments to TEST" |
︙ | ︙ | |||
608 609 610 611 612 613 614 | # if {$iFail > ($iFailStart+1)} return } } # Turn of the Tcl interface's prepared statement caching facility. db cache size 0 | | | 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 | # if {$iFail > ($iFailStart+1)} return } } # Turn of the Tcl interface's prepared statement caching facility. db cache size 0 run_test $::run_test_script 76 6 # run_test [lrange $::run_test_script 0 3] 0 63 sqlite_malloc_fail 0 db close pp_check_for_leaks finish_test |
Changes to test/quick.test.
1 2 3 4 5 6 7 8 | # # 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 runs all tests. # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # # 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 runs all tests. # # $Id: quick.test,v 1.42 2006/01/20 10:55:05 danielk1977 Exp $ proc lshift {lvar} { upvar $lvar l set ret [lindex $l 0] set l [lrange $l 1 end] return $ret } |
︙ | ︙ | |||
41 42 43 44 45 46 47 | btree5.test btree6.test corrupt.test crash.test malloc.test malloc2.test malloc3.test | < > | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | btree5.test btree6.test corrupt.test crash.test malloc.test malloc2.test malloc3.test memleak.test misuse.test quick.test autovacuum_crash.test btree8.test utf16.test shared_err.test } if {[sqlite3 -has-codec]} { # lappend EXCLUDE \ # conflict.test } |
︙ | ︙ |
Added test/shared_err.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 | # 2005 December 30 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The focus of the tests in this file are IO errors that occur in a shared # cache context. What happens to connection B if one connection A encounters # an IO-error whilst reading or writing the file-system? # # $Id: shared_err.test,v 1.1 2006/01/20 10:55:05 danielk1977 Exp $ proc skip {args} {} set testdir [file dirname $argv0] source $testdir/tester.tcl db close ifcapable !shared_cache||!subquery { finish_test return } set ::enable_shared_cache [sqlite3_enable_shared_cache 1] skip \ do_ioerr_test shared_ioerr-1 -tclprep { sqlite3 db2 test.db execsql { PRAGMA read_uncommitted = 1; CREATE TABLE t1(a,b,c); BEGIN; SELECT * FROM sqlite_master; } db2 } -sqlbody { SELECT * FROM sqlite_master; INSERT INTO t1 VALUES(1,2,3); BEGIN TRANSACTION; INSERT INTO t1 VALUES(1,2,3); INSERT INTO t1 VALUES(4,5,6); ROLLBACK; SELECT * FROM t1; BEGIN TRANSACTION; INSERT INTO t1 VALUES(1,2,3); INSERT INTO t1 VALUES(4,5,6); COMMIT; SELECT * FROM t1; DELETE FROM t1 WHERE a<100; } -cleanup { do_test shared_ioerr-$n.cleanup.1 { set res [catchsql { SELECT * FROM t1; } db2] set possible_results [list \ "1 {disk I/O error}" \ "0 {1 2 3}" \ "0 {1 2 3 1 2 3 4 5 6}" \ "0 {1 2 3 1 2 3 4 5 6 1 2 3 4 5 6}" \ "0 {}" \ ] set rc [expr [lsearch -exact $possible_results $res] >= 0] if {$rc != 1} { puts "" puts "Result: $res" } set rc } {1} db2 close } do_ioerr_test shared_ioerr-2 -tclprep { sqlite3 db2 test.db execsql { PRAGMA read_uncommitted = 1; BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1(oid) VALUES(NULL); INSERT INTO t1(oid) SELECT NULL FROM t1; INSERT INTO t1(oid) SELECT NULL FROM t1; INSERT INTO t1(oid) SELECT NULL FROM t1; INSERT INTO t1(oid) SELECT NULL FROM t1; INSERT INTO t1(oid) SELECT NULL FROM t1; INSERT INTO t1(oid) SELECT NULL FROM t1; INSERT INTO t1(oid) SELECT NULL FROM t1; INSERT INTO t1(oid) SELECT NULL FROM t1; INSERT INTO t1(oid) SELECT NULL FROM t1; INSERT INTO t1(oid) SELECT NULL FROM t1; UPDATE t1 set a = oid, b = 'abcdefghijklmnopqrstuvwxyz0123456789'; CREATE INDEX i1 ON t1(a); COMMIT; BEGIN; SELECT * FROM sqlite_master; } db2 } -tclbody { set ::residx 0 execsql {DELETE FROM t1 WHERE 0 = (a % 2);} incr ::residx # When this transaction begins the table contains 512 entries. The # two statements together add 512+146 more if it succeeds. # (1024/7==146) execsql {BEGIN;} execsql {INSERT INTO t1 SELECT a+1, b FROM t1;} execsql {INSERT INTO t1 SELECT 'string' || a, b FROM t1 WHERE 0 = (a%7);} execsql {COMMIT;} incr ::residx } -cleanup { do_test shared_ioerr-2.$n.cleanup.1 { set res [catchsql { SELECT max(a), min(a), count(*) FROM (SELECT a FROM t1 order by a); } db2] set possible_results [list \ {0 {1024 1 1024}} \ {0 {1023 1 512}} \ {0 {string994 1 1170}} \ ] set idx [lsearch -exact $possible_results $res] set success [expr {$idx==$::residx || $res=="1 {disk I/O error}"}] if {!$success} { puts "" puts "Result: \"$res\" ($::residx)" } set success } {1} db2 close } catch {db close} sqlite3_enable_shared_cache $::enable_shared_cache finish_test |