/ Check-in [169b5505]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Have calls to sqlite3_backup_init() fail if there is already a read or read-write transaction open on the destination database.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 169b5505498c0a7ee2b5dbb2ba13c41dfaa7c62f
User & Date: dan 2014-11-13 14:18:25
Context
2014-11-13
14:30
Modify the documentation for sqlite3_backup_init() to indicate that it will fail if there is already a read or read-write transaction open on the destination database. check-in: ef03a203 user: dan tags: trunk
14:18
Have calls to sqlite3_backup_init() fail if there is already a read or read-write transaction open on the destination database. check-in: 169b5505 user: dan tags: trunk
2014-11-12
17:45
Add further tests for rollback operations in the presence of ongoing selects. check-in: eaf3aae0 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/backup.c.

117
118
119
120
121
122
123














124
125
126
127
128
129
130
...
177
178
179
180
181
182
183

184


185

186
187
188
189
190
191
192
193
194
195
196
** of the source.
*/
static int setDestPgsz(sqlite3_backup *p){
  int rc;
  rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0);
  return rc;
}















/*
** Create an sqlite3_backup process to copy the contents of zSrcDb from
** connection handle pSrcDb to zDestDb in pDestDb. If successful, return
** a pointer to the new sqlite3_backup object.
**
** If an error occurs, NULL is returned and an error code and error message
................................................................................
    p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb);
    p->pDest = findBtree(pDestDb, pDestDb, zDestDb);
    p->pDestDb = pDestDb;
    p->pSrcDb = pSrcDb;
    p->iNext = 1;
    p->isAttached = 0;


    if( 0==p->pSrc || 0==p->pDest || setDestPgsz(p)==SQLITE_NOMEM ){


      /* One (or both) of the named databases did not exist or an OOM

      ** error was hit.  The error has already been written into the
      ** pDestDb handle.  All that is left to do here is free the
      ** sqlite3_backup structure.
      */
      sqlite3_free(p);
      p = 0;
    }
  }
  if( p ){
    p->pSrc->nBackup++;
  }







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







 







>
|
>
>

>
|
|
|
<







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
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206

207
208
209
210
211
212
213
** of the source.
*/
static int setDestPgsz(sqlite3_backup *p){
  int rc;
  rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0);
  return rc;
}

/*
** Check that there is no open read-transaction on the b-tree passed as the
** second argument. If there is not, return SQLITE_OK. Otherwise, if there
** is an open read-transaction, return SQLITE_ERROR and leave an error 
** message in database handle db.
*/
static int checkReadTransaction(sqlite3 *db, Btree *p){
  if( sqlite3BtreeIsInReadTrans(p) ){
    sqlite3ErrorWithMsg(db, SQLITE_ERROR, "destination database is in use");
    return SQLITE_ERROR;
  }
  return SQLITE_OK;
}

/*
** Create an sqlite3_backup process to copy the contents of zSrcDb from
** connection handle pSrcDb to zDestDb in pDestDb. If successful, return
** a pointer to the new sqlite3_backup object.
**
** If an error occurs, NULL is returned and an error code and error message
................................................................................
    p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb);
    p->pDest = findBtree(pDestDb, pDestDb, zDestDb);
    p->pDestDb = pDestDb;
    p->pSrcDb = pSrcDb;
    p->iNext = 1;
    p->isAttached = 0;

    if( 0==p->pSrc || 0==p->pDest 
     || setDestPgsz(p)==SQLITE_NOMEM 
     || checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK 
     ){
      /* One (or both) of the named databases did not exist or an OOM
      ** error was hit. Or there is a transaction open on the destination
      ** database. The error has already been written into the pDestDb 
      ** handle. All that is left to do here is free the sqlite3_backup 
      ** structure.  */

      sqlite3_free(p);
      p = 0;
    }
  }
  if( p ){
    p->pSrc->nBackup++;
  }

Changes to test/backup.test.

213
214
215
216
217
218
219

220
221
222
223
224
225
226
        CREATE INDEX ${file_dest}.i1 ON t1(a, b);
      " $db_dest
      for {set ii 0} {$ii < $rows_dest} {incr ii} {
        execsql "
          INSERT INTO ${file_dest}.t1 VALUES(1, randstr(1000,1000))
        " $db_dest
      }

    }
  
    # Backup the source database.
    do_test backup-2.$iTest.1 {
      sqlite3_backup B $db_dest $file_dest db main
      while {[B step $nPagePerStep]=="SQLITE_OK"} {}
      B finish







>







213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
        CREATE INDEX ${file_dest}.i1 ON t1(a, b);
      " $db_dest
      for {set ii 0} {$ii < $rows_dest} {incr ii} {
        execsql "
          INSERT INTO ${file_dest}.t1 VALUES(1, randstr(1000,1000))
        " $db_dest
      }
      execsql COMMIT $db_dest
    }
  
    # Backup the source database.
    do_test backup-2.$iTest.1 {
      sqlite3_backup B $db_dest $file_dest db main
      while {[B step $nPagePerStep]=="SQLITE_OK"} {}
      B finish

Added test/backup5.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
# 2014 November 13
#
# 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.
#
#***********************************************************************
#

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

forcedelete test2.db

do_execsql_test 1.0 {
  CREATE TABLE t1(a, b);
  CREATE TABLE t2(a, b);
  INSERT INTO t2 VALUES(1, 1);
  INSERT INTO t2 VALUES(2, 2);
  INSERT INTO t2 VALUES(3, 3);
}

do_test 1.1 {
  forcecopy test.db test.db2
  db eval {
    DROP TABLE t2;
    INSERT INTO t1 VALUES(zeroblob(1000), zeroblob(1000));
    INSERT INTO t1 VALUES(randomblob(1000), randomblob(1000));
  }
} {}

do_test 1.2 {
  sqlite3 db2 test.db2
  set stmt [sqlite3_prepare_v2 db2 "SELECT * FROM t2" -1 dummy]
  sqlite3_step $stmt
} {SQLITE_ROW}

do_test 1.3 {
  list [catch { sqlite3_backup B db2 main db main } msg] $msg
} {1 {sqlite3_backup_init() failed}}

do_test 1.4 {
  sqlite3_errmsg db2
} {destination database is in use}

do_test 1.5 {
  sqlite3_reset $stmt
  sqlite3_backup B db2 main db main
  B step 200
  B finish
} {SQLITE_OK}

do_test 1.6 {
  list [sqlite3_step $stmt] [sqlite3_finalize $stmt]
} {SQLITE_ERROR SQLITE_ERROR}

do_test 1.7 {
  sqlite3_errmsg db2
} {no such table: t2}

finish_test