Enhance the command-line completion extension to return the names of
[sqlite.git] / ext / session / changeset.c
blobedb43ff8e52d601a70327bedea746b02ad7627ad
1 /*
2 ** 2014-08-18
3 **
4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
6 **
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.
16 #include "sqlite3.h"
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <assert.h>
21 #include <ctype.h>
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);
29 fprintf(stderr,
30 "COMMANDs:\n"
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"
37 exit(1);
41 ** Read the content of a disk file into an in-memory buffer
43 static void readFile(const char *zFilename, int *pSz, void **ppBuf){
44 FILE *f;
45 int sz;
46 void *pBuf;
47 f = fopen(zFilename, "rb");
48 if( f==0 ){
49 fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
50 exit(1);
52 fseek(f, 0, SEEK_END);
53 sz = (int)ftell(f);
54 rewind(f);
55 pBuf = sqlite3_malloc( sz ? sz : 1 );
56 if( pBuf==0 ){
57 fprintf(stderr, "cannot allocate %d to hold content of \"%s\"\n",
58 sz, zFilename);
59 exit(1);
61 if( sz>0 ){
62 if( fread(pBuf, sz, 1, f)!=1 ){
63 fprintf(stderr, "cannot read all %d bytes of \"%s\"\n", sz, zFilename);
64 exit(1);
66 fclose(f);
68 *pSz = sz;
69 *ppBuf = pBuf;
72 /* Array for converting from half-bytes (nybbles) into ASCII hex
73 ** digits. */
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) ){
84 case SQLITE_FLOAT: {
85 double r1;
86 char zBuf[50];
87 r1 = sqlite3_value_double(pVal);
88 sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
89 printf("%s", zBuf);
90 break;
92 case SQLITE_INTEGER: {
93 printf("%lld", sqlite3_value_int64(pVal));
94 break;
96 case SQLITE_BLOB: {
97 char const *zBlob = sqlite3_value_blob(pVal);
98 int nBlob = sqlite3_value_bytes(pVal);
99 int i;
100 printf("x'");
101 for(i=0; i<nBlob; i++){
102 putchar(hexdigits[(zBlob[i]>>4)&0x0F]);
103 putchar(hexdigits[(zBlob[i])&0x0F]);
105 putchar('\'');
106 break;
108 case SQLITE_TEXT: {
109 const unsigned char *zArg = sqlite3_value_text(pVal);
110 putchar('\'');
111 while( zArg[0] ){
112 putchar(zArg[0]);
113 if( zArg[0]=='\'' ) putchar(zArg[0]);
114 zArg++;
116 putchar('\'');
117 break;
119 default: {
120 assert( sqlite3_value_type(pVal)==SQLITE_NULL );
121 printf("NULL");
122 break;
128 ** Number of conflicts seen
130 static int nConflict = 0;
133 ** The conflict callback
135 static int conflictCallback(
136 void *pCtx,
137 int eConflict,
138 sqlite3_changeset_iter *pIter
140 int op, bIndirect, nCol, i;
141 const char *zTab;
142 unsigned char *abPK;
143 const char *zType = "";
144 const char *zOp = "";
145 const char *zSep = " ";
147 nConflict++;
148 sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
149 sqlite3changeset_pk(pIter, &abPK, 0);
150 switch( eConflict ){
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;
157 switch( op ){
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++){
164 sqlite3_value *pVal;
165 if( abPK[i]==0 ) continue;
166 printf("%s", zSep);
167 if( op==SQLITE_INSERT ){
168 sqlite3changeset_new(pIter, i, &pVal);
169 }else{
170 sqlite3changeset_old(pIter, i, &pVal);
172 renderValue(pVal);
173 zSep = ",";
175 printf("\n");
176 return SQLITE_CHANGESET_OMIT;
179 int main(int argc, char **argv){
180 int sz, rc;
181 void *pBuf = 0;
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 ){
189 sqlite3 *db;
190 if( argc!=4 ) usage(argv[0]);
191 rc = sqlite3_open(argv[3], &db);
192 if( rc!=SQLITE_OK ){
193 fprintf(stderr, "unable to open database file \"%s\": %s\n",
194 argv[3], sqlite3_errmsg(db));
195 sqlite3_close(db);
196 exit(1);
198 sqlite3_exec(db, "BEGIN", 0, 0, 0);
199 nConflict = 0;
200 rc = sqlite3changeset_apply(db, sz, pBuf, 0, conflictCallback, 0);
201 if( rc ){
202 fprintf(stderr, "sqlite3changeset_apply() returned %d\n", rc);
204 if( nConflict ){
205 fprintf(stderr, "%d conflicts - no changes applied\n", nConflict);
206 sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
207 }else if( rc ){
208 fprintf(stderr, "sqlite3changeset_apply() returns %d "
209 "- no changes applied\n", rc);
210 sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
211 }else{
212 sqlite3_exec(db, "COMMIT", 0, 0, 0);
214 sqlite3_close(db);
215 }else
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 ){
222 int szB;
223 void *pB;
224 int szOut;
225 void *pOutBuf;
226 FILE *out;
227 const char *zOut = argv[4];
228 if( argc!=5 ) usage(argv[0]);
229 out = fopen(zOut, "wb");
230 if( out==0 ){
231 fprintf(stderr, "cannot open \"%s\" for writing\n", zOut);
232 exit(1);
234 readFile(argv[3], &szB, &pB);
235 rc = sqlite3changeset_concat(sz, pBuf, szB, pB, &szOut, &pOutBuf);
236 if( rc!=SQLITE_OK ){
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",
240 szOut, zOut);
242 fclose(out);
243 sqlite3_free(pOutBuf);
244 sqlite3_free(pB);
245 }else
247 /* changeset FILENAME dump
248 ** Show the complete content of the changeset in FILENAME
250 if( strcmp(argv[2],"dump")==0 ){
251 int cnt = 0;
252 int i;
253 sqlite3_changeset_iter *pIter;
254 rc = sqlite3changeset_start(&pIter, sz, pBuf);
255 if( rc!=SQLITE_OK ){
256 fprintf(stderr, "sqlite3changeset_start() returns %d\n", rc);
257 exit(1);
259 while( sqlite3changeset_next(pIter)==SQLITE_ROW ){
260 int op, bIndirect, nCol;
261 const char *zTab;
262 unsigned char *abPK;
263 sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
264 cnt++;
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++){
271 sqlite3_value *pVal;
272 pVal = 0;
273 sqlite3changeset_old(pIter, i, &pVal);
274 if( pVal ){
275 printf(" old[%d]%s = ", i, abPK[i] ? "pk" : " ");
276 renderValue(pVal);
277 printf("\n");
279 pVal = 0;
280 sqlite3changeset_new(pIter, i, &pVal);
281 if( pVal ){
282 printf(" new[%d]%s = ", i, abPK[i] ? "pk" : " ");
283 renderValue(pVal);
284 printf("\n");
288 sqlite3changeset_finalize(pIter);
289 }else
291 /* changeset FILENAME invert OUT
292 ** Invert the changes in FILENAME and writes the result on OUT
294 if( strcmp(argv[2],"invert")==0 ){
295 FILE *out;
296 int szOut = 0;
297 void *pOutBuf = 0;
298 const char *zOut = argv[3];
299 if( argc!=4 ) usage(argv[0]);
300 out = fopen(zOut, "wb");
301 if( out==0 ){
302 fprintf(stderr, "cannot open \"%s\" for writing\n", zOut);
303 exit(1);
305 rc = sqlite3changeset_invert(sz, pBuf, &szOut, &pOutBuf);
306 if( rc!=SQLITE_OK ){
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",
310 szOut, zOut);
312 fclose(out);
313 sqlite3_free(pOutBuf);
314 }else
316 /* changeset FILE sql
317 ** Show the content of the changeset as pseudo-SQL
319 if( strcmp(argv[2],"sql")==0 ){
320 int cnt = 0;
321 char *zPrevTab = 0;
322 char *zSQLTabName = 0;
323 sqlite3_changeset_iter *pIter = 0;
324 rc = sqlite3changeset_start(&pIter, sz, pBuf);
325 if( rc!=SQLITE_OK ){
326 fprintf(stderr, "sqlite3changeset_start() returns %d\n", rc);
327 exit(1);
329 printf("BEGIN;\n");
330 while( sqlite3changeset_next(pIter)==SQLITE_ROW ){
331 int op, bIndirect, nCol;
332 const char *zTab;
333 sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
334 cnt++;
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);
341 }else{
342 zSQLTabName = sqlite3_mprintf("%s", zTab);
344 printf("/****** Changes for table %s ***************/\n", zSQLTabName);
346 switch( op ){
347 case SQLITE_DELETE: {
348 unsigned char *abPK;
349 int i;
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++){
354 sqlite3_value *pVal;
355 if( abPK[i]==0 ) continue;
356 printf("%sc%d=", zSep, i+1);
357 zSep = " AND ";
358 sqlite3changeset_old(pIter, i, &pVal);
359 renderValue(pVal);
361 printf(";\n");
362 break;
364 case SQLITE_UPDATE: {
365 unsigned char *abPK;
366 int i;
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);
373 if( pVal ){
374 printf("%sc%d=", zSep, i+1);
375 zSep = ", ";
376 renderValue(pVal);
379 printf(" WHERE");
380 zSep = " ";
381 for(i=0; i<nCol; i++){
382 sqlite3_value *pVal;
383 if( abPK[i]==0 ) continue;
384 printf("%sc%d=", zSep, i+1);
385 zSep = " AND ";
386 sqlite3changeset_old(pIter, i, &pVal);
387 renderValue(pVal);
389 printf(";\n");
390 break;
392 case SQLITE_INSERT: {
393 int i;
394 printf("/* %d */ INSERT INTO %s VALUES", cnt, zSQLTabName);
395 for(i=0; i<nCol; i++){
396 sqlite3_value *pVal;
397 printf("%c", i==0 ? '(' : ',');
398 sqlite3changeset_new(pIter, i, &pVal);
399 renderValue(pVal);
401 printf(");\n");
402 break;
406 printf("COMMIT;\n");
407 sqlite3changeset_finalize(pIter);
408 sqlite3_free(zPrevTab);
409 sqlite3_free(zSQLTabName);
410 }else
412 /* If nothing else matches, show the usage comment */
413 usage(argv[0]);
414 sqlite3_free(pBuf);
415 return 0;