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_malloc( sz
? sz
: 1 );
57 fprintf(stderr
, "cannot allocate %d to hold content of \"%s\"\n",
62 if( fread(pBuf
, sz
, 1, f
)!=1 ){
63 fprintf(stderr
, "cannot read all %d bytes of \"%s\"\n", sz
, zFilename
);
72 /* Array for converting from half-bytes (nybbles) into ASCII hex
74 static const char hexdigits
[] = {
75 '0', '1', '2', '3', '4', '5', '6', '7',
76 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
80 ** Render an sqlite3_value as an SQL string.
82 static void renderValue(sqlite3_value
*pVal
){
83 switch( sqlite3_value_type(pVal
) ){
87 r1
= sqlite3_value_double(pVal
);
88 sqlite3_snprintf(sizeof(zBuf
), zBuf
, "%!.15g", r1
);
92 case SQLITE_INTEGER
: {
93 printf("%lld", sqlite3_value_int64(pVal
));
97 char const *zBlob
= sqlite3_value_blob(pVal
);
98 int nBlob
= sqlite3_value_bytes(pVal
);
101 for(i
=0; i
<nBlob
; i
++){
102 putchar(hexdigits
[(zBlob
[i
]>>4)&0x0F]);
103 putchar(hexdigits
[(zBlob
[i
])&0x0F]);
109 const unsigned char *zArg
= sqlite3_value_text(pVal
);
113 if( zArg
[0]=='\'' ) putchar(zArg
[0]);
120 assert( sqlite3_value_type(pVal
)==SQLITE_NULL
);
128 ** Number of conflicts seen
130 static int nConflict
= 0;
133 ** The conflict callback
135 static int conflictCallback(
138 sqlite3_changeset_iter
*pIter
140 int op
, bIndirect
, nCol
, i
;
143 const char *zType
= "";
144 const char *zOp
= "";
145 const char *zSep
= " ";
148 sqlite3changeset_op(pIter
, &zTab
, &nCol
, &op
, &bIndirect
);
149 sqlite3changeset_pk(pIter
, &abPK
, 0);
151 case SQLITE_CHANGESET_DATA
: zType
= "DATA"; break;
152 case SQLITE_CHANGESET_NOTFOUND
: zType
= "NOTFOUND"; break;
153 case SQLITE_CHANGESET_CONFLICT
: zType
= "PRIMARY KEY"; break;
154 case SQLITE_CHANGESET_FOREIGN_KEY
: zType
= "FOREIGN KEY"; break;
155 case SQLITE_CHANGESET_CONSTRAINT
: zType
= "CONSTRAINT"; break;
158 case SQLITE_UPDATE
: zOp
= "UPDATE of"; break;
159 case SQLITE_INSERT
: zOp
= "INSERT into"; break;
160 case SQLITE_DELETE
: zOp
= "DELETE from"; break;
162 printf("%s conflict on %s table %s with primary key", zType
, zOp
, zTab
);
163 for(i
=0; i
<nCol
; i
++){
165 if( abPK
[i
]==0 ) continue;
167 if( op
==SQLITE_INSERT
){
168 sqlite3changeset_new(pIter
, i
, &pVal
);
170 sqlite3changeset_old(pIter
, i
, &pVal
);
176 return SQLITE_CHANGESET_OMIT
;
179 int main(int argc
, char **argv
){
182 if( argc
<3 ) usage(argv
[0]);
183 readFile(argv
[1], &sz
, &pBuf
);
185 /* changeset FILENAME apply DB
186 ** Apply the changeset in FILENAME to the database file DB
188 if( strcmp(argv
[2],"apply")==0 ){
190 if( argc
!=4 ) usage(argv
[0]);
191 rc
= sqlite3_open(argv
[3], &db
);
193 fprintf(stderr
, "unable to open database file \"%s\": %s\n",
194 argv
[3], sqlite3_errmsg(db
));
198 sqlite3_exec(db
, "BEGIN", 0, 0, 0);
200 rc
= sqlite3changeset_apply(db
, sz
, pBuf
, 0, conflictCallback
, 0);
202 fprintf(stderr
, "sqlite3changeset_apply() returned %d\n", rc
);
205 fprintf(stderr
, "%d conflicts - no changes applied\n", nConflict
);
206 sqlite3_exec(db
, "ROLLBACK", 0, 0, 0);
208 fprintf(stderr
, "sqlite3changeset_apply() returns %d "
209 "- no changes applied\n", rc
);
210 sqlite3_exec(db
, "ROLLBACK", 0, 0, 0);
212 sqlite3_exec(db
, "COMMIT", 0, 0, 0);
217 /* changeset FILENAME concat FILE2 OUT
218 ** Add changeset FILE2 onto the end of the changeset in FILENAME
219 ** and write the result into OUT.
221 if( strcmp(argv
[2],"concat")==0 ){
227 const char *zOut
= argv
[4];
228 if( argc
!=5 ) usage(argv
[0]);
229 out
= fopen(zOut
, "wb");
231 fprintf(stderr
, "cannot open \"%s\" for writing\n", zOut
);
234 readFile(argv
[3], &szB
, &pB
);
235 rc
= sqlite3changeset_concat(sz
, pBuf
, szB
, pB
, &szOut
, &pOutBuf
);
237 fprintf(stderr
, "sqlite3changeset_concat() returns %d\n", rc
);
238 }else if( szOut
>0 && fwrite(pOutBuf
, szOut
, 1, out
)!=1 ){
239 fprintf(stderr
, "unable to write all %d bytes of output to \"%s\"\n",
243 sqlite3_free(pOutBuf
);
247 /* changeset FILENAME dump
248 ** Show the complete content of the changeset in FILENAME
250 if( strcmp(argv
[2],"dump")==0 ){
253 sqlite3_changeset_iter
*pIter
;
254 rc
= sqlite3changeset_start(&pIter
, sz
, pBuf
);
256 fprintf(stderr
, "sqlite3changeset_start() returns %d\n", rc
);
259 while( sqlite3changeset_next(pIter
)==SQLITE_ROW
){
260 int op
, bIndirect
, nCol
;
263 sqlite3changeset_op(pIter
, &zTab
, &nCol
, &op
, &bIndirect
);
265 printf("%d: %s table=[%s] indirect=%d nColumn=%d\n",
266 cnt
, op
==SQLITE_INSERT
? "INSERT" :
267 op
==SQLITE_UPDATE
? "UPDATE" : "DELETE",
268 zTab
, bIndirect
, nCol
);
269 sqlite3changeset_pk(pIter
, &abPK
, 0);
270 for(i
=0; i
<nCol
; i
++){
273 sqlite3changeset_old(pIter
, i
, &pVal
);
275 printf(" old[%d]%s = ", i
, abPK
[i
] ? "pk" : " ");
280 sqlite3changeset_new(pIter
, i
, &pVal
);
282 printf(" new[%d]%s = ", i
, abPK
[i
] ? "pk" : " ");
288 sqlite3changeset_finalize(pIter
);
291 /* changeset FILENAME invert OUT
292 ** Invert the changes in FILENAME and writes the result on OUT
294 if( strcmp(argv
[2],"invert")==0 ){
298 const char *zOut
= argv
[3];
299 if( argc
!=4 ) usage(argv
[0]);
300 out
= fopen(zOut
, "wb");
302 fprintf(stderr
, "cannot open \"%s\" for writing\n", zOut
);
305 rc
= sqlite3changeset_invert(sz
, pBuf
, &szOut
, &pOutBuf
);
307 fprintf(stderr
, "sqlite3changeset_invert() returns %d\n", rc
);
308 }else if( szOut
>0 && fwrite(pOutBuf
, szOut
, 1, out
)!=1 ){
309 fprintf(stderr
, "unable to write all %d bytes of output to \"%s\"\n",
313 sqlite3_free(pOutBuf
);
316 /* changeset FILE sql
317 ** Show the content of the changeset as pseudo-SQL
319 if( strcmp(argv
[2],"sql")==0 ){
322 char *zSQLTabName
= 0;
323 sqlite3_changeset_iter
*pIter
= 0;
324 rc
= sqlite3changeset_start(&pIter
, sz
, pBuf
);
326 fprintf(stderr
, "sqlite3changeset_start() returns %d\n", rc
);
330 while( sqlite3changeset_next(pIter
)==SQLITE_ROW
){
331 int op
, bIndirect
, nCol
;
333 sqlite3changeset_op(pIter
, &zTab
, &nCol
, &op
, &bIndirect
);
335 if( zPrevTab
==0 || strcmp(zPrevTab
,zTab
)!=0 ){
336 sqlite3_free(zPrevTab
);
337 sqlite3_free(zSQLTabName
);
338 zPrevTab
= sqlite3_mprintf("%s", zTab
);
339 if( !isalnum(zTab
[0]) || sqlite3_strglob("*[^a-zA-Z0-9]*",zTab
)==0 ){
340 zSQLTabName
= sqlite3_mprintf("\"%w\"", zTab
);
342 zSQLTabName
= sqlite3_mprintf("%s", zTab
);
344 printf("/****** Changes for table %s ***************/\n", zSQLTabName
);
347 case SQLITE_DELETE
: {
350 const char *zSep
= " ";
351 sqlite3changeset_pk(pIter
, &abPK
, 0);
352 printf("/* %d */ DELETE FROM %s WHERE", cnt
, zSQLTabName
);
353 for(i
=0; i
<nCol
; i
++){
355 if( abPK
[i
]==0 ) continue;
356 printf("%sc%d=", zSep
, i
+1);
358 sqlite3changeset_old(pIter
, i
, &pVal
);
364 case SQLITE_UPDATE
: {
367 const char *zSep
= " ";
368 sqlite3changeset_pk(pIter
, &abPK
, 0);
369 printf("/* %d */ UPDATE %s SET", cnt
, zSQLTabName
);
370 for(i
=0; i
<nCol
; i
++){
371 sqlite3_value
*pVal
= 0;
372 sqlite3changeset_new(pIter
, i
, &pVal
);
374 printf("%sc%d=", zSep
, i
+1);
381 for(i
=0; i
<nCol
; i
++){
383 if( abPK
[i
]==0 ) continue;
384 printf("%sc%d=", zSep
, i
+1);
386 sqlite3changeset_old(pIter
, i
, &pVal
);
392 case SQLITE_INSERT
: {
394 printf("/* %d */ INSERT INTO %s VALUES", cnt
, zSQLTabName
);
395 for(i
=0; i
<nCol
; i
++){
397 printf("%c", i
==0 ? '(' : ',');
398 sqlite3changeset_new(pIter
, i
, &pVal
);
407 sqlite3changeset_finalize(pIter
);
408 sqlite3_free(zPrevTab
);
409 sqlite3_free(zSQLTabName
);
412 /* If nothing else matches, show the usage comment */