wasCSharpSQLite – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | using System; |
2 | using System.Diagnostics; |
||
3 | using System.Text; |
||
4 | |||
5 | using i64 = System.Int64; |
||
6 | using u8 = System.Byte; |
||
7 | using u32 = System.UInt32; |
||
8 | |||
9 | using Pgno = System.UInt32; |
||
10 | |||
11 | |||
12 | namespace Community.CsharpSqlite |
||
13 | { |
||
14 | using sqlite3_int64 = System.Int64; |
||
15 | using DbPage = Sqlite3.PgHdr; |
||
16 | public partial class Sqlite3 |
||
17 | { |
||
18 | /* |
||
19 | ** 2009 January 28 |
||
20 | ** |
||
21 | ** The author disclaims copyright to this source code. In place of |
||
22 | ** a legal notice, here is a blessing: |
||
23 | ** |
||
24 | ** May you do good and not evil. |
||
25 | ** May you find forgiveness for yourself and forgive others. |
||
26 | ** May you share freely, never taking more than you give. |
||
27 | ** |
||
28 | ************************************************************************* |
||
29 | ** This file contains the implementation of the sqlite3_backup_XXX() |
||
30 | ** API functions and the related features. |
||
31 | ************************************************************************* |
||
32 | ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart |
||
33 | ** C#-SQLite is an independent reimplementation of the SQLite software library |
||
34 | ** |
||
35 | ** SQLITE_SOURCE_ID: 2011-05-19 13:26:54 ed1da510a239ea767a01dc332b667119fa3c908e |
||
36 | ** |
||
37 | ************************************************************************* |
||
38 | */ |
||
39 | //#include "sqliteInt.h" |
||
40 | //#include "btreeInt.h" |
||
41 | |||
42 | /* Macro to find the minimum of two numeric values. |
||
43 | */ |
||
44 | #if !MIN |
||
45 | //# define MIN(x,y) ((x)<(y)?(x):(y)) |
||
46 | #endif |
||
47 | |||
48 | /* |
||
49 | ** Structure allocated for each backup operation. |
||
50 | */ |
||
51 | public class sqlite3_backup |
||
52 | { |
||
53 | public sqlite3 pDestDb; /* Destination database handle */ |
||
54 | public Btree pDest; /* Destination b-tree file */ |
||
55 | public u32 iDestSchema; /* Original schema cookie in destination */ |
||
56 | public int bDestLocked; /* True once a write-transaction is open on pDest */ |
||
57 | |||
58 | public Pgno iNext; /* Page number of the next source page to copy */ |
||
59 | public sqlite3 pSrcDb; /* Source database handle */ |
||
60 | public Btree pSrc; /* Source b-tree file */ |
||
61 | |||
62 | public int rc; /* Backup process error code */ |
||
63 | |||
64 | /* These two variables are set by every call to backup_step(). They are |
||
65 | ** read by calls to backup_remaining() and backup_pagecount(). |
||
66 | */ |
||
67 | public Pgno nRemaining; /* Number of pages left to copy */ |
||
68 | public Pgno nPagecount; /* Total number of pages to copy */ |
||
69 | |||
70 | public int isAttached; /* True once backup has been registered with pager */ |
||
71 | public sqlite3_backup pNext; /* Next backup associated with source pager */ |
||
72 | }; |
||
73 | |||
74 | /* |
||
75 | ** THREAD SAFETY NOTES: |
||
76 | ** |
||
77 | ** Once it has been created using backup_init(), a single sqlite3_backup |
||
78 | ** structure may be accessed via two groups of thread-safe entry points: |
||
79 | ** |
||
80 | ** * Via the sqlite3_backup_XXX() API function backup_step() and |
||
81 | ** backup_finish(). Both these functions obtain the source database |
||
82 | ** handle mutex and the mutex associated with the source BtShared |
||
83 | ** structure, in that order. |
||
84 | ** |
||
85 | ** * Via the BackupUpdate() and BackupRestart() functions, which are |
||
86 | ** invoked by the pager layer to report various state changes in |
||
87 | ** the page cache associated with the source database. The mutex |
||
88 | ** associated with the source database BtShared structure will always |
||
89 | ** be held when either of these functions are invoked. |
||
90 | ** |
||
91 | ** The other sqlite3_backup_XXX() API functions, backup_remaining() and |
||
92 | ** backup_pagecount() are not thread-safe functions. If they are called |
||
93 | ** while some other thread is calling backup_step() or backup_finish(), |
||
94 | ** the values returned may be invalid. There is no way for a call to |
||
95 | ** BackupUpdate() or BackupRestart() to interfere with backup_remaining() |
||
96 | ** or backup_pagecount(). |
||
97 | ** |
||
98 | ** Depending on the SQLite configuration, the database handles and/or |
||
99 | ** the Btree objects may have their own mutexes that require locking. |
||
100 | ** Non-sharable Btrees (in-memory databases for example), do not have |
||
101 | ** associated mutexes. |
||
102 | */ |
||
103 | |||
104 | /* |
||
105 | ** Return a pointer corresponding to database zDb (i.e. "main", "temp") |
||
106 | ** in connection handle pDb. If such a database cannot be found, return |
||
107 | ** a NULL pointer and write an error message to pErrorDb. |
||
108 | ** |
||
109 | ** If the "temp" database is requested, it may need to be opened by this |
||
110 | ** function. If an error occurs while doing so, return 0 and write an |
||
111 | ** error message to pErrorDb. |
||
112 | */ |
||
113 | static Btree findBtree( sqlite3 pErrorDb, sqlite3 pDb, string zDb ) |
||
114 | { |
||
115 | int i = sqlite3FindDbName( pDb, zDb ); |
||
116 | |||
117 | if ( i == 1 ) |
||
118 | { |
||
119 | Parse pParse; |
||
120 | int rc = 0; |
||
121 | pParse = new Parse();//sqlite3StackAllocZero(pErrorDb, sizeof(*pParse)); |
||
122 | if ( pParse == null ) |
||
123 | { |
||
124 | sqlite3Error( pErrorDb, SQLITE_NOMEM, "out of memory" ); |
||
125 | rc = SQLITE_NOMEM; |
||
126 | } |
||
127 | else |
||
128 | { |
||
129 | pParse.db = pDb; |
||
130 | if ( sqlite3OpenTempDatabase( pParse ) != 0 ) |
||
131 | { |
||
132 | sqlite3Error( pErrorDb, pParse.rc, "%s", pParse.zErrMsg ); |
||
133 | rc = SQLITE_ERROR; |
||
134 | } |
||
135 | sqlite3DbFree( pErrorDb, ref pParse.zErrMsg ); |
||
136 | //sqlite3StackFree( pErrorDb, pParse ); |
||
137 | } |
||
138 | if ( rc != 0 ) |
||
139 | { |
||
140 | return null; |
||
141 | } |
||
142 | } |
||
143 | |||
144 | if ( i < 0 ) |
||
145 | { |
||
146 | sqlite3Error( pErrorDb, SQLITE_ERROR, "unknown database %s", zDb ); |
||
147 | return null; |
||
148 | } |
||
149 | |||
150 | return pDb.aDb[i].pBt; |
||
151 | } |
||
152 | |||
153 | /* |
||
154 | ** Attempt to set the page size of the destination to match the page size |
||
155 | ** of the source. |
||
156 | */ |
||
157 | static int setDestPgsz( sqlite3_backup p ) |
||
158 | { |
||
159 | int rc; |
||
160 | rc = sqlite3BtreeSetPageSize( p.pDest, sqlite3BtreeGetPageSize( p.pSrc ), -1, 0 ); |
||
161 | return rc; |
||
162 | } |
||
163 | |||
164 | /* |
||
165 | ** Create an sqlite3_backup process to copy the contents of zSrcDb from |
||
166 | ** connection handle pSrcDb to zDestDb in pDestDb. If successful, return |
||
167 | ** a pointer to the new sqlite3_backup object. |
||
168 | ** |
||
169 | ** If an error occurs, NULL is returned and an error code and error message |
||
170 | ** stored in database handle pDestDb. |
||
171 | */ |
||
172 | static public sqlite3_backup sqlite3_backup_init( |
||
173 | sqlite3 pDestDb, /* Database to write to */ |
||
174 | string zDestDb, /* Name of database within pDestDb */ |
||
175 | sqlite3 pSrcDb, /* Database connection to read from */ |
||
176 | string zSrcDb /* Name of database within pSrcDb */ |
||
177 | ) |
||
178 | { |
||
179 | sqlite3_backup p; /* Value to return */ |
||
180 | |||
181 | /* Lock the source database handle. The destination database |
||
182 | ** handle is not locked in this routine, but it is locked in |
||
183 | ** sqlite3_backup_step(). The user is required to ensure that no |
||
184 | ** other thread accesses the destination handle for the duration |
||
185 | ** of the backup operation. Any attempt to use the destination |
||
186 | ** database connection while a backup is in progress may cause |
||
187 | ** a malfunction or a deadlock. |
||
188 | */ |
||
189 | sqlite3_mutex_enter( pSrcDb.mutex ); |
||
190 | sqlite3_mutex_enter( pDestDb.mutex ); |
||
191 | |||
192 | if ( pSrcDb == pDestDb ) |
||
193 | { |
||
194 | sqlite3Error( |
||
195 | pDestDb, SQLITE_ERROR, "source and destination must be distinct" |
||
196 | ); |
||
197 | p = null; |
||
198 | } |
||
199 | else |
||
200 | { |
||
201 | /* Allocate space for a new sqlite3_backup object... |
||
202 | ** EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a |
||
203 | ** call to sqlite3_backup_init() and is destroyed by a call to |
||
204 | ** sqlite3_backup_finish(). */ |
||
205 | p = new sqlite3_backup();// (sqlite3_backup)sqlite3_malloc( sizeof( sqlite3_backup ) ); |
||
206 | //if ( null == p ) |
||
207 | //{ |
||
208 | // sqlite3Error( pDestDb, SQLITE_NOMEM, 0 ); |
||
209 | //} |
||
210 | } |
||
211 | |||
212 | /* If the allocation succeeded, populate the new object. */ |
||
213 | if ( p != null ) |
||
214 | { |
||
215 | // memset( p, 0, sizeof( sqlite3_backup ) ); |
||
216 | p.pSrc = findBtree( pDestDb, pSrcDb, zSrcDb ); |
||
217 | p.pDest = findBtree( pDestDb, pDestDb, zDestDb ); |
||
218 | p.pDestDb = pDestDb; |
||
219 | p.pSrcDb = pSrcDb; |
||
220 | p.iNext = 1; |
||
221 | p.isAttached = 0; |
||
222 | |||
223 | if ( null == p.pSrc || null == p.pDest || setDestPgsz( p ) == SQLITE_NOMEM ) |
||
224 | { |
||
225 | /* One (or both) of the named databases did not exist or an OOM |
||
226 | ** error was hit. The error has already been written into the |
||
227 | ** pDestDb handle. All that is left to do here is free the |
||
228 | ** sqlite3_backup structure. |
||
229 | */ |
||
230 | //sqlite3_free( ref p ); |
||
231 | p = null; |
||
232 | } |
||
233 | } |
||
234 | |||
235 | if ( p != null ) |
||
236 | { |
||
237 | p.pSrc.nBackup++; |
||
238 | } |
||
239 | |||
240 | sqlite3_mutex_leave( pDestDb.mutex ); |
||
241 | sqlite3_mutex_leave( pSrcDb.mutex ); |
||
242 | return p; |
||
243 | } |
||
244 | |||
245 | /* |
||
246 | ** Argument rc is an SQLite error code. Return true if this error is |
||
247 | ** considered fatal if encountered during a backup operation. All errors |
||
248 | ** are considered fatal except for SQLITE_BUSY and SQLITE_LOCKED. |
||
249 | */ |
||
250 | static bool isFatalError( int rc ) |
||
251 | { |
||
252 | return ( rc != SQLITE_OK && rc != SQLITE_BUSY && ALWAYS( rc != SQLITE_LOCKED ) ); |
||
253 | } |
||
254 | |||
255 | /* |
||
256 | ** Parameter zSrcData points to a buffer containing the data for |
||
257 | ** page iSrcPg from the source database. Copy this data into the |
||
258 | ** destination database. |
||
259 | */ |
||
260 | static int backupOnePage( sqlite3_backup p, Pgno iSrcPg, byte[] zSrcData ) |
||
261 | { |
||
262 | Pager pDestPager = sqlite3BtreePager( p.pDest ); |
||
263 | int nSrcPgsz = sqlite3BtreeGetPageSize( p.pSrc ); |
||
264 | int nDestPgsz = sqlite3BtreeGetPageSize( p.pDest ); |
||
265 | int nCopy = MIN( nSrcPgsz, nDestPgsz ); |
||
266 | i64 iEnd = (i64)iSrcPg * (i64)nSrcPgsz; |
||
267 | #if SQLITE_HAS_CODEC |
||
268 | int nSrcReserve = sqlite3BtreeGetReserve(p.pSrc); |
||
269 | int nDestReserve = sqlite3BtreeGetReserve(p.pDest); |
||
270 | #endif |
||
271 | |||
272 | int rc = SQLITE_OK; |
||
273 | i64 iOff; |
||
274 | |||
275 | Debug.Assert( p.bDestLocked != 0 ); |
||
276 | Debug.Assert( !isFatalError( p.rc ) ); |
||
277 | Debug.Assert( iSrcPg != PENDING_BYTE_PAGE( p.pSrc.pBt ) ); |
||
278 | Debug.Assert( zSrcData != null ); |
||
279 | |||
280 | /* Catch the case where the destination is an in-memory database and the |
||
281 | ** page sizes of the source and destination differ. |
||
282 | */ |
||
283 | if ( nSrcPgsz != nDestPgsz && sqlite3PagerIsMemdb( pDestPager ) ) |
||
284 | { |
||
285 | rc = SQLITE_READONLY; |
||
286 | } |
||
287 | |||
288 | #if SQLITE_HAS_CODEC |
||
289 | /* Backup is not possible if the page size of the destination is changing |
||
290 | ** and a codec is in use. |
||
291 | */ |
||
292 | if ( nSrcPgsz != nDestPgsz && sqlite3PagerGetCodec( pDestPager ) != null ) |
||
293 | { |
||
294 | rc = SQLITE_READONLY; |
||
295 | } |
||
296 | |||
297 | /* Backup is not possible if the number of bytes of reserve space differ |
||
298 | ** between source and destination. If there is a difference, try to |
||
299 | ** fix the destination to agree with the source. If that is not possible, |
||
300 | ** then the backup cannot proceed. |
||
301 | */ |
||
302 | if ( nSrcReserve != nDestReserve ) |
||
303 | { |
||
304 | u32 newPgsz = (u32)nSrcPgsz; |
||
305 | rc = sqlite3PagerSetPagesize( pDestPager, ref newPgsz, nSrcReserve ); |
||
306 | if ( rc == SQLITE_OK && newPgsz != nSrcPgsz ) |
||
307 | rc = SQLITE_READONLY; |
||
308 | } |
||
309 | #endif |
||
310 | |||
311 | /* This loop runs once for each destination page spanned by the source |
||
312 | ** page. For each iteration, variable iOff is set to the byte offset |
||
313 | ** of the destination page. |
||
314 | */ |
||
315 | for ( iOff = iEnd - (i64)nSrcPgsz; rc == SQLITE_OK && iOff < iEnd; iOff += nDestPgsz ) |
||
316 | { |
||
317 | DbPage pDestPg = null; |
||
318 | u32 iDest = (u32)( iOff / nDestPgsz ) + 1; |
||
319 | if ( iDest == PENDING_BYTE_PAGE( p.pDest.pBt ) ) |
||
320 | continue; |
||
321 | if ( SQLITE_OK == ( rc = sqlite3PagerGet( pDestPager, iDest, ref pDestPg ) ) |
||
322 | && SQLITE_OK == ( rc = sqlite3PagerWrite( pDestPg ) ) |
||
323 | ) |
||
324 | { |
||
325 | //string zIn = &zSrcData[iOff%nSrcPgsz]; |
||
326 | byte[] zDestData = sqlite3PagerGetData( pDestPg ); |
||
327 | //string zOut = &zDestData[iOff % nDestPgsz]; |
||
328 | |||
329 | /* Copy the data from the source page into the destination page. |
||
330 | ** Then clear the Btree layer MemPage.isInit flag. Both this module |
||
331 | ** and the pager code use this trick (clearing the first byte |
||
332 | ** of the page 'extra' space to invalidate the Btree layers |
||
333 | ** cached parse of the page). MemPage.isInit is marked |
||
334 | ** "MUST BE FIRST" for this purpose. |
||
335 | */ |
||
336 | Buffer.BlockCopy( zSrcData, (int)( iOff % nSrcPgsz ), zDestData, (int)( iOff % nDestPgsz ), nCopy );// memcpy( zOut, zIn, nCopy ); |
||
337 | sqlite3PagerGetExtra( pDestPg ).isInit = 0;// ( sqlite3PagerGetExtra( pDestPg ) )[0] = 0; |
||
338 | } |
||
339 | sqlite3PagerUnref( pDestPg ); |
||
340 | } |
||
341 | |||
342 | return rc; |
||
343 | } |
||
344 | |||
345 | /* |
||
346 | ** If pFile is currently larger than iSize bytes, then truncate it to |
||
347 | ** exactly iSize bytes. If pFile is not larger than iSize bytes, then |
||
348 | ** this function is a no-op. |
||
349 | ** |
||
350 | ** Return SQLITE_OK if everything is successful, or an SQLite error |
||
351 | ** code if an error occurs. |
||
352 | */ |
||
353 | static int backupTruncateFile( sqlite3_file pFile, int iSize ) |
||
354 | { |
||
355 | long iCurrent = 0; |
||
356 | int rc = sqlite3OsFileSize( pFile, ref iCurrent ); |
||
357 | if ( rc == SQLITE_OK && iCurrent > iSize ) |
||
358 | { |
||
359 | rc = sqlite3OsTruncate( pFile, iSize ); |
||
360 | } |
||
361 | return rc; |
||
362 | } |
||
363 | |||
364 | /* |
||
365 | ** Register this backup object with the associated source pager for |
||
366 | ** callbacks when pages are changed or the cache invalidated. |
||
367 | */ |
||
368 | static void attachBackupObject( sqlite3_backup p ) |
||
369 | { |
||
370 | sqlite3_backup pp; |
||
371 | Debug.Assert( sqlite3BtreeHoldsMutex( p.pSrc ) ); |
||
372 | pp = sqlite3PagerBackupPtr( sqlite3BtreePager( p.pSrc ) ); |
||
373 | p.pNext = pp; |
||
374 | sqlite3BtreePager( p.pSrc ).pBackup = p; //*pp = p; |
||
375 | p.isAttached = 1; |
||
376 | } |
||
377 | |||
378 | /* |
||
379 | ** Copy nPage pages from the source b-tree to the destination. |
||
380 | */ |
||
381 | static public int sqlite3_backup_step( sqlite3_backup p, int nPage ) |
||
382 | { |
||
383 | int rc; |
||
384 | int destMode; /* Destination journal mode */ |
||
385 | int pgszSrc = 0; /* Source page size */ |
||
386 | int pgszDest = 0; /* Destination page size */ |
||
387 | |||
388 | sqlite3_mutex_enter( p.pSrcDb.mutex ); |
||
389 | sqlite3BtreeEnter( p.pSrc ); |
||
390 | if ( p.pDestDb != null ) |
||
391 | { |
||
392 | sqlite3_mutex_enter( p.pDestDb.mutex ); |
||
393 | } |
||
394 | |||
395 | rc = p.rc; |
||
396 | if ( !isFatalError( rc ) ) |
||
397 | { |
||
398 | Pager pSrcPager = sqlite3BtreePager( p.pSrc ); /* Source pager */ |
||
399 | Pager pDestPager = sqlite3BtreePager( p.pDest ); /* Dest pager */ |
||
400 | int ii; /* Iterator variable */ |
||
401 | Pgno nSrcPage = 0; /* Size of source db in pages */ |
||
402 | int bCloseTrans = 0; /* True if src db requires unlocking */ |
||
403 | |||
404 | /* If the source pager is currently in a write-transaction, return |
||
405 | ** SQLITE_BUSY immediately. |
||
406 | */ |
||
407 | if ( p.pDestDb != null && p.pSrc.pBt.inTransaction == TRANS_WRITE ) |
||
408 | { |
||
409 | rc = SQLITE_BUSY; |
||
410 | } |
||
411 | else |
||
412 | { |
||
413 | rc = SQLITE_OK; |
||
414 | } |
||
415 | |||
416 | /* Lock the destination database, if it is not locked already. */ |
||
417 | if ( SQLITE_OK == rc && p.bDestLocked == 0 |
||
418 | && SQLITE_OK == ( rc = sqlite3BtreeBeginTrans( p.pDest, 2 ) ) |
||
419 | ) |
||
420 | { |
||
421 | p.bDestLocked = 1; |
||
422 | sqlite3BtreeGetMeta( p.pDest, BTREE_SCHEMA_VERSION, ref p.iDestSchema ); |
||
423 | } |
||
424 | |||
425 | /* If there is no open read-transaction on the source database, open |
||
426 | ** one now. If a transaction is opened here, then it will be closed |
||
427 | ** before this function exits. |
||
428 | */ |
||
429 | if ( rc == SQLITE_OK && !sqlite3BtreeIsInReadTrans( p.pSrc ) ) |
||
430 | { |
||
431 | rc = sqlite3BtreeBeginTrans( p.pSrc, 0 ); |
||
432 | bCloseTrans = 1; |
||
433 | } |
||
434 | |||
435 | /* Do not allow backup if the destination database is in WAL mode |
||
436 | ** and the page sizes are different between source and destination */ |
||
437 | pgszSrc = sqlite3BtreeGetPageSize( p.pSrc ); |
||
438 | pgszDest = sqlite3BtreeGetPageSize( p.pDest ); |
||
439 | destMode = sqlite3PagerGetJournalMode( sqlite3BtreePager( p.pDest ) ); |
||
440 | if ( SQLITE_OK == rc && destMode == PAGER_JOURNALMODE_WAL && pgszSrc != pgszDest ) |
||
441 | { |
||
442 | rc = SQLITE_READONLY; |
||
443 | } |
||
444 | |||
445 | /* Now that there is a read-lock on the source database, query the |
||
446 | ** source pager for the number of pages in the database. |
||
447 | */ |
||
448 | nSrcPage = sqlite3BtreeLastPage( p.pSrc ); |
||
449 | Debug.Assert( nSrcPage >= 0 ); |
||
450 | |||
451 | for ( ii = 0; ( nPage < 0 || ii < nPage ) && p.iNext <= nSrcPage && 0 == rc; ii++ ) |
||
452 | { |
||
453 | Pgno iSrcPg = p.iNext; /* Source page number */ |
||
454 | if ( iSrcPg != PENDING_BYTE_PAGE( p.pSrc.pBt ) ) |
||
455 | { |
||
456 | DbPage pSrcPg = null; /* Source page object */ |
||
457 | rc = sqlite3PagerGet( pSrcPager, (u32)iSrcPg, ref pSrcPg ); |
||
458 | if ( rc == SQLITE_OK ) |
||
459 | { |
||
460 | rc = backupOnePage( p, iSrcPg, sqlite3PagerGetData( pSrcPg ) ); |
||
461 | sqlite3PagerUnref( pSrcPg ); |
||
462 | } |
||
463 | } |
||
464 | p.iNext++; |
||
465 | } |
||
466 | if ( rc == SQLITE_OK ) |
||
467 | { |
||
468 | p.nPagecount = nSrcPage; |
||
469 | p.nRemaining = ( nSrcPage + 1 - p.iNext ); |
||
470 | if ( p.iNext > nSrcPage ) |
||
471 | { |
||
472 | rc = SQLITE_DONE; |
||
473 | } |
||
474 | else if ( 0 == p.isAttached ) |
||
475 | { |
||
476 | attachBackupObject( p ); |
||
477 | } |
||
478 | } |
||
479 | |||
480 | |||
481 | /* Update the schema version field in the destination database. This |
||
482 | ** is to make sure that the schema-version really does change in |
||
483 | ** the case where the source and destination databases have the |
||
484 | ** same schema version. |
||
485 | */ |
||
486 | if ( rc == SQLITE_DONE |
||
487 | && ( rc = sqlite3BtreeUpdateMeta( p.pDest, 1, p.iDestSchema + 1 ) ) == SQLITE_OK |
||
488 | ) |
||
489 | { |
||
490 | Pgno nDestTruncate; |
||
491 | if ( p.pDestDb != null ) |
||
492 | { |
||
493 | sqlite3ResetInternalSchema( p.pDestDb, -1 ); |
||
494 | } |
||
495 | |||
496 | /* Set nDestTruncate to the final number of pages in the destination |
||
497 | ** database. The complication here is that the destination page |
||
498 | ** size may be different to the source page size. |
||
499 | ** |
||
500 | ** If the source page size is smaller than the destination page size, |
||
501 | ** round up. In this case the call to sqlite3OsTruncate() below will |
||
502 | ** fix the size of the file. However it is important to call |
||
503 | ** sqlite3PagerTruncateImage() here so that any pages in the |
||
504 | ** destination file that lie beyond the nDestTruncate page mark are |
||
505 | ** journalled by PagerCommitPhaseOne() before they are destroyed |
||
506 | ** by the file truncation. |
||
507 | */ |
||
508 | Debug.Assert( pgszSrc == sqlite3BtreeGetPageSize( p.pSrc ) ); |
||
509 | Debug.Assert( pgszDest == sqlite3BtreeGetPageSize( p.pDest ) ); |
||
510 | if ( pgszSrc < pgszDest ) |
||
511 | { |
||
512 | int ratio = pgszDest / pgszSrc; |
||
513 | nDestTruncate = (Pgno)( ( nSrcPage + ratio - 1 ) / ratio ); |
||
514 | if ( nDestTruncate == (int)PENDING_BYTE_PAGE( p.pDest.pBt ) ) |
||
515 | { |
||
516 | nDestTruncate--; |
||
517 | } |
||
518 | } |
||
519 | else |
||
520 | { |
||
521 | nDestTruncate = (Pgno)( nSrcPage * ( pgszSrc / pgszDest ) ); |
||
522 | } |
||
523 | sqlite3PagerTruncateImage( pDestPager, nDestTruncate ); |
||
524 | |||
525 | if ( pgszSrc < pgszDest ) |
||
526 | { |
||
527 | /* If the source page-size is smaller than the destination page-size, |
||
528 | ** two extra things may need to happen: |
||
529 | ** |
||
530 | ** * The destination may need to be truncated, and |
||
531 | ** |
||
532 | ** * Data stored on the pages immediately following the |
||
533 | ** pending-byte page in the source database may need to be |
||
534 | ** copied into the destination database. |
||
535 | */ |
||
536 | int iSize = (int)( pgszSrc * nSrcPage ); |
||
537 | sqlite3_file pFile = sqlite3PagerFile( pDestPager ); |
||
538 | i64 iOff; |
||
539 | i64 iEnd; |
||
540 | |||
541 | Debug.Assert( pFile != null ); |
||
542 | Debug.Assert( (i64)nDestTruncate * (i64)pgszDest >= iSize || ( |
||
543 | nDestTruncate == (int)( PENDING_BYTE_PAGE( p.pDest.pBt ) - 1 ) |
||
544 | && iSize >= PENDING_BYTE && iSize <= PENDING_BYTE + pgszDest |
||
545 | ) ); |
||
546 | |||
547 | /* This call ensures that all data required to recreate the original |
||
548 | ** database has been stored in the journal for pDestPager and the |
||
549 | ** journal synced to disk. So at this point we may safely modify |
||
550 | ** the database file in any way, knowing that if a power failure |
||
551 | ** occurs, the original database will be reconstructed from the |
||
552 | ** journal file. */ |
||
553 | rc = sqlite3PagerCommitPhaseOne( pDestPager, null, true ); |
||
554 | |||
555 | /* Write the extra pages and truncate the database file as required. */ |
||
556 | iEnd = MIN( PENDING_BYTE + pgszDest, iSize ); |
||
557 | for ( |
||
558 | iOff = PENDING_BYTE + pgszSrc; |
||
559 | rc == SQLITE_OK && iOff < iEnd; |
||
560 | iOff += pgszSrc |
||
561 | ) |
||
562 | { |
||
563 | PgHdr pSrcPg = null; |
||
564 | u32 iSrcPg = (u32)( ( iOff / pgszSrc ) + 1 ); |
||
565 | rc = sqlite3PagerGet( pSrcPager, iSrcPg, ref pSrcPg ); |
||
566 | if ( rc == SQLITE_OK ) |
||
567 | { |
||
568 | byte[] zData = sqlite3PagerGetData( pSrcPg ); |
||
569 | rc = sqlite3OsWrite( pFile, zData, pgszSrc, iOff ); |
||
570 | } |
||
571 | sqlite3PagerUnref( pSrcPg ); |
||
572 | } |
||
573 | if ( rc == SQLITE_OK ) |
||
574 | { |
||
575 | rc = backupTruncateFile( pFile, (int)iSize ); |
||
576 | } |
||
577 | |||
578 | /* Sync the database file to disk. */ |
||
579 | if ( rc == SQLITE_OK ) |
||
580 | { |
||
581 | rc = sqlite3PagerSync( pDestPager ); |
||
582 | } |
||
583 | } |
||
584 | else |
||
585 | { |
||
586 | rc = sqlite3PagerCommitPhaseOne( pDestPager, null, false ); |
||
587 | } |
||
588 | |||
589 | /* Finish committing the transaction to the destination database. */ |
||
590 | if ( SQLITE_OK == rc |
||
591 | && SQLITE_OK == ( rc = sqlite3BtreeCommitPhaseTwo( p.pDest, 0 ) ) |
||
592 | ) |
||
593 | { |
||
594 | rc = SQLITE_DONE; |
||
595 | } |
||
596 | } |
||
597 | |||
598 | /* If bCloseTrans is true, then this function opened a read transaction |
||
599 | ** on the source database. Close the read transaction here. There is |
||
600 | ** no need to check the return values of the btree methods here, as |
||
601 | ** "committing" a read-only transaction cannot fail. |
||
602 | */ |
||
603 | if ( bCloseTrans != 0 ) |
||
604 | { |
||
605 | #if !NDEBUG || SQLITE_COVERAGE_TEST |
||
606 | //TESTONLY( int rc2 ); |
||
607 | //TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p.pSrc, 0); |
||
608 | //TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p.pSrc); |
||
609 | int rc2; |
||
610 | rc2 = sqlite3BtreeCommitPhaseOne( p.pSrc, string.Empty ); |
||
611 | rc2 |= sqlite3BtreeCommitPhaseTwo( p.pSrc, 0 ); |
||
612 | Debug.Assert( rc2 == SQLITE_OK ); |
||
613 | #else |
||
614 | sqlite3BtreeCommitPhaseOne(p.pSrc, null); |
||
615 | sqlite3BtreeCommitPhaseTwo(p.pSrc, 0); |
||
616 | #endif |
||
617 | } |
||
618 | |||
619 | if ( rc == SQLITE_IOERR_NOMEM ) |
||
620 | { |
||
621 | rc = SQLITE_NOMEM; |
||
622 | } |
||
623 | p.rc = rc; |
||
624 | } |
||
625 | if ( p.pDestDb != null ) |
||
626 | { |
||
627 | sqlite3_mutex_leave( p.pDestDb.mutex ); |
||
628 | } |
||
629 | sqlite3BtreeLeave( p.pSrc ); |
||
630 | sqlite3_mutex_leave( p.pSrcDb.mutex ); |
||
631 | return rc; |
||
632 | } |
||
633 | |||
634 | /* |
||
635 | ** Release all resources associated with an sqlite3_backup* handle. |
||
636 | */ |
||
637 | static public int sqlite3_backup_finish( sqlite3_backup p ) |
||
638 | { |
||
639 | sqlite3_backup pp; /* Ptr to head of pagers backup list */ |
||
640 | sqlite3_mutex mutex; /* Mutex to protect source database */ |
||
641 | int rc; /* Value to return */ |
||
642 | |||
643 | /* Enter the mutexes */ |
||
644 | if ( p == null ) |
||
645 | return SQLITE_OK; |
||
646 | sqlite3_mutex_enter( p.pSrcDb.mutex ); |
||
647 | sqlite3BtreeEnter( p.pSrc ); |
||
648 | mutex = p.pSrcDb.mutex; |
||
649 | if ( p.pDestDb != null ) |
||
650 | { |
||
651 | sqlite3_mutex_enter( p.pDestDb.mutex ); |
||
652 | } |
||
653 | |||
654 | /* Detach this backup from the source pager. */ |
||
655 | if ( p.pDestDb != null ) |
||
656 | { |
||
657 | p.pSrc.nBackup--; |
||
658 | } |
||
659 | if ( p.isAttached != 0 ) |
||
660 | { |
||
661 | pp = sqlite3PagerBackupPtr( sqlite3BtreePager( p.pSrc ) ); |
||
662 | while ( pp != p ) |
||
663 | { |
||
664 | pp = ( pp ).pNext; |
||
665 | } |
||
666 | sqlite3BtreePager( p.pSrc ).pBackup = p.pNext; |
||
667 | } |
||
668 | |||
669 | /* If a transaction is still open on the Btree, roll it back. */ |
||
670 | sqlite3BtreeRollback( p.pDest ); |
||
671 | |||
672 | /* Set the error code of the destination database handle. */ |
||
673 | rc = ( p.rc == SQLITE_DONE ) ? SQLITE_OK : p.rc; |
||
674 | sqlite3Error( p.pDestDb, rc, 0 ); |
||
675 | |||
676 | /* Exit the mutexes and free the backup context structure. */ |
||
677 | if ( p.pDestDb != null ) |
||
678 | { |
||
679 | sqlite3_mutex_leave( p.pDestDb.mutex ); |
||
680 | } |
||
681 | sqlite3BtreeLeave( p.pSrc ); |
||
682 | if ( p.pDestDb != null ) |
||
683 | { |
||
684 | /* EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a |
||
685 | ** call to sqlite3_backup_init() and is destroyed by a call to |
||
686 | ** sqlite3_backup_finish(). */ |
||
687 | //sqlite3_free( ref p ); |
||
688 | } |
||
689 | sqlite3_mutex_leave( mutex ); |
||
690 | return rc; |
||
691 | } |
||
692 | |||
693 | /* |
||
694 | ** Return the number of pages still to be backed up as of the most recent |
||
695 | ** call to sqlite3_backup_step(). |
||
696 | */ |
||
697 | static int sqlite3_backup_remaining( sqlite3_backup p ) |
||
698 | { |
||
699 | return (int)p.nRemaining; |
||
700 | } |
||
701 | |||
702 | /* |
||
703 | ** Return the total number of pages in the source database as of the most |
||
704 | ** recent call to sqlite3_backup_step(). |
||
705 | */ |
||
706 | static int sqlite3_backup_pagecount( sqlite3_backup p ) |
||
707 | { |
||
708 | return (int)p.nPagecount; |
||
709 | } |
||
710 | |||
711 | /* |
||
712 | ** This function is called after the contents of page iPage of the |
||
713 | ** source database have been modified. If page iPage has already been |
||
714 | ** copied into the destination database, then the data written to the |
||
715 | ** destination is now invalidated. The destination copy of iPage needs |
||
716 | ** to be updated with the new data before the backup operation is |
||
717 | ** complete. |
||
718 | ** |
||
719 | ** It is assumed that the mutex associated with the BtShared object |
||
720 | ** corresponding to the source database is held when this function is |
||
721 | ** called. |
||
722 | */ |
||
723 | static void sqlite3BackupUpdate( sqlite3_backup pBackup, Pgno iPage, byte[] aData ) |
||
724 | { |
||
725 | sqlite3_backup p; /* Iterator variable */ |
||
726 | for ( p = pBackup; p != null; p = p.pNext ) |
||
727 | { |
||
728 | Debug.Assert( sqlite3_mutex_held( p.pSrc.pBt.mutex ) ); |
||
729 | if ( !isFatalError( p.rc ) && iPage < p.iNext ) |
||
730 | { |
||
731 | /* The backup process p has already copied page iPage. But now it |
||
732 | ** has been modified by a transaction on the source pager. Copy |
||
733 | ** the new data into the backup. |
||
734 | */ |
||
735 | int rc; |
||
736 | Debug.Assert( p.pDestDb != null ); |
||
737 | sqlite3_mutex_enter( p.pDestDb.mutex ); |
||
738 | rc = backupOnePage( p, iPage, aData ); |
||
739 | sqlite3_mutex_leave( p.pDestDb.mutex ); |
||
740 | Debug.Assert( rc != SQLITE_BUSY && rc != SQLITE_LOCKED ); |
||
741 | if ( rc != SQLITE_OK ) |
||
742 | { |
||
743 | p.rc = rc; |
||
744 | } |
||
745 | } |
||
746 | } |
||
747 | } |
||
748 | |||
749 | /* |
||
750 | ** Restart the backup process. This is called when the pager layer |
||
751 | ** detects that the database has been modified by an external database |
||
752 | ** connection. In this case there is no way of knowing which of the |
||
753 | ** pages that have been copied into the destination database are still |
||
754 | ** valid and which are not, so the entire process needs to be restarted. |
||
755 | ** |
||
756 | ** It is assumed that the mutex associated with the BtShared object |
||
757 | ** corresponding to the source database is held when this function is |
||
758 | ** called. |
||
759 | */ |
||
760 | static void sqlite3BackupRestart( sqlite3_backup pBackup ) |
||
761 | { |
||
762 | sqlite3_backup p; /* Iterator variable */ |
||
763 | for ( p = pBackup; p != null; p = p.pNext ) |
||
764 | { |
||
765 | Debug.Assert( sqlite3_mutex_held( p.pSrc.pBt.mutex ) ); |
||
766 | p.iNext = 1; |
||
767 | } |
||
768 | } |
||
769 | |||
770 | #if !SQLITE_OMIT_VACUUM |
||
771 | /* |
||
772 | ** Copy the complete content of pBtFrom into pBtTo. A transaction |
||
773 | ** must be active for both files. |
||
774 | ** |
||
775 | ** The size of file pTo may be reduced by this operation. If anything |
||
776 | ** goes wrong, the transaction on pTo is rolled back. If successful, the |
||
777 | ** transaction is committed before returning. |
||
778 | */ |
||
779 | static int sqlite3BtreeCopyFile( Btree pTo, Btree pFrom ) |
||
780 | { |
||
781 | int rc; |
||
782 | sqlite3_backup b; |
||
783 | sqlite3BtreeEnter( pTo ); |
||
784 | sqlite3BtreeEnter( pFrom ); |
||
785 | |||
786 | /* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set |
||
787 | ** to 0. This is used by the implementations of sqlite3_backup_step() |
||
788 | ** and sqlite3_backup_finish() to detect that they are being called |
||
789 | ** from this function, not directly by the user. |
||
790 | */ |
||
791 | b = new sqlite3_backup();// memset( &b, 0, sizeof( b ) ); |
||
792 | b.pSrcDb = pFrom.db; |
||
793 | b.pSrc = pFrom; |
||
794 | b.pDest = pTo; |
||
795 | b.iNext = 1; |
||
796 | |||
797 | /* 0x7FFFFFFF is the hard limit for the number of pages in a database |
||
798 | ** file. By passing this as the number of pages to copy to |
||
799 | ** sqlite3_backup_step(), we can guarantee that the copy finishes |
||
800 | ** within a single call (unless an error occurs). The Debug.Assert() statement |
||
801 | ** checks this assumption - (p.rc) should be set to either SQLITE_DONE |
||
802 | ** or an error code. |
||
803 | */ |
||
804 | sqlite3_backup_step( b, 0x7FFFFFFF ); |
||
805 | Debug.Assert( b.rc != SQLITE_OK ); |
||
806 | rc = sqlite3_backup_finish( b ); |
||
807 | if ( rc == SQLITE_OK ) |
||
808 | { |
||
809 | pTo.pBt.pageSizeFixed = false; |
||
810 | } |
||
811 | |||
812 | sqlite3BtreeLeave( pFrom ); |
||
813 | sqlite3BtreeLeave( pTo ); |
||
814 | return rc; |
||
815 | } |
||
816 | #endif //* SQLITE_OMIT_VACUUM */ |
||
817 | } |
||
818 | } |