From: drh Date: Thu, 15 Sep 2016 21:35:24 +0000 (+0000) Subject: Add the ".testcase" and ".check" dot-commands in the shell, when compiled X-Git-Tag: version-3.15.0~91 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2db8211526f3af3e6ef96aa1482c931472375638;p=thirdparty%2Fsqlite.git Add the ".testcase" and ".check" dot-commands in the shell, when compiled using SQLITE_DEBUG. FossilOrigin-Name: 62289f27ee276090a855982bd8216a465e7d0a27 --- diff --git a/manifest b/manifest index 2adb407a89..2812980f69 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Omit\sthe\ssqlite3Apis\sconstant\sobject\swhen\scompiling\swith\nSQLITE_OMIT_LOAD_EXTENSION,\ssince\sit\sis\snot\sused. -D 2016-09-15T19:15:19.226 +C Add\sthe\s".testcase"\sand\s".check"\sdot-commands\sin\sthe\sshell,\swhen\scompiled\nusing\sSQLITE_DEBUG. +D 2016-09-15T21:35:24.532 F Makefile.in 6fd48ffcf7c2deea7499062d1f3747f986c19678 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc e1aa788e84f926e42239ee167c53f785bedacacd @@ -387,7 +387,7 @@ F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/resolve.c 24f40fd0c3475821d1ad762a3f2c3455cc839b42 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c 244f9cc5e4662987cd2ef5c22d1b7027560f3425 -F src/shell.c de7c7e98846cacbfbe062cbd98bca899dfb720e3 +F src/shell.c 89a3adbfcaac17070372ce631161bbbf21ec2010 F src/sqlite.h.in 46ed821aeed0ba45559fb15597d9a400083154a2 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae @@ -1525,7 +1525,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P b9f5bdbf40ac6382e48f09ebcd53cc908e065527 -R 1e31cb38dfd2c7bbbb492df3fcbfb729 +P 7b10461370828b9c57acaaaea518031d53986fa3 +R 1d8691a695a03272769e683d2ed4d0b4 U drh -Z 8ce374e092d6c15d6e5f3b801f1063a4 +Z 15bb0e2011d42f4d3abf5a593439e046 diff --git a/manifest.uuid b/manifest.uuid index 7241c19155..d6648be1c2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7b10461370828b9c57acaaaea518031d53986fa3 \ No newline at end of file +62289f27ee276090a855982bd8216a465e7d0a27 \ No newline at end of file diff --git a/src/shell.c b/src/shell.c index 21cba66b1c..2991e7c13f 100644 --- a/src/shell.c +++ b/src/shell.c @@ -2136,6 +2136,9 @@ static char zHelp[] = ".bail on|off Stop after hitting an error. Default OFF\n" ".binary on|off Turn binary output on or off. Default OFF\n" ".changes on|off Show number of rows changed by SQL\n" +#ifdef SQLITE_DEBUG + ".check GLOB Fail if output since .testcase does not match\n" +#endif ".clone NEWDB Clone data into NEWDB from the existing database\n" ".databases List names and files of attached databases\n" ".dbinfo ?DB? Show status information about the database\n" @@ -2196,6 +2199,9 @@ static char zHelp[] = ".tables ?TABLE? List names of tables\n" " If TABLE specified, only list tables matching\n" " LIKE pattern TABLE.\n" +#ifdef SQLITE_DEBUG + ".testcase Begin redirecting output to 'testcase-out.txt'\n" +#endif ".timeout MS Try opening locked tables for MS milliseconds\n" ".timer on|off Turn SQL timer on or off\n" ".trace FILE|off Output each SQL statement as it is run\n" @@ -2232,6 +2238,31 @@ void session_help(ShellState *p){ /* Forward reference */ static int process_input(ShellState *p, FILE *in); + + +/* +** Read the content of a file into memory obtained from sqlite3_malloc64(). +** The caller is responsible for freeing the memory. +** +** NULL is returned if any error is encountered. +*/ +static char *readFile(const char *zName){ + FILE *in = fopen(zName, "rb"); + long nIn; + char *pBuf; + if( in==0 ) return 0; + fseek(in, 0, SEEK_END); + nIn = ftell(in); + rewind(in); + pBuf = sqlite3_malloc64( nIn ); + if( pBuf==0 ) return 0; + if( 1!=fread(pBuf, nIn, 1, in) ){ + sqlite3_free(pBuf); + return 0; + } + return pBuf; +} + /* ** Implementation of the "readfile(X)" SQL function. The entire content ** of the file named X is read and returned as a BLOB. NULL is returned @@ -2243,25 +2274,13 @@ static void readfileFunc( sqlite3_value **argv ){ const char *zName; - FILE *in; - long nIn; void *pBuf; UNUSED_PARAMETER(argc); zName = (const char*)sqlite3_value_text(argv[0]); if( zName==0 ) return; - in = fopen(zName, "rb"); - if( in==0 ) return; - fseek(in, 0, SEEK_END); - nIn = ftell(in); - rewind(in); - pBuf = sqlite3_malloc64( nIn ); - if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ - sqlite3_result_blob(context, pBuf, nIn, sqlite3_free); - }else{ - sqlite3_free(pBuf); - } - fclose(in); + pBuf = readFile(zName); + if( pBuf ) sqlite3_result_blob(context, pBuf, -1, sqlite3_free); } /* @@ -3063,6 +3082,106 @@ static int shellNomemError(void){ return 1; } +#ifdef SQLITE_DEBUG +/* +** Compare the pattern in zGlob[] against the text in z[]. Return TRUE +** if they match and FALSE (0) if they do not match. +** +** Globbing rules: +** +** '*' Matches any sequence of zero or more characters. +** +** '?' Matches exactly one character. +** +** [...] Matches one character from the enclosed list of +** characters. +** +** [^...] Matches one character not in the enclosed list. +** +** '#' Matches any sequence of one or more digits with an +** optional + or - sign in front +** +** ' ' Any span of whitespace matches any other span of +** whitespace. +** +** Extra whitespace at the end of z[] is ignored. +*/ +static int testcase_glob(const char *zGlob, const char *z){ + int c, c2; + int invert; + int seen; + + while( (c = (*(zGlob++)))!=0 ){ + if( IsSpace(c) ){ + if( !IsSpace(*z) ) return 0; + while( IsSpace(*zGlob) ) zGlob++; + while( IsSpace(*z) ) z++; + }else if( c=='*' ){ + while( (c=(*(zGlob++))) == '*' || c=='?' ){ + if( c=='?' && (*(z++))==0 ) return 0; + } + if( c==0 ){ + return 1; + }else if( c=='[' ){ + while( *z && testcase_glob(zGlob-1,z)==0 ){ + z++; + } + return (*z)!=0; + } + while( (c2 = (*(z++)))!=0 ){ + while( c2!=c ){ + c2 = *(z++); + if( c2==0 ) return 0; + } + if( testcase_glob(zGlob,z) ) return 1; + } + return 0; + }else if( c=='?' ){ + if( (*(z++))==0 ) return 0; + }else if( c=='[' ){ + int prior_c = 0; + seen = 0; + invert = 0; + c = *(z++); + if( c==0 ) return 0; + c2 = *(zGlob++); + if( c2=='^' ){ + invert = 1; + c2 = *(zGlob++); + } + if( c2==']' ){ + if( c==']' ) seen = 1; + c2 = *(zGlob++); + } + while( c2 && c2!=']' ){ + if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){ + c2 = *(zGlob++); + if( c>=prior_c && c<=c2 ) seen = 1; + prior_c = 0; + }else{ + if( c==c2 ){ + seen = 1; + } + prior_c = c2; + } + c2 = *(zGlob++); + } + if( c2==0 || (seen ^ invert)==0 ) return 0; + }else if( c=='#' ){ + if( (z[0]=='-' || z[0]=='+') && IsDigit(z[1]) ) z++; + if( !IsDigit(z[0]) ) return 0; + z++; + while( IsDigit(z[0]) ){ z++; } + }else{ + if( c!=(*(z++)) ) return 0; + } + } + while( IsSpace(*z) ){ z++; } + return *z==0; +} +#endif /* defined(SQLITE_DEBUG) */ + + /* ** Compare the string as a command-line option with either one or two ** initial "-" characters. @@ -3225,6 +3344,29 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else +#ifdef SQLITE_DEBUG + /* Cancel output redirection, if it is currently set (by .testcase) + ** Then read the content of the testcase-out.txt file and compare against + ** azArg[1]. If there are differences, report an error and exit. + */ + if( c=='c' && n>=3 && strncmp(azArg[0], "check", n)==0 ){ + char *zRes = 0; + output_reset(p); + if( nArg!=2 ){ + raw_printf(stderr, "Usage: .check GLOB-PATTERN\n"); + rc = 1; + }else if( (zRes = readFile("testcase-out.txt"))==0 ){ + raw_printf(stderr, "Error: cannot read 'testcase-out.txt'\n"); + rc = 2; + }else if( testcase_glob(azArg[1],zRes)==0 ){ + raw_printf(stderr, ".check failed\n Expected: [%s]\n Got: [%s]\n", + azArg[1], zRes); + rc = 2; + } + sqlite3_free(zRes); + }else +#endif + if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){ if( nArg==2 ){ tryToClone(p, azArg[1]); @@ -4495,6 +4637,17 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_free(azResult); }else +#ifdef SQLITE_DEBUG + /* Begin redirecting output to the file "testcase-out.txt" */ + if( c=='t' && strcmp(azArg[0],"testcase")==0 ){ + output_reset(p); + p->out = output_file_open("testcase-out.txt"); + if( p->out==0 ){ + utf8_printf(stderr, "Error: cannot open 'testcase-out.txt'\n"); + } + }else +#endif + if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){ static const struct { const char *zCtrlName; /* Name of a test-control option */