forked from sqlcipher/sqlcipher
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsqlite3recover.c
More file actions
2870 lines (2620 loc) · 86.4 KB
/
sqlite3recover.c
File metadata and controls
2870 lines (2620 loc) · 86.4 KB
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
114
115
116
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
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
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
** 2022-08-27
**
** 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.
**
*************************************************************************
**
*/
#include "sqlite3recover.h"
#include <assert.h>
#include <string.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Declaration for public API function in file dbdata.c. This may be called
** with NULL as the final two arguments to register the sqlite_dbptr and
** sqlite_dbdata virtual tables with a database handle.
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*);
typedef unsigned int u32;
typedef unsigned char u8;
typedef sqlite3_int64 i64;
typedef struct RecoverTable RecoverTable;
typedef struct RecoverColumn RecoverColumn;
/*
** When recovering rows of data that can be associated with table
** definitions recovered from the sqlite_schema table, each table is
** represented by an instance of the following object.
**
** iRoot:
** The root page in the original database. Not necessarily (and usually
** not) the same in the recovered database.
**
** zTab:
** Name of the table.
**
** nCol/aCol[]:
** aCol[] is an array of nCol columns. In the order in which they appear
** in the table.
**
** bIntkey:
** Set to true for intkey tables, false for WITHOUT ROWID.
**
** iRowidBind:
** Each column in the aCol[] array has associated with it the index of
** the bind parameter its values will be bound to in the INSERT statement
** used to construct the output database. If the table does has a rowid
** but not an INTEGER PRIMARY KEY column, then iRowidBind contains the
** index of the bind paramater to which the rowid value should be bound.
** Otherwise, it contains -1. If the table does contain an INTEGER PRIMARY
** KEY column, then the rowid value should be bound to the index associated
** with the column.
**
** pNext:
** All RecoverTable objects used by the recovery operation are allocated
** and populated as part of creating the recovered database schema in
** the output database, before any non-schema data are recovered. They
** are then stored in a singly-linked list linked by this variable beginning
** at sqlite3_recover.pTblList.
*/
struct RecoverTable {
u32 iRoot; /* Root page in original database */
char *zTab; /* Name of table */
int nCol; /* Number of columns in table */
RecoverColumn *aCol; /* Array of columns */
int bIntkey; /* True for intkey, false for without rowid */
int iRowidBind; /* If >0, bind rowid to INSERT here */
RecoverTable *pNext;
};
/*
** Each database column is represented by an instance of the following object
** stored in the RecoverTable.aCol[] array of the associated table.
**
** iField:
** The index of the associated field within database records. Or -1 if
** there is no associated field (e.g. for virtual generated columns).
**
** iBind:
** The bind index of the INSERT statement to bind this columns values
** to. Or 0 if there is no such index (iff (iField<0)).
**
** bIPK:
** True if this is the INTEGER PRIMARY KEY column.
**
** zCol:
** Name of column.
**
** eHidden:
** A RECOVER_EHIDDEN_* constant value (see below for interpretation of each).
*/
struct RecoverColumn {
int iField; /* Field in record on disk */
int iBind; /* Binding to use in INSERT */
int bIPK; /* True for IPK column */
char *zCol;
int eHidden;
};
#define RECOVER_EHIDDEN_NONE 0 /* Normal database column */
#define RECOVER_EHIDDEN_HIDDEN 1 /* Column is __HIDDEN__ */
#define RECOVER_EHIDDEN_VIRTUAL 2 /* Virtual generated column */
#define RECOVER_EHIDDEN_STORED 3 /* Stored generated column */
/*
** Bitmap object used to track pages in the input database. Allocated
** and manipulated only by the following functions:
**
** recoverBitmapAlloc()
** recoverBitmapFree()
** recoverBitmapSet()
** recoverBitmapQuery()
**
** nPg:
** Largest page number that may be stored in the bitmap. The range
** of valid keys is 1 to nPg, inclusive.
**
** aElem[]:
** Array large enough to contain a bit for each key. For key value
** iKey, the associated bit is the bit (iKey%32) of aElem[iKey/32].
** In other words, the following is true if bit iKey is set, or
** false if it is clear:
**
** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0
*/
typedef struct RecoverBitmap RecoverBitmap;
struct RecoverBitmap {
i64 nPg; /* Size of bitmap */
u32 aElem[1]; /* Array of 32-bit bitmasks */
};
/*
** State variables (part of the sqlite3_recover structure) used while
** recovering data for tables identified in the recovered schema (state
** RECOVER_STATE_WRITING).
*/
typedef struct RecoverStateW1 RecoverStateW1;
struct RecoverStateW1 {
sqlite3_stmt *pTbls;
sqlite3_stmt *pSel;
sqlite3_stmt *pInsert;
int nInsert;
RecoverTable *pTab; /* Table currently being written */
int nMax; /* Max column count in any schema table */
sqlite3_value **apVal; /* Array of nMax values */
int nVal; /* Number of valid entries in apVal[] */
int bHaveRowid;
i64 iRowid;
i64 iPrevPage;
int iPrevCell;
};
/*
** State variables (part of the sqlite3_recover structure) used while
** recovering data destined for the lost and found table (states
** RECOVER_STATE_LOSTANDFOUND[123]).
*/
typedef struct RecoverStateLAF RecoverStateLAF;
struct RecoverStateLAF {
RecoverBitmap *pUsed;
i64 nPg; /* Size of db in pages */
sqlite3_stmt *pAllAndParent;
sqlite3_stmt *pMapInsert;
sqlite3_stmt *pMaxField;
sqlite3_stmt *pUsedPages;
sqlite3_stmt *pFindRoot;
sqlite3_stmt *pInsert; /* INSERT INTO lost_and_found ... */
sqlite3_stmt *pAllPage;
sqlite3_stmt *pPageData;
sqlite3_value **apVal;
int nMaxField;
};
/*
** Main recover handle structure.
*/
struct sqlite3_recover {
/* Copies of sqlite3_recover_init[_sql]() parameters */
sqlite3 *dbIn; /* Input database */
char *zDb; /* Name of input db ("main" etc.) */
char *zUri; /* URI for output database */
void *pSqlCtx; /* SQL callback context */
int (*xSql)(void*,const char*); /* Pointer to SQL callback function */
/* Values configured by sqlite3_recover_config() */
char *zStateDb; /* State database to use (or NULL) */
char *zLostAndFound; /* Name of lost-and-found table (or NULL) */
int bFreelistCorrupt; /* SQLITE_RECOVER_FREELIST_CORRUPT setting */
int bRecoverRowid; /* SQLITE_RECOVER_ROWIDS setting */
int bSlowIndexes; /* SQLITE_RECOVER_SLOWINDEXES setting */
int pgsz;
int detected_pgsz;
int nReserve;
u8 *pPage1Disk;
u8 *pPage1Cache;
/* Error code and error message */
int errCode; /* For sqlite3_recover_errcode() */
char *zErrMsg; /* For sqlite3_recover_errmsg() */
int eState;
int bCloseTransaction;
/* Variables used with eState==RECOVER_STATE_WRITING */
RecoverStateW1 w1;
/* Variables used with states RECOVER_STATE_LOSTANDFOUND[123] */
RecoverStateLAF laf;
/* Fields used within sqlite3_recover_run() */
sqlite3 *dbOut; /* Output database */
sqlite3_stmt *pGetPage; /* SELECT against input db sqlite_dbdata */
RecoverTable *pTblList; /* List of tables recovered from schema */
};
/*
** The various states in which an sqlite3_recover object may exist:
**
** RECOVER_STATE_INIT:
** The object is initially created in this state. sqlite3_recover_step()
** has yet to be called. This is the only state in which it is permitted
** to call sqlite3_recover_config().
**
** RECOVER_STATE_WRITING:
**
** RECOVER_STATE_LOSTANDFOUND1:
** State to populate the bitmap of pages used by other tables or the
** database freelist.
**
** RECOVER_STATE_LOSTANDFOUND2:
** Populate the recovery.map table - used to figure out a "root" page
** for each lost page from in the database from which records are
** extracted.
**
** RECOVER_STATE_LOSTANDFOUND3:
** Populate the lost-and-found table itself.
*/
#define RECOVER_STATE_INIT 0
#define RECOVER_STATE_WRITING 1
#define RECOVER_STATE_LOSTANDFOUND1 2
#define RECOVER_STATE_LOSTANDFOUND2 3
#define RECOVER_STATE_LOSTANDFOUND3 4
#define RECOVER_STATE_SCHEMA2 5
#define RECOVER_STATE_DONE 6
/*
** Global variables used by this extension.
*/
typedef struct RecoverGlobal RecoverGlobal;
struct RecoverGlobal {
const sqlite3_io_methods *pMethods;
sqlite3_recover *p;
};
static RecoverGlobal recover_g;
/*
** Use this static SQLite mutex to protect the globals during the
** first call to sqlite3_recover_step().
*/
#define RECOVER_MUTEX_ID SQLITE_MUTEX_STATIC_APP2
/*
** Default value for SQLITE_RECOVER_ROWIDS (sqlite3_recover.bRecoverRowid).
*/
#define RECOVER_ROWID_DEFAULT 1
/*
** Mutex handling:
**
** recoverEnterMutex() - Enter the recovery mutex
** recoverLeaveMutex() - Leave the recovery mutex
** recoverAssertMutexHeld() - Assert that the recovery mutex is held
*/
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
# define recoverEnterMutex()
# define recoverLeaveMutex()
#else
static void recoverEnterMutex(void){
sqlite3_mutex_enter(sqlite3_mutex_alloc(RECOVER_MUTEX_ID));
}
static void recoverLeaveMutex(void){
sqlite3_mutex_leave(sqlite3_mutex_alloc(RECOVER_MUTEX_ID));
}
#endif
#if SQLITE_THREADSAFE+0>=1 && defined(SQLITE_DEBUG)
static void recoverAssertMutexHeld(void){
assert( sqlite3_mutex_held(sqlite3_mutex_alloc(RECOVER_MUTEX_ID)) );
}
#else
# define recoverAssertMutexHeld()
#endif
/*
** Like strlen(). But handles NULL pointer arguments.
*/
static int recoverStrlen(const char *zStr){
if( zStr==0 ) return 0;
return (int)(strlen(zStr)&0x7fffffff);
}
/*
** This function is a no-op if the recover handle passed as the first
** argument already contains an error (if p->errCode!=SQLITE_OK).
**
** Otherwise, an attempt is made to allocate, zero and return a buffer nByte
** bytes in size. If successful, a pointer to the new buffer is returned. Or,
** if an OOM error occurs, NULL is returned and the handle error code
** (p->errCode) set to SQLITE_NOMEM.
*/
static void *recoverMalloc(sqlite3_recover *p, i64 nByte){
void *pRet = 0;
assert( nByte>0 );
if( p->errCode==SQLITE_OK ){
pRet = sqlite3_malloc64(nByte);
if( pRet ){
memset(pRet, 0, nByte);
}else{
p->errCode = SQLITE_NOMEM;
}
}
return pRet;
}
/*
** Set the error code and error message for the recover handle passed as
** the first argument. The error code is set to the value of parameter
** errCode.
**
** Parameter zFmt must be a printf() style formatting string. The handle
** error message is set to the result of using any trailing arguments for
** parameter substitutions in the formatting string.
**
** For example:
**
** recoverError(p, SQLITE_ERROR, "no such table: %s", zTablename);
*/
static int recoverError(
sqlite3_recover *p,
int errCode,
const char *zFmt, ...
){
char *z = 0;
va_list ap;
va_start(ap, zFmt);
if( zFmt ){
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
}
sqlite3_free(p->zErrMsg);
p->zErrMsg = z;
p->errCode = errCode;
return errCode;
}
/*
** This function is a no-op if p->errCode is initially other than SQLITE_OK.
** In this case it returns NULL.
**
** Otherwise, an attempt is made to allocate and return a bitmap object
** large enough to store a bit for all page numbers between 1 and nPg,
** inclusive. The bitmap is initially zeroed.
*/
static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){
int nElem = (nPg+1+31) / 32;
int nByte = sizeof(RecoverBitmap) + nElem*sizeof(u32);
RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte);
if( pRet ){
pRet->nPg = nPg;
}
return pRet;
}
/*
** Free a bitmap object allocated by recoverBitmapAlloc().
*/
static void recoverBitmapFree(RecoverBitmap *pMap){
sqlite3_free(pMap);
}
/*
** Set the bit associated with page iPg in bitvec pMap.
*/
static void recoverBitmapSet(RecoverBitmap *pMap, i64 iPg){
if( iPg<=pMap->nPg ){
int iElem = (iPg / 32);
int iBit = (iPg % 32);
pMap->aElem[iElem] |= (((u32)1) << iBit);
}
}
/*
** Query bitmap object pMap for the state of the bit associated with page
** iPg. Return 1 if it is set, or 0 otherwise.
*/
static int recoverBitmapQuery(RecoverBitmap *pMap, i64 iPg){
int ret = 1;
if( iPg<=pMap->nPg && iPg>0 ){
int iElem = (iPg / 32);
int iBit = (iPg % 32);
ret = (pMap->aElem[iElem] & (((u32)1) << iBit)) ? 1 : 0;
}
return ret;
}
/*
** Set the recover handle error to the error code and message returned by
** calling sqlite3_errcode() and sqlite3_errmsg(), respectively, on database
** handle db.
*/
static int recoverDbError(sqlite3_recover *p, sqlite3 *db){
return recoverError(p, sqlite3_errcode(db), "%s", sqlite3_errmsg(db));
}
/*
** This function is a no-op if recover handle p already contains an error
** (if p->errCode!=SQLITE_OK).
**
** Otherwise, it attempts to prepare the SQL statement in zSql against
** database handle db. If successful, the statement handle is returned.
** Or, if an error occurs, NULL is returned and an error left in the
** recover handle.
*/
static sqlite3_stmt *recoverPrepare(
sqlite3_recover *p,
sqlite3 *db,
const char *zSql
){
sqlite3_stmt *pStmt = 0;
if( p->errCode==SQLITE_OK ){
if( sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) ){
recoverDbError(p, db);
}
}
return pStmt;
}
/*
** This function is a no-op if recover handle p already contains an error
** (if p->errCode!=SQLITE_OK).
**
** Otherwise, argument zFmt is used as a printf() style format string,
** along with any trailing arguments, to create an SQL statement. This
** SQL statement is prepared against database handle db and, if successful,
** the statment handle returned. Or, if an error occurs - either during
** the printf() formatting or when preparing the resulting SQL - an
** error code and message are left in the recover handle.
*/
static sqlite3_stmt *recoverPreparePrintf(
sqlite3_recover *p,
sqlite3 *db,
const char *zFmt, ...
){
sqlite3_stmt *pStmt = 0;
if( p->errCode==SQLITE_OK ){
va_list ap;
char *z;
va_start(ap, zFmt);
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
if( z==0 ){
p->errCode = SQLITE_NOMEM;
}else{
pStmt = recoverPrepare(p, db, z);
sqlite3_free(z);
}
}
return pStmt;
}
/*
** Reset SQLite statement handle pStmt. If the call to sqlite3_reset()
** indicates that an error occurred, and there is not already an error
** in the recover handle passed as the first argument, set the error
** code and error message appropriately.
**
** This function returns a copy of the statement handle pointer passed
** as the second argument.
*/
static sqlite3_stmt *recoverReset(sqlite3_recover *p, sqlite3_stmt *pStmt){
int rc = sqlite3_reset(pStmt);
if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT && p->errCode==SQLITE_OK ){
recoverDbError(p, sqlite3_db_handle(pStmt));
}
return pStmt;
}
/*
** Finalize SQLite statement handle pStmt. If the call to sqlite3_reset()
** indicates that an error occurred, and there is not already an error
** in the recover handle passed as the first argument, set the error
** code and error message appropriately.
*/
static void recoverFinalize(sqlite3_recover *p, sqlite3_stmt *pStmt){
sqlite3 *db = sqlite3_db_handle(pStmt);
int rc = sqlite3_finalize(pStmt);
if( rc!=SQLITE_OK && p->errCode==SQLITE_OK ){
recoverDbError(p, db);
}
}
/*
** This function is a no-op if recover handle p already contains an error
** (if p->errCode!=SQLITE_OK). A copy of p->errCode is returned in this
** case.
**
** Otherwise, execute SQL script zSql. If successful, return SQLITE_OK.
** Or, if an error occurs, leave an error code and message in the recover
** handle and return a copy of the error code.
*/
static int recoverExec(sqlite3_recover *p, sqlite3 *db, const char *zSql){
if( p->errCode==SQLITE_OK ){
int rc = sqlite3_exec(db, zSql, 0, 0, 0);
if( rc ){
recoverDbError(p, db);
}
}
return p->errCode;
}
/*
** Bind the value pVal to parameter iBind of statement pStmt. Leave an
** error in the recover handle passed as the first argument if an error
** (e.g. an OOM) occurs.
*/
static void recoverBindValue(
sqlite3_recover *p,
sqlite3_stmt *pStmt,
int iBind,
sqlite3_value *pVal
){
if( p->errCode==SQLITE_OK ){
int rc = sqlite3_bind_value(pStmt, iBind, pVal);
if( rc ) recoverError(p, rc, 0);
}
}
/*
** This function is a no-op if recover handle p already contains an error
** (if p->errCode!=SQLITE_OK). NULL is returned in this case.
**
** Otherwise, an attempt is made to interpret zFmt as a printf() style
** formatting string and the result of using the trailing arguments for
** parameter substitution with it written into a buffer obtained from
** sqlite3_malloc(). If successful, a pointer to the buffer is returned.
** It is the responsibility of the caller to eventually free the buffer
** using sqlite3_free().
**
** Or, if an error occurs, an error code and message is left in the recover
** handle and NULL returned.
*/
static char *recoverMPrintf(sqlite3_recover *p, const char *zFmt, ...){
va_list ap;
char *z;
va_start(ap, zFmt);
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
if( p->errCode==SQLITE_OK ){
if( z==0 ) p->errCode = SQLITE_NOMEM;
}else{
sqlite3_free(z);
z = 0;
}
return z;
}
/*
** This function is a no-op if recover handle p already contains an error
** (if p->errCode!=SQLITE_OK). Zero is returned in this case.
**
** Otherwise, execute "PRAGMA page_count" against the input database. If
** successful, return the integer result. Or, if an error occurs, leave an
** error code and error message in the sqlite3_recover handle and return
** zero.
*/
static i64 recoverPageCount(sqlite3_recover *p){
i64 nPg = 0;
if( p->errCode==SQLITE_OK ){
sqlite3_stmt *pStmt = 0;
pStmt = recoverPreparePrintf(p, p->dbIn, "PRAGMA %Q.page_count", p->zDb);
if( pStmt ){
sqlite3_step(pStmt);
nPg = sqlite3_column_int64(pStmt, 0);
}
recoverFinalize(p, pStmt);
}
return nPg;
}
/*
** Implementation of SQL scalar function "read_i32". The first argument to
** this function must be a blob. The second a non-negative integer. This
** function reads and returns a 32-bit big-endian integer from byte
** offset (4*<arg2>) of the blob.
**
** SELECT read_i32(<blob>, <idx>)
*/
static void recoverReadI32(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const unsigned char *pBlob;
int nBlob;
int iInt;
assert( argc==2 );
nBlob = sqlite3_value_bytes(argv[0]);
pBlob = (const unsigned char*)sqlite3_value_blob(argv[0]);
iInt = sqlite3_value_int(argv[1]) & 0xFFFF;
if( (iInt+1)*4<=nBlob ){
const unsigned char *a = &pBlob[iInt*4];
i64 iVal = ((i64)a[0]<<24)
+ ((i64)a[1]<<16)
+ ((i64)a[2]<< 8)
+ ((i64)a[3]<< 0);
sqlite3_result_int64(context, iVal);
}
}
/*
** Implementation of SQL scalar function "page_is_used". This function
** is used as part of the procedure for locating orphan rows for the
** lost-and-found table, and it depends on those routines having populated
** the sqlite3_recover.laf.pUsed variable.
**
** The only argument to this function is a page-number. It returns true
** if the page has already been used somehow during data recovery, or false
** otherwise.
**
** SELECT page_is_used(<pgno>);
*/
static void recoverPageIsUsed(
sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx);
i64 pgno = sqlite3_value_int64(apArg[0]);
assert( nArg==1 );
sqlite3_result_int(pCtx, recoverBitmapQuery(p->laf.pUsed, pgno));
}
/*
** The implementation of a user-defined SQL function invoked by the
** sqlite_dbdata and sqlite_dbptr virtual table modules to access pages
** of the database being recovered.
**
** This function always takes a single integer argument. If the argument
** is zero, then the value returned is the number of pages in the db being
** recovered. If the argument is greater than zero, it is a page number.
** The value returned in this case is an SQL blob containing the data for
** the identified page of the db being recovered. e.g.
**
** SELECT getpage(0); -- return number of pages in db
** SELECT getpage(4); -- return page 4 of db as a blob of data
*/
static void recoverGetPage(
sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx);
i64 pgno = sqlite3_value_int64(apArg[0]);
sqlite3_stmt *pStmt = 0;
assert( nArg==1 );
if( pgno==0 ){
i64 nPg = recoverPageCount(p);
sqlite3_result_int64(pCtx, nPg);
return;
}else{
if( p->pGetPage==0 ){
pStmt = p->pGetPage = recoverPreparePrintf(
p, p->dbIn, "SELECT data FROM sqlite_dbpage(%Q) WHERE pgno=?", p->zDb
);
}else if( p->errCode==SQLITE_OK ){
pStmt = p->pGetPage;
}
if( pStmt ){
sqlite3_bind_int64(pStmt, 1, pgno);
if( SQLITE_ROW==sqlite3_step(pStmt) ){
const u8 *aPg;
int nPg;
assert( p->errCode==SQLITE_OK );
aPg = sqlite3_column_blob(pStmt, 0);
nPg = sqlite3_column_bytes(pStmt, 0);
if( pgno==1 && nPg==p->pgsz && 0==memcmp(p->pPage1Cache, aPg, nPg) ){
aPg = p->pPage1Disk;
}
sqlite3_result_blob(pCtx, aPg, nPg-p->nReserve, SQLITE_TRANSIENT);
}
recoverReset(p, pStmt);
}
}
if( p->errCode ){
if( p->zErrMsg ) sqlite3_result_error(pCtx, p->zErrMsg, -1);
sqlite3_result_error_code(pCtx, p->errCode);
}
}
/*
** Find a string that is not found anywhere in z[]. Return a pointer
** to that string.
**
** Try to use zA and zB first. If both of those are already found in z[]
** then make up some string and store it in the buffer zBuf.
*/
static const char *recoverUnusedString(
const char *z, /* Result must not appear anywhere in z */
const char *zA, const char *zB, /* Try these first */
char *zBuf /* Space to store a generated string */
){
unsigned i = 0;
if( strstr(z, zA)==0 ) return zA;
if( strstr(z, zB)==0 ) return zB;
do{
sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++);
}while( strstr(z,zBuf)!=0 );
return zBuf;
}
/*
** Implementation of scalar SQL function "escape_crnl". The argument passed to
** this function is the output of built-in function quote(). If the first
** character of the input is "'", indicating that the value passed to quote()
** was a text value, then this function searches the input for "\n" and "\r"
** characters and adds a wrapper similar to the following:
**
** replace(replace(<input>, '\n', char(10), '\r', char(13));
**
** Or, if the first character of the input is not "'", then a copy of the input
** is returned.
*/
static void recoverEscapeCrnl(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zText = (const char*)sqlite3_value_text(argv[0]);
(void)argc;
if( zText && zText[0]=='\'' ){
int nText = sqlite3_value_bytes(argv[0]);
int i;
char zBuf1[20];
char zBuf2[20];
const char *zNL = 0;
const char *zCR = 0;
int nCR = 0;
int nNL = 0;
for(i=0; zText[i]; i++){
if( zNL==0 && zText[i]=='\n' ){
zNL = recoverUnusedString(zText, "\\n", "\\012", zBuf1);
nNL = (int)strlen(zNL);
}
if( zCR==0 && zText[i]=='\r' ){
zCR = recoverUnusedString(zText, "\\r", "\\015", zBuf2);
nCR = (int)strlen(zCR);
}
}
if( zNL || zCR ){
int iOut = 0;
i64 nMax = (nNL > nCR) ? nNL : nCR;
i64 nAlloc = nMax * nText + (nMax+64)*2;
char *zOut = (char*)sqlite3_malloc64(nAlloc);
if( zOut==0 ){
sqlite3_result_error_nomem(context);
return;
}
if( zNL && zCR ){
memcpy(&zOut[iOut], "replace(replace(", 16);
iOut += 16;
}else{
memcpy(&zOut[iOut], "replace(", 8);
iOut += 8;
}
for(i=0; zText[i]; i++){
if( zText[i]=='\n' ){
memcpy(&zOut[iOut], zNL, nNL);
iOut += nNL;
}else if( zText[i]=='\r' ){
memcpy(&zOut[iOut], zCR, nCR);
iOut += nCR;
}else{
zOut[iOut] = zText[i];
iOut++;
}
}
if( zNL ){
memcpy(&zOut[iOut], ",'", 2); iOut += 2;
memcpy(&zOut[iOut], zNL, nNL); iOut += nNL;
memcpy(&zOut[iOut], "', char(10))", 12); iOut += 12;
}
if( zCR ){
memcpy(&zOut[iOut], ",'", 2); iOut += 2;
memcpy(&zOut[iOut], zCR, nCR); iOut += nCR;
memcpy(&zOut[iOut], "', char(13))", 12); iOut += 12;
}
sqlite3_result_text(context, zOut, iOut, SQLITE_TRANSIENT);
sqlite3_free(zOut);
return;
}
}
sqlite3_result_value(context, argv[0]);
}
/*
** This function is a no-op if recover handle p already contains an error
** (if p->errCode!=SQLITE_OK). A copy of the error code is returned in
** this case.
**
** Otherwise, attempt to populate temporary table "recovery.schema" with the
** parts of the database schema that can be extracted from the input database.
**
** If no error occurs, SQLITE_OK is returned. Otherwise, an error code
** and error message are left in the recover handle and a copy of the
** error code returned. It is not considered an error if part of all of
** the database schema cannot be recovered due to corruption.
*/
static int recoverCacheSchema(sqlite3_recover *p){
return recoverExec(p, p->dbOut,
"WITH RECURSIVE pages(p) AS ("
" SELECT 1"
" UNION"
" SELECT child FROM sqlite_dbptr('getpage()'), pages WHERE pgno=p"
")"
"INSERT INTO recovery.schema SELECT"
" max(CASE WHEN field=0 THEN value ELSE NULL END),"
" max(CASE WHEN field=1 THEN value ELSE NULL END),"
" max(CASE WHEN field=2 THEN value ELSE NULL END),"
" max(CASE WHEN field=3 THEN value ELSE NULL END),"
" max(CASE WHEN field=4 THEN value ELSE NULL END)"
"FROM sqlite_dbdata('getpage()') WHERE pgno IN ("
" SELECT p FROM pages"
") GROUP BY pgno, cell"
);
}
/*
** If this recover handle is not in SQL callback mode (i.e. was not created
** using sqlite3_recover_init_sql()) of if an error has already occurred,
** this function is a no-op. Otherwise, issue a callback with SQL statement
** zSql as the parameter.
**
** If the callback returns non-zero, set the recover handle error code to
** the value returned (so that the caller will abandon processing).
*/
static void recoverSqlCallback(sqlite3_recover *p, const char *zSql){
if( p->errCode==SQLITE_OK && p->xSql ){
int res = p->xSql(p->pSqlCtx, zSql);
if( res ){
recoverError(p, SQLITE_ERROR, "callback returned an error - %d", res);
}
}
}
/*
** Transfer the following settings from the input database to the output
** database:
**
** + page-size,
** + auto-vacuum settings,
** + database encoding,
** + user-version (PRAGMA user_version), and
** + application-id (PRAGMA application_id), and
*/
static void recoverTransferSettings(sqlite3_recover *p){
const char *aPragma[] = {
"encoding",
"page_size",
"auto_vacuum",
"user_version",
"application_id"
};
int ii;
/* Truncate the output database to 0 pages in size. This is done by
** opening a new, empty, temp db, then using the backup API to clobber
** any existing output db with a copy of it. */
if( p->errCode==SQLITE_OK ){
sqlite3 *db2 = 0;
int rc = sqlite3_open("", &db2);
if( rc!=SQLITE_OK ){
recoverDbError(p, db2);
return;
}
for(ii=0; ii<(int)(sizeof(aPragma)/sizeof(aPragma[0])); ii++){
const char *zPrag = aPragma[ii];
sqlite3_stmt *p1 = 0;
p1 = recoverPreparePrintf(p, p->dbIn, "PRAGMA %Q.%s", p->zDb, zPrag);
if( p->errCode==SQLITE_OK && sqlite3_step(p1)==SQLITE_ROW ){
const char *zArg = (const char*)sqlite3_column_text(p1, 0);
char *z2 = recoverMPrintf(p, "PRAGMA %s = %Q", zPrag, zArg);
recoverSqlCallback(p, z2);
recoverExec(p, db2, z2);
sqlite3_free(z2);
if( zArg==0 ){
recoverError(p, SQLITE_NOMEM, 0);
}
}
recoverFinalize(p, p1);
}
recoverExec(p, db2, "CREATE TABLE t1(a); DROP TABLE t1;");
if( p->errCode==SQLITE_OK ){
sqlite3 *db = p->dbOut;
sqlite3_backup *pBackup = sqlite3_backup_init(db, "main", db2, "main");
if( pBackup ){
sqlite3_backup_step(pBackup, -1);
p->errCode = sqlite3_backup_finish(pBackup);
}else{
recoverDbError(p, db);
}
}
sqlite3_close(db2);
}
}
/*
** This function is a no-op if recover handle p already contains an error
** (if p->errCode!=SQLITE_OK). A copy of the error code is returned in
** this case.
**
** Otherwise, an attempt is made to open the output database, attach
** and create the schema of the temporary database used to store
** intermediate data, and to register all required user functions and
** virtual table modules with the output handle.
**
** If no error occurs, SQLITE_OK is returned. Otherwise, an error code
** and error message are left in the recover handle and a copy of the
** error code returned.
*/
static int recoverOpenOutput(sqlite3_recover *p){
struct Func {
const char *zName;
int nArg;
void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
} aFunc[] = {
{ "getpage", 1, recoverGetPage },
{ "page_is_used", 1, recoverPageIsUsed },
{ "read_i32", 2, recoverReadI32 },
{ "escape_crnl", 1, recoverEscapeCrnl },
};
const int flags = SQLITE_OPEN_URI|SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE;
sqlite3 *db = 0; /* New database handle */
int ii; /* For iterating through aFunc[] */
assert( p->dbOut==0 );
if( sqlite3_open_v2(p->zUri, &db, flags, 0) ){
recoverDbError(p, db);
}
/* Register the sqlite_dbdata and sqlite_dbptr virtual table modules.
** These two are registered with the output database handle - this
** module depends on the input handle supporting the sqlite_dbpage
** virtual table only. */
if( p->errCode==SQLITE_OK ){
p->errCode = sqlite3_dbdata_init(db, 0, 0);
}
/* Register the custom user-functions with the output handle. */
for(ii=0;
p->errCode==SQLITE_OK && ii<(int)(sizeof(aFunc)/sizeof(aFunc[0]));
ii++){
p->errCode = sqlite3_create_function(db, aFunc[ii].zName,
aFunc[ii].nArg, SQLITE_UTF8, (void*)p, aFunc[ii].xFunc, 0, 0
);