/ Check-in [9b8c8b18]
Login

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

Overview
Comment:Changes to the backup API: (1) if a negative number is passed as the second argument to backup_step(), this is interpreted as "copy all remaining pages" and (2) if backup_finish() is called after backup_step() fails with a BUSY or LOCKED error, then this error is also returned by backup_finish() (same handling as for other errors encountered by backup_step()). (CVS 6266)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:9b8c8b18cf6b7d44d5fd64760537bc030097756b
User & Date: danielk1977 2009-02-06 05:59:44
Context
2009-02-09
05:18
Attempt to reproduce ticket #3643. No luck yet. (CVS 6267) check-in: 8f2f98d2 user: danielk1977 tags: trunk
2009-02-06
05:59
Changes to the backup API: (1) if a negative number is passed as the second argument to backup_step(), this is interpreted as "copy all remaining pages" and (2) if backup_finish() is called after backup_step() fails with a BUSY or LOCKED error, then this error is also returned by backup_finish() (same handling as for other errors encountered by backup_step()). (CVS 6266) check-in: 9b8c8b18 user: danielk1977 tags: trunk
2009-02-05
17:00
Fix a test case that was still using the old pending_byte mechanism. (CVS 6265) check-in: 08ec4f2c user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/backup.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
182
183
184
185
186
187
188









189
190
191
192
193
194
195
...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
...
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
...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
...
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
...
495
496
497
498
499
500
501
502
503
504
505
506
507

508
509
510
511
512
513
514
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains the implementation of the sqlite3_backup_XXX() 
** API functions and the related features.
**
** $Id: backup.c,v 1.7 2009/02/04 17:40:58 drh Exp $
*/
#include "sqliteInt.h"
#include "btreeInt.h"

/* Macro to find the minimum of two numeric values.
*/
#ifndef MIN
................................................................................
    p->pSrc->nBackup++;
  }

  sqlite3_mutex_leave(pDestDb->mutex);
  sqlite3_mutex_leave(pSrcDb->mutex);
  return p;
}










/*
** Parameter zSrcData points to a buffer containing the data for 
** page iSrcPg from the source database. Copy this data into the 
** destination database.
*/
static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
................................................................................
  const int nCopy = MIN(nSrcPgsz, nDestPgsz);
  const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;

  int rc = SQLITE_OK;
  i64 iOff;

  assert( p->bDestLocked );
  assert( p->rc==SQLITE_OK );
  assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
  assert( zSrcData );

  /* Catch the case where the destination is an in-memory database and the
  ** page sizes of the source and destination differ. 
  */
  if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(sqlite3BtreePager(p->pDest)) ){
................................................................................
  sqlite3_mutex_enter(p->pSrcDb->mutex);
  sqlite3BtreeEnter(p->pSrc);
  if( p->pDestDb ){
    sqlite3_mutex_enter(p->pDestDb->mutex);
  }

  rc = p->rc;
  if( rc==SQLITE_OK ){
    Pager * const pSrcPager = sqlite3BtreePager(p->pSrc);     /* Source pager */
    Pager * const pDestPager = sqlite3BtreePager(p->pDest);   /* Dest pager */
    int ii;                            /* Iterator variable */
    int nSrcPage = -1;                 /* Size of source db in pages */
    int bCloseTrans = 0;               /* True if src db requires unlocking */

    /* If the source pager is currently in a write-transaction, return
    ** SQLITE_LOCKED immediately.
    */
    if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){
      rc = SQLITE_LOCKED;


    }

    /* Lock the destination database, if it is not locked already. */
    if( SQLITE_OK==rc && p->bDestLocked==0
     && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2)) 
    ){
      p->bDestLocked = 1;
................................................................................
  
    /* Now that there is a read-lock on the source database, query the
    ** source pager for the number of pages in the database.
    */
    if( rc==SQLITE_OK ){
      rc = sqlite3PagerPagecount(pSrcPager, &nSrcPage);
    }
    for(ii=0; ii<nPage && p->iNext<=(Pgno)nSrcPage && rc==SQLITE_OK; ii++){
      const Pgno iSrcPg = p->iNext;                 /* Source page number */
      if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
        DbPage *pSrcPg;                             /* Source page object */
        rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
        if( rc==SQLITE_OK ){
          rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg));
          sqlite3PagerUnref(pSrcPg);
................................................................................
    if( bCloseTrans ){
      TESTONLY( int rc2 );
      TESTONLY( rc2  = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0);
      TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc);
      assert( rc2==SQLITE_OK );
    }
  
    if( rc!=SQLITE_LOCKED && rc!=SQLITE_BUSY ){
      p->rc = rc;
    }
  }
  if( p->pDestDb ){
    sqlite3_mutex_leave(p->pDestDb->mutex);
  }
  sqlite3BtreeLeave(p->pSrc);
  sqlite3_mutex_leave(p->pSrcDb->mutex);
  return rc;
................................................................................
** corresponding to the source database is held when this function is
** called.
*/
void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){
  sqlite3_backup *p;                   /* Iterator variable */
  for(p=pBackup; p; p=p->pNext){
    assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) );
    if( p->rc==SQLITE_OK && iPage<p->iNext ){
      /* The backup process p has already copied page iPage. But now it
      ** has been modified by a transaction on the source pager. Copy
      ** the new data into the backup.
      */
      int rc = backupOnePage(p, iPage, aData);

      if( rc!=SQLITE_OK ){
        p->rc = rc;
      }
    }
  }
}








|







 







>
>
>
>
>
>
>
>
>







 







|







 







|











>
>







 







|







 







<
|
<







 







|





>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
...
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
...
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
...
417
418
419
420
421
422
423

424

425
426
427
428
429
430
431
...
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains the implementation of the sqlite3_backup_XXX() 
** API functions and the related features.
**
** $Id: backup.c,v 1.8 2009/02/06 05:59:44 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "btreeInt.h"

/* Macro to find the minimum of two numeric values.
*/
#ifndef MIN
................................................................................
    p->pSrc->nBackup++;
  }

  sqlite3_mutex_leave(pDestDb->mutex);
  sqlite3_mutex_leave(pSrcDb->mutex);
  return p;
}

/*
** Argument rc is an SQLite error code. Return true if this error is 
** considered fatal if encountered during a backup operation. All errors
** are considered fatal except for SQLITE_BUSY and SQLITE_LOCKED.
*/
static int isFatalError(int rc){
  return (rc!=SQLITE_OK && rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED);
}

/*
** Parameter zSrcData points to a buffer containing the data for 
** page iSrcPg from the source database. Copy this data into the 
** destination database.
*/
static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
................................................................................
  const int nCopy = MIN(nSrcPgsz, nDestPgsz);
  const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;

  int rc = SQLITE_OK;
  i64 iOff;

  assert( p->bDestLocked );
  assert( !isFatalError(p->rc) );
  assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
  assert( zSrcData );

  /* Catch the case where the destination is an in-memory database and the
  ** page sizes of the source and destination differ. 
  */
  if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(sqlite3BtreePager(p->pDest)) ){
................................................................................
  sqlite3_mutex_enter(p->pSrcDb->mutex);
  sqlite3BtreeEnter(p->pSrc);
  if( p->pDestDb ){
    sqlite3_mutex_enter(p->pDestDb->mutex);
  }

  rc = p->rc;
  if( !isFatalError(rc) ){
    Pager * const pSrcPager = sqlite3BtreePager(p->pSrc);     /* Source pager */
    Pager * const pDestPager = sqlite3BtreePager(p->pDest);   /* Dest pager */
    int ii;                            /* Iterator variable */
    int nSrcPage = -1;                 /* Size of source db in pages */
    int bCloseTrans = 0;               /* True if src db requires unlocking */

    /* If the source pager is currently in a write-transaction, return
    ** SQLITE_LOCKED immediately.
    */
    if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){
      rc = SQLITE_LOCKED;
    }else{
      rc = SQLITE_OK;
    }

    /* Lock the destination database, if it is not locked already. */
    if( SQLITE_OK==rc && p->bDestLocked==0
     && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2)) 
    ){
      p->bDestLocked = 1;
................................................................................
  
    /* Now that there is a read-lock on the source database, query the
    ** source pager for the number of pages in the database.
    */
    if( rc==SQLITE_OK ){
      rc = sqlite3PagerPagecount(pSrcPager, &nSrcPage);
    }
    for(ii=0; (nPage<0 || ii<nPage) && p->iNext<=(Pgno)nSrcPage && !rc; ii++){
      const Pgno iSrcPg = p->iNext;                 /* Source page number */
      if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
        DbPage *pSrcPg;                             /* Source page object */
        rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
        if( rc==SQLITE_OK ){
          rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg));
          sqlite3PagerUnref(pSrcPg);
................................................................................
    if( bCloseTrans ){
      TESTONLY( int rc2 );
      TESTONLY( rc2  = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0);
      TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc);
      assert( rc2==SQLITE_OK );
    }
  

    p->rc = rc;

  }
  if( p->pDestDb ){
    sqlite3_mutex_leave(p->pDestDb->mutex);
  }
  sqlite3BtreeLeave(p->pSrc);
  sqlite3_mutex_leave(p->pSrcDb->mutex);
  return rc;
................................................................................
** corresponding to the source database is held when this function is
** called.
*/
void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){
  sqlite3_backup *p;                   /* Iterator variable */
  for(p=pBackup; p; p=p->pNext){
    assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) );
    if( !isFatalError(p->rc) && iPage<p->iNext ){
      /* The backup process p has already copied page iPage. But now it
      ** has been modified by a transaction on the source pager. Copy
      ** the new data into the backup.
      */
      int rc = backupOnePage(p, iPage, aData);
      assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED );
      if( rc!=SQLITE_OK ){
        p->rc = rc;
      }
    }
  }
}

Changes to src/sqlite.h.in.

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
....
6783
6784
6785
6786
6787
6788
6789
6790

6791
6792
6793
6794
6795
6796
6797
** on how SQLite interfaces are suppose to operate.
**
** The name of this file under configuration management is "sqlite.h.in".
** The makefile makes some minor changes to this file (such as inserting
** the version number) and changes its name to "sqlite3.h" as
** part of the build process.
**
** @(#) $Id: sqlite.h.in,v 1.427 2009/02/05 16:31:46 drh Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
** sqlite3_backup_finish() functions to perform the specified backup 
** operation.
**
** <b>sqlite3_backup_step()</b>
**
** Function [sqlite3_backup_step()] is used to copy up to nPage pages between 
** the source and destination databases, where nPage is the value of the 
** second parameter passed to sqlite3_backup_step(). If nPage pages are 

** succesfully copied, but there are still more pages to copy before the 
** backup is complete, it returns [SQLITE_OK]. If no error occured and there 
** are no more pages to copy, then [SQLITE_DONE] is returned. If an error 
** occurs, then an SQLite error code is returned. As well as [SQLITE_OK] and
** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY],
** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an
** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code.







|







 







|
>







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
....
6783
6784
6785
6786
6787
6788
6789
6790
6791
6792
6793
6794
6795
6796
6797
6798
** on how SQLite interfaces are suppose to operate.
**
** The name of this file under configuration management is "sqlite.h.in".
** The makefile makes some minor changes to this file (such as inserting
** the version number) and changes its name to "sqlite3.h" as
** part of the build process.
**
** @(#) $Id: sqlite.h.in,v 1.428 2009/02/06 05:59:44 danielk1977 Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
#include <stdarg.h>     /* Needed for the definition of va_list */

/*
** Make sure we can call this stuff from C++.
................................................................................
** sqlite3_backup_finish() functions to perform the specified backup 
** operation.
**
** <b>sqlite3_backup_step()</b>
**
** Function [sqlite3_backup_step()] is used to copy up to nPage pages between 
** the source and destination databases, where nPage is the value of the 
** second parameter passed to sqlite3_backup_step(). If nPage is a negative
** value, all remaining source pages are copied. If the required pages are 
** succesfully copied, but there are still more pages to copy before the 
** backup is complete, it returns [SQLITE_OK]. If no error occured and there 
** are no more pages to copy, then [SQLITE_DONE] is returned. If an error 
** occurs, then an SQLite error code is returned. As well as [SQLITE_OK] and
** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY],
** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an
** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code.

Changes to test/backup.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
31
32
33
34
35
36
37



38
39
40
41
42
43
44
...
759
760
761
762
763
764
765


766


































767

#    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 sqlite3_backup_XXX API.
#
# $Id: backup.test,v 1.3 2009/02/04 22:46:47 drh Exp $

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

#---------------------------------------------------------------------
# Test organization:
#
................................................................................
# backup-5.*: Test the source database being modified during a backup.
#
# backup-6.*: Test the backup_remaining() and backup_pagecount() APIs.
#
# backup-7.*: Test SQLITE_BUSY and SQLITE_LOCKED errors.
#
# backup-8.*: Test multiple simultaneous backup operations.



#

proc data_checksum {db file} { $db one "SELECT md5sum(a, b) FROM ${file}.t1" }
proc test_contents {name db1 file1 db2 file2} {
  $db2 eval {select * from sqlite_master}
  $db1 eval {select * from sqlite_master}
  set checksum [data_checksum $db2 $file2]
................................................................................
} {main shared temp closed}
do_test backup-8.10 {
  execsql COMMIT
} {}
catch { db2 close }
catch { db3 close }






































finish_test








|







 







>
>
>







 







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

>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
...
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
#    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 sqlite3_backup_XXX API.
#
# $Id: backup.test,v 1.4 2009/02/06 05:59:45 danielk1977 Exp $

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

#---------------------------------------------------------------------
# Test organization:
#
................................................................................
# backup-5.*: Test the source database being modified during a backup.
#
# backup-6.*: Test the backup_remaining() and backup_pagecount() APIs.
#
# backup-7.*: Test SQLITE_BUSY and SQLITE_LOCKED errors.
#
# backup-8.*: Test multiple simultaneous backup operations.
#
# backup-9.*: Test that passing a negative argument to backup_step() is
#             interpreted as "copy the whole file".
#

proc data_checksum {db file} { $db one "SELECT md5sum(a, b) FROM ${file}.t1" }
proc test_contents {name db1 file1 db2 file2} {
  $db2 eval {select * from sqlite_master}
  $db1 eval {select * from sqlite_master}
  set checksum [data_checksum $db2 $file2]
................................................................................
} {main shared temp closed}
do_test backup-8.10 {
  execsql COMMIT
} {}
catch { db2 close }
catch { db3 close }

#-----------------------------------------------------------------------
# The following tests, backup-9.*, test that:
# 
#   * Passing 0 as an argument to sqlite3_backup_step() means no pages
#     are backed up (backup-9.1.*), and 
#   * Passing a negative value as an argument to sqlite3_backup_step() means 
#     all pages are backed up (backup-9.2.*).
#
# These tests reuse the database "test.db" left over from backup-7.*.
# 
do_test backup-9.1.1 {
  sqlite3 db2 test2.db
  sqlite3_backup B db2 main db main
  B step 1
} {SQLITE_OK}
do_test backup-9.1.2 {
  set nRemaining [B remaining]
  expr {$nRemaining>100}
} {1}
do_test backup-9.1.3 {
  B step 0
} {SQLITE_OK}
do_test backup-9.1.4 {
  B remaining
} $nRemaining

do_test backup-9.2.1 {
  B step -1
} {SQLITE_DONE}
do_test backup-9.2.2 {
  B remaining
} {0}
do_test backup-9.2.3 {
  B finish
} {SQLITE_OK}
catch {db2 close}

finish_test