/ Check-in [555351dd]
Login
Overview
Comment::-) (CVS 208)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:555351dd1918f96167e2cb46cc1db6496e8d10a3
User & Date: drh 2001-04-11 14:29:22
Context
2001-04-12
23:21
More testing (CVS 209) check-in: 3bde1284 user: drh tags: trunk
2001-04-11
14:29
:-) (CVS 208) check-in: 555351dd user: drh tags: trunk
14:28
better handling of out-of-memory errors (CVS 207) check-in: 86b30cd0 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added src/pager.c.











































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
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
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
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
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
/*
** Copyright (c) 2001 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*************************************************************************
** This is the implementation of the page cache subsystem.
** 
** The page cache is used to access a database file.  The pager journals
** all writes in order to support rollback.  Locking is used to limit
** access to one or more reader or on writer.
**
** @(#) $Id: pager.c,v 1.1 2001/04/11 14:29:22 drh Exp $
*/
#include "pager.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>

/*
** The page cache as a whole is always in one of the following
** states:
**
**   SQLITE_UNLOCK       The page cache is not currently reading or 
**                       writing the database file.  There is no
**                       data held in memory.  This is the initial
**                       state.
**
**   SQLITE_READLOCK     The page cache is reading the database.
**                       Writing is not permitted.  There can be
**                       multiple readers accessing the same database
**                       at the same time.
**
**   SQLITE_WRITELOCK    The page cache is writing the database.
**                       Access is exclusive.  No other processes or
**                       threads can be reading or writing while one
**                       process is writing.
**
** The page cache comes up in PCS_UNLOCK.  The first time a
** sqlite_page_get() occurs, the state transitions to PCS_READLOCK.
** After all pages have been released using sqlite_page_unref(),
** the state transitions back to PCS_UNLOCK.  The first time
** that sqlite_page_write() is called, the state transitions to
** PCS_WRITELOCK.  The sqlite_page_rollback() and sqlite_page_commit()
** functions transition the state back to PCS_READLOCK.
*/
#define SQLITE_UNLOCK      0
#define SQLITE_READLOCK    1
#define SQLITE_WRITELOCK   2

/*
** Each in-memory image of a page begins with the following header.
*/
struct PgHdr {
  Pager *pPager;                 /* The pager to which this page belongs */
  Pgno pgno;                     /* The page number for this page */
  PgHdr *pNextHash, *pPrevHash;  /* Hash collision change for PgHdr.pgno */
  int nRef;                      /* Number of users of this page */
  PgHdr *pNext, *pPrev;          /* Freelist of pages where nRef==0 */
  char inJournal;                /* TRUE if has been written to journal */
  char dirty;                    /* TRUE if we need to write back changes */
  /* The page data follows this header */
};

/*
** Convert a pointer to a PgHdr into a pointer to its data,
** and vice verse.
*/
#define PGHDR_TO_DATA(P)  ((void*)(&(P)[1]))
#define DATA_TO_PGHDR(D)  (&((PgHdr*)(D))[-1])

/*
** The number of page numbers that will fit on one page.
*/
#define SQLITE_INDEX_SIZE   (SQLITE_PAGE_SIZE/sizeof(Pgno))

/*
** How big to make the hash table used for locating in-memory pages
** by page number.
*/
#define N_PG_HASH 353

/*
** A open page cache is an instance of the following structure.
*/
struct Pager {
  char *zFilename;            /* Name of the database file */
  char *zJournal;             /* Name of the journal file */
  int fd, jfd;                /* File descriptors for database and journal */
  int nRef;                   /* Sum of PgHdr.nRef */
  int dbSize;                 /* Number of pages in the file */
  int jSize;                  /* Number of pages in the journal */
  int nPage;                  /* Total number of in-memory pages */
  int mxPage;                 /* Maximum number of pages to hold in cache */
  Pgno *aIdx;                 /* Current journal index page */
  char state;                 /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */
  PgHdr *pFirst, *pLast;      /* List of free pages */
  PgHdr *aHash[N_PG_HASH];    /* Hash table to map page number of PgHdr */
};

/*
** Hash a page number
*/
#define sqlite_pager_hash(PN)  ((PN)%N_PG_HASH)

/*
** Attempt to acquire a read lock (if wrlock==0) or a write lock (if wrlock==1)
** on the database file.  Return 0 on success and non-zero if the lock 
** could not be acquired.
*/
static int sqlite_pager_lock(int fd, int wrlock){
  struct flock lock;
  lock.l_type = write_lock ? F_WRLCK : F_RDLCK;
  return fcntl(fd, F_SETLK, &lock)!=0;
}

/*
** Unlock the database file.
*/
static int sqlite_pager_unlock(fd){
  struct flock lock;
  lock.l_type = F_UNLCK;
  return fcntl(fd, F_SETLK, &lock)!=0;
}

/*
** Find a page in the hash table given its page number.  Return
** a pointer to the page or NULL if not found.
*/
static PgHdr *sqlite_pager_lookup(Pager *pPager, Pgno pgno){
  PgHdr *p = pPager->aHash[pgno % N_PG_HASH];
  while( p && p->pgno!=pgno ){
    p = p->pNextHash;
  }
  return p;
}

/*
** Unlock the database and clear the in-memory cache.  This routine
** sets the state of the pager back to what it was when it was first
** opened.  Any outstanding pages are invalidated and subsequent attempts
** to access those pages will likely result in a coredump.
*/
static void sqlite_pager_reset(Pager *pPager){
  PgHdr *pPg, *pNext;
  for(pPg=pPager->pFirst; pPg; pPg=pNext){
    pNext = pPg->pNext;
    sqlite_free(pPg);
  }
  pPager->pFirst = 0;
  pPager->pNext = 0;
  memset(pPager->aHash, 0, sizeof(pPager->aHash));
  pPager->nPage = 0;
  if( pPager->state==SQLITE_WRITELOCK ){
    sqlite_pager_rollback(pPager);
  }
  sqlite_pager_unlock(pPager->fd);
  pPager->state = SQLITE_UNLOCK;
  pPager->nRef = 0;
}

/*
** When this routine is called, the pager has the journal file open and
** a write lock on the database.  This routine releases the database
** write lock and acquires a read lock in its place.  The journal file
** is deleted and closed.
**
** We have to release the write lock before acquiring the read lock,
** so there is a race condition where another process can get the lock
** while we are not holding it.  But, no other process should do this
** because we are also holding a lock on the journal, and no process
** should get a write lock on the database without first getting a lock
** on the journal.  So this routine should never fail.  But it can fail
** if another process is not playing by the rules.  If it does fail,
** all in-memory cache pages are invalidated and this routine returns
** SQLITE_PROTOCOL.  SQLITE_OK is returned on success.
*/
static int sqlite_pager_unwritelock(Pager *pPager){
  int rc;
  assert( pPager->state==SQLITE_WRITELOCK );
  sqlite_pager_unlock(pPager->fd);
  rc = sqlite_pager_lock(pPager->fd, 0);
  unlink(pPager->zJournal);
  close(pPager->jfd);
  pPager->jfd = -1;
  if( rc!=SQLITE_OK ){
    pPager->state = SQLITE_UNLOCK;
    sqlite_pager_reset(pPager);
    rc = SQLITE_PROTOCOL;
  }else{
    pPager->state = SQLITE_READLOCK;
  }
  return rc;
}


/*
** Playback the journal and thus restore the database file to
** the state it was in before we started making changes.  
**
** A journal consists of multiple segments.  Every segment begins
** with a single page containing SQLITE_INDEX_SIZE page numbers.  This
** first page is called the index.  Most segments have SQLITE_INDEX_SIZE
** additional pages after the index.  The N-th page after the index
** contains the contents of a page in the database file before that
** page was changed.  The N-th entry in the index tells which page
** of the index file the data is for.
**
** The first segment of a journal is formatted slightly differently.
** The first segment contains an index but only SQLITE_INDEX_SIZE-1
** data pages.  The first page number in the index is actually the
** total number of pages in the original file.  This number is used
** to truncate the original database file back to its original size.
** The second number in the index page is the page number for the
** first data page.  And so forth.
**
** We really need to playback the journal beginning at the end
** and working backwards toward the beginning.  That way changes
** to the database are undone in the reverse order from the way they
** were applied.  This is important if the same page is changed
** more than once.  But many operating systems work more efficiently
** if data is read forward instead of backwards.  So for efficiency
** we want to read the data in the forward direction.
**
** This routine starts with the last segment and works backwards
** toward the first.  Within each segment, however, data is read
** in the forward direction for efficiency.  Care is taken that
** only the first appearance of each page is copied over to the
** database file.  If a page appears in the index more than once,
** only the first occurrance is written.  A hash table is used to
** keep track of  which pages have been written and which have not.
*/
static int sqlite_pager_playback(Pager *pPager){
  int nSeg;                           /* Number of segments */
  int i, j;                           /* Loop counters */
  Pgno mxPg = 0;                      /* Size of the original file in pages */
  struct stat statbuf;                /* Used to size the journal */
  Pgno aIndex[SQLITE_INDEX_SIZE];     /* The index page */
  char aBuf[SQLITE_PAGE_SIZE];        /* Page transfer buffer */
  Pgno aHash[SQLITE_INDEX_SIZE*2-1];  /* Hash table for pages read so far */
  int rc;

  /* Figure out how many segments are in the journal.  Remember that
  ** the first segment is one page shorter than the others and that
  ** the last segment may be incomplete.
  */
  if( fstat(pPager->jfd; &statbuf)!=0 ){
    return SQLITE_OK;
  }
  if( statbuf.st_size <= SQLITE_INDEX_SIZE*SQLITE_PAGE_SIZE ){
    nSeg = 1;
  }else{
    int nPage = statbuf.st_size/SQLITE_PAGE_SIZE;
    nPage -= SQLITE_INDEX_SIZE;
    nSeg = 1 + nPage/(SQLITE_INDEX_SIZE+1);
  }

  /* Process segments beginning with the last and working backwards
  ** to the first.
  */
  for(i=nSeg-1; i>=0; i--){
    /* Seek to the beginning of the segment */
    sqlite_pager_seekpage(pPager->jfd, 
        i>0 ? i*(SQLITE_INDEX_SIZE + 1) - 1 : 0,
        SEEK_SET
    );

    /* Initialize the hash table used to avoid copying duplicate pages */
    memset(aHash, 0, sizeof(aHash));

    /* Read the index page */
    sqlite_pager_readpage(pPager->jfd, aIndex);

    /* Extract the original file size from the first index entry if this
    ** is the first segment */   
    if( i==0 ){
      mxPg = aIndex[0];
      aIndex[0] = 0;
    }

    /* Process pages of this segment in forward order
    */
    for(j=0; j<SQLITE_INDEX_SIZE; j++){
      Pgno pgno = aIndex[i];
      void *pBuf;
      PgHdr *pPg;

      /* 0 means "no such page".  Skip zero entries */
      if( pgno==0 ) continue;

      /* Check to see if pgno is in the hash table.  Skip this
      ** entry if it is.
      */
      h = pgno % (SQLITE_PAGE_SIZE-1);
      while( aHash[h]!=0 && aHash[h]!=pgno ){
        h++;
        if( h>=SQLITE_PAGE_SIZE-1 ) h = 0;
      }
      if( aHash[h]==pgno ){
        lseek(pPager->jfd, SQLITE_PAGE_SIZE, SEEK_CUR);
        continue;
      }
      aHash[h] = pgno;

      /* Playback the page.  Update the in-memory copy of the page
      ** at the same time, if there is one.
      */
      pPg = sqlite_pager_lookup(pPager, pgno);
      if( pPg ){
        pBuf = PGHDR_TO_DATA(pPg);
      }else{
        pBuf = aBuf;
      }
      sqlite_pager_readpage(pPager->jfd, pBuf);
      sqlite_pager_seekpage(pPager->fd, pgno, SEEK_SET);
      rc = sqlite_pager_writepage(pPager->fd, pBuf);
      if( rc!=SQLITE_OK ) return rc;
    }
  }

  /* Truncate the database back to its original size
  */
  if( mxPg>0 ){
    ftrucate(pPager->fd, mxPg * SQLITE_PAGE_SIZE);
  }
  return SQLITE_OK;
}

/*
** Create a new page cache and put a pointer to the page cache in *ppPager.
** The file to be cached need not exist.  The file is not opened until
** the first call to sqlite_pager_get() and is only held open until the
** last page is released using sqlite_pager_unref().
*/
int sqlite_pager_open(Pager **ppPager, const char *zFilename, int mxPage){
  Pager *pPager;
  int nameLen;
  int fd;

  fd = open(zFilename, O_RDWR, 0644);
  if( fd<0 ){
    return SQLITE_CANTOPEN;
  }
  nameLen = strlen(zFilename);
  pPager = sqliteMalloc( sizeof(*pPager) + nameLen*2 + 30 );
  if( pPager==0 ) return SQLITE_NOMEM;
  pPager->zFilename = (char*)&pPager[1];
  pPager->zJournal = &pPager->zFilename[nameLen+1];
  strcpy(pPager->zFilename, zFilename);
  strcpy(pPager->zJournal, zFilename);
  strcpy(&pPager->zJournal[nameLen], "-journal");
  pPager->fd = fd;
  pPager->jfd = -1;
  pPager->nRef = 0;
  pPager->dbSize = -1;
  pPager->nPage = 0;
  pPager->mxPage = mxPage>10 ? mxPage : 10;
  pPager->state = SQLITE_UNLOCK;
  pPager->pFirst = 0;
  pPager->pLast = 0;
  memset(pPager->aHash, 0, sizeof(pPager->aHash));
  *ppPager = pPager;
  return SQLITE_OK;
}

/*
** Return the total number of pages in the file opened by pPager.
*/
int sqlite_pager_pagecount(Pager *pPager){
  int n;
  struct stat statbuf;
  if( pPager->dbSize>=0 ){
    return pPager->dbSize;
  }
  if( fstat(pPager->fd, &statbuf)!=0 ){
    n = 0;
  }else{
    n = statbuf.st_size/SQLITE_PAGE_SIZE;
  }
  if( pPager->state!=SQLITE_NOLOCK ){
    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
** with this page cache after this function returns will likely
** result in a coredump.
*/
int sqlite_pager_close(Pager *pPager){
  int i;
  PgHdr *pPg;
  switch( pPager->state ){
    case SQLITE_WRITELOCK: {
      sqlite_pager_rollback(pPager);
      sqlite_pager_unlock(pPager->fd);
      break;
    }
    case SQLITE_READLOCK: {
      sqlite_pager_unlock(pPager->fd);
      break;
    }
    default: {
      /* Do nothing */
      break;
    }
  }
  for(i=0; i<N_PG_HASH; i++){
    PgHdr *pNext;
    for(pPg=pPager->aHash[i]; pPg; pPg=pNext){
      pNext = pPg->pNextHash;
      sqliteFree(pPg);
    }
  }
  if( pPager->fd>=0 ) close(pPager->fd);
  assert( pPager->jfd<0 );
  sqliteFree(pPager);
  return SQLITE_OK;
}

/*
** Return the page number for the given page data
*/
int sqlite_pager_pagenumber(void *pData){
  PgHdr *p = DATA_TO_PGHDR(pData);
  return p->pgno;
}

/*
** Acquire a page
*/
int sqlite_pager_get(Pager *pPager, int pgno, void **ppPage){
  PgHdr *pPg;

  /* If this is the first page accessed, then get a read lock
  ** on the database file.
  */
  if( pPager->nRef==0 ){
    if( sqlite_pager_lock(pPager->fd, 0)!=0 ){
      *ppPage = 0;
      return SQLITE_BUSY;
    }

    /* If a journal file exists, try to play it back.
    */
    if( access(pPager->zJournal,0)==0 ){
       int rc;

       /* Open the journal for exclusive access.  Return SQLITE_BUSY if
       ** we cannot get exclusive access to the journal file
       */
       pPager->jfd = open(pPager->zJournal, O_RDONLY, 0);
       if( pPager->jfd<0 || sqlite_pager_lock(pPager->jfd, 1)!=0 ){
         if( pPager->jfd>=0 ){ close(pPager->jfd); pPager->jfd = -1; }
         sqlite_pager_unlock(pPager->fd);
         *ppPage = 0;
         return SQLITE_BUSY;
       }

       /* Get a write lock on the database */
       sqlite_pager_unlock(pPager->fd);
       if( sqlite_pager_lock(pPager->fd, 1)!=0 ){
         *ppPage = 0;
         return SQLITE_PROTOCOL;
       }

       /* Playback and delete the journal.  Drop the database write
       ** lock and reacquire the read lock.
       */
       sqlite_pager_playback(pPager);
       rc = sqlite_pager_unwritelock(pPager);
       if( rc!=SQLITE_OK ){ return SQLITE_PROTOCOL; }
    }
    pPg = 0;
  }else{
    /* Search for page in cache */
    pPg = sqlite_pager_lookup(pPager, pgno);
  }
  if( pPg==0 ){
    int h;
    if( pPager->nPage<pPager->mxPage || pPager->pFirst==0 ){
      /* Create a new page */
      pPg = sqlite_malloc( sizeof(*pPg) + SQLITE_PAGE_SIZE );
      pPg->pPager = pPager;
    }else{
      /* Recycle an older page */
      pPg = pPager->pFirst;
      if( pPg->dirty ){
        int rc;
        sqlite_pager_seekpage(pPager->fd, pPg->pgno, SEEK_SET);
        rc = sqlite_pager_writepage(pPager->fd, PGHDR_TO_DATA(pPg));
        if( rc!=SQLITE_OK ){
          *ppPage = 0;
          return rc;
        }
      } 
      pPager->pFirst = pPg->pNext;
      if( pPager->pFirst ){
        pPager->pFirst->pPrev = 0;
      }else{
        pPager->pLast = 0;
      }
      if( pPg->pNextHash ){
        pPg->pNextHash->pPrevHash = pPg->pPrevHash;
      }
      if( pPg->pPrevHash ){
        pPg->pPrevHash->pNextHash = pPg->pNextHash;
      }else{
        h = sqlite_pager_hash(pPg->pgno);
        assert( pPager->aHash[h]==pPg );
        pPager->aHash[h] = pPg->pNextHash;
      }
    }
    pPg->pgno = pgno;
    pPg->inJournal = 0;
    pPg->dirty = 0;
    pPg->nRef = 1;
    h = sqlite_pager_hash(pgno);
    pPg->pNextHash = pPager->aHash[h];
    pPager->aHash[h] = pPg;
    if( pPg->pNextHash ){
      assert( pPg->pNextHash->pPrevHash==0 );
      pPg->pNextHash->pPrevHash = pPg;
    }
    sqlite_pager_seekpage(pPager->fd, pgno, SEEK_SET);
    sqlite_pager_readpage(pPager->fd, PGHDR_TO_DATA(pPg));
  }else{
    if( pPg->nRef==0 ){
      if( pPg->pPrev ){
        pPg->pPrev->pNext = pPg->pNext;
      }else{
        pPager->pFirst = pPg->pNext;
      }
      if( pPg->pNext ){
        pPg->pNext->pPrev = pPg->pPrev;
      }else{
        pPager->pLast = pPg->pPrev;
      }
    }
    pPg->nRef++;
  }
  *ppPage = PGHDR_TO_DATA(pPg);
  return SQLITE_OK;
}

/*
** Release a page.
**
** If the number of references to the page drop to zero, then the
** page is added to the LRU list.  When all references to all pages
** are released, a rollback occurs, and the lock on the database is
** removed.
*/
int sqlite_pager_unref(void *pData){
  Pager *pPager;
  PgHdr *pPg;
  pPg = DATA_TO_PGHDR(pData);
  assert( pPg->nRef>0 );
  pPager = pPg->pPager;
  pPg->nRef--;
  if( pPg->nRef==0 ){
    pPg->pNext = 0;
    pPg->pPrev = pPager->pLast;
    pPager->pLast = pPg;
    if( pPg->pPrev ){
      pPg->pPrev->pNext = pPg;
    }else{
      pPager->pFirst = pPg;
    }
  }
  pPager->nRef--;
  assert( pPager->nRef>=0 );
  if( pPager->nRef==0 ){
    sqlite_pager_reset(pPager);
  }
}

/*
** Mark a data page as writeable.  The page is written into the journal 
** if it is not there already.  This routine must be called before making
** changes to a page.
**
** The first time this routine is called, the pager creates a new
** journal and acquires a write lock on the database.  If the write
** lock could not be acquired, this routine returns SQLITE_BUSY.  The
** calling routine must check for that routine and be careful not to
** change any page data until this routine returns SQLITE_OK.
*/
int sqlite_pager_write(void *pData){
  if( pPager->state==SQLITE_READLOCK ){
    pPager->jfd = open(pPager->zJournal, O_RDWR|O_CREAT, 0644);
    if( pPager->jfd<0 ){
      return SQLITE_CANTOPEN;
    }
    if( sqlite_pager_lock(pPager->jfd, 1) ){
      close(pPager->jfd);
      pPager->jfd = -1;
      return SQLITE_BUSY;
    }
    sqlite_pager_unlock(pPager->fd);
    if( sqlite_pager_lock(pPager->fd, 1) ){
      close(pPager->jfd);
      pPager->jfd = -1;
      pPager->state = SQLITE_UNLOCK;
      sqlite_pager_reset(pPager);
      return SQLITE_PROTOCOL;
    }
    pPager->state = SQLITE_WRITELOCK;
    pPager->jSize = 0;
  }
  /* Write this page to the journal */
}

/*
** Commit all changes to the database and release the write lock.
*/
int sqlite_pager_commit(Pager*){
  int i, rc;
  PgHdr *pPg;
  assert( pPager->state==SQLITE_WRITELOCK );
  assert( pPager->jfd>=0 );
  if( fsync(pPager->jfd) ){
    return SQLITE_IOERR;
  }
  for(i=0; i<N_PG_HASH; i++){
    for(pPg=pPager->aHash[i]; pPg; pPg=pPg->pNextHash){
      if( pPg->dirty==0 ) continue;
      rc = sqlite_pager_seekpage(pPager->fd, pPg->pgno, SEEK_SET);
      if( rc!=SQLITE_OK ) return rc;
      rc = sqlite_pager_writePage(pPager->fd, PGHDR_TO_DATA(pPg));
      if( rc!=SQLITE_OK ) return rc;
    }
  }
  if( fsync(pPager->fd) ){
    return SQLITE_IOERR;
  }
  rc = sqlite_pager_unwritelock(pPager);
  return rc;
}

/*
** Rollback all changes.  The database falls back to read-only mode.
** All in-memory cache pages revert to their original data contents.
** The journal is deleted.
*/
int sqlite_pager_rollback(Pager *pPager){
  int rc;
  if( pPager->state!=SQLITE_WRITELOCK ) return SQLITE_OK;
  rc = sqlite_pager_playback(pPager);
  if( rc!=SQLITE_OK ){
    rc = sqlite_pager_unwritelock(pPager);
  }
  return rc;
};

Added test/malloc.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
# Copyright (c) 2001 D. Richard Hipp
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
# 
# You should have received a copy of the GNU General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA  02111-1307, USA.
#
# Author contact information:
#   drh@hwaci.com
#   http://www.hwaci.com/drh/
#
#***********************************************************************
# This file attempts to check the library in an out-of-memory situation.
# When compiled with -DMEMORY_DEBUG=1, the SQLite library accepts a special
# command (--malloc-fail=N) which causes the N-th malloc to fail.  This
# special feature is used to see what happens in the library if a malloc
# were to really fail due to an out-of-memory situation.
#
# $Id: malloc.test,v 1.1 2001/04/11 14:29:22 drh 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_fail]==""} {
   puts "Skipping malloc tests: not compiled with -DMEMORY_DEBUG..."
   finish_test
   return
}

for {set go 1; set i 1} {$go} {incr i} {
  do_test malloc-1.$i {
     sqlite_malloc_fail 0
     catch {execsql {DROP TABLE t1}}
     sqlite_malloc_fail $i
     set v [catch {execsql {
        CREATE TABLE t1(
           a int, b float, c double, d text, e varchar(20),
           primary key(a,b,c)
        );
        CREATE INDEX i1 ON t1(a,b);
        INSERT INTO t1 VALUES(1,2.3,4.5,'hi','there');
        INSERT INTO t1 VALUES(6,7.0,0.8,'hello','out yonder');
        SELECT * FROM t1;
        SELECT avg(b) FROM t1 GROUP BY a;
        DELETE FROM t1 WHERE a==6;
        SELECT count(*) FROM t1;
     }} msg]
     if {[lindex [sqlite_malloc_stat] 2]>0} {
       set ::go 0
       set v {1 1}
     } else {
       lappend v [expr {$msg=="" || $msg=="out of memory"}]
     }
  } {1 1}
}
sqlite_malloc_fail 0
finish_test