/* ** A read or write transaction may or may not be active on database handle ** db. If a transaction is active, commit it. If there is a ** write-transaction spanning more than one database file, this routine ** takes care of the master journal trickery. */ static int vdbeCommit( sqlite3 db, Vdbe p ) { int i; int nTrans = 0; /* Number of databases with an active write-transaction */ int rc = SQLITE_OK; bool needXcommit = false; #if SQLITE_OMIT_VIRTUALTABLE /* With this option, sqlite3VtabSync() is defined to be simply ** SQLITE_OK so p is not used. */ UNUSED_PARAMETER( p ); #endif /* 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, ref 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 = true; 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 != null ) { rc = db.xCommitCallback( db.pCommitArg ); if ( rc != 0 ) { return SQLITE_CONSTRAINT; } } /* The simple case - no more than one database file (not counting the ** TEMP database) has a transaction active. There is no need for the ** master-journal. ** ** If the return value of sqlite3BtreeGetFilename() is a zero length ** string, it means the main database is :memory: or a temp file. In ** that case we do not support atomic multi-file commits, so use the ** simple case then too. */ if ( 0 == sqlite3Strlen30( sqlite3BtreeGetFilename( db.aDb[0].pBt ) ) || nTrans <= 1 ) { for ( i = 0; rc == SQLITE_OK && i < db.nDb; i++ ) { Btree pBt = db.aDb[i].pBt; if ( pBt != null ) { rc = sqlite3BtreeCommitPhaseOne( pBt, null ); } } /* Do the commit only if all databases successfully complete phase 1. ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an ** IO error while deleting or truncating a journal file. It is unlikely, ** but could happen. In this case abandon processing and return the error. */ for ( i = 0; rc == SQLITE_OK && i < db.nDb; i++ ) { Btree pBt = db.aDb[i].pBt; if ( pBt != null ) { rc = sqlite3BtreeCommitPhaseTwo( pBt, 0 ); } } if ( rc == SQLITE_OK ) { sqlite3VtabCommit( db ); } } /* The complex case - There is a multi-file write-transaction active. ** This requires a master journal file to ensure the transaction is ** committed atomicly. */ #if !SQLITE_OMIT_DISKIO else { sqlite3_vfs pVfs = db.pVfs; bool needSync = false; string zMaster = ""; /* File-name for the master journal */ string zMainFile = sqlite3BtreeGetFilename( db.aDb[0].pBt ); sqlite3_file pMaster = null; i64 offset = 0; int res = 0; /* Select a master journal file name */ do { i64 iRandom = 0; sqlite3DbFree( db, ref zMaster ); sqlite3_randomness( sizeof( u32 ), ref iRandom );//random.Length zMaster = sqlite3MPrintf( db, "%s-mj%08X", zMainFile, iRandom & 0x7fffffff ); //if (!zMaster) //{ // return SQLITE_NOMEM; //} sqlite3FileSuffix3( zMainFile, zMaster ); rc = sqlite3OsAccess( pVfs, zMaster, SQLITE_ACCESS_EXISTS, ref res ); } while ( rc == SQLITE_OK && res == 1 ); if ( rc == SQLITE_OK ) { /* Open the master journal. */ rc = sqlite3OsOpenMalloc( ref pVfs, zMaster, ref pMaster, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_MASTER_JOURNAL, ref rc ); } if ( rc != SQLITE_OK ) { sqlite3DbFree( db, ref zMaster ); return rc; } /* Write the name of each database file in the transaction into the new ** master journal file. If an error occurs at this point close ** and delete the master journal file. All the individual journal files ** still have 'null' as the master journal pointer, so they will roll ** back independently if a failure occurs. */ for ( i = 0; i < db.nDb; i++ ) { Btree pBt = db.aDb[i].pBt; if ( sqlite3BtreeIsInTrans( pBt ) ) { string zFile = sqlite3BtreeGetJournalname( pBt ); if ( zFile == null ) { continue; /* Ignore TEMP and :memory: databases */ } Debug.Assert( zFile != "" ); if ( !needSync && 0 == sqlite3BtreeSyncDisabled( pBt ) ) { needSync = true; } rc = sqlite3OsWrite( pMaster, Encoding.UTF8.GetBytes( zFile ), sqlite3Strlen30( zFile ), offset ); offset += sqlite3Strlen30( zFile ); if ( rc != SQLITE_OK ) { sqlite3OsCloseFree( pMaster ); sqlite3OsDelete( pVfs, zMaster, 0 ); sqlite3DbFree( db, ref zMaster ); return rc; } } } /* Sync the master journal file. If the IOCAP_SEQUENTIAL device ** flag is set this is not required. */ if ( needSync && 0 == ( sqlite3OsDeviceCharacteristics( pMaster ) & SQLITE_IOCAP_SEQUENTIAL ) && SQLITE_OK != ( rc = sqlite3OsSync( pMaster, SQLITE_SYNC_NORMAL ) ) ) { sqlite3OsCloseFree( pMaster ); sqlite3OsDelete( pVfs, zMaster, 0 ); sqlite3DbFree( db, ref zMaster ); return rc; } /* Sync all the db files involved in the transaction. The same call ** sets the master journal pointer in each individual journal. If ** an error occurs here, do not delete the master journal file. ** ** If the error occurs during the first call to ** sqlite3BtreeCommitPhaseOne(), then there is a chance that the ** master journal file will be orphaned. But we cannot delete it, ** in case the master journal file name was written into the journal ** file before the failure occurred. */ for ( i = 0; rc == SQLITE_OK && i < db.nDb; i++ ) { Btree pBt = db.aDb[i].pBt; if ( pBt != null ) { rc = sqlite3BtreeCommitPhaseOne( pBt, zMaster ); } } sqlite3OsCloseFree( pMaster ); Debug.Assert( rc != SQLITE_BUSY ); if ( rc != SQLITE_OK ) { sqlite3DbFree( db, ref zMaster ); return rc; } /* Delete the master journal file. This commits the transaction. After ** doing this the directory is synced again before any individual ** transaction files are deleted. */ rc = sqlite3OsDelete( pVfs, zMaster, 1 ); sqlite3DbFree( db, ref zMaster ); if ( rc != 0 ) { return rc; } /* All files and directories have already been synced, so the following ** calls to sqlite3BtreeCommitPhaseTwo() are only closing files and ** deleting or truncating journals. If something goes wrong while ** this is happening we don't really care. The integrity of the ** transaction is already guaranteed, but some stray 'cold' journals ** may be lying around. Returning an error code won't help matters. */ #if SQLITE_TEST disable_simulated_io_errors(); #endif sqlite3BeginBenignMalloc(); for ( i = 0; i < db.nDb; i++ ) { Btree pBt = db.aDb[i].pBt; if ( pBt != null ) { sqlite3BtreeCommitPhaseTwo( pBt, 0 ); } } sqlite3EndBenignMalloc(); #if SQLITE_TEST enable_simulated_io_errors(); #endif sqlite3VtabCommit( db ); } #endif return rc; }