4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 *************************************************************************
12 ** This file contains code to implement the "changeset" command line
13 ** utility for displaying and transforming changesets generated by
14 ** the Sessions extension.
25 ** Show a usage message on stderr then quit.
27 static void usage(const char *argv0
){
28 fprintf(stderr
, "Usage: %s FILENAME COMMAND ...\n", argv0
);
31 " apply DB Apply the changeset to database file DB\n"
32 " concat FILE2 OUT Concatenate FILENAME and FILE2 into OUT\n"
33 " dump Show the complete content of the changeset\n"
34 " invert OUT Write an inverted changeset into file OUT\n"
35 " sql Give a pseudo-SQL rendering of the changeset\n"
41 ** Read the content of a disk file into an in-memory buffer
43 static void readFile(const char *zFilename
, int *pSz
, void **ppBuf
){
47 f
= fopen(zFilename
, "rb");
49 fprintf(stderr
, "cannot open \"%s\" for reading\n", zFilename
);
52 fseek(f
, 0, SEEK_END
);
55 pBuf
= sqlite3_malloc64( sz
? sz
: 1 );
57 fprintf(stderr
, "cannot allocate %d to hold content of \"%s\"\n",
62 if( fread(pBuf
, (size_t)sz
, 1, f
)!=1 ){
63 fprintf(stderr
, "cannot read all %d bytes of \"%s\"\n",
73 /* Array for converting from half-bytes (nybbles) into ASCII hex
75 static const char hexdigits
[] = {
76 '0', '1', '2', '3', '4', '5', '6', '7',
77 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
81 ** Render an sqlite3_value as an SQL string.
83 static void renderValue(sqlite3_value
*pVal
){
84 switch( sqlite3_value_type(pVal
) ){
88 r1
= sqlite3_value_double(pVal
);
89 sqlite3_snprintf(sizeof(zBuf
), zBuf
, "%!.15g", r1
);
93 case SQLITE_INTEGER
: {
94 printf("%lld", sqlite3_value_int64(pVal
));
98 char const *zBlob
= sqlite3_value_blob(pVal
);
99 int nBlob
= sqlite3_value_bytes(pVal
);
102 for(i
=0; i
<nBlob
; i
++){
103 putchar(hexdigits
[(zBlob
[i
]>>4)&0x0F]);
104 putchar(hexdigits
[(zBlob
[i
])&0x0F]);
110 const unsigned char *zArg
= sqlite3_value_text(pVal
);
114 if( zArg
[0]=='\'' ) putchar(zArg
[0]);
121 assert( sqlite3_value_type(pVal
)==SQLITE_NULL
);
129 ** Number of conflicts seen
131 static int nConflict
= 0;
134 ** The conflict callback
136 static int conflictCallback(
139 sqlite3_changeset_iter
*pIter
141 int op
, bIndirect
, nCol
, i
;
144 const char *zType
= "";
145 const char *zOp
= "";
146 const char *zSep
= " ";
149 sqlite3changeset_op(pIter
, &zTab
, &nCol
, &op
, &bIndirect
);
150 sqlite3changeset_pk(pIter
, &abPK
, 0);
152 case SQLITE_CHANGESET_DATA
: zType
= "DATA"; break;
153 case SQLITE_CHANGESET_NOTFOUND
: zType
= "NOTFOUND"; break;
154 case SQLITE_CHANGESET_CONFLICT
: zType
= "PRIMARY KEY"; break;
155 case SQLITE_CHANGESET_FOREIGN_KEY
: zType
= "FOREIGN KEY"; break;
156 case SQLITE_CHANGESET_CONSTRAINT
: zType
= "CONSTRAINT"; break;
159 case SQLITE_UPDATE
: zOp
= "UPDATE of"; break;
160 case SQLITE_INSERT
: zOp
= "INSERT into"; break;
161 case SQLITE_DELETE
: zOp
= "DELETE from"; break;
163 printf("%s conflict on %s table %s with primary key", zType
, zOp
, zTab
);
164 for(i
=0; i
<nCol
; i
++){
166 if( abPK
[i
]==0 ) continue;
168 if( op
==SQLITE_INSERT
){
169 sqlite3changeset_new(pIter
, i
, &pVal
);
171 sqlite3changeset_old(pIter
, i
, &pVal
);
177 return SQLITE_CHANGESET_OMIT
;
180 int main(int argc
, char **argv
){
183 if( argc
<3 ) usage(argv
[0]);
184 readFile(argv
[1], &sz
, &pBuf
);
186 /* changeset FILENAME apply DB
187 ** Apply the changeset in FILENAME to the database file DB
189 if( strcmp(argv
[2],"apply")==0 ){
191 if( argc
!=4 ) usage(argv
[0]);
192 rc
= sqlite3_open(argv
[3], &db
);
194 fprintf(stderr
, "unable to open database file \"%s\": %s\n",
195 argv
[3], sqlite3_errmsg(db
));
199 sqlite3_exec(db
, "BEGIN", 0, 0, 0);
201 rc
= sqlite3changeset_apply(db
, sz
, pBuf
, 0, conflictCallback
, 0);
203 fprintf(stderr
, "sqlite3changeset_apply() returned %d\n", rc
);
206 fprintf(stderr
, "%d conflicts - no changes applied\n", nConflict
);
207 sqlite3_exec(db
, "ROLLBACK", 0, 0, 0);
209 fprintf(stderr
, "sqlite3changeset_apply() returns %d "
210 "- no changes applied\n", rc
);
211 sqlite3_exec(db
, "ROLLBACK", 0, 0, 0);
213 sqlite3_exec(db
, "COMMIT", 0, 0, 0);
218 /* changeset FILENAME concat FILE2 OUT
219 ** Add changeset FILE2 onto the end of the changeset in FILENAME
220 ** and write the result into OUT.
222 if( strcmp(argv
[2],"concat")==0 ){
228 const char *zOut
= argv
[4];
229 if( argc
!=5 ) usage(argv
[0]);
230 out
= fopen(zOut
, "wb");
232 fprintf(stderr
, "cannot open \"%s\" for writing\n", zOut
);
235 readFile(argv
[3], &szB
, &pB
);
236 rc
= sqlite3changeset_concat(sz
, pBuf
, szB
, pB
, &szOut
, &pOutBuf
);
238 fprintf(stderr
, "sqlite3changeset_concat() returns %d\n", rc
);
239 }else if( szOut
>0 && fwrite(pOutBuf
, szOut
, 1, out
)!=1 ){
240 fprintf(stderr
, "unable to write all %d bytes of output to \"%s\"\n",
244 sqlite3_free(pOutBuf
);
248 /* changeset FILENAME dump
249 ** Show the complete content of the changeset in FILENAME
251 if( strcmp(argv
[2],"dump")==0 ){
254 sqlite3_changeset_iter
*pIter
;
255 rc
= sqlite3changeset_start(&pIter
, sz
, pBuf
);
257 fprintf(stderr
, "sqlite3changeset_start() returns %d\n", rc
);
260 while( sqlite3changeset_next(pIter
)==SQLITE_ROW
){
261 int op
, bIndirect
, nCol
;
264 sqlite3changeset_op(pIter
, &zTab
, &nCol
, &op
, &bIndirect
);
266 printf("%d: %s table=[%s] indirect=%d nColumn=%d\n",
267 cnt
, op
==SQLITE_INSERT
? "INSERT" :
268 op
==SQLITE_UPDATE
? "UPDATE" : "DELETE",
269 zTab
, bIndirect
, nCol
);
270 sqlite3changeset_pk(pIter
, &abPK
, 0);
271 for(i
=0; i
<nCol
; i
++){
274 sqlite3changeset_old(pIter
, i
, &pVal
);
276 printf(" old[%d]%s = ", i
, abPK
[i
] ? "pk" : " ");
281 sqlite3changeset_new(pIter
, i
, &pVal
);
283 printf(" new[%d]%s = ", i
, abPK
[i
] ? "pk" : " ");
289 sqlite3changeset_finalize(pIter
);
292 /* changeset FILENAME invert OUT
293 ** Invert the changes in FILENAME and writes the result on OUT
295 if( strcmp(argv
[2],"invert")==0 ){
299 const char *zOut
= argv
[3];
300 if( argc
!=4 ) usage(argv
[0]);
301 out
= fopen(zOut
, "wb");
303 fprintf(stderr
, "cannot open \"%s\" for writing\n", zOut
);
306 rc
= sqlite3changeset_invert(sz
, pBuf
, &szOut
, &pOutBuf
);
308 fprintf(stderr
, "sqlite3changeset_invert() returns %d\n", rc
);
309 }else if( szOut
>0 && fwrite(pOutBuf
, szOut
, 1, out
)!=1 ){
310 fprintf(stderr
, "unable to write all %d bytes of output to \"%s\"\n",
314 sqlite3_free(pOutBuf
);
317 /* changeset FILE sql
318 ** Show the content of the changeset as pseudo-SQL
320 if( strcmp(argv
[2],"sql")==0 ){
323 char *zSQLTabName
= 0;
324 sqlite3_changeset_iter
*pIter
= 0;
325 rc
= sqlite3changeset_start(&pIter
, sz
, pBuf
);
327 fprintf(stderr
, "sqlite3changeset_start() returns %d\n", rc
);
331 while( sqlite3changeset_next(pIter
)==SQLITE_ROW
){
332 int op
, bIndirect
, nCol
;
334 sqlite3changeset_op(pIter
, &zTab
, &nCol
, &op
, &bIndirect
);
336 if( zPrevTab
==0 || strcmp(zPrevTab
,zTab
)!=0 ){
337 sqlite3_free(zPrevTab
);
338 sqlite3_free(zSQLTabName
);
339 zPrevTab
= sqlite3_mprintf("%s", zTab
);
340 if( !isalnum(zTab
[0]) || sqlite3_strglob("*[^a-zA-Z0-9]*",zTab
)==0 ){
341 zSQLTabName
= sqlite3_mprintf("\"%w\"", zTab
);
343 zSQLTabName
= sqlite3_mprintf("%s", zTab
);
345 printf("/****** Changes for table %s ***************/\n", zSQLTabName
);
348 case SQLITE_DELETE
: {
351 const char *zSep
= " ";
352 sqlite3changeset_pk(pIter
, &abPK
, 0);
353 printf("/* %d */ DELETE FROM %s WHERE", cnt
, zSQLTabName
);
354 for(i
=0; i
<nCol
; i
++){
356 if( abPK
[i
]==0 ) continue;
357 printf("%sc%d=", zSep
, i
+1);
359 sqlite3changeset_old(pIter
, i
, &pVal
);
365 case SQLITE_UPDATE
: {
368 const char *zSep
= " ";
369 sqlite3changeset_pk(pIter
, &abPK
, 0);
370 printf("/* %d */ UPDATE %s SET", cnt
, zSQLTabName
);
371 for(i
=0; i
<nCol
; i
++){
372 sqlite3_value
*pVal
= 0;
373 sqlite3changeset_new(pIter
, i
, &pVal
);
375 printf("%sc%d=", zSep
, i
+1);
382 for(i
=0; i
<nCol
; i
++){
384 if( abPK
[i
]==0 ) continue;
385 printf("%sc%d=", zSep
, i
+1);
387 sqlite3changeset_old(pIter
, i
, &pVal
);
393 case SQLITE_INSERT
: {
395 printf("/* %d */ INSERT INTO %s VALUES", cnt
, zSQLTabName
);
396 for(i
=0; i
<nCol
; i
++){
398 printf("%c", i
==0 ? '(' : ',');
399 sqlite3changeset_new(pIter
, i
, &pVal
);
408 sqlite3changeset_finalize(pIter
);
409 sqlite3_free(zPrevTab
);
410 sqlite3_free(zSQLTabName
);
413 /* If nothing else matches, show the usage comment */