/ Check-in [40f7f0a5]
Login

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

Overview
Comment:Cherry-pick the multi-file transaction fix for ticket [f3e5abed55] out of the experimental branch.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 40f7f0a583e6bba66cd006253a0ef4623ea74f17
User & Date: drh 2010-07-30 11:20:36
Context
2010-07-30
11:31
Changes to the comments describing the Pager.setMaster variable in pager.c. Add an assert() statement to verify that two master journal pointers are not written to a single journal file. check-in: ad78ccac user: dan tags: trunk
11:20
Cherry-pick the multi-file transaction fix for ticket [f3e5abed55] out of the experimental branch. check-in: 40f7f0a5 user: drh tags: trunk
05:06
Add tests to check that the ICU regexp() function can only be called with exactly two arguments. check-in: 451d9657 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

5056
5057
5058
5059
5060
5061
5062




















5063
5064
5065
5066
5067
5068
5069
  if( pPager->noSync ){
    rc = SQLITE_OK;
  }else{
    rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
  }
  return rc;
}





















/*
** Sync the database file for the pager pPager. zMaster points to the name
** of a master journal file that should be written into the individual
** journal file. zMaster may be NULL, which is interpreted as no master
** journal (a single database transaction).
**







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







5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
  if( pPager->noSync ){
    rc = SQLITE_OK;
  }else{
    rc = sqlite3OsSync(pPager->fd, pPager->sync_flags);
  }
  return rc;
}

/*
** This function may only be called while a write-transaction is active in
** rollback. If the connection is in WAL mode, this call is a no-op. 
** Otherwise, if the connection does not already have an EXCLUSIVE lock on 
** the database file, an attempt is made to obtain one.
**
** If the EXCLUSIVE lock is already held or the attempt to obtain it is
** successful, or the connection is in WAL mode, SQLITE_OK is returned.
** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is 
** returned.
*/
int sqlite3PagerExclusiveLock(Pager *pPager){
  int rc = SQLITE_OK;
  assert( pPager->state>=PAGER_RESERVED );
  if( 0==pagerUseWal(pPager) ){
    rc = pager_wait_on_lock(pPager, PAGER_EXCLUSIVE);
  }
  return rc;
}

/*
** Sync the database file for the pager pPager. zMaster points to the name
** of a master journal file that should be written into the individual
** journal file. zMaster may be NULL, which is interpreted as no master
** journal (a single database transaction).
**

Changes to src/pager.h.

125
126
127
128
129
130
131

132
133
134
135
136
137
138
void *sqlite3PagerGetData(DbPage *); 
void *sqlite3PagerGetExtra(DbPage *); 

/* Functions used to manage pager transactions and savepoints. */
int sqlite3PagerPagecount(Pager*, int*);
int sqlite3PagerBegin(Pager*, int exFlag, int);
int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);

int sqlite3PagerSync(Pager *pPager);
int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager);








>







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
void *sqlite3PagerGetData(DbPage *); 
void *sqlite3PagerGetExtra(DbPage *); 

/* Functions used to manage pager transactions and savepoints. */
int sqlite3PagerPagecount(Pager*, int*);
int sqlite3PagerBegin(Pager*, int exFlag, int);
int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
int sqlite3PagerExclusiveLock(Pager*);
int sqlite3PagerSync(Pager *pPager);
int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager);

Changes to src/vdbeaux.c.

1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663

1664



1665
1666
1667
1668
1669
1670
1671
....
1797
1798
1799
1800
1801
1802
1803

1804
1805
1806
1807
1808
1809
1810
  /* Before doing anything else, call the xSync() callback for any
  ** virtual module tables written in this transaction. This has to
  ** be done before determining whether a master journal file is 
  ** required, as an xSync() callback may add an attached database
  ** to the transaction.
  */
  rc = sqlite3VtabSync(db, &p->zErrMsg);
  if( rc!=SQLITE_OK ){
    return rc;
  }

  /* This loop determines (a) if the commit hook should be invoked and
  ** (b) how many database files have open write transactions, not 
  ** including the temp database. (b) is important because if more than 
  ** one database file has an open write transaction, a master journal
  ** file is required for an atomic commit.
  */ 
  for(i=0; i<db->nDb; i++){ 
    Btree *pBt = db->aDb[i].pBt;
    if( sqlite3BtreeIsInTrans(pBt) ){
      needXcommit = 1;
      if( i!=1 ) nTrans++;

    }



  }

  /* If there are any write-transactions at all, invoke the commit hook */
  if( needXcommit && db->xCommitCallback ){
    rc = db->xCommitCallback(db->pCommitArg);
    if( rc ){
      return SQLITE_CONSTRAINT;
................................................................................
    for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ 
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        rc = sqlite3BtreeCommitPhaseOne(pBt, zMaster);
      }
    }
    sqlite3OsCloseFree(pMaster);

    if( rc!=SQLITE_OK ){
      sqlite3DbFree(db, zMaster);
      return rc;
    }

    /* Delete the master journal file. This commits the transaction. After
    ** doing this the directory is synced again before any individual







<
<
<







|




>

>
>
>







 







>







1642
1643
1644
1645
1646
1647
1648



1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
....
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
  /* Before doing anything else, call the xSync() callback for any
  ** virtual module tables written in this transaction. This has to
  ** be done before determining whether a master journal file is 
  ** required, as an xSync() callback may add an attached database
  ** to the transaction.
  */
  rc = sqlite3VtabSync(db, &p->zErrMsg);




  /* This loop determines (a) if the commit hook should be invoked and
  ** (b) how many database files have open write transactions, not 
  ** including the temp database. (b) is important because if more than 
  ** one database file has an open write transaction, a master journal
  ** file is required for an atomic commit.
  */ 
  for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ 
    Btree *pBt = db->aDb[i].pBt;
    if( sqlite3BtreeIsInTrans(pBt) ){
      needXcommit = 1;
      if( i!=1 ) nTrans++;
      rc = sqlite3PagerExclusiveLock(sqlite3BtreePager(pBt));
    }
  }
  if( rc!=SQLITE_OK ){
    return rc;
  }

  /* If there are any write-transactions at all, invoke the commit hook */
  if( needXcommit && db->xCommitCallback ){
    rc = db->xCommitCallback(db->pCommitArg);
    if( rc ){
      return SQLITE_CONSTRAINT;
................................................................................
    for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ 
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        rc = sqlite3BtreeCommitPhaseOne(pBt, zMaster);
      }
    }
    sqlite3OsCloseFree(pMaster);
    assert( rc!=SQLITE_BUSY );
    if( rc!=SQLITE_OK ){
      sqlite3DbFree(db, zMaster);
      return rc;
    }

    /* Delete the master journal file. This commits the transaction. After
    ** doing this the directory is synced again before any individual

Added test/tkt-f3e5abed55.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
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
# 2010 July 29
#
# 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
source $testdir/malloc_common.tcl

foreach f [glob -nocomplain test.db*mj*] { file delete -force $f }
file delete -force test.db2

do_test tkt-f3e5abed55-1.1 {
  execsql {
    ATTACH 'test.db2' AS aux;
    CREATE TABLE main.t1(a, b);
    CREATE TABLE aux.t2(c, d);
  }
} {}

do_test tkt-f3e5abed55-1.2 {
  glob -nocomplain test.db*mj*
} {}

do_test tkt-f3e5abed55-1.3 {
  sqlite3 db2 test.db
  execsql { BEGIN; SELECT * FROM t1 } db2
} {}

do_test tkt-f3e5abed55-1.4 {
  execsql {
    BEGIN;
      INSERT INTO t1 VALUES(1, 2);
      INSERT INTO t2 VALUES(1, 2);
  }
  catchsql COMMIT
} {1 {database is locked}}

do_test tkt-f3e5abed55-1.5 {
  execsql COMMIT db2
  execsql COMMIT
} {}

do_test tkt-f3e5abed55-1.6 {
  glob -nocomplain test.db*mj*
} {}
foreach f [glob -nocomplain test.db*mj*] { file delete -force $f }
db close
db2 close



# Set up a testvfs so that the next time SQLite tries to delete the
# file "test.db-journal", a snapshot of the current file-system contents
# is taken.
#
testvfs tvfs -default 1
tvfs script xDelete
tvfs filter xDelete
proc xDelete {method file args} {
  if {[file tail $file] == "test.db-journal"} {
    faultsim_save
    tvfs filter {}
  }
  return "SQLITE_OK"
}

sqlite3 db  test.db
sqlite3 db2 test.db
do_test tkt-f3e5abed55-2.1 {
  execsql {
    ATTACH 'test.db2' AS aux;
    BEGIN;
      INSERT INTO t1 VALUES(3, 4);
      INSERT INTO t2 VALUES(3, 4);
  }
} {}
do_test tkt-f3e5abed55-2.2 {
  execsql { BEGIN; SELECT * FROM t1 } db2
} {1 2}
do_test tkt-f3e5abed55-2.3 {
  catchsql COMMIT
} {1 {database is locked}}

do_test tkt-f3e5abed55-2.4 {
  execsql COMMIT db2
  execsql {
    COMMIT;
    SELECT * FROM t1;
    SELECT * FROM t2;
  }
} {1 2 3 4 1 2 3 4}
do_test tkt-f3e5abed55-2.5 {
  db close
  db2 close
  faultsim_restore_and_reopen
  execsql {
    ATTACH 'test.db2' AS aux;
    SELECT * FROM t1;
    SELECT * FROM t2;
  }
} {1 2 3 4 1 2 3 4}


finish_test