Fossil SCM

fossil-scm / src / sqldiff.c
Blame History Raw 1007 lines
1
/*
2
** Copyright (c) 2024 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
**
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** This file contains code used to compute a "diff" between two SQLite
19
** database files for display by Fossil.
20
**
21
** Fossil normally only computes diffs on text files. But I was inspired
22
** by a Hacker News post to add support for diffs of other kinds of files
23
** as well. The HN post in question is:
24
**
25
** https://news.ycombinator.com/item?id=42141370
26
**
27
** eternityforest | on: On Building Git for Lawyers
28
** I really think Git should just add builtin support for binaries,
29
** and diffing for SQLite and .zip. it's not like it would be all
30
** that much code....
31
**
32
** This file borrows a lot of code from the "sqldiff.c" module of
33
** SQLite. (https://sqlite.org/src/file/tool/sqldiff.c)
34
*/
35
#include "config.h"
36
#include "sqldiff.h"
37
#include <ctype.h>
38
39
#if INTERFACE
40
/*
41
** Context for an SQL diff
42
*/
43
struct SqlDiffCtx {
44
int bSchemaOnly; /* Only show schema differences */
45
int bSchemaPK; /* Use the schema-defined PK, not the true PK */
46
int bHandleVtab; /* Handle fts3, fts4, fts5 and rtree vtabs */
47
unsigned fDebug; /* Debug flags */
48
int bSchemaCompare; /* Doing single-table sqlite_schema compare */
49
int nErr; /* Number of errors encountered */
50
Blob *out; /* Write the diff output here */
51
sqlite3 *db; /* The database connection */
52
};
53
54
/*
55
** Allowed values for SqlDiffCtx.fDebug
56
*/
57
#define SQLDIFF_COLUMN_NAMES 0x000001
58
#define SQLDIFF_DIFF_SQL 0x000002
59
#define SQLDIFF_SHOW_ERRORS 0x000004
60
61
#endif /* INTERFACE */
62
63
64
/*
65
** Return true if the input Blob superficially resembles an SQLite
66
** database file.
67
*/
68
static int looks_like_sqlite_db(const Blob *pDb){
69
int sz = blob_size(pDb);
70
const u8 *a = (const u8*)blob_buffer(pDb);
71
static const u8 aSqliteHeader[16] = {
72
0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66,
73
0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00
74
};
75
76
if( sz<512 ) return 0;
77
if( (sz%512)!=0 ) return 0;
78
if( memcmp(aSqliteHeader,a,16)!=0 ) return 0;
79
return 1;
80
}
81
82
/*
83
** Clear and free an sqlite3_str object
84
*/
85
static void strFree(sqlite3_str *pStr){
86
sqlite3_free(sqlite3_str_finish(pStr));
87
}
88
89
/*
90
** Print an error message for an error that occurs at runtime.
91
*/
92
static void sqldiffError(SqlDiffCtx *p, const char *zFormat, ...){
93
if( p->fDebug & SQLDIFF_SHOW_ERRORS ){
94
sqlite3_str *pOut = sqlite3_str_new(0);
95
va_list ap;
96
va_start(ap, zFormat);
97
sqlite3_str_vappendf(pOut, zFormat, ap);
98
va_end(ap);
99
fossil_print("%s\n", sqlite3_str_value(pOut));
100
strFree(pOut);
101
}
102
p->nErr++;
103
}
104
105
/* Safely quote an SQL identifier. Use the minimum amount of transformation
106
** necessary to allow the string to be used with %s.
107
**
108
** Space to hold the returned string is obtained from sqlite3_malloc(). The
109
** caller is responsible for ensuring this space is freed when no longer
110
** needed.
111
*/
112
static char *safeId(const char *zId){
113
int i, x;
114
char c;
115
if( zId[0]==0 ) return sqlite3_mprintf("\"\"");
116
for(i=x=0; (c = zId[i])!=0; i++){
117
if( !isalpha(c) && c!='_' ){
118
if( i>0 && isdigit(c) ){
119
x++;
120
}else{
121
return sqlite3_mprintf("\"%w\"", zId);
122
}
123
}
124
}
125
if( x || !sqlite3_keyword_check(zId,i) ){
126
return sqlite3_mprintf("%s", zId);
127
}
128
return sqlite3_mprintf("\"%w\"", zId);
129
}
130
131
/*
132
** Prepare a new SQL statement. Print an error and abort if anything
133
** goes wrong.
134
*/
135
static sqlite3_stmt *sqldiff_vprepare(
136
SqlDiffCtx *p,
137
const char *zFormat,
138
va_list ap
139
){
140
char *zSql;
141
int rc;
142
sqlite3_stmt *pStmt;
143
144
zSql = sqlite3_vmprintf(zFormat, ap);
145
if( zSql==0 ) fossil_fatal("out of memory\n");
146
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
147
if( rc ){
148
sqldiffError(p, "SQL statement error: %s\n\"%s\"", sqlite3_errmsg(p->db),
149
zSql);
150
sqlite3_finalize(pStmt);
151
pStmt = 0;
152
}
153
sqlite3_free(zSql);
154
return pStmt;
155
}
156
static sqlite3_stmt *sqldiff_prepare(SqlDiffCtx *p, const char *zFormat, ...){
157
va_list ap;
158
sqlite3_stmt *pStmt;
159
va_start(ap, zFormat);
160
pStmt = sqldiff_vprepare(p, zFormat, ap);
161
va_end(ap);
162
return pStmt;
163
}
164
165
/*
166
** Free a list of strings
167
*/
168
static void namelistFree(char **az){
169
if( az ){
170
int i;
171
for(i=0; az[i]; i++) sqlite3_free(az[i]);
172
sqlite3_free(az);
173
}
174
}
175
176
/*
177
** Return a list of column names [a] for the table zDb.zTab. Space to
178
** hold the list is obtained from sqlite3_malloc() and should released
179
** using namelistFree() when no longer needed.
180
**
181
** Primary key columns are listed first, followed by data columns.
182
** The number of columns in the primary key is returned in *pnPkey.
183
**
184
** Normally [a], the "primary key" in the previous sentence is the true
185
** primary key - the rowid or INTEGER PRIMARY KEY for ordinary tables
186
** or the declared PRIMARY KEY for WITHOUT ROWID tables. However, if
187
** the p->bSchemaPK flag is set, then the schema-defined PRIMARY KEY is
188
** used in all cases. In that case, entries that have NULL values in
189
** any of their primary key fields will be excluded from the analysis.
190
**
191
** If the primary key for a table is the rowid but rowid is inaccessible,
192
** then this routine returns a NULL pointer.
193
**
194
** [a. If the lone, named table is "sqlite_schema", "rootpage" column is
195
** omitted and the "type" and "name" columns are made to be the PK.]
196
**
197
** Examples:
198
** CREATE TABLE t1(a INT UNIQUE, b INTEGER, c TEXT, PRIMARY KEY(c));
199
** *pnPKey = 1;
200
** az = { "rowid", "a", "b", "c", 0 } // Normal case
201
** az = { "c", "a", "b", 0 } // g.bSchemaPK==1
202
**
203
** CREATE TABLE t2(a INT UNIQUE, b INTEGER, c TEXT, PRIMARY KEY(b));
204
** *pnPKey = 1;
205
** az = { "b", "a", "c", 0 }
206
**
207
** CREATE TABLE t3(x,y,z,PRIMARY KEY(y,z));
208
** *pnPKey = 1 // Normal case
209
** az = { "rowid", "x", "y", "z", 0 } // Normal case
210
** *pnPKey = 2 // g.bSchemaPK==1
211
** az = { "y", "x", "z", 0 } // g.bSchemaPK==1
212
**
213
** CREATE TABLE t4(x,y,z,PRIMARY KEY(y,z)) WITHOUT ROWID;
214
** *pnPKey = 2
215
** az = { "y", "z", "x", 0 }
216
**
217
** CREATE TABLE t5(rowid,_rowid_,oid);
218
** az = 0 // The rowid is not accessible
219
*/
220
static char **columnNames(
221
SqlDiffCtx *p, /* Diffing context */
222
const char *zDb, /* Database ("aaa" or "bbb") to query */
223
const char *zTab, /* Name of table to return details of */
224
int *pnPKey, /* OUT: Number of PK columns */
225
int *pbRowid /* OUT: True if PK is an implicit rowid */
226
){
227
char **az = 0; /* List of column names to be returned */
228
int naz = 0; /* Number of entries in az[] */
229
sqlite3_stmt *pStmt; /* SQL statement being run */
230
char *zPkIdxName = 0; /* Name of the PRIMARY KEY index */
231
int truePk = 0; /* PRAGMA table_info indentifies the PK to use */
232
int nPK = 0; /* Number of PRIMARY KEY columns */
233
int i, j; /* Loop counters */
234
235
if( p->bSchemaPK==0 ){
236
/* Normal case: Figure out what the true primary key is for the table.
237
** * For WITHOUT ROWID tables, the true primary key is the same as
238
** the schema PRIMARY KEY, which is guaranteed to be present.
239
** * For rowid tables with an INTEGER PRIMARY KEY, the true primary
240
** key is the INTEGER PRIMARY KEY.
241
** * For all other rowid tables, the rowid is the true primary key.
242
*/
243
pStmt = sqldiff_prepare(p, "PRAGMA %s.index_list=%Q", zDb, zTab);
244
while( SQLITE_ROW==sqlite3_step(pStmt) ){
245
if( sqlite3_stricmp((const char*)sqlite3_column_text(pStmt,3),"pk")==0 ){
246
zPkIdxName = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
247
break;
248
}
249
}
250
sqlite3_finalize(pStmt);
251
if( zPkIdxName ){
252
int nKey = 0;
253
int nCol = 0;
254
truePk = 0;
255
pStmt = sqldiff_prepare(p, "PRAGMA %s.index_xinfo=%Q", zDb, zPkIdxName);
256
while( SQLITE_ROW==sqlite3_step(pStmt) ){
257
nCol++;
258
if( sqlite3_column_int(pStmt,5) ){ nKey++; continue; }
259
if( sqlite3_column_int(pStmt,1)>=0 ) truePk = 1;
260
}
261
if( nCol==nKey ) truePk = 1;
262
if( truePk ){
263
nPK = nKey;
264
}else{
265
nPK = 1;
266
}
267
sqlite3_finalize(pStmt);
268
sqlite3_free(zPkIdxName);
269
}else{
270
truePk = 1;
271
nPK = 1;
272
}
273
pStmt = sqldiff_prepare(p, "PRAGMA %s.table_info=%Q", zDb, zTab);
274
}else{
275
/* The p->bSchemaPK==1 case: Use whatever primary key is declared
276
** in the schema. The "rowid" will still be used as the primary key
277
** if the table definition does not contain a PRIMARY KEY.
278
*/
279
nPK = 0;
280
pStmt = sqldiff_prepare(p, "PRAGMA %s.table_info=%Q", zDb, zTab);
281
while( SQLITE_ROW==sqlite3_step(pStmt) ){
282
if( sqlite3_column_int(pStmt,5)>0 ) nPK++;
283
}
284
sqlite3_reset(pStmt);
285
if( nPK==0 ) nPK = 1;
286
truePk = 1;
287
}
288
if( p->bSchemaCompare ){
289
assert( sqlite3_stricmp(zTab,"sqlite_schema")==0
290
|| sqlite3_stricmp(zTab,"sqlite_master")==0 );
291
/* For sqlite_schema, will use type and name as the PK. */
292
nPK = 2;
293
truePk = 0;
294
}
295
*pnPKey = nPK;
296
naz = nPK;
297
az = sqlite3_malloc( sizeof(char*)*(nPK+1) );
298
if( az==0 ) fossil_fatal("out of memory\n");
299
memset(az, 0, sizeof(char*)*(nPK+1));
300
if( p->bSchemaCompare ){
301
az[0] = sqlite3_mprintf("%s", "type");
302
az[1] = sqlite3_mprintf("%s", "name");
303
}
304
while( SQLITE_ROW==sqlite3_step(pStmt) ){
305
char * sid = safeId((char*)sqlite3_column_text(pStmt,1));
306
int iPKey;
307
if( truePk && (iPKey = sqlite3_column_int(pStmt,5))>0 ){
308
az[iPKey-1] = sid;
309
}else{
310
if( !p->bSchemaCompare
311
|| !(strcmp(sid,"rootpage")==0
312
||strcmp(sid,"name")==0
313
||strcmp(sid,"type")==0)){
314
az = sqlite3_realloc(az, sizeof(char*)*(naz+2) );
315
if( az==0 ) fossil_fatal("out of memory\n");
316
az[naz++] = sid;
317
}
318
}
319
}
320
sqlite3_finalize(pStmt);
321
if( az ) az[naz] = 0;
322
323
/* If it is non-NULL, set *pbRowid to indicate whether or not the PK of
324
** this table is an implicit rowid (*pbRowid==1) or not (*pbRowid==0). */
325
if( pbRowid ) *pbRowid = (az[0]==0);
326
327
/* If this table has an implicit rowid for a PK, figure out how to refer
328
** to it. There are usually three options - "rowid", "_rowid_" and "oid".
329
** Any of these will work, unless the table has an explicit column of the
330
** same name or the sqlite_schema tables are to be compared. In the latter
331
** case, pretend that the "true" primary key is the name column, which
332
** avoids extraneous diffs against the schemas due to rowid variance. */
333
if( az[0]==0 ){
334
const char *azRowid[] = { "rowid", "_rowid_", "oid" };
335
for(i=0; i<sizeof(azRowid)/sizeof(azRowid[0]); i++){
336
for(j=1; j<naz; j++){
337
if( sqlite3_stricmp(az[j], azRowid[i])==0 ) break;
338
}
339
if( j>=naz ){
340
az[0] = sqlite3_mprintf("%s", azRowid[i]);
341
break;
342
}
343
}
344
if( az[0]==0 ){
345
for(i=1; i<naz; i++) sqlite3_free(az[i]);
346
sqlite3_free(az);
347
az = 0;
348
}
349
}
350
return az;
351
}
352
353
/*
354
** Print the sqlite3_value X as an SQL literal.
355
*/
356
static void printQuoted(Blob *out, sqlite3_value *X){
357
switch( sqlite3_value_type(X) ){
358
case SQLITE_FLOAT: {
359
double r1;
360
char zBuf[50];
361
r1 = sqlite3_value_double(X);
362
sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
363
blob_appendf(out, "%s", zBuf);
364
break;
365
}
366
case SQLITE_INTEGER: {
367
blob_appendf(out, "%lld", sqlite3_value_int64(X));
368
break;
369
}
370
case SQLITE_BLOB: {
371
const unsigned char *zBlob = sqlite3_value_blob(X);
372
int nBlob = sqlite3_value_bytes(X);
373
if( zBlob ){
374
int i;
375
blob_appendf(out, "x'");
376
for(i=0; i<nBlob; i++){
377
blob_appendf(out, "%02x", zBlob[i]);
378
}
379
blob_appendf(out, "'");
380
}else{
381
/* Could be an OOM, could be a zero-byte blob */
382
blob_appendf(out, "X''");
383
}
384
break;
385
}
386
case SQLITE_TEXT: {
387
const unsigned char *zArg = sqlite3_value_text(X);
388
389
if( zArg==0 ){
390
blob_appendf(out, "NULL");
391
}else{
392
int inctl = 0;
393
int i, j;
394
blob_appendf(out, "'");
395
for(i=j=0; zArg[i]; i++){
396
char c = zArg[i];
397
int ctl = iscntrl((unsigned char)c);
398
if( ctl>inctl ){
399
inctl = ctl;
400
blob_appendf(out, "%.*s'||X'%02x", i-j, &zArg[j], c);
401
j = i+1;
402
}else if( ctl ){
403
blob_appendf(out, "%02x", c);
404
j = i+1;
405
}else{
406
if( inctl ){
407
inctl = 0;
408
blob_appendf(out, "'\n||'");
409
}
410
if( c=='\'' ){
411
blob_appendf(out, "%.*s'", i-j+1, &zArg[j]);
412
j = i+1;
413
}
414
}
415
}
416
blob_appendf(out, "%s'", &zArg[j]);
417
}
418
break;
419
}
420
case SQLITE_NULL: {
421
blob_appendf(out, "NULL");
422
break;
423
}
424
}
425
}
426
427
/*
428
** Output SQL that will recreate the bbb.zTab table.
429
*/
430
static void dump_table(SqlDiffCtx *p, const char *zTab){
431
char *zId = safeId(zTab); /* Name of the table */
432
char **az = 0; /* List of columns */
433
int nPk; /* Number of true primary key columns */
434
int nCol; /* Number of data columns */
435
int i; /* Loop counter */
436
sqlite3_stmt *pStmt; /* SQL statement */
437
const char *zSep; /* Separator string */
438
sqlite3_str *pIns; /* Beginning of the INSERT statement */
439
440
pStmt = sqldiff_prepare(p,
441
"SELECT sql FROM bbb.sqlite_schema WHERE name=%Q", zTab);
442
if( SQLITE_ROW==sqlite3_step(pStmt) ){
443
blob_appendf(p->out, "%s;\n", sqlite3_column_text(pStmt,0));
444
}
445
sqlite3_finalize(pStmt);
446
if( !p->bSchemaOnly ){
447
az = columnNames(p, "bbb", zTab, &nPk, 0);
448
pIns = sqlite3_str_new(0);
449
if( az==0 ){
450
pStmt = sqldiff_prepare(p, "SELECT * FROM bbb.%s", zId);
451
sqlite3_str_appendf(pIns,"INSERT INTO %s VALUES", zId);
452
}else{
453
sqlite3_str *pSql = sqlite3_str_new(0);
454
zSep = "SELECT";
455
for(i=0; az[i]; i++){
456
sqlite3_str_appendf(pSql, "%s %s", zSep, az[i]);
457
zSep = ",";
458
}
459
sqlite3_str_appendf(pSql," FROM bbb.%s", zId);
460
zSep = " ORDER BY";
461
for(i=1; i<=nPk; i++){
462
sqlite3_str_appendf(pSql, "%s %d", zSep, i);
463
zSep = ",";
464
}
465
pStmt = sqldiff_prepare(p, "%s", sqlite3_str_value(pSql));
466
strFree(pSql);
467
sqlite3_str_appendf(pIns, "INSERT INTO %s", zId);
468
zSep = "(";
469
for(i=0; az[i]; i++){
470
sqlite3_str_appendf(pIns, "%s%s", zSep, az[i]);
471
zSep = ",";
472
}
473
sqlite3_str_appendf(pIns,") VALUES");
474
namelistFree(az);
475
}
476
nCol = sqlite3_column_count(pStmt);
477
while( SQLITE_ROW==sqlite3_step(pStmt) ){
478
blob_appendf(p->out, "%s",sqlite3_str_value(pIns));
479
zSep = "(";
480
for(i=0; i<nCol; i++){
481
blob_appendf(p->out, "%s",zSep);
482
printQuoted(p->out, sqlite3_column_value(pStmt,i));
483
zSep = ",";
484
}
485
blob_appendf(p->out, ");\n");
486
}
487
sqlite3_finalize(pStmt);
488
strFree(pIns);
489
} /* endif !p->bSchemaOnly */
490
pStmt = sqldiff_prepare(p, "SELECT sql FROM bbb.sqlite_schema"
491
" WHERE type='index' AND tbl_name=%Q AND sql IS NOT NULL",
492
zTab);
493
while( SQLITE_ROW==sqlite3_step(pStmt) ){
494
blob_appendf(p->out, "%s;\n", sqlite3_column_text(pStmt,0));
495
}
496
sqlite3_finalize(pStmt);
497
sqlite3_free(zId);
498
}
499
500
501
/*
502
** Compute all differences for a single table, except if the
503
** table name is sqlite_schema, ignore the rootpage column.
504
*/
505
static void diff_one_table(SqlDiffCtx *p, const char *zTab){
506
char *zId = safeId(zTab); /* Name of table (translated for us in SQL) */
507
char **az = 0; /* Columns in aaa */
508
char **az2 = 0; /* Columns in bbb */
509
int nPk; /* Primary key columns in aaa */
510
int nPk2; /* Primary key columns in bbb */
511
int n = 0; /* Number of columns in aaa */
512
int n2; /* Number of columns in bbb */
513
int nQ; /* Number of output columns in the diff query */
514
int i; /* Loop counter */
515
const char *zSep; /* Separator string */
516
sqlite3_str *pSql; /* Comparison query */
517
sqlite3_stmt *pStmt; /* Query statement to do the diff */
518
const char *zLead = /* Becomes line-comment for sqlite_schema */
519
(p->bSchemaCompare)? "-- " : "";
520
521
pSql = sqlite3_str_new(0);
522
if( p->fDebug==SQLDIFF_COLUMN_NAMES ){
523
/* Simply run columnNames() on all tables of the origin
524
** database and show the results. This is used for testing
525
** and debugging of the columnNames() function.
526
*/
527
az = columnNames(p, "bbb",zTab, &nPk, 0);
528
if( az==0 ){
529
fossil_print("Rowid not accessible for %s\n", zId);
530
}else{
531
fossil_print("%s:", zId);
532
for(i=0; az[i]; i++){
533
fossil_print(" %s", az[i]);
534
if( i+1==nPk ) fossil_print(" *");
535
}
536
fossil_print("\n");
537
}
538
goto end_diff_one_table;
539
}
540
541
if( sqlite3_table_column_metadata(p->db,"bbb",zTab,0,0,0,0,0,0) ){
542
if( !sqlite3_table_column_metadata(p->db,"aaa",zTab,0,0,0,0,0,0) ){
543
/* Table missing from second database. */
544
if( p->bSchemaCompare ){
545
blob_appendf(p->out, "-- 2nd DB has no %s table\n", zTab);
546
}else{
547
blob_appendf(p->out, "DROP TABLE %s;\n", zId);
548
}
549
}
550
goto end_diff_one_table;
551
}
552
553
if( sqlite3_table_column_metadata(p->db,"aaa",zTab,0,0,0,0,0,0) ){
554
/* Table missing from source */
555
if( p->bSchemaCompare ){
556
blob_appendf(p->out, "-- 1st DB has no %s table\n", zTab);
557
}else{
558
dump_table(p, zTab);
559
}
560
goto end_diff_one_table;
561
}
562
563
az = columnNames(p, "aaa", zTab, &nPk, 0);
564
az2 = columnNames(p, "bbb", zTab, &nPk2, 0);
565
if( az && az2 ){
566
for(n=0; az[n] && az2[n]; n++){
567
if( sqlite3_stricmp(az[n],az2[n])!=0 ) break;
568
}
569
}
570
if( az==0
571
|| az2==0
572
|| nPk!=nPk2
573
|| az[n]
574
){
575
/* Schema mismatch */
576
blob_appendf(p->out, "%sDROP TABLE %s; -- due to schema mismatch\n",
577
zLead, zId);
578
dump_table(p, zTab);
579
goto end_diff_one_table;
580
}
581
582
/* Build the comparison query */
583
for(n2=n; az2[n2]; n2++){
584
char *zNTab = safeId(az2[n2]);
585
blob_appendf(p->out, "ALTER TABLE %s ADD COLUMN %s;\n", zId, zNTab);
586
sqlite3_free(zNTab);
587
}
588
nQ = nPk2+1+2*(n2-nPk2);
589
if( n2>nPk2 ){
590
zSep = "SELECT ";
591
for(i=0; i<nPk; i++){
592
sqlite3_str_appendf(pSql, "%sB.%s", zSep, az[i]);
593
zSep = ", ";
594
}
595
sqlite3_str_appendf(pSql, ", 1 /* changed row */");
596
while( az[i] ){
597
sqlite3_str_appendf(pSql, ", A.%s IS NOT B.%s, B.%s",
598
az[i], az2[i], az2[i]);
599
i++;
600
}
601
while( az2[i] ){
602
sqlite3_str_appendf(pSql, ", B.%s IS NOT NULL, B.%s",
603
az2[i], az2[i]);
604
i++;
605
}
606
sqlite3_str_appendf(pSql, "\n FROM aaa.%s A, bbb.%s B\n", zId, zId);
607
zSep = " WHERE";
608
for(i=0; i<nPk; i++){
609
sqlite3_str_appendf(pSql, "%s A.%s=B.%s", zSep, az[i], az[i]);
610
zSep = " AND";
611
}
612
zSep = "\n AND (";
613
while( az[i] ){
614
sqlite3_str_appendf(pSql, "%sA.%s IS NOT B.%s%s\n",
615
zSep, az[i], az2[i], az2[i+1]==0 ? ")" : "");
616
zSep = " OR ";
617
i++;
618
}
619
while( az2[i] ){
620
sqlite3_str_appendf(pSql, "%sB.%s IS NOT NULL%s\n",
621
zSep, az2[i], az2[i+1]==0 ? ")" : "");
622
zSep = " OR ";
623
i++;
624
}
625
sqlite3_str_appendf(pSql, " UNION ALL\n");
626
}
627
zSep = "SELECT ";
628
for(i=0; i<nPk; i++){
629
sqlite3_str_appendf(pSql, "%sA.%s", zSep, az[i]);
630
zSep = ", ";
631
}
632
sqlite3_str_appendf(pSql, ", 2 /* deleted row */");
633
while( az2[i] ){
634
sqlite3_str_appendf(pSql, ", NULL, NULL");
635
i++;
636
}
637
sqlite3_str_appendf(pSql, "\n FROM aaa.%s A\n", zId);
638
sqlite3_str_appendf(pSql, " WHERE NOT EXISTS(SELECT 1 FROM bbb.%s B\n", zId);
639
zSep = " WHERE";
640
for(i=0; i<nPk; i++){
641
sqlite3_str_appendf(pSql, "%s A.%s=B.%s", zSep, az[i], az[i]);
642
zSep = " AND";
643
}
644
sqlite3_str_appendf(pSql, ")\n");
645
zSep = " UNION ALL\nSELECT ";
646
for(i=0; i<nPk; i++){
647
sqlite3_str_appendf(pSql, "%sB.%s", zSep, az[i]);
648
zSep = ", ";
649
}
650
sqlite3_str_appendf(pSql, ", 3 /* inserted row */");
651
while( az2[i] ){
652
sqlite3_str_appendf(pSql, ", 1, B.%s", az2[i]);
653
i++;
654
}
655
sqlite3_str_appendf(pSql, "\n FROM bbb.%s B\n", zId);
656
sqlite3_str_appendf(pSql, " WHERE NOT EXISTS(SELECT 1 FROM aaa.%s A\n", zId);
657
zSep = " WHERE";
658
for(i=0; i<nPk; i++){
659
sqlite3_str_appendf(pSql, "%s A.%s=B.%s", zSep, az[i], az[i]);
660
zSep = " AND";
661
}
662
sqlite3_str_appendf(pSql, ")\n ORDER BY");
663
zSep = " ";
664
for(i=1; i<=nPk; i++){
665
sqlite3_str_appendf(pSql, "%s%d", zSep, i);
666
zSep = ", ";
667
}
668
sqlite3_str_appendf(pSql, ";\n");
669
670
if( p->fDebug & SQLDIFF_DIFF_SQL ){
671
fossil_print("SQL for %s:\n%s\n", zId, sqlite3_str_value(pSql));
672
goto end_diff_one_table;
673
}
674
675
/* Drop indexes that are missing in the destination */
676
pStmt = sqldiff_prepare(p,
677
"SELECT name FROM aaa.sqlite_schema"
678
" WHERE type='index' AND tbl_name=%Q"
679
" AND sql IS NOT NULL"
680
" AND sql NOT IN (SELECT sql FROM bbb.sqlite_schema"
681
" WHERE type='index' AND tbl_name=%Q"
682
" AND sql IS NOT NULL)",
683
zTab, zTab);
684
while( SQLITE_ROW==sqlite3_step(pStmt) ){
685
char *z = safeId((const char*)sqlite3_column_text(pStmt,0));
686
blob_appendf(p->out, "DROP INDEX %s;\n", z);
687
sqlite3_free(z);
688
}
689
sqlite3_finalize(pStmt);
690
691
/* Run the query and output differences */
692
if( !p->bSchemaOnly ){
693
pStmt = sqldiff_prepare(p, "%s", sqlite3_str_value(pSql));
694
while( SQLITE_ROW==sqlite3_step(pStmt) ){
695
int iType = sqlite3_column_int(pStmt, nPk);
696
if( iType==1 || iType==2 ){
697
if( iType==1 ){ /* Change the content of a row */
698
blob_appendf(p->out, "%sUPDATE %s", zLead, zId);
699
zSep = " SET";
700
for(i=nPk+1; i<nQ; i+=2){
701
if( sqlite3_column_int(pStmt,i)==0 ) continue;
702
blob_appendf(p->out, "%s %s=", zSep, az2[(i+nPk-1)/2]);
703
zSep = ",";
704
printQuoted(p->out, sqlite3_column_value(pStmt,i+1));
705
}
706
}else{ /* Delete a row */
707
blob_appendf(p->out, "%sDELETE FROM %s", zLead, zId);
708
}
709
zSep = " WHERE";
710
for(i=0; i<nPk; i++){
711
blob_appendf(p->out, "%s %s=", zSep, az2[i]);
712
printQuoted(p->out, sqlite3_column_value(pStmt,i));
713
zSep = " AND";
714
}
715
blob_appendf(p->out, ";\n");
716
}else{ /* Insert a row */
717
blob_appendf(p->out, "%sINSERT INTO %s(%s", zLead, zId, az2[0]);
718
for(i=1; az2[i]; i++) blob_appendf(p->out, ",%s", az2[i]);
719
blob_appendf(p->out, ") VALUES");
720
zSep = "(";
721
for(i=0; i<nPk2; i++){
722
blob_appendf(p->out, "%s", zSep);
723
zSep = ",";
724
printQuoted(p->out, sqlite3_column_value(pStmt,i));
725
}
726
for(i=nPk2+2; i<nQ; i+=2){
727
blob_appendf(p->out, ",");
728
printQuoted(p->out, sqlite3_column_value(pStmt,i));
729
}
730
blob_appendf(p->out, ");\n");
731
}
732
}
733
sqlite3_finalize(pStmt);
734
} /* endif !p->bSchemaOnly */
735
736
/* Create indexes that are missing in the source */
737
pStmt = sqldiff_prepare(p,
738
"SELECT sql FROM bbb.sqlite_schema"
739
" WHERE type='index' AND tbl_name=%Q"
740
" AND sql IS NOT NULL"
741
" AND sql NOT IN (SELECT sql FROM aaa.sqlite_schema"
742
" WHERE type='index' AND tbl_name=%Q"
743
" AND sql IS NOT NULL)",
744
zTab, zTab);
745
while( SQLITE_ROW==sqlite3_step(pStmt) ){
746
blob_appendf(p->out, "%s;\n", sqlite3_column_text(pStmt,0));
747
}
748
sqlite3_finalize(pStmt);
749
750
end_diff_one_table:
751
strFree(pSql);
752
sqlite3_free(zId);
753
namelistFree(az);
754
namelistFree(az2);
755
return;
756
}
757
758
#if 0
759
/*
760
** Check that table zTab exists and has the same schema in both the "aaa"
761
** and "bbb" databases currently opened by the global db handle. If they
762
** do not, output an error message on stderr and exit(1). Otherwise, if
763
** the schemas do match, return control to the caller.
764
*/
765
static void checkSchemasMatch(SqlDiffCtx *p, const char *zTab){
766
sqlite3_stmt *pStmt = sqldiff_prepare(p,
767
"SELECT A.sql=B.sql FROM aaa.sqlite_schema A, bbb.sqlite_schema B"
768
" WHERE A.name=%Q AND B.name=%Q", zTab, zTab
769
);
770
if( SQLITE_ROW==sqlite3_step(pStmt) ){
771
if( sqlite3_column_int(pStmt,0)==0 ){
772
sqldiffError(p, "schema changes for table %s", safeId(zTab));
773
}
774
}else{
775
sqldiffError(p, "table %s missing from one or both databases",safeId(zTab));
776
}
777
sqlite3_finalize(pStmt);
778
}
779
#endif
780
781
/*
782
** Return true if the ascii character passed as the only argument is a
783
** whitespace character. Otherwise return false.
784
*/
785
static int is_whitespace(char x){
786
return (x==' ' || x=='\t' || x=='\n' || x=='\r');
787
}
788
789
/*
790
** Extract the next SQL keyword or quoted string from buffer zIn and copy it
791
** (or a prefix of it if it will not fit) into buffer zBuf, size nBuf bytes.
792
** Return a pointer to the character within zIn immediately following
793
** the token or quoted string just extracted.
794
*/
795
static const char *gobble_token(const char *zIn, char *zBuf, int nBuf){
796
const char *p = zIn;
797
char *pOut = zBuf;
798
char *pEnd = &pOut[nBuf-1];
799
char q = 0; /* quote character, if any */
800
801
if( p==0 ) return 0;
802
while( is_whitespace(*p) ) p++;
803
switch( *p ){
804
case '"': q = '"'; break;
805
case '\'': q = '\''; break;
806
case '`': q = '`'; break;
807
case '[': q = ']'; break;
808
}
809
810
if( q ){
811
p++;
812
while( *p && pOut<pEnd ){
813
if( *p==q ){
814
p++;
815
if( *p!=q ) break;
816
}
817
if( pOut<pEnd ) *pOut++ = *p;
818
p++;
819
}
820
}else{
821
while( *p && !is_whitespace(*p) && *p!='(' ){
822
if( pOut<pEnd ) *pOut++ = *p;
823
p++;
824
}
825
}
826
827
*pOut = '\0';
828
return p;
829
}
830
831
/*
832
** This function is the implementation of SQL scalar function "module_name":
833
**
834
** module_name(SQL)
835
**
836
** The only argument should be an SQL statement of the type that may appear
837
** in the sqlite_schema table. If the statement is a "CREATE VIRTUAL TABLE"
838
** statement, then the value returned is the name of the module that it
839
** uses. Otherwise, if the statement is not a CVT, NULL is returned.
840
*/
841
static void module_name_func(
842
sqlite3_context *pCtx,
843
int nVal, sqlite3_value **apVal
844
){
845
const char *zSql;
846
char zToken[32];
847
848
assert( nVal==1 );
849
zSql = (const char*)sqlite3_value_text(apVal[0]);
850
851
zSql = gobble_token(zSql, zToken, sizeof(zToken));
852
if( zSql==0 || sqlite3_stricmp(zToken, "create") ) return;
853
zSql = gobble_token(zSql, zToken, sizeof(zToken));
854
if( zSql==0 || sqlite3_stricmp(zToken, "virtual") ) return;
855
zSql = gobble_token(zSql, zToken, sizeof(zToken));
856
if( zSql==0 || sqlite3_stricmp(zToken, "table") ) return;
857
zSql = gobble_token(zSql, zToken, sizeof(zToken));
858
if( zSql==0 ) return;
859
zSql = gobble_token(zSql, zToken, sizeof(zToken));
860
if( zSql==0 || sqlite3_stricmp(zToken, "using") ) return;
861
zSql = gobble_token(zSql, zToken, sizeof(zToken));
862
863
sqlite3_result_text(pCtx, zToken, -1, SQLITE_TRANSIENT);
864
}
865
866
/*
867
** Return the text of an SQL statement that itself returns the list of
868
** tables to process within the database.
869
*/
870
const char *all_tables_sql(SqlDiffCtx *p){
871
if( p->bHandleVtab ){
872
int rc;
873
874
rc = sqlite3_exec(p->db,
875
"CREATE TEMP TABLE tblmap(module COLLATE nocase, postfix);"
876
"INSERT INTO temp.tblmap VALUES"
877
"('fts3', '_content'), ('fts3', '_segments'), ('fts3', '_segdir'),"
878
879
"('fts4', '_content'), ('fts4', '_segments'), ('fts4', '_segdir'),"
880
"('fts4', '_docsize'), ('fts4', '_stat'),"
881
882
"('fts5', '_data'), ('fts5', '_idx'), ('fts5', '_content'),"
883
"('fts5', '_docsize'), ('fts5', '_config'),"
884
885
"('rtree', '_node'), ('rtree', '_rowid'), ('rtree', '_parent');"
886
, 0, 0, 0
887
);
888
assert( rc==SQLITE_OK );
889
890
rc = sqlite3_create_function(
891
p->db, "module_name", 1, SQLITE_UTF8, 0, module_name_func, 0, 0
892
);
893
assert( rc==SQLITE_OK );
894
895
return
896
"SELECT name FROM aaa.sqlite_schema\n"
897
" WHERE type='table' AND (\n"
898
" module_name(sql) IS NULL OR \n"
899
" module_name(sql) IN (SELECT module FROM temp.tblmap)\n"
900
" ) AND name NOT IN (\n"
901
" SELECT a.name || b.postfix \n"
902
"FROM aaa.sqlite_schema AS a, temp.tblmap AS b \n"
903
"WHERE module_name(a.sql) = b.module\n"
904
" )\n"
905
"UNION \n"
906
"SELECT name FROM bbb.sqlite_schema\n"
907
" WHERE type='table' AND (\n"
908
" module_name(sql) IS NULL OR \n"
909
" module_name(sql) IN (SELECT module FROM temp.tblmap)\n"
910
" ) AND name NOT IN (\n"
911
" SELECT a.name || b.postfix \n"
912
"FROM bbb.sqlite_schema AS a, temp.tblmap AS b \n"
913
"WHERE module_name(a.sql) = b.module\n"
914
" )\n"
915
" ORDER BY name";
916
}else{
917
return
918
"SELECT name FROM aaa.sqlite_schema\n"
919
" WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
920
" UNION\n"
921
"SELECT name FROM bbb.sqlite_schema\n"
922
" WHERE type='table' AND sql NOT LIKE 'CREATE VIRTUAL%%'\n"
923
" ORDER BY name";
924
}
925
}
926
927
/*
928
** Check to see if the two input blobs, pA and pB, are both
929
** SQLite database files. If they are, then output an SQL diff
930
** into pOut and return true. If either of the inputs is not
931
** a well-formed SQLite database, then return 0.
932
**
933
** A semantic-level diff is computed. In other words, it is the
934
** content of the database that matters. If the databases have
935
** different page sizes or text representations or if the pages
936
** are in a different order, that does not affect the output.
937
** Only content differences are shown.
938
*/
939
int sqldiff(
940
Blob *pA, /* FROM file */
941
Blob *pB, /* TO file */
942
Blob *pOut, /* Write diff here */
943
DiffConfig *pCfg /* Configuration options */
944
){
945
SqlDiffCtx s;
946
sqlite3_stmt *pStmt;
947
int rc;
948
u8 *aA, *aB;
949
int szA, szB;
950
u8 aModeA[2];
951
u8 aModeB[2];
952
953
if( pOut==0 ) return 0;
954
if( !looks_like_sqlite_db(pA) ) return 0;
955
if( !looks_like_sqlite_db(pB) ) return 0;
956
memset(&s, 0, sizeof(s));
957
s.out = pOut;
958
rc = sqlite3_open(":memory:", &s.db);
959
if( rc ){
960
fossil_fatal("Unable to open an auxiliary in-memory database\n");
961
}
962
rc = sqlite3_exec(s.db, "ATTACH ':memory:' AS aaa;", 0, 0, 0);
963
if( rc ){
964
fossil_fatal("Unable to attach an in-memory database\n");
965
}
966
rc = sqlite3_exec(s.db, "ATTACH ':memory:' AS bbb;", 0, 0, 0);
967
if( rc ){
968
fossil_fatal("Unable to attach an in-memory database\n");
969
}
970
aA = (u8*)blob_buffer(pA);
971
szA = blob_size(pA);
972
memcpy(aModeA, &aA[18], 2);
973
aA[18] = aA[19] = 1;
974
aB = (u8*)blob_buffer(pB);
975
szB = blob_size(pB);
976
memcpy(aModeB, &aB[18], 2);
977
aB[18] = aB[19] = 1;
978
rc = sqlite3_deserialize(s.db, "aaa", aA, szA, szA,
979
SQLITE_DESERIALIZE_READONLY);
980
if( rc ){
981
s.nErr++;
982
goto not_a_valid_diff;
983
}
984
rc = sqlite3_deserialize(s.db, "bbb", aB, szB, szB,
985
SQLITE_DESERIALIZE_READONLY);
986
if( rc ){
987
s.nErr++;
988
goto not_a_valid_diff;
989
}
990
if( pCfg->diffFlags & DIFF_HTML ) blob_appendf(pOut, "<pre>\n");
991
pStmt = sqldiff_prepare(&s, "%s", all_tables_sql(&s) );
992
if( pStmt ){
993
while( SQLITE_ROW==sqlite3_step(pStmt) ){
994
diff_one_table(&s, (const char*)sqlite3_column_text(pStmt,0));
995
}
996
sqlite3_finalize(pStmt);
997
}
998
if( pCfg->diffFlags & DIFF_HTML ) blob_appendf(pOut, "</pre>\n");
999
1000
not_a_valid_diff:
1001
sqlite3_close(s.db);
1002
if( s.nErr ) blob_reset(pOut);
1003
memcpy(&aA[18], aModeA, 2);
1004
memcpy(&aB[18], aModeB, 2);
1005
return s.nErr==0;
1006
}
1007

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button