Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | VACUUM works even on an empty database. Ticket #1512. (CVS 2760) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
1b6bf4188e8ebf55cf1972b7081f6d31 |
User & Date: | drh 2005-11-04 22:03:30.000 |
Context
2005-11-05
| ||
15:04 | Improved table compression in lemon reduces the overall library size by about 1.5KiB. (CVS 2761) (check-in: 86ac114769 user: drh tags: trunk) | |
2005-11-04
| ||
22:03 | VACUUM works even on an empty database. Ticket #1512. (CVS 2760) (check-in: 1b6bf4188e user: drh tags: trunk) | |
2005-11-03
| ||
14:29 | sqlite3_column_int() and similar routines return 0 or 0.0 on an out-of-memory condition, not some mysterious error code. (CVS 2759) (check-in: 7780f5e9d5 user: drh tags: trunk) | |
Changes
Changes to src/os_common.h.
︙ | ︙ | |||
84 85 86 87 88 89 90 91 92 93 94 95 96 97 | /* ** If we compile with the SQLITE_TEST macro set, then the following block ** of code will give us the ability to simulate a disk I/O error. This ** is used for testing the I/O recovery logic. */ #ifdef SQLITE_TEST int sqlite3_io_error_pending = 0; int sqlite3_diskfull_pending = 0; int sqlite3_diskfull = 0; #define SimulateIOError(A) \ if( sqlite3_io_error_pending ) \ if( sqlite3_io_error_pending-- == 1 ){ local_ioerr(); return A; } static void local_ioerr(){ | > | | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | /* ** If we compile with the SQLITE_TEST macro set, then the following block ** of code will give us the ability to simulate a disk I/O error. This ** is used for testing the I/O recovery logic. */ #ifdef SQLITE_TEST int sqlite3_io_error_hit = 0; int sqlite3_io_error_pending = 0; int sqlite3_diskfull_pending = 0; int sqlite3_diskfull = 0; #define SimulateIOError(A) \ if( sqlite3_io_error_pending ) \ if( sqlite3_io_error_pending-- == 1 ){ local_ioerr(); return A; } static void local_ioerr(){ sqlite3_io_error_hit = 1; /* Really just a place to set a breakpoint */ } #define SimulateDiskfullError \ if( sqlite3_diskfull_pending ){ \ if( sqlite3_diskfull_pending == 1 ){ \ local_ioerr(); \ sqlite3_diskfull = 1; \ return SQLITE_FULL; \ |
︙ | ︙ |
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.216 2005/11/04 22:03:30 drh Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" #include "os.h" #include "pager.h" #include <assert.h> #include <string.h> |
︙ | ︙ | |||
1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 | assert( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE ); if( !pPager->memDb ){ pPager->pageSize = pageSize; } return pPager->pageSize; } /* ** Read the first N bytes from the beginning of the file into memory ** that pDest points to. No error checking is done. */ void sqlite3pager_read_fileheader(Pager *pPager, int N, unsigned char *pDest){ memset(pDest, 0, N); if( MEMDB==0 ){ sqlite3OsSeek(&pPager->fd, 0); sqlite3OsRead(&pPager->fd, pDest, N); } } /* ** Return the total number of pages in the disk file associated with ** pPager. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 | assert( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE ); if( !pPager->memDb ){ pPager->pageSize = pageSize; } return pPager->pageSize; } /* ** The following set of routines are used to disable the simulated ** I/O error mechanism. These routines are used to avoid simulated ** errors in places where we do not care about errors. ** ** Unless -DSQLITE_TEST=1 is used, these routines are all no-ops ** and generate no code. */ #ifdef SQLITE_TEST extern int sqlite3_io_error_pending; extern int sqlite3_io_error_hit; static int saved_cnt; void clear_simulated_io_error(){ sqlite3_io_error_hit = 0; } void disable_simulated_io_errors(void){ saved_cnt = sqlite3_io_error_pending; sqlite3_io_error_pending = -1; } void enable_simulated_io_errors(void){ sqlite3_io_error_pending = saved_cnt; } #else # define clear_simulated_io_error(X) # define disable_simulated_io_errors(X) # define enable_simulated_io_errors(X) #endif /* ** Read the first N bytes from the beginning of the file into memory ** that pDest points to. No error checking is done. */ void sqlite3pager_read_fileheader(Pager *pPager, int N, unsigned char *pDest){ memset(pDest, 0, N); if( MEMDB==0 ){ sqlite3OsSeek(&pPager->fd, 0); sqlite3OsRead(&pPager->fd, pDest, N); clear_simulated_io_error(); } } /* ** Return the total number of pages in the disk file associated with ** pPager. ** |
︙ | ︙ | |||
1957 1958 1959 1960 1961 1962 1963 | case PAGER_RESERVED: case PAGER_SYNCED: case PAGER_EXCLUSIVE: { /* We ignore any IO errors that occur during the rollback ** operation. So disable IO error simulation so that testing ** works more easily. */ | < < < | < < | < | 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 | case PAGER_RESERVED: case PAGER_SYNCED: case PAGER_EXCLUSIVE: { /* We ignore any IO errors that occur during the rollback ** operation. So disable IO error simulation so that testing ** works more easily. */ disable_simulated_io_errors(); sqlite3pager_rollback(pPager); enable_simulated_io_errors(); if( !MEMDB ){ sqlite3OsUnlock(&pPager->fd, NO_LOCK); } assert( pPager->errMask || pPager->journalOpen==0 ); break; } case PAGER_SHARED: { |
︙ | ︙ | |||
2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 | if( rc!=SQLITE_OK ){ i64 fileSize; if( sqlite3OsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK || fileSize>=pgno*pPager->pageSize ){ sqlite3pager_unref(PGHDR_TO_DATA(pPg)); return rc; }else{ memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize); } }else{ TEST_INCR(pPager->nRead); } } #ifdef SQLITE_CHECK_PAGES | > | 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 | if( rc!=SQLITE_OK ){ i64 fileSize; if( sqlite3OsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK || fileSize>=pgno*pPager->pageSize ){ sqlite3pager_unref(PGHDR_TO_DATA(pPg)); return rc; }else{ clear_simulated_io_error(); memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize); } }else{ TEST_INCR(pPager->nRead); } } #ifdef SQLITE_CHECK_PAGES |
︙ | ︙ |
Changes to src/test2.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** Code for testing the pager.c module in SQLite. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** | | | 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 pager.c module in SQLite. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** ** $Id: test2.c,v 1.35 2005/11/04 22:03:30 drh Exp $ */ #include "sqliteInt.h" #include "os.h" #include "pager.h" #include "tcl.h" #include <stdlib.h> #include <string.h> |
︙ | ︙ | |||
556 557 558 559 560 561 562 563 564 565 566 567 568 569 | #endif /* ** Register commands with the TCL interpreter. */ int Sqlitetest2_Init(Tcl_Interp *interp){ extern int sqlite3_io_error_pending; extern int sqlite3_diskfull_pending; extern int sqlite3_diskfull; static struct { char *zName; Tcl_CmdProc *xProc; } aCmd[] = { { "pager_open", (Tcl_CmdProc*)pager_open }, | > | 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 | #endif /* ** Register commands with the TCL interpreter. */ int Sqlitetest2_Init(Tcl_Interp *interp){ extern int sqlite3_io_error_pending; extern int sqlite3_io_error_hit; extern int sqlite3_diskfull_pending; extern int sqlite3_diskfull; static struct { char *zName; Tcl_CmdProc *xProc; } aCmd[] = { { "pager_open", (Tcl_CmdProc*)pager_open }, |
︙ | ︙ | |||
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 | }; 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, "sqlite_io_error_pending", (char*)&sqlite3_io_error_pending, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_diskfull_pending", (char*)&sqlite3_diskfull_pending, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_diskfull", (char*)&sqlite3_diskfull, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_pending_byte", (char*)&sqlite3_pending_byte, TCL_LINK_INT); Tcl_LinkVar(interp, "pager_pagesize", (char*)&test_pagesize, TCL_LINK_INT); return TCL_OK; } | > > | 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 | }; 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, "sqlite_io_error_pending", (char*)&sqlite3_io_error_pending, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_io_error_hit", (char*)&sqlite3_io_error_hit, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_diskfull_pending", (char*)&sqlite3_diskfull_pending, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_diskfull", (char*)&sqlite3_diskfull, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_pending_byte", (char*)&sqlite3_pending_byte, TCL_LINK_INT); Tcl_LinkVar(interp, "pager_pagesize", (char*)&test_pagesize, TCL_LINK_INT); return TCL_OK; } |
Changes to src/vacuum.c.
︙ | ︙ | |||
10 11 12 13 14 15 16 | ** ************************************************************************* ** This file contains code used to implement the VACUUM command. ** ** Most of the code in this file may be omitted by defining the ** SQLITE_OMIT_VACUUM macro. ** | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ** ************************************************************************* ** This file contains code used to implement the VACUUM command. ** ** Most of the code in this file may be omitted by defining the ** SQLITE_OMIT_VACUUM macro. ** ** $Id: vacuum.c,v 1.47 2005/11/04 22:03:30 drh Exp $ */ #include "sqliteInt.h" #include "os.h" #ifndef SQLITE_OMIT_VACUUM /* ** Generate a random name of 20 character in length. |
︙ | ︙ | |||
96 97 98 99 100 101 102 103 104 105 106 107 108 109 | #ifndef SQLITE_OMIT_VACUUM const char *zFilename; /* full pathname of the database file */ int nFilename; /* number of characters in zFilename[] */ char *zTemp = 0; /* a temporary file in same directory as zFilename */ Btree *pMain; /* The database being vacuumed */ Btree *pTemp; char *zSql = 0; int saved_flags; /* Saved value of the db->flags */ /* Save the current value of the write-schema flag before setting it. */ saved_flags = db->flags; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; if( !db->autoCommit ){ | > | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | #ifndef SQLITE_OMIT_VACUUM const char *zFilename; /* full pathname of the database file */ int nFilename; /* number of characters in zFilename[] */ char *zTemp = 0; /* a temporary file in same directory as zFilename */ Btree *pMain; /* The database being vacuumed */ Btree *pTemp; char *zSql = 0; int rc2; int saved_flags; /* Saved value of the db->flags */ /* Save the current value of the write-schema flag before setting it. */ saved_flags = db->flags; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; if( !db->autoCommit ){ |
︙ | ︙ | |||
172 173 174 175 176 177 178 | execSql(db, "PRAGMA vacuum_db.synchronous=OFF"); #ifndef SQLITE_OMIT_AUTOVACUUM sqlite3BtreeSetAutoVacuum(pTemp, sqlite3BtreeGetAutoVacuum(pMain)); #endif /* Begin a transaction */ | | | 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | execSql(db, "PRAGMA vacuum_db.synchronous=OFF"); #ifndef SQLITE_OMIT_AUTOVACUUM sqlite3BtreeSetAutoVacuum(pTemp, sqlite3BtreeGetAutoVacuum(pMain)); #endif /* Begin a transaction */ rc = execSql(db, "BEGIN EXCLUSIVE;"); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Query the schema of the main database. Create a mirror schema ** in the temporary database. */ rc = execExecSql(db, "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14,100000000) " |
︙ | ︙ | |||
243 244 245 246 247 248 249 | ** transaction open on the vacuum database, but not on the main database. ** Open a btree level transaction on the main database. This allows a ** call to sqlite3BtreeCopyFile(). The main database btree level ** transaction is then committed, so the SQL level never knows it was ** opened for writing. This way, the SQL transaction used to create the ** temporary database never needs to be committed. */ | | | | < > > > > | < < | | 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 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | ** transaction open on the vacuum database, but not on the main database. ** Open a btree level transaction on the main database. This allows a ** call to sqlite3BtreeCopyFile(). The main database btree level ** transaction is then committed, so the SQL level never knows it was ** opened for writing. This way, the SQL transaction used to create the ** temporary database never needs to be committed. */ if( rc==SQLITE_OK ){ u32 meta; int i; /* This array determines which meta meta values are preserved in the ** vacuum. Even entries are the meta value number and odd entries ** are an increment to apply to the meta value after the vacuum. ** The increment is used to increase the schema cookie so that other ** connections to the same database will know to reread the schema. */ static const unsigned char aCopy[] = { 1, 1, /* Add one to the old schema cookie */ 3, 0, /* Preserve the default page cache size */ 5, 0, /* Preserve the default text encoding */ 6, 0, /* Preserve the user version */ }; assert( 1==sqlite3BtreeIsInTrans(pTemp) ); assert( 1==sqlite3BtreeIsInTrans(pMain) ); /* Copy Btree meta values */ for(i=0; i<sizeof(aCopy)/sizeof(aCopy[0]); i+=2){ rc = sqlite3BtreeGetMeta(pMain, aCopy[i], &meta); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeUpdateMeta(pTemp, aCopy[i], meta+aCopy[i+1]); if( rc!=SQLITE_OK ) goto end_of_vacuum; } rc = sqlite3BtreeCopyFile(pMain, pTemp); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeCommit(pTemp); if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = sqlite3BtreeCommit(pMain); } end_of_vacuum: /* Restore the original value of db->flags */ db->flags = saved_flags; /* Currently there is an SQL level transaction open on the vacuum ** database. No locks are held on any other files (since the main file ** was committed at the btree level). So it safe to end the transaction ** by manually setting the autoCommit flag to true and detaching the ** vacuum database. The vacuum_db journal file is deleted when the pager ** is closed by the DETACH. */ db->autoCommit = 1; rc2 = execSql(db, "DETACH vacuum_db;"); if( rc==SQLITE_OK ){ rc = rc2; } if( zTemp ){ sqlite3OsDelete(zTemp); sqliteFree(zTemp); } sqliteFree( zSql ); sqlite3ResetInternalSchema(db, 0); #endif return rc; } |
Changes to test/tester.tcl.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 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 some common TCL routines used for regression # testing the SQLite library # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # 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 some common TCL routines used for regression # testing the SQLite library # # $Id: tester.tcl,v 1.51 2005/11/04 22:03:30 drh Exp $ # Make sure tclsqlite3 was compiled correctly. Abort now with an # error message if not. # if {[sqlite3 -tcl-uses-utf]} { if {"\u1234"=="u1234"} { puts stderr "***** BUILD PROBLEM *****" |
︙ | ︙ | |||
358 359 360 361 362 363 364 | # Execute the TCL Script created in the above block. If # there are at least N IO operations performed by SQLite as # a result of the script, the Nth will fail. do_test $testname.$n.3 { set r [catch $::ioerrorbody msg] set ::go [expr {$::sqlite_io_error_pending<=0}] | | | 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | # Execute the TCL Script created in the above block. If # there are at least N IO operations performed by SQLite as # a result of the script, the Nth will fail. do_test $testname.$n.3 { set r [catch $::ioerrorbody msg] set ::go [expr {$::sqlite_io_error_pending<=0}] set s [expr $::sqlite_io_error_hit==0] # puts "$::sqlite_io_error_pending $r $msg" expr { ($s && !$r) || (!$s && $r) } # expr {$::sqlite_io_error_pending>0 || $r!=0} } {1} # If an IO error occured, then the checksum of the database should # be the same as before the script that caused the IO error was run. |
︙ | ︙ |
Added test/tkt1512.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 | # 2005 September 19 # # 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. # # This file implements tests to verify that ticket #1512 is # fixed. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test tkt1512-1.1 { execsql { CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2); INSERT INTO t1 VALUES(3,4); SELECT * FROM t1 } } {1 2 3 4} do_test tkt1512-1.2 { file size test.db } {2048} do_test tkt1512-1.3 { execsql { DROP TABLE t1; } file size test.db } {2048} do_test tkt1512-1.4 { execsql { VACUUM; } file size test.db } {1024} finish_test |
Changes to tool/showdb.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #include <unistd.h> #include <stdlib.h> static int pagesize = 1024; static int db = -1; static int mxPage = 0; static void out_of_memory(void){ fprintf(stderr,"Out of memory...\n"); exit(1); } static print_page(int iPg){ unsigned char *aData; int i, j; aData = malloc(pagesize); if( aData==0 ) out_of_memory(); lseek(db, (iPg-1)*pagesize, SEEK_SET); read(db, aData, pagesize); fprintf(stdout, "Page %d:\n", iPg); | > | | | | 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 | #include <unistd.h> #include <stdlib.h> static int pagesize = 1024; static int db = -1; static int mxPage = 0; static int perLine = 32; static void out_of_memory(void){ fprintf(stderr,"Out of memory...\n"); exit(1); } static print_page(int iPg){ unsigned char *aData; int i, j; aData = malloc(pagesize); if( aData==0 ) out_of_memory(); lseek(db, (iPg-1)*pagesize, SEEK_SET); read(db, aData, pagesize); fprintf(stdout, "Page %d:\n", iPg); for(i=0; i<pagesize; i += perLine){ fprintf(stdout, " %03x: ",i); for(j=0; j<perLine; j++){ fprintf(stdout,"%02x ", aData[i+j]); } for(j=0; j<perLine; j++){ fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.'); } fprintf(stdout,"\n"); } free(aData); } |
︙ | ︙ |