/ Check-in [caa96028]
Login

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

Overview
Comment:Make VACUUM work even if multiple processes have the database open at once. (CVS 933)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: caa960289f3d1f5e8f35a94e9e4321996c211ed2
User & Date: drh 2003-04-25 13:22:52
Context
2003-04-25
13:28
Work around a name collision problem on windows. (CVS 934) check-in: c3b1f84d user: drh tags: trunk
13:22
Make VACUUM work even if multiple processes have the database open at once. (CVS 933) check-in: caa96028 user: drh tags: trunk
03:13
Explicit casts to squelch bogus warnings for vc++. Ticket #194. (CVS 932) check-in: cb808c14 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
....
3486
3487
3488
3489
3490
3491
3492
3493




3494
3495
3496






















3497
3498
3499
3500
3501
3502
3503
....
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
** 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.90 2003/04/25 03:13:25 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.
................................................................................
*/
static const char *fileBtreeGetFilename(Btree *pBt){
  assert( pBt->pPager!=0 );
  return sqlitepager_filename(pBt->pPager);
}

/*
** Change the name of the underlying database file.




*/
static int fileBtreeChangeFilename(Btree *pBt, const char *zNew){
  return sqlitepager_rename(pBt->pPager, zNew);






















}

/*
** The following tables contain pointers to all of the interface
** routines for this implementation of the B*Tree backend.  To
** substitute a different implemention of the backend, one has merely
** to provide pointers to alternative functions in similar tables.
................................................................................
    fileBtreeDropTable,
    fileBtreeClearTable,
    fileBtreeCursor,
    fileBtreeGetMeta,
    fileBtreeUpdateMeta,
    fileBtreeIntegrityCheck,
    fileBtreeGetFilename,
    fileBtreeChangeFilename,
#ifdef SQLITE_TEST
    fileBtreePageDump,
    fileBtreePager
#endif
};
static BtCursorOps sqliteBtreeCursorOps = {
    fileBtreeMoveto,







|







 







|
>
>
>
>

<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498


3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
....
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
** 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.91 2003/04/25 13:22:52 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.
................................................................................
*/
static const char *fileBtreeGetFilename(Btree *pBt){
  assert( pBt->pPager!=0 );
  return sqlitepager_filename(pBt->pPager);
}

/*
** Copy the complete content of pBtFrom into pBtTo.  A transaction
** must be active for both files.
**
** The size of file pBtFrom may be reduced by this operation.
** If anything goes wrong, the transaction on pBtFrom is rolled back.
*/


static int fileBtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){
  int rc = SQLITE_OK;
  Pgno i, nPage;

  if( !pBtTo->inTrans || !pBtFrom->inTrans ) return SQLITE_ERROR;
  if( pBtTo->needSwab!=pBtFrom->needSwab ) return SQLITE_ERROR;
  if( pBtTo->pCursor ) return SQLITE_BUSY;
  memcpy(pBtTo->page1, pBtFrom->page1, SQLITE_PAGE_SIZE);
  sqlitepager_overwrite(pBtTo->pPager, 1, pBtFrom->page1);
  nPage = sqlitepager_pagecount(pBtFrom->pPager);
  for(i=2; i<=nPage; i++){
    void *pPage;
    rc = sqlitepager_get(pBtFrom->pPager, i, &pPage);
    if( rc ) break;
    sqlitepager_overwrite(pBtTo->pPager, i, pPage);
    sqlitepager_unref(pPage);
  }
  if( !rc ) rc = sqlitepager_truncate(pBtTo->pPager, nPage);
  if( rc ){
    fileBtreeRollback(pBtTo);
  }
  return rc;  
}

/*
** The following tables contain pointers to all of the interface
** routines for this implementation of the B*Tree backend.  To
** substitute a different implemention of the backend, one has merely
** to provide pointers to alternative functions in similar tables.
................................................................................
    fileBtreeDropTable,
    fileBtreeClearTable,
    fileBtreeCursor,
    fileBtreeGetMeta,
    fileBtreeUpdateMeta,
    fileBtreeIntegrityCheck,
    fileBtreeGetFilename,
    fileBtreeCopyFile,
#ifdef SQLITE_TEST
    fileBtreePageDump,
    fileBtreePager
#endif
};
static BtCursorOps sqliteBtreeCursorOps = {
    fileBtreeMoveto,

Changes to src/btree.h.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
**    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.32 2003/04/25 02:43:08 drh Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_

/*
** Forward declarations of structure
*/
................................................................................
    int (*DropTable)(Btree*, int);
    int (*ClearTable)(Btree*, int);
    int (*Cursor)(Btree*, int iTable, int wrFlag, BtCursor **ppCur);
    int (*GetMeta)(Btree*, int*);
    int (*UpdateMeta)(Btree*, int*);
    char *(*IntegrityCheck)(Btree*, int*, int);
    const char *(*GetFilename)(Btree*);
    int (*ChangeFilename)(Btree*, const char *zNew);
#ifdef SQLITE_TEST
    int (*PageDump)(Btree*, int, int);
    struct Pager *(*Pager)(Btree*);
#endif
};

/*
................................................................................
                (btCOps(pCur)->Data(pCur, offset, amt, zBuf))
#define sqliteBtreeCloseCursor(pCur)      (btCOps(pCur)->CloseCursor(pCur))
#define sqliteBtreeGetMeta(pBt, aMeta)    (btOps(pBt)->GetMeta(pBt, aMeta))
#define sqliteBtreeUpdateMeta(pBt, aMeta) (btOps(pBt)->UpdateMeta(pBt, aMeta))
#define sqliteBtreeIntegrityCheck(pBt, aRoot, nRoot)\
                (btOps(pBt)->IntegrityCheck(pBt, aRoot, nRoot))
#define sqliteBtreeGetFilename(pBt)       (btOps(pBt)->GetFilename(pBt))
#define sqliteBtreeChangeFilename(pBt, zNew)\
                (btOps(pBt)->ChangeFilename(pBt, zNew))

#ifdef SQLITE_TEST
#define sqliteBtreePageDump(pBt, pgno, recursive)\
                (btOps(pBt)->PageDump(pBt, pgno, recursive))
#define sqliteBtreeCursorDump(pCur, aResult)\
                (btCOps(pCur)->CursorDump(pCur, aResult))
#define sqliteBtreePager(pBt)             (btOps(pBt)->Pager(pBt))
int btree_native_byte_order;
#endif /* SQLITE_TEST */


#endif /* _BTREE_H_ */







|







 







|







 







|
<












9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
...
137
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152
153
154
155
156
**    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.33 2003/04/25 13:22:53 drh Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_

/*
** Forward declarations of structure
*/
................................................................................
    int (*DropTable)(Btree*, int);
    int (*ClearTable)(Btree*, int);
    int (*Cursor)(Btree*, int iTable, int wrFlag, BtCursor **ppCur);
    int (*GetMeta)(Btree*, int*);
    int (*UpdateMeta)(Btree*, int*);
    char *(*IntegrityCheck)(Btree*, int*, int);
    const char *(*GetFilename)(Btree*);
    int (*CopyFile)(Btree*,Btree*);
#ifdef SQLITE_TEST
    int (*PageDump)(Btree*, int, int);
    struct Pager *(*Pager)(Btree*);
#endif
};

/*
................................................................................
                (btCOps(pCur)->Data(pCur, offset, amt, zBuf))
#define sqliteBtreeCloseCursor(pCur)      (btCOps(pCur)->CloseCursor(pCur))
#define sqliteBtreeGetMeta(pBt, aMeta)    (btOps(pBt)->GetMeta(pBt, aMeta))
#define sqliteBtreeUpdateMeta(pBt, aMeta) (btOps(pBt)->UpdateMeta(pBt, aMeta))
#define sqliteBtreeIntegrityCheck(pBt, aRoot, nRoot)\
                (btOps(pBt)->IntegrityCheck(pBt, aRoot, nRoot))
#define sqliteBtreeGetFilename(pBt)       (btOps(pBt)->GetFilename(pBt))
#define sqliteBtreeCopyFile(pBt1, pBt2)   (btOps(pBt1)->CopyFile(pBt1, pBt2))


#ifdef SQLITE_TEST
#define sqliteBtreePageDump(pBt, pgno, recursive)\
                (btOps(pBt)->PageDump(pBt, pgno, recursive))
#define sqliteBtreeCursorDump(pCur, aResult)\
                (btCOps(pCur)->CursorDump(pCur, aResult))
#define sqliteBtreePager(pBt)             (btOps(pBt)->Pager(pBt))
int btree_native_byte_order;
#endif /* SQLITE_TEST */


#endif /* _BTREE_H_ */

Changes to src/btree_rb.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
....
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
** 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_rb.c,v 1.8 2003/04/25 02:43:08 drh Exp $
**
** This file implements an in-core database using Red-Black balanced
** binary trees.
**
** It was contributed to SQLite by anonymous on 2003-Feb-04 23:24:49 UTC.
*/
#include "btree.h"
................................................................................
** Return the full pathname of the underlying database file.
*/
static const char *memBtreeGetFilename(Btree *pBt){
  return 0;  /* A NULL return indicates there is no underlying file */
}

/*
** Change the name of the underlying database file.
*/
static int memBtreeChangeFilename(Btree *pBt, const char *zNew){
  return SQLITE_OK;
}

static BtOps sqliteBtreeOps = {
    memBtreeClose,
    memBtreeSetCacheSize,
    memBtreeSetSafetyLevel,
    memBtreeBeginTrans,
................................................................................
    memBtreeDropTable,
    memBtreeClearTable,
    memBtreeCursor,
    memBtreeGetMeta,
    memBtreeUpdateMeta,
    memBtreeIntegrityCheck,
    memBtreeGetFilename,
    memBtreeChangeFilename,

#ifdef SQLITE_TEST
    memBtreePageDump,
    memBtreePager
#endif
};








|







 







|

|
|







 







|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
....
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
** 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_rb.c,v 1.9 2003/04/25 13:22:53 drh Exp $
**
** This file implements an in-core database using Red-Black balanced
** binary trees.
**
** It was contributed to SQLite by anonymous on 2003-Feb-04 23:24:49 UTC.
*/
#include "btree.h"
................................................................................
** Return the full pathname of the underlying database file.
*/
static const char *memBtreeGetFilename(Btree *pBt){
  return 0;  /* A NULL return indicates there is no underlying file */
}

/*
** The copy file function is not implemented for the in-memory database
*/
static int memBtreeCopyFile(Btree *pBt, Btree *pBt2){
  return SQLITE_INTERNAL;  /* Not implemented */
}

static BtOps sqliteBtreeOps = {
    memBtreeClose,
    memBtreeSetCacheSize,
    memBtreeSetSafetyLevel,
    memBtreeBeginTrans,
................................................................................
    memBtreeDropTable,
    memBtreeClearTable,
    memBtreeCursor,
    memBtreeGetMeta,
    memBtreeUpdateMeta,
    memBtreeIntegrityCheck,
    memBtreeGetFilename,
    memBtreeCopyFile,

#ifdef SQLITE_TEST
    memBtreePageDump,
    memBtreePager
#endif
};

Changes to src/os.c.

278
279
280
281
282
283
284

285
286
287
288
289
290
291
...
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
#endif
#if OS_MAC
  return access(zFilename, 0)==0;
#endif
}



/*
** Change the name of an existing file.
*/
int sqliteOsFileRename(const char *zOldName, const char *zNewName){
#if OS_UNIX
  if( link(zOldName, zNewName) ){
    return SQLITE_ERROR;
................................................................................
  return SQLITE_OK;
#endif
#if OS_MAC
  /**** FIX ME ***/
  return SQLITE_ERROR;
#endif
}


/*
** Attempt to open a file for both reading and writing.  If that
** fails, try opening it read-only.  If the file does not exist,
** try to create it.
**
** On success, a handle for the open file is written to *id







>







 







|







278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#endif
#if OS_MAC
  return access(zFilename, 0)==0;
#endif
}


#if 0 /* NOT USED */
/*
** Change the name of an existing file.
*/
int sqliteOsFileRename(const char *zOldName, const char *zNewName){
#if OS_UNIX
  if( link(zOldName, zNewName) ){
    return SQLITE_ERROR;
................................................................................
  return SQLITE_OK;
#endif
#if OS_MAC
  /**** FIX ME ***/
  return SQLITE_ERROR;
#endif
}
#endif /* NOT USED */

/*
** Attempt to open a file for both reading and writing.  If that
** fails, try opening it read-only.  If the file does not exist,
** try to create it.
**
** On success, a handle for the open file is written to *id

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
922
923
924
925
926
927
928






















929
930
931
932
933
934
935
....
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
** 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.81 2003/04/06 20:52:32 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
  }
  n /= SQLITE_PAGE_SIZE;
  if( pPager->state!=SQLITE_UNLOCK ){
    pPager->dbSize = n;
  }
  return n;
}























/*
** Shutdown the page cache.  Free all memory and close all files.
**
** If a transaction was in progress when this routine is called, that
** transaction is rolled back.  All outstanding pages are invalidated
** and their memory is freed.  Any attempt to use a page associated
................................................................................
/*
** Return the full pathname of the database file.
*/
const char *sqlitepager_filename(Pager *pPager){
  return pPager->zFilename;
}

/*
** Rename the database file
*/
int sqlitepager_rename(Pager *pPager, const char *zNewName){
  char *zNew;
  char *zJournal;
  int nName;
  int rc;

  nName = strlen(zNewName);
  zNew = sqliteMalloc( nName*2 + 30 );
  if( zNew==0 ){
    return SQLITE_NOMEM;
  }
  memcpy(zNew, zNewName, nName+1);
  zJournal = &zNew[nName+1];
  memcpy(zJournal, zNew, nName);
  strcpy(&zJournal[nName], "-journal");
  if( pPager->journalOpen ){
    rc = sqliteOsFileRename(pPager->zJournal, zJournal);
    if( rc ){
      sqliteFree(zNew);
      return rc;
    }
  }
  rc = sqliteOsFileRename(pPager->zFilename, zNew);
  if( rc ){
    sqliteFree(zNew);
    return rc;
  }
  if( pPager->zFilename!=(char*)&pPager[1] ){
    sqliteFree(pPager->zFilename);
  }
  pPager->zFilename = zNew;
  return SQLITE_OK;
}

#ifdef SQLITE_TEST
/*
** Print a listing of all referenced pages and their ref count.
*/
void sqlitepager_refdump(Pager *pPager){
  PgHdr *pPg;
  for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
    if( pPg->nRef<=0 ) continue;
    printf("PAGE %3d addr=0x%08x nRef=%d\n", 
       pPg->pgno, (int)PGHDR_TO_DATA(pPg), pPg->nRef);
  }
}
#endif







|







 







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







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<













14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
....
2055
2056
2057
2058
2059
2060
2061





































2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
** 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.82 2003/04/25 13:22:53 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
  }
  n /= SQLITE_PAGE_SIZE;
  if( pPager->state!=SQLITE_UNLOCK ){
    pPager->dbSize = n;
  }
  return n;
}

/*
** Forward declaration
*/
static int syncAllPages(Pager*);

/*
** Truncate the file to the number of pages specified.
*/
int sqlitepager_truncate(Pager *pPager, Pgno nPage){
  int rc;
  if( pPager->dbSize<0 ) sqlitepager_pagecount(pPager);
  if( nPage>=pPager->dbSize ){
    return SQLITE_OK;
  }
  syncAllPages(pPager);
  rc = sqliteOsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)nPage);
  if( rc==SQLITE_OK ){
    pPager->dbSize = nPage;
  }
  return rc;
}

/*
** Shutdown the page cache.  Free all memory and close all files.
**
** If a transaction was in progress when this routine is called, that
** transaction is rolled back.  All outstanding pages are invalidated
** and their memory is freed.  Any attempt to use a page associated
................................................................................
/*
** Return the full pathname of the database file.
*/
const char *sqlitepager_filename(Pager *pPager){
  return pPager->zFilename;
}






































#ifdef SQLITE_TEST
/*
** Print a listing of all referenced pages and their ref count.
*/
void sqlitepager_refdump(Pager *pPager){
  PgHdr *pPg;
  for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
    if( pPg->nRef<=0 ) continue;
    printf("PAGE %3d addr=0x%08x nRef=%d\n", 
       pPg->pgno, (int)PGHDR_TO_DATA(pPg), pPg->nRef);
  }
}
#endif

Changes to src/pager.h.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
57
58
59
60
61
62
63

64
65
66
67
68
69
70
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite page cache
** subsystem.  The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
** @(#) $Id: pager.h,v 1.22 2003/04/06 20:44:45 drh Exp $
*/

/*
** The size of one page
**
** You can change this value to another (reasonable) power of two
** such as 512, 2048, 4096, or 8192 and things will still work.  But
................................................................................
int sqlitepager_ref(void*);
int sqlitepager_unref(void*);
Pgno sqlitepager_pagenumber(void*);
int sqlitepager_write(void*);
int sqlitepager_iswriteable(void*);
int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void*);
int sqlitepager_pagecount(Pager*);

int sqlitepager_begin(void*);
int sqlitepager_commit(Pager*);
int sqlitepager_rollback(Pager*);
int sqlitepager_isreadonly(Pager*);
int sqlitepager_ckpt_begin(Pager*);
int sqlitepager_ckpt_commit(Pager*);
int sqlitepager_ckpt_rollback(Pager*);







|







 







>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite page cache
** subsystem.  The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
** @(#) $Id: pager.h,v 1.23 2003/04/25 13:22:53 drh Exp $
*/

/*
** The size of one page
**
** You can change this value to another (reasonable) power of two
** such as 512, 2048, 4096, or 8192 and things will still work.  But
................................................................................
int sqlitepager_ref(void*);
int sqlitepager_unref(void*);
Pgno sqlitepager_pagenumber(void*);
int sqlitepager_write(void*);
int sqlitepager_iswriteable(void*);
int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void*);
int sqlitepager_pagecount(Pager*);
int sqlitepager_truncate(Pager*,Pgno);
int sqlitepager_begin(void*);
int sqlitepager_commit(Pager*);
int sqlitepager_rollback(Pager*);
int sqlitepager_isreadonly(Pager*);
int sqlitepager_ckpt_begin(Pager*);
int sqlitepager_ckpt_commit(Pager*);
int sqlitepager_ckpt_rollback(Pager*);

Changes to src/vacuum.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
205
206
207
208
209
210
211
212
213
214

215
216
217
218
219
220
221
222
223
224
225
226
...
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
...
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
312
313
314
315
316
317
318
319
320
321
322


323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
**
*************************************************************************
** 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.4 2003/04/25 02:43:08 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"

#define SQLITE_OMIT_VACUUM 1

/*
................................................................................
** become a no-op.
*/
void sqliteVacuum(Parse *pParse, Token *pTableName){
#ifdef 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 */
  char *zTemp2;           /* Another temp file in the same directory */
  sqlite *dbNew = 0;      /* The new vacuumed database */
  sqlite *db;             /* The original database */

  int rc, i;
  char *zErrMsg = 0;
  char *zSql = 0;
  int safety = 0;
  vacuumStruct sVac;

  /* These are all of the pragmas that need to be transferred over
  ** to the new database */
  static const char *zPragma[] = {
     "default_synchronous",
     "default_cache_size",
     /* "default_temp_store", */
................................................................................
  zFilename = sqliteBtreeGetFilename(db->aDb[0].pBt);
  if( zFilename==0 ){
    /* This only happens with the in-memory database.  VACUUM is a no-op
    ** there, so just return */
    return;
  }
  nFilename = strlen(zFilename);
  zTemp = sqliteMalloc( 2*(nFilename+40) );
  if( zTemp==0 ) return;
  zTemp2 = &zTemp[nFilename+40];
  strcpy(zTemp, zFilename);
  strcpy(zTemp2, zFilename);
  for(i=0; i<10; i++){
    zTemp[nFilename] = '-';
    randomName(&zTemp[nFilename+1]);
    zTemp2[nFilename] = '-';
    randomName(&zTemp2[nFilename+1]);
    if( !sqliteOsFileExists(zTemp) && !sqliteOsFileExists(zTemp2) ) break;
  }
  if( i>=10 ){
    sqliteErrorMsg(pParse, "unable to create a temporary database files "
       "in the same directory as the original database");
    goto end_of_vacuum;
  }

  
  dbNew = sqlite_open(zTemp, 0, &zErrMsg);
  if( dbNew==0 ){
................................................................................
  }
  if( sqliteSafetyOff(db) ){
    sqliteErrorMsg(pParse, "library routines called out of sequence");
    goto end_of_vacuum;
  }
  safety = 1;
  if( execsql(pParse, db, "BEGIN") ) goto end_of_vacuum;

  if( execsql(pParse, dbNew, "BEGIN") ) goto end_of_vacuum;

  sVac.dbOld = db;
  sVac.dbNew = dbNew;
  sVac.pParse = pParse;
  for(i=0; i<sizeof(zPragma)/sizeof(zPragma[0]); i++){
    char zBuf[200];
    assert( strlen(zPragma[i])<100 );
    sprintf(zBuf, "PRAGMA %s;", zPragma[i]);
    sVac.zPragma = zPragma[i];
    rc = sqlite_exec(db, zBuf, vacuumCallback3, &sVac, &zErrMsg);
    if( rc ) goto vacuum_error;
  }

  rc = sqlite_exec(db, "SELECT type, name, sql FROM sqlite_master "
           "WHERE sql NOT NULL", vacuumCallback1, &sVac, &zErrMsg);
  if( rc ) goto vacuum_error;

  if( sqliteOsFileRename(zFilename, zTemp2) ){
    sqliteErrorMsg(pParse, "unable to rename database file");
    goto end_of_vacuum;
  }
  if( sqliteOsFileRename(zTemp, zFilename) ){
    sqliteOsFileRename(zTemp2, zFilename);
    sqliteErrorMsg(pParse, "unable to rename database file");
    goto end_of_vacuum;
  }
  if( execsql(pParse, dbNew, "COMMIT;") ){
    sqliteOsDelete(zFilename);
    sqliteOsFileRename(zTemp2, zFilename);
    goto end_of_vacuum;
  }
  execsql(pParse, db, "COMMIT;");  /* Nothing was written so its gotta work */
  sqlite_close(dbNew);
  dbNew = 0;
  if( sqliteOsDelete(zTemp2) ){
    sqliteErrorMsg(pParse, "unable to delete old database: %s", zTemp2);
  }
  sqliteBtreeClose(db->aDb[0].pBt);
  zTemp2[nFilename] = 0;
  if( sqliteBtreeOpen(zTemp2, 0, MAX_PAGES, &db->aDb[0].pBt) ){
     sqliteErrorMsg(pParse, "unable to reopen database after vacuuming");
  }


  sqliteResetInternalSchema(db, 0);

end_of_vacuum:
  sqlite_exec(db, "COMMIT", 0, 0, 0);
  if( safety) {
    sqliteSafetyOn(db);
  }
  if( dbNew ) sqlite_close(dbNew);
  sqliteOsDelete(zTemp);
  sqliteFree(zTemp);
  sqliteFree(zSql);
  sqliteFree(sVac.s1.z);
  sqliteFree(sVac.s2.z);
  if( zErrMsg ) sqlite_freemem(zErrMsg);
  return;

vacuum_error:
  if( pParse->zErrMsg==0 ){
    sqliteErrorMsg(pParse, "unable to vacuum database - %s", zErrMsg);
  }
  goto end_of_vacuum;
#endif
}







|







 







<


>
|
|
<
|
|







 







|

<

<



<
<
|


|







 







>
|
>











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


<
<
<
<
<
<
<
<
<
<
<
>
>




|





<






<
<
<
<


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
205
206
207
208
209
210
211

212
213
214
215
216

217
218
219
220
221
222
223
224
225
...
243
244
245
246
247
248
249
250
251

252

253
254
255


256
257
258
259
260
261
262
263
264
265
266
...
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
312

313
314
315
316
317
318




319
320
**
*************************************************************************
** 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.5 2003/04/25 13:22:53 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"

#define SQLITE_OMIT_VACUUM 1

/*
................................................................................
** become a no-op.
*/
void sqliteVacuum(Parse *pParse, Token *pTableName){
#ifdef 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 */

  sqlite *dbNew = 0;      /* The new vacuumed database */
  sqlite *db;             /* The original database */
  int rc = SQLITE_OK;     /* Return code from service routines */
  int i;                  /* Loop counter */
  char *zErrMsg = 0;      /* Error messages stored here */

  int safety = 0;         /* TRUE if safety is off */
  vacuumStruct sVac;      /* Information passed to callbacks */

  /* These are all of the pragmas that need to be transferred over
  ** to the new database */
  static const char *zPragma[] = {
     "default_synchronous",
     "default_cache_size",
     /* "default_temp_store", */
................................................................................
  zFilename = sqliteBtreeGetFilename(db->aDb[0].pBt);
  if( zFilename==0 ){
    /* This only happens with the in-memory database.  VACUUM is a no-op
    ** there, so just return */
    return;
  }
  nFilename = strlen(zFilename);
  zTemp = sqliteMalloc( nFilename+100 );
  if( zTemp==0 ) return;

  strcpy(zTemp, zFilename);

  for(i=0; i<10; i++){
    zTemp[nFilename] = '-';
    randomName(&zTemp[nFilename+1]);


    if( !sqliteOsFileExists(zTemp) ) break;
  }
  if( i>=10 ){
    sqliteErrorMsg(pParse, "unable to create a temporary database file "
       "in the same directory as the original database");
    goto end_of_vacuum;
  }

  
  dbNew = sqlite_open(zTemp, 0, &zErrMsg);
  if( dbNew==0 ){
................................................................................
  }
  if( sqliteSafetyOff(db) ){
    sqliteErrorMsg(pParse, "library routines called out of sequence");
    goto end_of_vacuum;
  }
  safety = 1;
  if( execsql(pParse, db, "BEGIN") ) goto end_of_vacuum;
  if( execsql(pParse, dbNew, "PRAGMA synchronous=off; BEGIN") ){
    goto end_of_vacuum;
  }
  sVac.dbOld = db;
  sVac.dbNew = dbNew;
  sVac.pParse = pParse;
  for(i=0; i<sizeof(zPragma)/sizeof(zPragma[0]); i++){
    char zBuf[200];
    assert( strlen(zPragma[i])<100 );
    sprintf(zBuf, "PRAGMA %s;", zPragma[i]);
    sVac.zPragma = zPragma[i];
    rc = sqlite_exec(db, zBuf, vacuumCallback3, &sVac, &zErrMsg);
    if( rc ) goto vacuum_error;
  }
  if( rc==SQLITE_OK ){
    rc = sqlite_exec(db, "SELECT type, name, sql FROM sqlite_master "
             "WHERE sql NOT NULL", vacuumCallback1, &sVac, &zErrMsg);

  }
  if( rc ){
    if( pParse->zErrMsg==0 ){




      sqliteErrorMsg(pParse, "unable to vacuum database - %s", zErrMsg);

    }



    goto end_of_vacuum;
  }











  rc = sqliteBtreeCopyFile(db->aDb[0].pBt, dbNew->aDb[0].pBt);
  sqlite_exec(db, "COMMIT", 0, 0, 0);
  sqliteResetInternalSchema(db, 0);

end_of_vacuum:
  sqlite_exec(db, "COMMIT", 0, 0, 0);
  if( safety ) {
    sqliteSafetyOn(db);
  }
  if( dbNew ) sqlite_close(dbNew);
  sqliteOsDelete(zTemp);
  sqliteFree(zTemp);

  sqliteFree(sVac.s1.z);
  sqliteFree(sVac.s2.z);
  if( zErrMsg ) sqlite_freemem(zErrMsg);
  return;

vacuum_error:




#endif
}

Changes to test/vacuum.test.

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
..
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
..
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
#    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 VACUUM statement.
#
# $Id: vacuum.test,v 1.8 2003/04/25 02:43:08 drh Exp $

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

proc cksum {filename} {
  set txt [db eval {SELECT name, type, sql FROM sqlite_master}]\n
  foreach tbl [db eval {SELECT name FROM sqlite_master WHERE type='table'}] {
    append txt [db eval "SELECT * FROM $tbl"]\n
  }
  foreach prag {default_synchronous default_cache_size} {
    append txt $prag-[db eval "PRAGMA $prag"]\n
  }
  # set fd [open $filename w]
  # puts $fd $txt
  # close $fd
  return [string length $txt]-[md5 $txt]


}

do_test vacuum-1.1 {
  execsql {
    BEGIN;
    CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
    INSERT INTO t1 VALUES(NULL,randstr(10,100),randstr(5,50));
................................................................................
    INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
    CREATE INDEX i1 ON t1(b,c);
    CREATE TABLE t2 AS SELECT * FROM t1;
    COMMIT;
    DROP TABLE t2;
  }
  set ::size1 [file size test.db]
  set ::cksum [cksum vacuum1.txt]
  expr {$::cksum!=""}
} {1}
do_test vacuum-1.2 {
  execsql {
    VACUUM;
  }
  cksum vacuum2.txt
} $cksum
do_test vacuum-1.3 {
  expr {[file size test.db]<$::size1}
} {1}
do_test vacuum-1.4 {
  execsql {
    BEGIN;
................................................................................
    CREATE TRIGGER r1 AFTER DELETE ON t2 BEGIN
      SELECT 1;
    END;
    COMMIT;
    DROP TABLE t2;
  }
  set ::size1 [file size test.db]
  set ::cksum [cksum vacuum3.txt]
  expr {$::cksum!=""}
} {1}
do_test vacuum-1.5 {
  execsql {
    VACUUM;
  }
  cksum vacuum4.txt
} $cksum
do_test vacuum-1.6 {
  expr {[file size test.db]<$::size1}
} {1}

do_test vacuum-2.1 {
  catchsql {
    BEGIN;
    VACUUM;
    COMMIT;
  }
} {1 {cannot VACUUM from within a transaction}}



execsql COMMIT


























# finish_test







|




|
|
|
|


|

<
<
<
|
>
>







 







|






|







 







|






|












>
>
>
|
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

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
..
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
..
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
#    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 VACUUM statement.
#
# $Id: vacuum.test,v 1.9 2003/04/25 13:22:53 drh Exp $

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

proc cksum {{db db}} {
  set txt [$db eval {SELECT name, type, sql FROM sqlite_master}]\n
  foreach tbl [$db eval {SELECT name FROM sqlite_master WHERE type='table'}] {
    append txt [$db eval "SELECT * FROM $tbl"]\n
  }
  foreach prag {default_synchronous default_cache_size} {
    append txt $prag-[$db eval "PRAGMA $prag"]\n
  }



  set cksum [string length $txt]-[md5 $txt]
  puts $cksum-[file size test.db]
  return $cksum
}

do_test vacuum-1.1 {
  execsql {
    BEGIN;
    CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
    INSERT INTO t1 VALUES(NULL,randstr(10,100),randstr(5,50));
................................................................................
    INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
    CREATE INDEX i1 ON t1(b,c);
    CREATE TABLE t2 AS SELECT * FROM t1;
    COMMIT;
    DROP TABLE t2;
  }
  set ::size1 [file size test.db]
  set ::cksum [cksum]
  expr {$::cksum!=""}
} {1}
do_test vacuum-1.2 {
  execsql {
    VACUUM;
  }
  cksum
} $cksum
do_test vacuum-1.3 {
  expr {[file size test.db]<$::size1}
} {1}
do_test vacuum-1.4 {
  execsql {
    BEGIN;
................................................................................
    CREATE TRIGGER r1 AFTER DELETE ON t2 BEGIN
      SELECT 1;
    END;
    COMMIT;
    DROP TABLE t2;
  }
  set ::size1 [file size test.db]
  set ::cksum [cksum]
  expr {$::cksum!=""}
} {1}
do_test vacuum-1.5 {
  execsql {
    VACUUM;
  }
  cksum
} $cksum
do_test vacuum-1.6 {
  expr {[file size test.db]<$::size1}
} {1}

do_test vacuum-2.1 {
  catchsql {
    BEGIN;
    VACUUM;
    COMMIT;
  }
} {1 {cannot VACUUM from within a transaction}}
catch {db eval COMMIT}
do_test vacuum-2.2 {
  sqlite db2 test.db
  execsql {
    BEGIN;
    CREATE TABLE t4 AS SELECT * FROM t1;
    CREATE TABLE t5 AS SELECT * FROM t1;
    COMMIT;
    DROP TABLE t4;
    DROP TABLE t5;
  } db2
  set ::cksum [cksum db2]
  catchsql {
    VACUUM
  }
} {1 {database schema has changed}}
do_test vacuum-2.3 {
  execsql {
    VACUUM;
  }
  cksum
} $cksum
do_test vacuum-2.4 {
  catch {db2 eval {SELECT count(*) FROM sqlite_master}}
  cksum db2
} $cksum


catch {db2 close}

# finish_test