diff options
author | drh <drh@noemail.net> | 2014-08-18 20:01:31 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2014-08-18 20:01:31 +0000 |
commit | 03168cacd5c139471e651cf825501a953e0fcd4c (patch) | |
tree | 5821feca83c2c290c15d65815450e41268abee36 | |
parent | 3a67b0453e359e3f9158f51682eb07125ec1d4db (diff) | |
download | sqlite-03168cacd5c139471e651cf825501a953e0fcd4c.tar.gz sqlite-03168cacd5c139471e651cf825501a953e0fcd4c.zip |
A reasonably complete implementation of the "changeset" command-line tool
and the ".sessions" command in the command-line shell.
FossilOrigin-Name: 7b12f1f9c012f33d376242920583807b014b3287
-rw-r--r-- | ext/session/changeset.c | 108 | ||||
-rw-r--r-- | manifest | 14 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/shell.c | 80 |
4 files changed, 191 insertions, 13 deletions
diff --git a/ext/session/changeset.c b/ext/session/changeset.c index 62817a64e..1ff5cde87 100644 --- a/ext/session/changeset.c +++ b/ext/session/changeset.c @@ -124,6 +124,58 @@ static void renderValue(sqlite3_value *pVal){ } } +/* +** Number of conflicts seen +*/ +static int nConflict = 0; + +/* +** The conflict callback +*/ +static int conflictCallback( + void *pCtx, + int eConflict, + sqlite3_changeset_iter *pIter +){ + int op, bIndirect, nCol, i; + const char *zTab; + unsigned char *abPK; + const char *zType = ""; + const char *zOp = ""; + const char *zSep = " "; + + nConflict++; + sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); + sqlite3changeset_pk(pIter, &abPK, 0); + switch( eConflict ){ + case SQLITE_CHANGESET_DATA: zType = "DATA"; break; + case SQLITE_CHANGESET_NOTFOUND: zType = "NOTFOUND"; break; + case SQLITE_CHANGESET_CONFLICT: zType = "PRIMARY KEY"; break; + case SQLITE_CHANGESET_FOREIGN_KEY: zType = "FOREIGN KEY"; break; + case SQLITE_CHANGESET_CONSTRAINT: zType = "CONSTRAINT"; break; + } + switch( op ){ + case SQLITE_UPDATE: zOp = "UPDATE of"; break; + case SQLITE_INSERT: zOp = "INSERT into"; break; + case SQLITE_DELETE: zOp = "DELETE from"; break; + } + printf("%s conflict on %s table %s with primary key", zType, zOp, zTab); + for(i=0; i<nCol; i++){ + sqlite3_value *pVal; + if( abPK[i]==0 ) continue; + printf("%s", zSep); + if( op==SQLITE_INSERT ){ + sqlite3changeset_new(pIter, i, &pVal); + }else{ + sqlite3changeset_old(pIter, i, &pVal); + } + renderValue(pVal); + zSep = ","; + } + printf("\n"); + return SQLITE_CHANGESET_OMIT; +} + int main(int argc, char **argv){ int sz, rc; void *pBuf = 0; @@ -134,7 +186,32 @@ int main(int argc, char **argv){ ** Apply the changeset in FILENAME to the database file DB */ if( strcmp(argv[2],"apply")==0 ){ - fprintf(stderr, "not yet implemented\n"); + sqlite3 *db; + if( argc!=4 ) usage(argv[0]); + rc = sqlite3_open(argv[3], &db); + if( rc!=SQLITE_OK ){ + fprintf(stderr, "unable to open database file \"%s\": %s\n", + argv[3], sqlite3_errmsg(db)); + sqlite3_close(db); + exit(1); + } + sqlite3_exec(db, "BEGIN", 0, 0, 0); + nConflict = 0; + rc = sqlite3changeset_apply(db, sz, pBuf, 0, conflictCallback, 0); + if( rc ){ + fprintf(stderr, "sqlite3changeset_apply() returned %d\n", rc); + } + if( nConflict ){ + fprintf(stderr, "%d conflicts - no changes applied\n", nConflict); + sqlite3_exec(db, "ROLLBACK", 0, 0, 0); + }else if( rc ){ + fprintf(stderr, "sqlite3changeset_apply() returns %d " + "- no changes applied\n", rc); + sqlite3_exec(db, "ROLLBACK", 0, 0, 0); + }else{ + sqlite3_exec(db, "COMMIT", 0, 0, 0); + } + sqlite3_close(db); }else /* changeset FILENAME concat FILE2 OUT @@ -142,7 +219,27 @@ int main(int argc, char **argv){ ** and write the result into OUT. */ if( strcmp(argv[2],"concat")==0 ){ - fprintf(stderr, "not yet implemented\n"); + int szB; + void *pB; + int szOut; + void *pOutBuf; + FILE *out; + const char *zOut = argv[4]; + if( argc!=5 ) usage(argv[0]); + out = fopen(zOut, "wb"); + if( out==0 ){ + fprintf(stderr, "cannot open \"%s\" for writing\n", zOut); + exit(1); + } + readFile(argv[3], &szB, &pB); + sqlite3changeset_concat(sz, pBuf, szB, pB, &szOut, &pOutBuf); + if( fwrite(pOutBuf, szOut, 1, out)!=1 ){ + fprintf(stderr, "unable to write all %d bytes of output to \"%s\"\n", + szOut, zOut); + } + fclose(out); + sqlite3_free(pOutBuf); + sqlite3_free(pB); }else /* changeset FILENAME dump @@ -196,16 +293,17 @@ int main(int argc, char **argv){ FILE *out; int szOut = 0; void *pOutBuf = 0; + const char *zOut = argv[3]; if( argc!=4 ) usage(argv[0]); - out = fopen(argv[3], "wb"); + out = fopen(zOut, "wb"); if( out==0 ){ - fprintf(stderr, "cannot open \"%s\" for writing\n", argv[3]); + fprintf(stderr, "cannot open \"%s\" for writing\n", zOut); exit(1); } sqlite3changeset_invert(sz, pBuf, &szOut, &pOutBuf); if( fwrite(pOutBuf, szOut, 1, out)!=1 ){ fprintf(stderr, "unable to write all %d bytes of output to \"%s\"\n", - szOut, argv[3]); + szOut, zOut); } fclose(out); sqlite3_free(pOutBuf); @@ -1,5 +1,5 @@ -C Add\sthe\s"changeset"\scommand-line\sutility\sfor\sgetting\san\sASCII\sdump\sof\nchange\ssets. -D 2014-08-18T17:56:31.306 +C A\sreasonably\scomplete\simplementation\sof\sthe\s"changeset"\scommand-line\stool\nand\sthe\s".sessions"\scommand\sin\sthe\scommand-line\sshell. +D 2014-08-18T20:01:31.177 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 639859a6f81bd15921ccd56ddbd6dfd335278377 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -143,7 +143,7 @@ F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea F ext/rtree/sqlite3rtree.h 83349d519fe5f518b3ea025d18dd1fe51b1684bd F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 -F ext/session/changeset.c d46c6467c223717d0e4783115f350c5962d53589 +F ext/session/changeset.c 9be709cea346d65c6d0cc8bf03569956af125462 F ext/session/session1.test 894e3bc9f497c4fa07a2aa3271e3911f3670c3d8 F ext/session/session2.test 99ca0da7ddb617d42bafd83adccf99f18ae0384b F ext/session/session3.test a7a9ce59b8d1e49e2cc23d81421ac485be0eea01 @@ -239,7 +239,7 @@ F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece F src/resolve.c 0ea356d32a5e884add23d1b9b4e8736681dd5697 F src/rowset.c a9c9aae3234b44a6d7c6f5a3cadf90dce1e627be F src/select.c ea48e891406ccdf748f3eb02893e056d134a0fea -F src/shell.c 0845863fc7e813aac148d3303faf9f93f86ad8c3 +F src/shell.c 1761e117dd58e2383a2d30319c49d0fca00f8bf0 F src/sqlite.h.in 021a1f5c50e83060675d994a6014fd409e611d9e F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc @@ -1203,7 +1203,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P c2fcf0b9f4bdc48dfc6530bda4f531b94a833207 -R ef02979994221e0299bd184663702a4d +P 55bb3544a6b474c04853270067a35ca4b0079f52 +R e1146ecf2575392bb0feff21eda4606c U drh -Z 24c20f108d2116be16c069d984599c3d +Z 0de9dc1123705f1abdab4ea8e245122b diff --git a/manifest.uuid b/manifest.uuid index 4a8f30318..447ba14ee 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -55bb3544a6b474c04853270067a35ca4b0079f52
\ No newline at end of file +7b12f1f9c012f33d376242920583807b014b3287
\ No newline at end of file diff --git a/src/shell.c b/src/shell.c index f9ade61e9..d45e59166 100644 --- a/src/shell.c +++ b/src/shell.c @@ -1789,6 +1789,21 @@ static void session_close_all(ShellState *p){ } /* +** Implementation of the xFilter function for an open session. Omit +** any tables named by ".session filter" but let all other table through. +*/ +#if defined(SQLITE_ENABLE_SESSION) +static int session_filter(void *pCtx, const char *zTab){ + OpenSession *pSession = (OpenSession*)pCtx; + int i; + for(i=0; i<pSession->nFilter; i++){ + if( sqlite3_strglob(pSession->azFilter[i], zTab)==0 ) return 0; + } + return 1; +} +#endif + +/* ** Make sure the database is open. If it is not, then open it. If ** the database fails to open, print an error message and exit. */ @@ -3235,12 +3250,75 @@ static int do_meta_command(char *zLine, ShellState *p){ ** Close the identified session */ if( strcmp(azCmd[0], "close")==0 ){ + if( nCmd!=1 ) goto session_syntax_error; if( p->nSession ){ session_close(pSession); p->aSession[iSes] = p->aSession[--p->nSession]; } }else + /* .session enable ?BOOLEAN? + ** Query or set the enable flag + */ + if( strcmp(azCmd[0], "enable")==0 ){ + int ii; + if( nCmd>2 ) goto session_syntax_error; + ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); + if( p->nSession ){ + ii = sqlite3session_enable(pSession->p, ii); + fprintf(p->out, "session %s enable flag = %d\n", pSession->zName, ii); + } + }else + + /* .session filter GLOB .... + ** Set a list of GLOB patterns of table names to be excluded. + */ + if( strcmp(azCmd[0], "filter")==0 ){ + int ii, nByte; + if( nCmd<2 ) goto session_syntax_error; + if( p->nSession ){ + for(ii=0; ii<pSession->nFilter; ii++){ + sqlite3_free(pSession->azFilter[ii]); + } + sqlite3_free(pSession->azFilter); + nByte = sizeof(pSession->azFilter[0])*(nCmd-1); + pSession->azFilter = sqlite3_malloc( nByte ); + if( pSession->azFilter==0 ){ + fprintf(stderr, "Error: out or memory\n"); + exit(1); + } + for(ii=1; ii<nCmd; ii++){ + pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]); + } + pSession->nFilter = ii-1; + } + }else + + /* .session indirect ?BOOLEAN? + ** Query or set the indirect flag + */ + if( strcmp(azCmd[0], "indirect")==0 ){ + int ii; + if( nCmd>2 ) goto session_syntax_error; + ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); + if( p->nSession ){ + ii = sqlite3session_indirect(pSession->p, ii); + fprintf(p->out, "session %s indirect flag = %d\n", pSession->zName,ii); + } + }else + + /* .session isempty + ** Determine if the session is empty + */ + if( strcmp(azCmd[0], "isempty")==0 ){ + int ii; + if( nCmd!=1 ) goto session_syntax_error; + if( p->nSession ){ + ii = sqlite3session_isempty(pSession->p); + fprintf(p->out, "session %s isempty flag = %d\n", pSession->zName, ii); + } + }else + /* .session list ** List all currently open sessions */ @@ -3276,6 +3354,8 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 0; goto meta_command_exit; } + pSession->nFilter = 0; + sqlite3session_table_filter(pSession->p, session_filter, pSession); p->nSession++; pSession->zName = sqlite3_mprintf("%s", zName); }else |