summaryrefslogtreecommitdiff
path: root/lib/libsqlite3/tool
diff options
context:
space:
mode:
authorJames Turner <jturner@cvs.openbsd.org>2015-12-23 20:07:39 +0000
committerJames Turner <jturner@cvs.openbsd.org>2015-12-23 20:07:39 +0000
commite428b61d371061fb076d32510c0e21eb2c4aa538 (patch)
tree1b5c34c2f5b7e70da3895ec94da2de72090c8f6b /lib/libsqlite3/tool
parent683347a40bb36f2f05d126582b768efaf0b409cd (diff)
Update sqlite3 to 3.9.2. Bump major, regen .pc and header. Changes
available here: http://sqlite.org/changes.html Tested in bulk by aja@. ok landry@
Diffstat (limited to 'lib/libsqlite3/tool')
-rwxr-xr-xlib/libsqlite3/tool/build-all-msvc.bat35
-rw-r--r--lib/libsqlite3/tool/fuzzershell.c6
-rw-r--r--lib/libsqlite3/tool/lemon.c299
-rw-r--r--lib/libsqlite3/tool/lempar.c112
-rw-r--r--lib/libsqlite3/tool/mksqlite3c.tcl5
-rw-r--r--lib/libsqlite3/tool/mksqlite3h.tcl57
-rw-r--r--lib/libsqlite3/tool/mkvsix.tcl5
-rw-r--r--lib/libsqlite3/tool/showdb.c330
-rw-r--r--lib/libsqlite3/tool/spaceanal.tcl58
-rw-r--r--lib/libsqlite3/tool/sqldiff.c648
-rw-r--r--lib/libsqlite3/tool/tostr.awk1
11 files changed, 1249 insertions, 307 deletions
diff --git a/lib/libsqlite3/tool/build-all-msvc.bat b/lib/libsqlite3/tool/build-all-msvc.bat
index 9f5176db9de..4842dc40744 100755
--- a/lib/libsqlite3/tool/build-all-msvc.bat
+++ b/lib/libsqlite3/tool/build-all-msvc.bat
@@ -313,6 +313,27 @@ IF "%VisualStudioVersion%" == "11.0" (
)
REM
+REM NOTE: This is the name of the sub-directory where the UCRT libraries may
+REM be found. It is only used when compiling against the UCRT.
+REM
+IF DEFINED UCRTVersion (
+ SET NUCRTVER=%UCRTVersion%
+) ELSE (
+ SET NUCRTVER=10.0.10240.0
+)
+
+REM
+REM NOTE: This is the name of the sub-directory where the Windows 10.0 SDK
+REM libraries may be found. It is only used when compiling with the
+REM Windows 10.0 SDK.
+REM
+IF DEFINED WindowsSDKLibVersion (
+ SET WIN10SDKVER=%WindowsSDKLibVersion:\=%
+) ELSE (
+ SET WIN10SDKVER=%NUCRTVER%
+)
+
+REM
REM NOTE: Check if this is the Windows Phone SDK. If so, a different batch
REM file is necessary to setup the build environment. Since the variable
REM values involved here may contain parenthesis, using GOTO instead of
@@ -354,6 +375,7 @@ FOR %%P IN (%PLATFORMS%) DO (
REM
CALL :fn_UnsetVariable CommandPromptType
CALL :fn_UnsetVariable DevEnvDir
+ CALL :fn_UnsetVariable DNX_HOME
CALL :fn_UnsetVariable ExtensionSdkDir
CALL :fn_UnsetVariable Framework35Version
CALL :fn_UnsetVariable Framework40Version
@@ -365,14 +387,19 @@ FOR %%P IN (%PLATFORMS%) DO (
CALL :fn_UnsetVariable INCLUDE
CALL :fn_UnsetVariable LIB
CALL :fn_UnsetVariable LIBPATH
+ CALL :fn_UnsetVariable NETFXSDKDir
CALL :fn_UnsetVariable Platform
+ CALL :fn_UnsetVariable UCRTVersion
CALL :fn_UnsetVariable UniversalCRTSdkDir
REM CALL :fn_UnsetVariable VCINSTALLDIR
CALL :fn_UnsetVariable VSINSTALLDIR
+ CALL :fn_UnsetVariable WindowsLibPath
CALL :fn_UnsetVariable WindowsPhoneKitDir
CALL :fn_UnsetVariable WindowsSdkDir
CALL :fn_UnsetVariable WindowsSdkDir_35
CALL :fn_UnsetVariable WindowsSdkDir_old
+ CALL :fn_UnsetVariable WindowsSDKLibVersion
+ CALL :fn_UnsetVariable WindowsSDKVersion
CALL :fn_UnsetVariable WindowsSDK_ExecutablePath_x86
CALL :fn_UnsetVariable WindowsSDK_ExecutablePath_x64
@@ -482,9 +509,9 @@ FOR %%P IN (%PLATFORMS%) DO (
REM different directory naming conventions.
REM
IF DEFINED USE_WINV100_NSDKLIBPATH (
- CALL :fn_AppendVariable NSDKLIBPATH \..\10\lib\10.0.10030.0\um\x86
- CALL :fn_CopyVariable UniversalCRTSdkDir PSDKLIBPATH
- CALL :fn_AppendVariable PSDKLIBPATH Lib\10.0.10030.0\um\%%D
+ CALL :fn_AppendVariable NSDKLIBPATH \..\10\lib\%WIN10SDKVER%\um\x86
+ CALL :fn_CopyVariable WindowsSdkDir PSDKLIBPATH
+ CALL :fn_AppendVariable PSDKLIBPATH lib\%WIN10SDKVER%\um\%%D
) ELSE IF DEFINED USE_WINV63_NSDKLIBPATH (
CALL :fn_AppendVariable NSDKLIBPATH \lib\winv6.3\um\x86
) ELSE IF "%VisualStudioVersion%" == "12.0" (
@@ -507,7 +534,7 @@ FOR %%P IN (%PLATFORMS%) DO (
IF DEFINED SET_NUCRTLIBPATH (
IF DEFINED UniversalCRTSdkDir (
CALL :fn_CopyVariable UniversalCRTSdkDir NUCRTLIBPATH
- CALL :fn_AppendVariable NUCRTLIBPATH \lib\winv10.0\ucrt\x86
+ CALL :fn_AppendVariable NUCRTLIBPATH \lib\%NUCRTVER%\ucrt\x86
)
)
diff --git a/lib/libsqlite3/tool/fuzzershell.c b/lib/libsqlite3/tool/fuzzershell.c
index 2778c9d2f76..6754a071e37 100644
--- a/lib/libsqlite3/tool/fuzzershell.c
+++ b/lib/libsqlite3/tool/fuzzershell.c
@@ -322,6 +322,7 @@ static void showHelp(void){
"Options:\n"
" --autovacuum Enable AUTOVACUUM mode\n"
" --database FILE Use database FILE instead of an in-memory database\n"
+" --disable-lookaside Turn off lookaside memory\n"
" --heap SZ MIN Memory allocator uses SZ bytes & min allocation MIN\n"
" --help Show this help text\n"
" --lookaside N SZ Configure lookaside for N slots of SZ bytes each\n"
@@ -457,6 +458,7 @@ int main(int argc, char **argv){
const char *zDbName = 0; /* Name of an on-disk database file to open */
iBegin = timeOfDay();
+ sqlite3_shutdown();
zFailCode = getenv("TEST_FAILURE");
g.zArgv0 = argv[0];
zPrompt = "<stdin>";
@@ -473,6 +475,10 @@ int main(int argc, char **argv){
zDbName = argv[i+1];
i += 1;
}else
+ if( strcmp(z,"disable-lookaside")==0 ){
+ nLook = 1;
+ szLook = 0;
+ }else
if( strcmp(z, "f")==0 && i+1<argc ){
i++;
goto addNewInFile;
diff --git a/lib/libsqlite3/tool/lemon.c b/lib/libsqlite3/tool/lemon.c
index 89d992c37ec..2e8054b5ccb 100644
--- a/lib/libsqlite3/tool/lemon.c
+++ b/lib/libsqlite3/tool/lemon.c
@@ -55,7 +55,7 @@ static char *msort(char*,char**,int(*)(const char*,const char*));
** saying they are unsafe. So we define our own versions of those routines too.
**
** There are three routines here: lemon_sprintf(), lemon_vsprintf(), and
-** lemon_addtext(). The first two are replacements for sprintf() and vsprintf().
+** lemon_addtext(). The first two are replacements for sprintf() and vsprintf().
** The third is a helper routine for vsnprintf() that adds texts to the end of a
** buffer, making sure the buffer is always zero-terminated.
**
@@ -316,7 +316,8 @@ enum e_action {
RRCONFLICT, /* Was a reduce, but part of a conflict */
SH_RESOLVED, /* Was a shift. Precedence resolved conflict */
RD_RESOLVED, /* Was reduce. Precedence resolved conflict */
- NOT_USED /* Deleted by compression */
+ NOT_USED, /* Deleted by compression */
+ SHIFTREDUCE /* Shift first, then reduce */
};
/* Every shift or reduce operation is stored as one of the following */
@@ -340,7 +341,9 @@ struct state {
struct action *ap; /* Array of actions for this state */
int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */
int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */
- int iDflt; /* Default action */
+ int iDfltReduce; /* Default action is to REDUCE by this rule */
+ struct rule *pDfltReduce;/* The default REDUCE rule. */
+ int autoReduce; /* True if this is an auto-reduce state */
};
#define NO_OFFSET (-2147483647)
@@ -360,6 +363,7 @@ struct lemon {
struct state **sorted; /* Table of states sorted by state number */
struct rule *rule; /* List of all rules */
int nstate; /* Number of states */
+ int nxstate; /* nstate with tail degenerate states removed */
int nrule; /* Number of rules */
int nsymbol; /* Number of terminal and nonterminal symbols */
int nterminal; /* Number of terminal symbols */
@@ -385,7 +389,8 @@ struct lemon {
char *outname; /* Name of the current output file */
char *tokenprefix; /* A prefix added to token names in the .h file */
int nconflict; /* Number of parsing conflicts */
- int tablesize; /* Size of the parse tables */
+ int nactiontab; /* Number of entries in the yy_action[] table */
+ int tablesize; /* Total table size of all tables in bytes */
int basisflag; /* Print only basis configurations */
int has_fallback; /* True if any %fallback is seen in the grammar */
int nolinenosflag; /* True if #line statements should not be printed */
@@ -483,7 +488,7 @@ static int actioncmp(
if( rc==0 ){
rc = (int)ap1->type - (int)ap2->type;
}
- if( rc==0 && ap1->type==REDUCE ){
+ if( rc==0 && (ap1->type==REDUCE || ap1->type==SHIFTREDUCE) ){
rc = ap1->x.rp->index - ap2->x.rp->index;
}
if( rc==0 ){
@@ -1375,14 +1380,16 @@ void Configlist_closure(struct lemon *lemp)
/* Sort the configuration list */
void Configlist_sort(){
- current = (struct config *)msort((char *)current,(char **)&(current->next),Configcmp);
+ current = (struct config*)msort((char*)current,(char**)&(current->next),
+ Configcmp);
currentend = 0;
return;
}
/* Sort the basis configuration list */
void Configlist_sortbasis(){
- basis = (struct config *)msort((char *)current,(char **)&(current->bp),Configcmp);
+ basis = (struct config*)msort((char*)current,(char**)&(current->bp),
+ Configcmp);
basisend = 0;
return;
}
@@ -1480,6 +1487,18 @@ static void handle_T_option(char *z){
lemon_strcpy(user_templatename, z);
}
+/* forward reference */
+static const char *minimum_size_type(int lwr, int upr, int *pnByte);
+
+/* Print a single line of the "Parser Stats" output
+*/
+static void stats_line(const char *zLabel, int iValue){
+ int nLabel = lemonStrlen(zLabel);
+ printf(" %s%.*s %5d\n", zLabel,
+ 35-nLabel, "................................",
+ iValue);
+}
+
/* The main program. Parse the command line and do it... */
int main(int argc, char **argv)
{
@@ -1611,10 +1630,15 @@ int main(int argc, char **argv)
if( !mhflag ) ReportHeader(&lem);
}
if( statistics ){
- printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n",
- lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule);
- printf(" %d states, %d parser table entries, %d conflicts\n",
- lem.nstate, lem.tablesize, lem.nconflict);
+ printf("Parser statistics:\n");
+ stats_line("terminal symbols", lem.nterminal);
+ stats_line("non-terminal symbols", lem.nsymbol - lem.nterminal);
+ stats_line("total symbols", lem.nsymbol);
+ stats_line("rules", lem.nrule);
+ stats_line("states", lem.nxstate);
+ stats_line("conflicts", lem.nconflict);
+ stats_line("action table entries", lem.nactiontab);
+ stats_line("total table size (bytes)", lem.tablesize);
}
if( lem.nconflict > 0 ){
fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict);
@@ -1873,7 +1897,8 @@ static int handleswitch(int i, FILE *err)
dv = strtod(cp,&end);
if( *end ){
if( err ){
- fprintf(err,"%sillegal character in floating-point argument.\n",emsg);
+ fprintf(err,
+ "%sillegal character in floating-point argument.\n",emsg);
errline(i,(int)((char*)end-(char*)argv[i]),err);
}
errcnt++;
@@ -2939,15 +2964,14 @@ void Reprint(struct lemon *lemp)
}
}
-void ConfigPrint(FILE *fp, struct config *cfp)
-{
- struct rule *rp;
+/* Print a single rule.
+*/
+void RulePrint(FILE *fp, struct rule *rp, int iCursor){
struct symbol *sp;
int i, j;
- rp = cfp->rp;
fprintf(fp,"%s ::=",rp->lhs->name);
for(i=0; i<=rp->nrhs; i++){
- if( i==cfp->dot ) fprintf(fp," *");
+ if( i==iCursor ) fprintf(fp," *");
if( i==rp->nrhs ) break;
sp = rp->rhs[i];
if( sp->type==MULTITERMINAL ){
@@ -2961,6 +2985,12 @@ void ConfigPrint(FILE *fp, struct config *cfp)
}
}
+/* Print the rule for a configuration.
+*/
+void ConfigPrint(FILE *fp, struct config *cfp){
+ RulePrint(fp, cfp->rp, cfp->dot);
+}
+
/* #define TEST */
#if 0
/* Print a set */
@@ -3000,15 +3030,30 @@ char *tag;
/* Print an action to the given file descriptor. Return FALSE if
** nothing was actually printed.
*/
-int PrintAction(struct action *ap, FILE *fp, int indent){
+int PrintAction(
+ struct action *ap, /* The action to print */
+ FILE *fp, /* Print the action here */
+ int indent /* Indent by this amount */
+){
int result = 1;
switch( ap->type ){
- case SHIFT:
- fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->statenum);
+ case SHIFT: {
+ struct state *stp = ap->x.stp;
+ fprintf(fp,"%*s shift %-7d",indent,ap->sp->name,stp->statenum);
+ break;
+ }
+ case REDUCE: {
+ struct rule *rp = ap->x.rp;
+ fprintf(fp,"%*s reduce %-7d",indent,ap->sp->name,rp->index);
+ RulePrint(fp, rp, -1);
break;
- case REDUCE:
- fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index);
+ }
+ case SHIFTREDUCE: {
+ struct rule *rp = ap->x.rp;
+ fprintf(fp,"%*s shift-reduce %-7d",indent,ap->sp->name,rp->index);
+ RulePrint(fp, rp, -1);
break;
+ }
case ACCEPT:
fprintf(fp,"%*s accept",indent,ap->sp->name);
break;
@@ -3017,16 +3062,16 @@ int PrintAction(struct action *ap, FILE *fp, int indent){
break;
case SRCONFLICT:
case RRCONFLICT:
- fprintf(fp,"%*s reduce %-3d ** Parsing conflict **",
+ fprintf(fp,"%*s reduce %-7d ** Parsing conflict **",
indent,ap->sp->name,ap->x.rp->index);
break;
case SSCONFLICT:
- fprintf(fp,"%*s shift %-3d ** Parsing conflict **",
+ fprintf(fp,"%*s shift %-7d ** Parsing conflict **",
indent,ap->sp->name,ap->x.stp->statenum);
break;
case SH_RESOLVED:
if( showPrecedenceConflict ){
- fprintf(fp,"%*s shift %-3d -- dropped by precedence",
+ fprintf(fp,"%*s shift %-7d -- dropped by precedence",
indent,ap->sp->name,ap->x.stp->statenum);
}else{
result = 0;
@@ -3034,7 +3079,7 @@ int PrintAction(struct action *ap, FILE *fp, int indent){
break;
case RD_RESOLVED:
if( showPrecedenceConflict ){
- fprintf(fp,"%*s reduce %-3d -- dropped by precedence",
+ fprintf(fp,"%*s reduce %-7d -- dropped by precedence",
indent,ap->sp->name,ap->x.rp->index);
}else{
result = 0;
@@ -3047,7 +3092,7 @@ int PrintAction(struct action *ap, FILE *fp, int indent){
return result;
}
-/* Generate the "y.output" log file */
+/* Generate the "*.out" log file */
void ReportOutput(struct lemon *lemp)
{
int i;
@@ -3058,7 +3103,7 @@ void ReportOutput(struct lemon *lemp)
fp = file_open(lemp,".out","wb");
if( fp==0 ) return;
- for(i=0; i<lemp->nstate; i++){
+ for(i=0; i<lemp->nxstate; i++){
stp = lemp->sorted[i];
fprintf(fp,"State %d:\n",stp->statenum);
if( lemp->basisflag ) cfp=stp->bp;
@@ -3166,10 +3211,11 @@ PRIVATE int compute_action(struct lemon *lemp, struct action *ap)
{
int act;
switch( ap->type ){
- case SHIFT: act = ap->x.stp->statenum; break;
- case REDUCE: act = ap->x.rp->index + lemp->nstate; break;
- case ERROR: act = lemp->nstate + lemp->nrule; break;
- case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break;
+ case SHIFT: act = ap->x.stp->statenum; break;
+ case SHIFTREDUCE: act = ap->x.rp->index + lemp->nstate; break;
+ case REDUCE: act = ap->x.rp->index + lemp->nstate+lemp->nrule; break;
+ case ERROR: act = lemp->nstate + lemp->nrule*2; break;
+ case ACCEPT: act = lemp->nstate + lemp->nrule*2 + 1; break;
default: act = -1; break;
}
return act;
@@ -3228,7 +3274,8 @@ PRIVATE FILE *tplt_open(struct lemon *lemp)
}
in = fopen(user_templatename,"rb");
if( in==0 ){
- fprintf(stderr,"Can't open the template file \"%s\".\n",user_templatename);
+ fprintf(stderr,"Can't open the template file \"%s\".\n",
+ user_templatename);
lemp->errorcnt++;
return 0;
}
@@ -3313,7 +3360,10 @@ void emit_destructor_code(
}else if( sp->destructor ){
cp = sp->destructor;
fprintf(out,"{\n"); (*lineno)++;
- if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,sp->destLineno,lemp->filename); }
+ if( !lemp->nolinenosflag ){
+ (*lineno)++;
+ tplt_linedir(out,sp->destLineno,lemp->filename);
+ }
}else if( lemp->vardest ){
cp = lemp->vardest;
if( cp==0 ) return;
@@ -3510,13 +3560,19 @@ PRIVATE void emit_code(
/* Generate code to do the reduce action */
if( rp->code ){
- if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,rp->line,lemp->filename); }
+ if( !lemp->nolinenosflag ){
+ (*lineno)++;
+ tplt_linedir(out,rp->line,lemp->filename);
+ }
fprintf(out,"{%s",rp->code);
for(cp=rp->code; *cp; cp++){
if( *cp=='\n' ) (*lineno)++;
} /* End loop */
fprintf(out,"}\n"); (*lineno)++;
- if (!lemp->nolinenosflag) { (*lineno)++; tplt_linedir(out,*lineno,lemp->outname); }
+ if( !lemp->nolinenosflag ){
+ (*lineno)++;
+ tplt_linedir(out,*lineno,lemp->outname);
+ }
} /* End if( rp->code ) */
return;
@@ -3647,24 +3703,32 @@ void print_stack_union(
/*
** Return the name of a C datatype able to represent values between
-** lwr and upr, inclusive.
+** lwr and upr, inclusive. If pnByte!=NULL then also write the sizeof
+** for that type (1, 2, or 4) into *pnByte.
*/
-static const char *minimum_size_type(int lwr, int upr){
+static const char *minimum_size_type(int lwr, int upr, int *pnByte){
+ const char *zType = "int";
+ int nByte = 4;
if( lwr>=0 ){
if( upr<=255 ){
- return "unsigned char";
+ zType = "unsigned char";
+ nByte = 1;
}else if( upr<65535 ){
- return "unsigned short int";
+ zType = "unsigned short int";
+ nByte = 2;
}else{
- return "unsigned int";
+ zType = "unsigned int";
+ nByte = 4;
}
}else if( lwr>=-127 && upr<=127 ){
- return "signed char";
+ zType = "signed char";
+ nByte = 1;
}else if( lwr>=-32767 && upr<32767 ){
- return "short";
- }else{
- return "int";
+ zType = "short";
+ nByte = 2;
}
+ if( pnByte ) *pnByte = nByte;
+ return zType;
}
/*
@@ -3689,7 +3753,7 @@ static int axset_compare(const void *a, const void *b){
int c;
c = p2->nAction - p1->nAction;
if( c==0 ){
- c = p2->iOrder - p1->iOrder;
+ c = p1->iOrder - p2->iOrder;
}
assert( c!=0 || p1==p2 );
return c;
@@ -3728,7 +3792,9 @@ void ReportTable(
struct action *ap;
struct rule *rp;
struct acttab *pActtab;
- int i, j, n;
+ int i, j, n, sz;
+ int szActionType; /* sizeof(YYACTIONTYPE) */
+ int szCodeType; /* sizeof(YYCODETYPE) */
const char *name;
int mnTknOfst, mxTknOfst;
int mnNtOfst, mxNtOfst;
@@ -3769,10 +3835,10 @@ void ReportTable(
/* Generate the defines */
fprintf(out,"#define YYCODETYPE %s\n",
- minimum_size_type(0, lemp->nsymbol+1)); lineno++;
+ minimum_size_type(0, lemp->nsymbol+1, &szCodeType)); lineno++;
fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++;
fprintf(out,"#define YYACTIONTYPE %s\n",
- minimum_size_type(0, lemp->nstate+lemp->nrule+5)); lineno++;
+ minimum_size_type(0,lemp->nstate+lemp->nrule*2+5,&szActionType)); lineno++;
if( lemp->wildcard ){
fprintf(out,"#define YYWILDCARD %d\n",
lemp->wildcard->index); lineno++;
@@ -3808,36 +3874,24 @@ void ReportTable(
if( mhflag ){
fprintf(out,"#endif\n"); lineno++;
}
- fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++;
- fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++;
if( lemp->errsym->useCnt ){
- fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++;
- fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++;
+ fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++;
+ fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++;
}
if( lemp->has_fallback ){
fprintf(out,"#define YYFALLBACK 1\n"); lineno++;
}
- tplt_xfer(lemp->name,in,out,&lineno);
- /* Generate the action table and its associates:
- **
- ** yy_action[] A single table containing all actions.
- ** yy_lookahead[] A table containing the lookahead for each entry in
- ** yy_action. Used to detect hash collisions.
- ** yy_shift_ofst[] For each state, the offset into yy_action for
- ** shifting terminals.
- ** yy_reduce_ofst[] For each state, the offset into yy_action for
- ** shifting non-terminals after a reduce.
- ** yy_default[] Default action for each state.
+ /* Compute the action table, but do not output it yet. The action
+ ** table must be computed before generating the YYNSTATE macro because
+ ** we need to know how many states can be eliminated.
*/
-
- /* Compute the actions on all states and count them up */
- ax = (struct axset *) calloc(lemp->nstate*2, sizeof(ax[0]));
+ ax = (struct axset *) calloc(lemp->nxstate*2, sizeof(ax[0]));
if( ax==0 ){
fprintf(stderr,"malloc failed\n");
exit(1);
}
- for(i=0; i<lemp->nstate; i++){
+ for(i=0; i<lemp->nxstate; i++){
stp = lemp->sorted[i];
ax[i*2].stp = stp;
ax[i*2].isTkn = 1;
@@ -3848,15 +3902,12 @@ void ReportTable(
}
mxTknOfst = mnTknOfst = 0;
mxNtOfst = mnNtOfst = 0;
-
- /* Compute the action table. In order to try to keep the size of the
- ** action table to a minimum, the heuristic of placing the largest action
- ** sets first is used.
- */
- for(i=0; i<lemp->nstate*2; i++) ax[i].iOrder = i;
- qsort(ax, lemp->nstate*2, sizeof(ax[0]), axset_compare);
+ /* In an effort to minimize the action table size, use the heuristic
+ ** of placing the largest action sets first */
+ for(i=0; i<lemp->nxstate*2; i++) ax[i].iOrder = i;
+ qsort(ax, lemp->nxstate*2, sizeof(ax[0]), axset_compare);
pActtab = acttab_alloc();
- for(i=0; i<lemp->nstate*2 && ax[i].nAction>0; i++){
+ for(i=0; i<lemp->nxstate*2 && ax[i].nAction>0; i++){
stp = ax[i].stp;
if( ax[i].isTkn ){
for(ap=stp->ap; ap; ap=ap->next){
@@ -3882,11 +3933,50 @@ void ReportTable(
if( stp->iNtOfst<mnNtOfst ) mnNtOfst = stp->iNtOfst;
if( stp->iNtOfst>mxNtOfst ) mxNtOfst = stp->iNtOfst;
}
+#if 0 /* Uncomment for a trace of how the yy_action[] table fills out */
+ { int jj, nn;
+ for(jj=nn=0; jj<pActtab->nAction; jj++){
+ if( pActtab->aAction[jj].action<0 ) nn++;
+ }
+ printf("%4d: State %3d %s n: %2d size: %5d freespace: %d\n",
+ i, stp->statenum, ax[i].isTkn ? "Token" : "Var ",
+ ax[i].nAction, pActtab->nAction, nn);
+ }
+#endif
}
free(ax);
+ /* Finish rendering the constants now that the action table has
+ ** been computed */
+ fprintf(out,"#define YYNSTATE %d\n",lemp->nxstate); lineno++;
+ fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++;
+ fprintf(out,"#define YY_MAX_SHIFT %d\n",lemp->nxstate-1); lineno++;
+ fprintf(out,"#define YY_MIN_SHIFTREDUCE %d\n",lemp->nstate); lineno++;
+ i = lemp->nstate + lemp->nrule;
+ fprintf(out,"#define YY_MAX_SHIFTREDUCE %d\n", i-1); lineno++;
+ fprintf(out,"#define YY_MIN_REDUCE %d\n", i); lineno++;
+ i = lemp->nstate + lemp->nrule*2;
+ fprintf(out,"#define YY_MAX_REDUCE %d\n", i-1); lineno++;
+ fprintf(out,"#define YY_ERROR_ACTION %d\n", i); lineno++;
+ fprintf(out,"#define YY_ACCEPT_ACTION %d\n", i+1); lineno++;
+ fprintf(out,"#define YY_NO_ACTION %d\n", i+2); lineno++;
+ tplt_xfer(lemp->name,in,out,&lineno);
+
+ /* Now output the action table and its associates:
+ **
+ ** yy_action[] A single table containing all actions.
+ ** yy_lookahead[] A table containing the lookahead for each entry in
+ ** yy_action. Used to detect hash collisions.
+ ** yy_shift_ofst[] For each state, the offset into yy_action for
+ ** shifting terminals.
+ ** yy_reduce_ofst[] For each state, the offset into yy_action for
+ ** shifting non-terminals after a reduce.
+ ** yy_default[] Default action for each state.
+ */
+
/* Output the yy_action table */
- n = acttab_size(pActtab);
+ lemp->nactiontab = n = acttab_size(pActtab);
+ lemp->tablesize += n*szActionType;
fprintf(out,"#define YY_ACTTAB_COUNT (%d)\n", n); lineno++;
fprintf(out,"static const YYACTIONTYPE yy_action[] = {\n"); lineno++;
for(i=j=0; i<n; i++){
@@ -3904,6 +3994,7 @@ void ReportTable(
fprintf(out, "};\n"); lineno++;
/* Output the yy_lookahead table */
+ lemp->tablesize += n*szCodeType;
fprintf(out,"static const YYCODETYPE yy_lookahead[] = {\n"); lineno++;
for(i=j=0; i<n; i++){
int la = acttab_yylookahead(pActtab, i);
@@ -3921,13 +4012,14 @@ void ReportTable(
/* Output the yy_shift_ofst[] table */
fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++;
- n = lemp->nstate;
+ n = lemp->nxstate;
while( n>0 && lemp->sorted[n-1]->iTknOfst==NO_OFFSET ) n--;
fprintf(out, "#define YY_SHIFT_COUNT (%d)\n", n-1); lineno++;
fprintf(out, "#define YY_SHIFT_MIN (%d)\n", mnTknOfst); lineno++;
fprintf(out, "#define YY_SHIFT_MAX (%d)\n", mxTknOfst); lineno++;
fprintf(out, "static const %s yy_shift_ofst[] = {\n",
- minimum_size_type(mnTknOfst-1, mxTknOfst)); lineno++;
+ minimum_size_type(mnTknOfst-1, mxTknOfst, &sz)); lineno++;
+ lemp->tablesize += n*sz;
for(i=j=0; i<n; i++){
int ofst;
stp = lemp->sorted[i];
@@ -3946,13 +4038,14 @@ void ReportTable(
/* Output the yy_reduce_ofst[] table */
fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++;
- n = lemp->nstate;
+ n = lemp->nxstate;
while( n>0 && lemp->sorted[n-1]->iNtOfst==NO_OFFSET ) n--;
fprintf(out, "#define YY_REDUCE_COUNT (%d)\n", n-1); lineno++;
fprintf(out, "#define YY_REDUCE_MIN (%d)\n", mnNtOfst); lineno++;
fprintf(out, "#define YY_REDUCE_MAX (%d)\n", mxNtOfst); lineno++;
fprintf(out, "static const %s yy_reduce_ofst[] = {\n",
- minimum_size_type(mnNtOfst-1, mxNtOfst)); lineno++;
+ minimum_size_type(mnNtOfst-1, mxNtOfst, &sz)); lineno++;
+ lemp->tablesize += n*sz;
for(i=j=0; i<n; i++){
int ofst;
stp = lemp->sorted[i];
@@ -3971,11 +4064,12 @@ void ReportTable(
/* Output the default action table */
fprintf(out, "static const YYACTIONTYPE yy_default[] = {\n"); lineno++;
- n = lemp->nstate;
+ n = lemp->nxstate;
+ lemp->tablesize += n*szActionType;
for(i=j=0; i<n; i++){
stp = lemp->sorted[i];
if( j==0 ) fprintf(out," /* %5d */ ", i);
- fprintf(out, " %4d,", stp->iDflt);
+ fprintf(out, " %4d,", stp->iDfltReduce+lemp->nstate+lemp->nrule);
if( j==9 || i==n-1 ){
fprintf(out, "\n"); lineno++;
j = 0;
@@ -3991,6 +4085,7 @@ void ReportTable(
if( lemp->has_fallback ){
int mx = lemp->nterminal - 1;
while( mx>0 && lemp->symbols[mx]->fallback==0 ){ mx--; }
+ lemp->tablesize += (mx+1)*szCodeType;
for(i=0; i<=mx; i++){
struct symbol *p = lemp->symbols[i];
if( p->fallback==0 ){
@@ -4255,6 +4350,32 @@ void CompressTables(struct lemon *lemp)
if( ap->type==REDUCE && ap->x.rp==rbest ) ap->type = NOT_USED;
}
stp->ap = Action_sort(stp->ap);
+
+ for(ap=stp->ap; ap; ap=ap->next){
+ if( ap->type==SHIFT ) break;
+ if( ap->type==REDUCE && ap->x.rp!=rbest ) break;
+ }
+ if( ap==0 ){
+ stp->autoReduce = 1;
+ stp->pDfltReduce = rbest;
+ }
+ }
+
+ /* Make a second pass over all states and actions. Convert
+ ** every action that is a SHIFT to an autoReduce state into
+ ** a SHIFTREDUCE action.
+ */
+ for(i=0; i<lemp->nstate; i++){
+ stp = lemp->sorted[i];
+ for(ap=stp->ap; ap; ap=ap->next){
+ struct state *pNextState;
+ if( ap->type!=SHIFT ) continue;
+ pNextState = ap->x.stp;
+ if( pNextState->autoReduce && pNextState->pDfltReduce!=0 ){
+ ap->type = SHIFTREDUCE;
+ ap->x.rp = pNextState->pDfltReduce;
+ }
+ }
}
}
@@ -4295,17 +4416,19 @@ void ResortStates(struct lemon *lemp)
for(i=0; i<lemp->nstate; i++){
stp = lemp->sorted[i];
stp->nTknAct = stp->nNtAct = 0;
- stp->iDflt = lemp->nstate + lemp->nrule;
+ stp->iDfltReduce = lemp->nrule; /* Init dflt action to "syntax error" */
stp->iTknOfst = NO_OFFSET;
stp->iNtOfst = NO_OFFSET;
for(ap=stp->ap; ap; ap=ap->next){
- if( compute_action(lemp,ap)>=0 ){
+ int iAction = compute_action(lemp,ap);
+ if( iAction>=0 ){
if( ap->sp->index<lemp->nterminal ){
stp->nTknAct++;
}else if( ap->sp->index<lemp->nsymbol ){
stp->nNtAct++;
}else{
- stp->iDflt = compute_action(lemp, ap);
+ assert( stp->autoReduce==0 || stp->pDfltReduce==ap->x.rp );
+ stp->iDfltReduce = iAction - lemp->nstate - lemp->nrule;
}
}
}
@@ -4315,6 +4438,10 @@ void ResortStates(struct lemon *lemp)
for(i=0; i<lemp->nstate; i++){
lemp->sorted[i]->statenum = i;
}
+ lemp->nxstate = lemp->nstate;
+ while( lemp->nxstate>1 && lemp->sorted[lemp->nxstate-1]->autoReduce ){
+ lemp->nxstate--;
+ }
}
diff --git a/lib/libsqlite3/tool/lempar.c b/lib/libsqlite3/tool/lempar.c
index fe56d2dc166..cdf4ca5a1a5 100644
--- a/lib/libsqlite3/tool/lempar.c
+++ b/lib/libsqlite3/tool/lempar.c
@@ -50,15 +50,19 @@
** ParseARG_PDECL A parameter declaration for the %extra_argument
** ParseARG_STORE Code to store %extra_argument into yypParser
** ParseARG_FETCH Code to extract %extra_argument from yypParser
-** YYNSTATE the combined number of states.
-** YYNRULE the number of rules in the grammar
** YYERRORSYMBOL is the code number of the error symbol. If not
** defined, then do no error processing.
+** YYNSTATE the combined number of states.
+** YYNRULE the number of rules in the grammar
+** YY_MAX_SHIFT Maximum value for shift actions
+** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
+** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
+** YY_MIN_REDUCE Maximum value for reduce actions
+** YY_ERROR_ACTION The yy_action[] code for syntax error
+** YY_ACCEPT_ACTION The yy_action[] code for accept
+** YY_NO_ACTION The yy_action[] code for no-op
*/
%%
-#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
-#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
-#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
/* The yyzerominor constant is used to initialize instances of
** YYMINORTYPE objects to zero. */
@@ -85,16 +89,20 @@ static const YYMINORTYPE yyzerominor = { 0 };
** Suppose the action integer is N. Then the action is determined as
** follows
**
-** 0 <= N < YYNSTATE Shift N. That is, push the lookahead
+** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead
** token onto the stack and goto state N.
**
-** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE.
+** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
+** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE.
**
-** N == YYNSTATE+YYNRULE A syntax error has occurred.
+** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
+** and YY_MAX_REDUCE
+
+** N == YY_ERROR_ACTION A syntax error has occurred.
**
-** N == YYNSTATE+YYNRULE+1 The parser accepts its input.
+** N == YY_ACCEPT_ACTION The parser accepts its input.
**
-** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused
+** N == YY_NO_ACTION No such action. Denotes unused
** slots in the yy_action[] table.
**
** The action table is constructed as a single large table named yy_action[].
@@ -153,9 +161,13 @@ static const YYCODETYPE yyFallback[] = {
** + The semantic value stored at this level of the stack. This is
** the information used by the action routines in the grammar.
** It is sometimes called the "minor" token.
+**
+** After the "shift" half of a SHIFTREDUCE action, the stateno field
+** actually contains the reduce action for the second half of the
+** SHIFTREDUCE.
*/
struct yyStackEntry {
- YYACTIONTYPE stateno; /* The state-number */
+ YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */
YYCODETYPE major; /* The major token value. This is the code
** number for the token at this stack level */
YYMINORTYPE minor; /* The user-supplied minor token value. This
@@ -384,11 +396,11 @@ static int yy_find_shift_action(
){
int i;
int stateno = pParser->yystack[pParser->yyidx].stateno;
-
- if( stateno>YY_SHIFT_COUNT
- || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){
- return yy_default[stateno];
- }
+
+ if( stateno>=YY_MIN_REDUCE ) return stateno;
+ assert( stateno <= YY_SHIFT_COUNT );
+ i = yy_shift_ofst[stateno];
+ if( i==YY_SHIFT_USE_DFLT ) return yy_default[stateno];
assert( iLookAhead!=YYNOCODE );
i += iLookAhead;
if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
@@ -489,7 +501,29 @@ static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
}
/*
-** Perform a shift action.
+** Print tracing information for a SHIFT action
+*/
+#ifndef NDEBUG
+static void yyTraceShift(yyParser *yypParser, int yyNewState){
+ if( yyTraceFILE ){
+ int i;
+ if( yyNewState<YYNSTATE ){
+ fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
+ fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
+ for(i=1; i<=yypParser->yyidx; i++)
+ fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
+ fprintf(yyTraceFILE,"\n");
+ }else{
+ fprintf(yyTraceFILE,"%sShift *\n",yyTracePrompt);
+ }
+ }
+}
+#else
+# define yyTraceShift(X,Y)
+#endif
+
+/*
+** Perform a shift action. Return the number of errors.
*/
static void yy_shift(
yyParser *yypParser, /* The parser to be shifted */
@@ -522,16 +556,7 @@ static void yy_shift(
yytos->stateno = (YYACTIONTYPE)yyNewState;
yytos->major = (YYCODETYPE)yyMajor;
yytos->minor = *yypMinor;
-#ifndef NDEBUG
- if( yyTraceFILE && yypParser->yyidx>0 ){
- int i;
- fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
- fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
- for(i=1; i<=yypParser->yyidx; i++)
- fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
- fprintf(yyTraceFILE,"\n");
- }
-#endif
+ yyTraceShift(yypParser, yyNewState);
}
/* The following table contains information about every rule that
@@ -564,8 +589,9 @@ static void yy_reduce(
#ifndef NDEBUG
if( yyTraceFILE && yyruleno>=0
&& yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
- fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
- yyRuleName[yyruleno]);
+ yysize = yyRuleInfo[yyruleno].nrhs;
+ fprintf(yyTraceFILE, "%sReduce [%s] -> state %d.\n", yyTracePrompt,
+ yyRuleName[yyruleno], yymsp[-yysize].stateno);
}
#endif /* NDEBUG */
@@ -602,9 +628,9 @@ static void yy_reduce(
yysize = yyRuleInfo[yyruleno].nrhs;
yypParser->yyidx -= yysize;
yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto);
- if( yyact < YYNSTATE ){
-#ifdef NDEBUG
- /* If we are not debugging and the reduce action popped at least
+ if( yyact <= YY_MAX_SHIFTREDUCE ){
+ if( yyact>YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
+ /* If the reduce action popped at least
** one element off the stack, then we can push the new element back
** onto the stack here, and skip the stack overflow test in yy_shift().
** That gives a significant speed improvement. */
@@ -614,13 +640,12 @@ static void yy_reduce(
yymsp->stateno = (YYACTIONTYPE)yyact;
yymsp->major = (YYCODETYPE)yygoto;
yymsp->minor = yygotominor;
- }else
-#endif
- {
+ yyTraceShift(yypParser, yyact);
+ }else{
yy_shift(yypParser,yyact,yygoto,&yygotominor);
}
}else{
- assert( yyact == YYNSTATE + YYNRULE + 1 );
+ assert( yyact == YY_ACCEPT_ACTION );
yy_accept(yypParser);
}
}
@@ -740,13 +765,13 @@ void Parse(
do{
yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
- if( yyact<YYNSTATE ){
- assert( !yyendofinput ); /* Impossible to shift the $ token */
+ if( yyact <= YY_MAX_SHIFTREDUCE ){
+ if( yyact > YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
yy_shift(yypParser,yyact,yymajor,&yyminorunion);
yypParser->yyerrcnt--;
yymajor = YYNOCODE;
- }else if( yyact < YYNSTATE + YYNRULE ){
- yy_reduce(yypParser,yyact-YYNSTATE);
+ }else if( yyact <= YY_MAX_REDUCE ){
+ yy_reduce(yypParser,yyact-YY_MIN_REDUCE);
}else{
assert( yyact == YY_ERROR_ACTION );
#ifdef YYERRORSYMBOL
@@ -796,7 +821,7 @@ void Parse(
yymx != YYERRORSYMBOL &&
(yyact = yy_find_reduce_action(
yypParser->yystack[yypParser->yyidx].stateno,
- YYERRORSYMBOL)) >= YYNSTATE
+ YYERRORSYMBOL)) >= YY_MIN_REDUCE
){
yy_pop_parser_stack(yypParser);
}
@@ -846,5 +871,10 @@ void Parse(
#endif
}
}while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sReturn\n",yyTracePrompt);
+ }
+#endif
return;
}
diff --git a/lib/libsqlite3/tool/mksqlite3c.tcl b/lib/libsqlite3/tool/mksqlite3c.tcl
index e52b036612a..23241e27a6e 100644
--- a/lib/libsqlite3/tool/mksqlite3c.tcl
+++ b/lib/libsqlite3/tool/mksqlite3c.tcl
@@ -211,7 +211,8 @@ proc copy_file {filename} {
puts $out "#if 0"
} elseif {!$linemacros && [regexp {^#line} $line]} {
# Skip #line directives.
- } elseif {$addstatic && ![regexp {^(static|typedef)} $line]} {
+ } elseif {$addstatic
+ && ![regexp {^(static|typedef|SQLITE_PRIVATE)} $line]} {
# Skip adding the SQLITE_PRIVATE or SQLITE_API keyword before
# functions if this header file does not need it.
if {![info exists varonly_hdr($tail)]
@@ -378,6 +379,8 @@ foreach file {
fts3_icu.c
sqlite3rbu.c
dbstat.c
+ json1.c
+ fts5.c
} {
copy_file tsrc/$file
}
diff --git a/lib/libsqlite3/tool/mksqlite3h.tcl b/lib/libsqlite3/tool/mksqlite3h.tcl
index f68f61a3689..3f59aef4675 100644
--- a/lib/libsqlite3/tool/mksqlite3h.tcl
+++ b/lib/libsqlite3/tool/mksqlite3h.tcl
@@ -63,14 +63,33 @@ close $in
# Set up patterns for recognizing API declarations.
#
set varpattern {^[a-zA-Z][a-zA-Z_0-9 *]+sqlite3_[_a-zA-Z0-9]+(\[|;| =)}
-set declpattern {^ *[a-zA-Z][a-zA-Z_0-9 ]+ \**sqlite3_[_a-zA-Z0-9]+\(}
+set declpattern {^ *([a-zA-Z][a-zA-Z_0-9 ]+ \**)(sqlite3_[_a-zA-Z0-9]+)(\(.*)$}
# Force the output to use unix line endings, even on Windows.
fconfigure stdout -translation lf
-# Process the src/sqlite.h.in ext/rtree/sqlite3rtree.h files.
+set filelist [subst {
+ $TOP/src/sqlite.h.in
+ $TOP/ext/rtree/sqlite3rtree.h
+ $TOP/ext/fts5/fts5.h
+}]
+
+# These are the functions that accept a variable number of arguments. They
+# always need to use the "cdecl" calling convention even when another calling
+# convention (e.g. "stcall") is being used for the rest of the library.
+set cdecllist {
+ sqlite3_config
+ sqlite3_db_config
+ sqlite3_log
+ sqlite3_mprintf
+ sqlite3_snprintf
+ sqlite3_test_control
+ sqlite3_vtab_config
+}
+
+# Process the source files.
#
-foreach file [list $TOP/src/sqlite.h.in $TOP/ext/rtree/sqlite3rtree.h] {
+foreach file $filelist {
set in [open $file]
while {![eof $in]} {
@@ -79,26 +98,28 @@ foreach file [list $TOP/src/sqlite.h.in $TOP/ext/rtree/sqlite3rtree.h] {
# File sqlite3rtree.h contains a line "#include <sqlite3.h>". Omit this
# line when copying sqlite3rtree.h into sqlite3.h.
#
- if {[string match {*#include*<sqlite3.h>*} $line]} continue
+ if {[string match {*#include*[<"]sqlite3.h[>"]*} $line]} continue
regsub -- --VERS-- $line $zVersion line
regsub -- --VERSION-NUMBER-- $line $nVersion line
regsub -- --SOURCE-ID-- $line "$zDate $zUuid" line
-
- if {[regexp {define SQLITE_EXTERN extern} $line]} {
- puts $line
- puts [gets $in]
- puts ""
- puts "#ifndef SQLITE_API"
- puts "# define SQLITE_API"
- puts "#endif"
- set line ""
- }
-
- if {([regexp $varpattern $line] && ![regexp {^ *typedef} $line])
- || ([regexp $declpattern $line])
- } {
+
+ if {[regexp $varpattern $line] && ![regexp {^ *typedef} $line]} {
set line "SQLITE_API $line"
+ } else {
+ if {[regexp $declpattern $line all rettype funcname rest]} {
+ set line SQLITE_API
+ append line " " [string trim $rettype]
+ if {[string index $rettype end] ne "*"} {
+ append line " "
+ }
+ if {[lsearch -exact $cdecllist $funcname] >= 0} {
+ append line SQLITE_CDECL
+ } else {
+ append line SQLITE_STDCALL
+ }
+ append line " " $funcname $rest
+ }
}
puts $line
}
diff --git a/lib/libsqlite3/tool/mkvsix.tcl b/lib/libsqlite3/tool/mkvsix.tcl
index 15ae2b0d3ae..b7c5983dbe2 100644
--- a/lib/libsqlite3/tool/mkvsix.tcl
+++ b/lib/libsqlite3/tool/mkvsix.tcl
@@ -261,12 +261,9 @@ proc getExtraFileListXmlChunk { packageFlavor vsVersion } {
"\r\n " {DependsOn="Microsoft.VCLibs, version=12.0"}]
}
2015 {
- #
- # TODO: Is the ".AppLocal" suffix always needed here?
- #
return [appendArgs \
"\r\n " AppliesTo=\" $appliesTo \" \
- "\r\n " {DependsOn="Microsoft.VCLibs.AppLocal, version=14.0"}]
+ "\r\n " {DependsOn="Microsoft.VCLibs, version=14.0"}]
}
default {
return ""
diff --git a/lib/libsqlite3/tool/showdb.c b/lib/libsqlite3/tool/showdb.c
index c90f410d95f..892cacd5520 100644
--- a/lib/libsqlite3/tool/showdb.c
+++ b/lib/libsqlite3/tool/showdb.c
@@ -15,13 +15,20 @@
#include <stdlib.h>
#include <string.h>
+#include <assert.h>
#include "sqlite3.h"
-static int pagesize = 1024; /* Size of a database page */
-static int db = -1; /* File descriptor for reading the DB */
-static int mxPage = 0; /* Last page number */
-static int perLine = 16; /* HEX elements to print per line */
+static struct GlobalData {
+ int pagesize; /* Size of a database page */
+ int dbfd; /* File descriptor for reading the DB */
+ int mxPage; /* Last page number */
+ int perLine; /* HEX elements to print per line */
+ int bRaw; /* True to access db file via OS APIs */
+ sqlite3_file *pFd; /* File descriptor for non-raw mode */
+ sqlite3 *pDb; /* Database handle that owns pFd */
+} g = {1024, -1, 0, 16, 0, 0, 0};
+
typedef long long int i64; /* Datatype for 64-bit integers */
@@ -57,24 +64,122 @@ static void out_of_memory(void){
}
/*
+** Open a database connection.
+*/
+static sqlite3 *openDatabase(const char *zPrg, const char *zName){
+ sqlite3 *db = 0;
+ int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI;
+ int rc = sqlite3_open_v2(zName, &db, flags, 0);
+ if( rc!=SQLITE_OK ){
+ const char *zErr = sqlite3_errmsg(db);
+ fprintf(stderr, "%s: can't open %s (%s)\n", zPrg, zName, zErr);
+ sqlite3_close(db);
+ exit(1);
+ }
+ return db;
+}
+
+/**************************************************************************
+** Beginning of low-level file access functions.
+**
+** All low-level access to the database file read by this program is
+** performed using the following four functions:
+**
+** fileOpen() - open the db file
+** fileClose() - close the db file
+** fileRead() - read raw data from the db file
+** fileGetsize() - return the size of the db file in bytes
+*/
+
+/*
+** Open the database file.
+*/
+static void fileOpen(const char *zPrg, const char *zName){
+ assert( g.dbfd<0 );
+ if( g.bRaw==0 ){
+ int rc;
+ void *pArg = (void *)(&g.pFd);
+ g.pDb = openDatabase(zPrg, zName);
+ rc = sqlite3_file_control(g.pDb, "main", SQLITE_FCNTL_FILE_POINTER, pArg);
+ if( rc!=SQLITE_OK ){
+ fprintf(stderr,
+ "%s: failed to obtain fd for %s (SQLite too old?)\n", zPrg, zName
+ );
+ exit(1);
+ }
+ }else{
+ g.dbfd = open(zName, O_RDONLY);
+ if( g.dbfd<0 ){
+ fprintf(stderr,"%s: can't open %s\n", zPrg, zName);
+ exit(1);
+ }
+ }
+}
+
+/*
+** Close the database file opened by fileOpen()
+*/
+static void fileClose(){
+ if( g.bRaw==0 ){
+ sqlite3_close(g.pDb);
+ g.pDb = 0;
+ g.pFd = 0;
+ }else{
+ close(g.dbfd);
+ g.dbfd = -1;
+ }
+}
+
+/*
** Read content from the file.
**
-** Space to hold the content is obtained from malloc() and needs to be
-** freed by the caller.
+** Space to hold the content is obtained from sqlite3_malloc() and needs
+** to be freed by the caller.
*/
-static unsigned char *getContent(int ofst, int nByte){
+static unsigned char *fileRead(sqlite3_int64 ofst, int nByte){
unsigned char *aData;
int got;
- aData = malloc(nByte+32);
+ aData = sqlite3_malloc(nByte+32);
if( aData==0 ) out_of_memory();
memset(aData, 0, nByte+32);
- lseek(db, ofst, SEEK_SET);
- got = read(db, aData, nByte);
- if( got>0 && got<nByte ) memset(aData+got, 0, nByte-got);
+ if( g.bRaw==0 ){
+ int rc = g.pFd->pMethods->xRead(g.pFd, (void*)aData, nByte, ofst);
+ if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){
+ fprintf(stderr, "error in xRead() - %d\n", rc);
+ exit(1);
+ }
+ }else{
+ lseek(g.dbfd, ofst, SEEK_SET);
+ got = read(g.dbfd, aData, nByte);
+ if( got>0 && got<nByte ) memset(aData+got, 0, nByte-got);
+ }
return aData;
}
/*
+** Return the size of the file in byte.
+*/
+static sqlite3_int64 fileGetsize(void){
+ sqlite3_int64 res = 0;
+ if( g.bRaw==0 ){
+ int rc = g.pFd->pMethods->xFileSize(g.pFd, &res);
+ if( rc!=SQLITE_OK ){
+ fprintf(stderr, "error in xFileSize() - %d\n", rc);
+ exit(1);
+ }
+ }else{
+ struct stat sbuf;
+ fstat(g.dbfd, &sbuf);
+ res = (sqlite3_int64)(sbuf.st_size);
+ }
+ return res;
+}
+
+/*
+** End of low-level file access functions.
+**************************************************************************/
+
+/*
** Print a range of bytes as hex and as ascii.
*/
static unsigned char *print_byte_range(
@@ -98,17 +203,17 @@ static unsigned char *print_byte_range(
zOfstFmt = " %08x: ";
}
- aData = getContent(ofst, nByte);
- for(i=0; i<nByte; i += perLine){
+ aData = fileRead(ofst, nByte);
+ for(i=0; i<nByte; i += g.perLine){
fprintf(stdout, zOfstFmt, i+printOfst);
- for(j=0; j<perLine; j++){
+ for(j=0; j<g.perLine; j++){
if( i+j>nByte ){
fprintf(stdout, " ");
}else{
fprintf(stdout,"%02x ", aData[i+j]);
}
}
- for(j=0; j<perLine; j++){
+ for(j=0; j<g.perLine; j++){
if( i+j>nByte ){
fprintf(stdout, " ");
}else{
@@ -126,11 +231,11 @@ static unsigned char *print_byte_range(
static void print_page(int iPg){
int iStart;
unsigned char *aData;
- iStart = (iPg-1)*pagesize;
+ iStart = (iPg-1)*g.pagesize;
fprintf(stdout, "Page %d: (offsets 0x%x..0x%x)\n",
- iPg, iStart, iStart+pagesize-1);
- aData = print_byte_range(iStart, pagesize, 0);
- free(aData);
+ iPg, iStart, iStart+g.pagesize-1);
+ aData = print_byte_range(iStart, g.pagesize, 0);
+ sqlite3_free(aData);
}
@@ -267,14 +372,14 @@ static i64 localPayload(i64 nPayload, char cType){
i64 nLocal;
if( cType==13 ){
/* Table leaf */
- maxLocal = pagesize-35;
- minLocal = (pagesize-12)*32/255-23;
+ maxLocal = g.pagesize-35;
+ minLocal = (g.pagesize-12)*32/255-23;
}else{
- maxLocal = (pagesize-12)*64/255-23;
- minLocal = (pagesize-12)*32/255-23;
+ maxLocal = (g.pagesize-12)*64/255-23;
+ minLocal = (g.pagesize-12)*32/255-23;
}
if( nPayload>maxLocal ){
- surplus = minLocal + (nPayload-minLocal)%(pagesize-4);
+ surplus = minLocal + (nPayload-minLocal)%(g.pagesize-4);
if( surplus<=maxLocal ){
nLocal = surplus;
}else{
@@ -581,8 +686,8 @@ static void decode_btree_page(
printf(" key: lx=left-child n=payload-size r=rowid\n");
}
if( showMap ){
- zMap = malloc(pagesize);
- memset(zMap, '.', pagesize);
+ zMap = sqlite3_malloc(g.pagesize);
+ memset(zMap, '.', g.pagesize);
memset(zMap, '1', hdrSize);
memset(&zMap[hdrSize], 'H', iCellPtr);
memset(&zMap[hdrSize+iCellPtr], 'P', 2*nCell);
@@ -611,10 +716,10 @@ static void decode_btree_page(
}
if( showMap ){
printf("Page map: (H=header P=cell-index 1=page-1-header .=free-space)\n");
- for(i=0; i<pagesize; i+=64){
+ for(i=0; i<g.pagesize; i+=64){
printf(" %03x: %.64s\n", i, &zMap[i]);
}
- free(zMap);
+ sqlite3_free(zMap);
}
}
@@ -623,14 +728,13 @@ static void decode_btree_page(
*/
static void decode_trunk_page(
int pgno, /* The page number */
- int pagesize, /* Size of each page */
int detail, /* Show leaf pages if true */
int recursive /* Follow the trunk change if true */
){
int n, i;
unsigned char *a;
while( pgno>0 ){
- a = getContent((pgno-1)*pagesize, pagesize);
+ a = fileRead((pgno-1)*g.pagesize, g.pagesize);
printf("Decode of freelist trunk page %d:\n", pgno);
print_decode_line(a, 0, 4, "Next freelist trunk page");
print_decode_line(a, 4, 4, "Number of entries on this page");
@@ -650,7 +754,7 @@ static void decode_trunk_page(
}else{
pgno = (int)decodeInt32(&a[0]);
}
- free(a);
+ sqlite3_free(a);
}
}
@@ -669,9 +773,9 @@ static void page_usage_msg(int pgno, const char *zFormat, ...){
va_start(ap, zFormat);
zMsg = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
- if( pgno<=0 || pgno>mxPage ){
+ if( pgno<=0 || pgno>g.mxPage ){
printf("ERROR: page %d out of range 1..%d: %s\n",
- pgno, mxPage, zMsg);
+ pgno, g.mxPage, zMsg);
sqlite3_free(zMsg);
return;
}
@@ -719,12 +823,12 @@ static void page_usage_cell(
if( nLocal<nPayload ){
int ovfl = decodeInt32(a+nLocal);
int cnt = 0;
- while( ovfl && (cnt++)<mxPage ){
+ while( ovfl && (cnt++)<g.mxPage ){
page_usage_msg(ovfl, "overflow %d from cell %d of page %d",
cnt, cellno, pgno);
- a = getContent((ovfl-1)*pagesize, 4);
+ a = fileRead((ovfl-1)*g.pagesize, 4);
ovfl = decodeInt32(a);
- free(a);
+ sqlite3_free(a);
}
}
}
@@ -745,8 +849,8 @@ static void page_usage_btree(
int i;
int hdr = pgno==1 ? 100 : 0;
- if( pgno<=0 || pgno>mxPage ) return;
- a = getContent((pgno-1)*pagesize, pagesize);
+ if( pgno<=0 || pgno>g.mxPage ) return;
+ a = fileRead((pgno-1)*g.pagesize, g.pagesize);
switch( a[hdr] ){
case 2: zType = "interior node of index"; break;
case 5: zType = "interior node of table"; break;
@@ -783,7 +887,7 @@ static void page_usage_btree(
page_usage_cell(a[hdr], a+ofst, pgno, i);
}
}
- free(a);
+ sqlite3_free(a);
}
/*
@@ -797,9 +901,9 @@ static void page_usage_freelist(int pgno){
int iNext;
int parent = 1;
- while( pgno>0 && pgno<=mxPage && (cnt++)<mxPage ){
+ while( pgno>0 && pgno<=g.mxPage && (cnt++)<g.mxPage ){
page_usage_msg(pgno, "freelist trunk #%d child of %d", cnt, parent);
- a = getContent((pgno-1)*pagesize, pagesize);
+ a = fileRead((pgno-1)*g.pagesize, g.pagesize);
iNext = decodeInt32(a);
n = decodeInt32(a+4);
for(i=0; i<n; i++){
@@ -807,7 +911,7 @@ static void page_usage_freelist(int pgno){
page_usage_msg(child, "freelist leaf, child %d of trunk page %d",
i, pgno);
}
- free(a);
+ sqlite3_free(a);
parent = pgno;
pgno = iNext;
}
@@ -818,10 +922,10 @@ static void page_usage_freelist(int pgno){
*/
static void page_usage_ptrmap(unsigned char *a){
if( a[55] ){
- int usable = pagesize - a[20];
+ int usable = g.pagesize - a[20];
int pgno = 2;
int perPage = usable/5;
- while( pgno<=mxPage ){
+ while( pgno<=g.mxPage ){
page_usage_msg(pgno, "PTRMAP page covering %d..%d",
pgno+1, pgno+perPage);
pgno += perPage + 1;
@@ -832,7 +936,7 @@ static void page_usage_ptrmap(unsigned char *a){
/*
** Try to figure out how every page in the database file is being used.
*/
-static void page_usage_report(const char *zDbName){
+static void page_usage_report(const char *zPrg, const char *zDbName){
int i, j;
int rc;
sqlite3 *db;
@@ -841,30 +945,25 @@ static void page_usage_report(const char *zDbName){
char zQuery[200];
/* Avoid the pathological case */
- if( mxPage<1 ){
+ if( g.mxPage<1 ){
printf("empty database\n");
return;
}
/* Open the database file */
- rc = sqlite3_open(zDbName, &db);
- if( rc ){
- printf("cannot open database: %s\n", sqlite3_errmsg(db));
- sqlite3_close(db);
- return;
- }
+ db = openDatabase(zPrg, zDbName);
- /* Set up global variables zPageUse[] and mxPage to record page
+ /* Set up global variables zPageUse[] and g.mxPage to record page
** usages */
- zPageUse = sqlite3_malloc( sizeof(zPageUse[0])*(mxPage+1) );
+ zPageUse = sqlite3_malloc( sizeof(zPageUse[0])*(g.mxPage+1) );
if( zPageUse==0 ) out_of_memory();
- memset(zPageUse, 0, sizeof(zPageUse[0])*(mxPage+1));
+ memset(zPageUse, 0, sizeof(zPageUse[0])*(g.mxPage+1));
/* Discover the usage of each page */
- a = getContent(0, 100);
+ a = fileRead(0, 100);
page_usage_freelist(decodeInt32(a+32));
page_usage_ptrmap(a);
- free(a);
+ sqlite3_free(a);
page_usage_btree(1, 0, 0, "sqlite_master");
sqlite3_exec(db, "PRAGMA writable_schema=ON", 0, 0, 0);
for(j=0; j<2; j++){
@@ -886,7 +985,7 @@ static void page_usage_report(const char *zDbName){
sqlite3_close(db);
/* Print the report and free memory used */
- for(i=1; i<=mxPage; i++){
+ for(i=1; i<=g.mxPage; i++){
printf("%5d: %s\n", i, zPageUse[i] ? zPageUse[i] : "???");
sqlite3_free(zPageUse[i]);
}
@@ -906,26 +1005,26 @@ static void ptrmap_coverage_report(const char *zDbName){
int i;
/* Avoid the pathological case */
- if( mxPage<1 ){
+ if( g.mxPage<1 ){
printf("empty database\n");
return;
}
/* Make sure PTRMAPs are used in this database */
- aHdr = getContent(0, 100);
+ aHdr = fileRead(0, 100);
if( aHdr[55]==0 ){
printf("database does not use PTRMAP pages\n");
return;
}
- usable = pagesize - aHdr[20];
+ usable = g.pagesize - aHdr[20];
perPage = usable/5;
- free(aHdr);
+ sqlite3_free(aHdr);
printf("%5d: root of sqlite_master\n", 1);
- for(pgno=2; pgno<=mxPage; pgno += perPage+1){
+ for(pgno=2; pgno<=g.mxPage; pgno += perPage+1){
printf("%5d: PTRMAP page covering %d..%d\n", pgno,
pgno+1, pgno+perPage);
- a = getContent((pgno-1)*pagesize, usable);
- for(i=0; i+5<=usable && pgno+1+i/5<=mxPage; i+=5){
+ a = fileRead((pgno-1)*g.pagesize, usable);
+ for(i=0; i+5<=usable && pgno+1+i/5<=g.mxPage; i+=5){
const char *zType = "???";
unsigned int iFrom = decodeInt32(&a[i+1]);
switch( a[i] ){
@@ -937,7 +1036,7 @@ static void ptrmap_coverage_report(const char *zDbName){
}
printf("%5d: %s, parent=%u\n", pgno+1+i/5, zType, iFrom);
}
- free(a);
+ sqlite3_free(a);
}
}
@@ -945,8 +1044,10 @@ static void ptrmap_coverage_report(const char *zDbName){
** Print a usage comment
*/
static void usage(const char *argv0){
- fprintf(stderr, "Usage %s FILENAME ?args...?\n\n", argv0);
+ fprintf(stderr, "Usage %s ?--uri? FILENAME ?args...?\n\n", argv0);
fprintf(stderr,
+ "switches:\n"
+ " --raw Read db file directly, bypassing SQLite VFS\n"
"args:\n"
" dbheader Show database header\n"
" pgidx Index of how each page is used\n"
@@ -964,58 +1065,71 @@ static void usage(const char *argv0){
}
int main(int argc, char **argv){
- struct stat sbuf;
- unsigned char zPgSz[2];
- if( argc<2 ){
- usage(argv[0]);
- exit(1);
+ sqlite3_int64 szFile;
+ unsigned char *zPgSz;
+ const char *zPrg = argv[0]; /* Name of this executable */
+ char **azArg = argv;
+ int nArg = argc;
+
+ /* Check for the "--uri" or "-uri" switch. */
+ if( nArg>1 ){
+ if( sqlite3_stricmp("-raw", azArg[1])==0
+ || sqlite3_stricmp("--raw", azArg[1])==0
+ ){
+ g.bRaw = 1;
+ azArg++;
+ nArg--;
+ }
}
- db = open(argv[1], O_RDONLY);
- if( db<0 ){
- fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]);
+
+ if( nArg<2 ){
+ usage(zPrg);
exit(1);
}
- zPgSz[0] = 0;
- zPgSz[1] = 0;
- lseek(db, 16, SEEK_SET);
- if( read(db, zPgSz, 2)<2 ) memset(zPgSz, 0, 2);
- pagesize = zPgSz[0]*256 + zPgSz[1]*65536;
- if( pagesize==0 ) pagesize = 1024;
- printf("Pagesize: %d\n", pagesize);
- fstat(db, &sbuf);
- mxPage = (sbuf.st_size+pagesize-1)/pagesize;
- printf("Available pages: 1..%d\n", mxPage);
- if( argc==2 ){
+
+ fileOpen(zPrg, azArg[1]);
+ szFile = fileGetsize();
+
+ zPgSz = fileRead(16, 2);
+ g.pagesize = zPgSz[0]*256 + zPgSz[1]*65536;
+ if( g.pagesize==0 ) g.pagesize = 1024;
+ sqlite3_free(zPgSz);
+
+ printf("Pagesize: %d\n", g.pagesize);
+ g.mxPage = (szFile+g.pagesize-1)/g.pagesize;
+
+ printf("Available pages: 1..%d\n", g.mxPage);
+ if( nArg==2 ){
int i;
- for(i=1; i<=mxPage; i++) print_page(i);
+ for(i=1; i<=g.mxPage; i++) print_page(i);
}else{
int i;
- for(i=2; i<argc; i++){
+ for(i=2; i<nArg; i++){
int iStart, iEnd;
char *zLeft;
- if( strcmp(argv[i], "dbheader")==0 ){
+ if( strcmp(azArg[i], "dbheader")==0 ){
print_db_header();
continue;
}
- if( strcmp(argv[i], "pgidx")==0 ){
- page_usage_report(argv[1]);
+ if( strcmp(azArg[i], "pgidx")==0 ){
+ page_usage_report(zPrg, azArg[1]);
continue;
}
- if( strcmp(argv[i], "ptrmap")==0 ){
- ptrmap_coverage_report(argv[1]);
+ if( strcmp(azArg[i], "ptrmap")==0 ){
+ ptrmap_coverage_report(azArg[1]);
continue;
}
- if( strcmp(argv[i], "help")==0 ){
- usage(argv[0]);
+ if( strcmp(azArg[i], "help")==0 ){
+ usage(zPrg);
continue;
}
- if( !isdigit(argv[i][0]) ){
- fprintf(stderr, "%s: unknown option: [%s]\n", argv[0], argv[i]);
+ if( !isdigit(azArg[i][0]) ){
+ fprintf(stderr, "%s: unknown option: [%s]\n", zPrg, azArg[i]);
continue;
}
- iStart = strtol(argv[i], &zLeft, 0);
+ iStart = strtol(azArg[i], &zLeft, 0);
if( zLeft && strcmp(zLeft,"..end")==0 ){
- iEnd = mxPage;
+ iEnd = g.mxPage;
}else if( zLeft && zLeft[0]=='.' && zLeft[1]=='.' ){
iEnd = strtol(&zLeft[2], 0, 0);
}else if( zLeft && zLeft[0]=='b' ){
@@ -1023,15 +1137,15 @@ int main(int argc, char **argv){
unsigned char *a;
if( iStart==1 ){
ofst = hdrSize = 100;
- nByte = pagesize-100;
+ nByte = g.pagesize-100;
}else{
hdrSize = 0;
- ofst = (iStart-1)*pagesize;
- nByte = pagesize;
+ ofst = (iStart-1)*g.pagesize;
+ nByte = g.pagesize;
}
- a = getContent(ofst, nByte);
+ a = fileRead(ofst, nByte);
decode_btree_page(a, iStart, hdrSize, &zLeft[1]);
- free(a);
+ sqlite3_free(a);
continue;
}else if( zLeft && zLeft[0]=='t' ){
int detail = 0;
@@ -1041,15 +1155,15 @@ int main(int argc, char **argv){
if( zLeft[i]=='r' ) recursive = 1;
if( zLeft[i]=='d' ) detail = 1;
}
- decode_trunk_page(iStart, pagesize, detail, recursive);
+ decode_trunk_page(iStart, detail, recursive);
continue;
}else{
iEnd = iStart;
}
- if( iStart<1 || iEnd<iStart || iEnd>mxPage ){
+ if( iStart<1 || iEnd<iStart || iEnd>g.mxPage ){
fprintf(stderr,
"Page argument should be LOWER?..UPPER?. Range 1 to %d\n",
- mxPage);
+ g.mxPage);
exit(1);
}
while( iStart<=iEnd ){
@@ -1058,6 +1172,6 @@ int main(int argc, char **argv){
}
}
}
- close(db);
+ fileClose();
return 0;
}
diff --git a/lib/libsqlite3/tool/spaceanal.tcl b/lib/libsqlite3/tool/spaceanal.tcl
index cd3785bd7d1..38d954162e4 100644
--- a/lib/libsqlite3/tool/spaceanal.tcl
+++ b/lib/libsqlite3/tool/spaceanal.tcl
@@ -26,7 +26,19 @@ proc is_without_rowid {tname} {
#
proc usage {} {
set argv0 [file rootname [file tail [info nameofexecutable]]]
- puts stderr "Usage: $argv0 database-name"
+ puts stderr "Usage: $argv0 ?--pageinfo? ?--stats? database-filename"
+ puts stderr {
+Analyze the SQLite3 database file specified by the "database-filename"
+argument and output a report detailing size and storage efficiency
+information for the database and its constituent tables and indexes.
+
+Options:
+
+ --stats Output SQL text that creates a new database containing
+ statistics about the database that was analyzed
+
+ --pageinfo Show how each page of the database-file is used
+}
exit 1
}
set file_to_analyze {}
@@ -142,6 +154,7 @@ set tabledef {CREATE TABLE space_used(
is_index boolean, -- TRUE if it is an index, false for a table
nentry int, -- Number of entries in the BTree
leaf_entries int, -- Number of leaf entries
+ depth int, -- Depth of the b-tree
payload int, -- Total amount of data stored in this table or index
ovfl_payload int, -- Total amount of data stored on overflow pages
ovfl_cnt int, -- Number of entries that use overflow
@@ -164,22 +177,9 @@ db eval {CREATE TEMP TABLE dbstat AS SELECT * FROM temp.stat
ORDER BY name, path}
db eval {DROP TABLE temp.stat}
-proc isleaf {pagetype is_index} {
- return [expr {$pagetype == "leaf" || ($pagetype == "internal" && $is_index)}]
-}
-proc isoverflow {pagetype is_index} {
- return [expr {$pagetype == "overflow"}]
-}
-proc isinternal {pagetype is_index} {
- return [expr {$pagetype == "internal" && $is_index==0}]
-}
-
-db func isleaf isleaf
-db func isinternal isinternal
-db func isoverflow isoverflow
-
set isCompressed 0
set compressOverhead 0
+set depth 0
set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 }
foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
@@ -188,18 +188,20 @@ foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
db eval {
SELECT
sum(ncell) AS nentry,
- sum(isleaf(pagetype, $idx_btree) * ncell) AS leaf_entries,
+ sum((pagetype=='leaf')*ncell) AS leaf_entries,
sum(payload) AS payload,
- sum(isoverflow(pagetype, $idx_btree) * payload) AS ovfl_payload,
+ sum((pagetype=='overflow') * payload) AS ovfl_payload,
sum(path LIKE '%+000000') AS ovfl_cnt,
max(mx_payload) AS mx_payload,
- sum(isinternal(pagetype, $idx_btree)) AS int_pages,
- sum(isleaf(pagetype, $idx_btree)) AS leaf_pages,
- sum(isoverflow(pagetype, $idx_btree)) AS ovfl_pages,
- sum(isinternal(pagetype, $idx_btree) * unused) AS int_unused,
- sum(isleaf(pagetype, $idx_btree) * unused) AS leaf_unused,
- sum(isoverflow(pagetype, $idx_btree) * unused) AS ovfl_unused,
- sum(pgsize) AS compressed_size
+ sum(pagetype=='internal') AS int_pages,
+ sum(pagetype=='leaf') AS leaf_pages,
+ sum(pagetype=='overflow') AS ovfl_pages,
+ sum((pagetype=='internal') * unused) AS int_unused,
+ sum((pagetype=='leaf') * unused) AS leaf_unused,
+ sum((pagetype=='overflow') * unused) AS ovfl_unused,
+ sum(pgsize) AS compressed_size,
+ max((length(CASE WHEN path LIKE '%+%' THEN '' ELSE path END)+3)/4)
+ AS depth
FROM temp.dbstat WHERE name = $name
} break
@@ -235,6 +237,7 @@ foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
$is_index,
$nentry,
$leaf_entries,
+ $depth,
$payload,
$ovfl_payload,
$ovfl_cnt,
@@ -344,7 +347,9 @@ proc subreport {title where showFrag} {
int(sum(int_unused)) AS int_unused,
int(sum(ovfl_unused)) AS ovfl_unused,
int(sum(gap_cnt)) AS gap_cnt,
- int(sum(compressed_size)) AS compressed_size
+ int(sum(compressed_size)) AS compressed_size,
+ int(max(depth)) AS depth,
+ count(*) AS cnt
FROM space_used WHERE $where" {} {}
# Output the sub-report title, nicely decorated with * characters.
@@ -381,7 +386,7 @@ proc subreport {title where showFrag} {
"]
set avg_fanout [mem eval "
SELECT (sum(leaf_pages+int_pages)-$nTab)/sum(int_pages) FROM space_used
- WHERE $where AND is_index = 0
+ WHERE $where
"]
set avg_fanout [format %.2f $avg_fanout]
}
@@ -399,6 +404,7 @@ proc subreport {title where showFrag} {
statline {Bytes used after compression} $compressed_size $pct
}
statline {Bytes of payload} $payload $payload_percent
+ if {$cnt==1} {statline {B-tree depth} $depth}
statline {Average payload per entry} $avg_payload
statline {Average unused bytes per entry} $avg_unused
if {[info exists avg_fanout]} {
diff --git a/lib/libsqlite3/tool/sqldiff.c b/lib/libsqlite3/tool/sqldiff.c
index 6d72303545c..9f0b705c40a 100644
--- a/lib/libsqlite3/tool/sqldiff.c
+++ b/lib/libsqlite3/tool/sqldiff.c
@@ -23,6 +23,7 @@
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
+#include <assert.h>
#include "sqlite3.h"
/*
@@ -259,7 +260,12 @@ static void namelistFree(char **az){
** CREATE TABLE t5(rowid,_rowid_,oid);
** az = 0 // The rowid is not accessible
*/
-static char **columnNames(const char *zDb, const char *zTab, int *pnPKey){
+static char **columnNames(
+ const char *zDb, /* Database ("main" or "aux") to query */
+ const char *zTab, /* Name of table to return details of */
+ int *pnPKey, /* OUT: Number of PK columns */
+ int *pbRowid /* OUT: True if PK is an implicit rowid */
+){
char **az = 0; /* List of column names to be returned */
int naz = 0; /* Number of entries in az[] */
sqlite3_stmt *pStmt; /* SQL statement being run */
@@ -338,6 +344,15 @@ static char **columnNames(const char *zDb, const char *zTab, int *pnPKey){
}
sqlite3_finalize(pStmt);
if( az ) az[naz] = 0;
+
+ /* If it is non-NULL, set *pbRowid to indicate whether or not the PK of
+ ** this table is an implicit rowid (*pbRowid==1) or not (*pbRowid==0). */
+ if( pbRowid ) *pbRowid = (az[0]==0);
+
+ /* If this table has an implicit rowid for a PK, figure out how to refer
+ ** to it. There are three options - "rowid", "_rowid_" and "oid". Any
+ ** of these will work, unless the table has an explicit column of the
+ ** same name. */
if( az[0]==0 ){
const char *azRowid[] = { "rowid", "_rowid_", "oid" };
for(i=0; i<sizeof(azRowid)/sizeof(azRowid[0]); i++){
@@ -434,7 +449,7 @@ static void dump_table(const char *zTab, FILE *out){
}
sqlite3_finalize(pStmt);
if( !g.bSchemaOnly ){
- az = columnNames("aux", zTab, &nPk);
+ az = columnNames("aux", zTab, &nPk, 0);
strInit(&ins);
if( az==0 ){
pStmt = db_prepare("SELECT * FROM aux.%s", zId);
@@ -511,7 +526,7 @@ static void diff_one_table(const char *zTab, FILE *out){
** database and show the results. This is used for testing
** and debugging of the columnNames() function.
*/
- az = columnNames("aux",zTab, &nPk);
+ az = columnNames("aux",zTab, &nPk, 0);
if( az==0 ){
printf("Rowid not accessible for %s\n", zId);
}else{
@@ -540,8 +555,8 @@ static void diff_one_table(const char *zTab, FILE *out){
goto end_diff_one_table;
}
- az = columnNames("main", zTab, &nPk);
- az2 = columnNames("aux", zTab, &nPk2);
+ az = columnNames("main", zTab, &nPk, 0);
+ az2 = columnNames("aux", zTab, &nPk2, 0);
if( az && az2 ){
for(n=0; az[n]; n++){
if( sqlite3_stricmp(az[n],az2[n])!=0 ) break;
@@ -720,6 +735,606 @@ end_diff_one_table:
}
/*
+** Check that table zTab exists and has the same schema in both the "main"
+** and "aux" databases currently opened by the global db handle. If they
+** do not, output an error message on stderr and exit(1). Otherwise, if
+** the schemas do match, return control to the caller.
+*/
+static void checkSchemasMatch(const char *zTab){
+ sqlite3_stmt *pStmt = db_prepare(
+ "SELECT A.sql=B.sql FROM main.sqlite_master A, aux.sqlite_master B"
+ " WHERE A.name=%Q AND B.name=%Q", zTab, zTab
+ );
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ if( sqlite3_column_int(pStmt,0)==0 ){
+ runtimeError("schema changes for table %s", safeId(zTab));
+ }
+ }else{
+ runtimeError("table %s missing from one or both databases", safeId(zTab));
+ }
+ sqlite3_finalize(pStmt);
+}
+
+/**************************************************************************
+** The following code is copied from fossil. It is used to generate the
+** fossil delta blobs sometimes used in RBU update records.
+*/
+
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned char u8;
+
+/*
+** The width of a hash window in bytes. The algorithm only works if this
+** is a power of 2.
+*/
+#define NHASH 16
+
+/*
+** The current state of the rolling hash.
+**
+** z[] holds the values that have been hashed. z[] is a circular buffer.
+** z[i] is the first entry and z[(i+NHASH-1)%NHASH] is the last entry of
+** the window.
+**
+** Hash.a is the sum of all elements of hash.z[]. Hash.b is a weighted
+** sum. Hash.b is z[i]*NHASH + z[i+1]*(NHASH-1) + ... + z[i+NHASH-1]*1.
+** (Each index for z[] should be module NHASH, of course. The %NHASH operator
+** is omitted in the prior expression for brevity.)
+*/
+typedef struct hash hash;
+struct hash {
+ u16 a, b; /* Hash values */
+ u16 i; /* Start of the hash window */
+ char z[NHASH]; /* The values that have been hashed */
+};
+
+/*
+** Initialize the rolling hash using the first NHASH characters of z[]
+*/
+static void hash_init(hash *pHash, const char *z){
+ u16 a, b, i;
+ a = b = 0;
+ for(i=0; i<NHASH; i++){
+ a += z[i];
+ b += (NHASH-i)*z[i];
+ pHash->z[i] = z[i];
+ }
+ pHash->a = a & 0xffff;
+ pHash->b = b & 0xffff;
+ pHash->i = 0;
+}
+
+/*
+** Advance the rolling hash by a single character "c"
+*/
+static void hash_next(hash *pHash, int c){
+ u16 old = pHash->z[pHash->i];
+ pHash->z[pHash->i] = (char)c;
+ pHash->i = (pHash->i+1)&(NHASH-1);
+ pHash->a = pHash->a - old + (char)c;
+ pHash->b = pHash->b - NHASH*old + pHash->a;
+}
+
+/*
+** Return a 32-bit hash value
+*/
+static u32 hash_32bit(hash *pHash){
+ return (pHash->a & 0xffff) | (((u32)(pHash->b & 0xffff))<<16);
+}
+
+/*
+** Write an base-64 integer into the given buffer.
+*/
+static void putInt(unsigned int v, char **pz){
+ static const char zDigits[] =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
+ /* 123456789 123456789 123456789 123456789 123456789 123456789 123 */
+ int i, j;
+ char zBuf[20];
+ if( v==0 ){
+ *(*pz)++ = '0';
+ return;
+ }
+ for(i=0; v>0; i++, v>>=6){
+ zBuf[i] = zDigits[v&0x3f];
+ }
+ for(j=i-1; j>=0; j--){
+ *(*pz)++ = zBuf[j];
+ }
+}
+
+/*
+** Return the number digits in the base-64 representation of a positive integer
+*/
+static int digit_count(int v){
+ unsigned int i, x;
+ for(i=1, x=64; (unsigned int)v>=x; i++, x <<= 6){}
+ return i;
+}
+
+/*
+** Compute a 32-bit checksum on the N-byte buffer. Return the result.
+*/
+static unsigned int checksum(const char *zIn, size_t N){
+ const unsigned char *z = (const unsigned char *)zIn;
+ unsigned sum0 = 0;
+ unsigned sum1 = 0;
+ unsigned sum2 = 0;
+ unsigned sum3 = 0;
+ while(N >= 16){
+ sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]);
+ sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]);
+ sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]);
+ sum3 += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
+ z += 16;
+ N -= 16;
+ }
+ while(N >= 4){
+ sum0 += z[0];
+ sum1 += z[1];
+ sum2 += z[2];
+ sum3 += z[3];
+ z += 4;
+ N -= 4;
+ }
+ sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
+ switch(N){
+ case 3: sum3 += (z[2] << 8);
+ case 2: sum3 += (z[1] << 16);
+ case 1: sum3 += (z[0] << 24);
+ default: ;
+ }
+ return sum3;
+}
+
+/*
+** Create a new delta.
+**
+** The delta is written into a preallocated buffer, zDelta, which
+** should be at least 60 bytes longer than the target file, zOut.
+** The delta string will be NUL-terminated, but it might also contain
+** embedded NUL characters if either the zSrc or zOut files are
+** binary. This function returns the length of the delta string
+** in bytes, excluding the final NUL terminator character.
+**
+** Output Format:
+**
+** The delta begins with a base64 number followed by a newline. This
+** number is the number of bytes in the TARGET file. Thus, given a
+** delta file z, a program can compute the size of the output file
+** simply by reading the first line and decoding the base-64 number
+** found there. The delta_output_size() routine does exactly this.
+**
+** After the initial size number, the delta consists of a series of
+** literal text segments and commands to copy from the SOURCE file.
+** A copy command looks like this:
+**
+** NNN@MMM,
+**
+** where NNN is the number of bytes to be copied and MMM is the offset
+** into the source file of the first byte (both base-64). If NNN is 0
+** it means copy the rest of the input file. Literal text is like this:
+**
+** NNN:TTTTT
+**
+** where NNN is the number of bytes of text (base-64) and TTTTT is the text.
+**
+** The last term is of the form
+**
+** NNN;
+**
+** In this case, NNN is a 32-bit bigendian checksum of the output file
+** that can be used to verify that the delta applied correctly. All
+** numbers are in base-64.
+**
+** Pure text files generate a pure text delta. Binary files generate a
+** delta that may contain some binary data.
+**
+** Algorithm:
+**
+** The encoder first builds a hash table to help it find matching
+** patterns in the source file. 16-byte chunks of the source file
+** sampled at evenly spaced intervals are used to populate the hash
+** table.
+**
+** Next we begin scanning the target file using a sliding 16-byte
+** window. The hash of the 16-byte window in the target is used to
+** search for a matching section in the source file. When a match
+** is found, a copy command is added to the delta. An effort is
+** made to extend the matching section to regions that come before
+** and after the 16-byte hash window. A copy command is only issued
+** if the result would use less space that just quoting the text
+** literally. Literal text is added to the delta for sections that
+** do not match or which can not be encoded efficiently using copy
+** commands.
+*/
+static int rbuDeltaCreate(
+ const char *zSrc, /* The source or pattern file */
+ unsigned int lenSrc, /* Length of the source file */
+ const char *zOut, /* The target file */
+ unsigned int lenOut, /* Length of the target file */
+ char *zDelta /* Write the delta into this buffer */
+){
+ unsigned int i, base;
+ char *zOrigDelta = zDelta;
+ hash h;
+ int nHash; /* Number of hash table entries */
+ int *landmark; /* Primary hash table */
+ int *collide; /* Collision chain */
+ int lastRead = -1; /* Last byte of zSrc read by a COPY command */
+
+ /* Add the target file size to the beginning of the delta
+ */
+ putInt(lenOut, &zDelta);
+ *(zDelta++) = '\n';
+
+ /* If the source file is very small, it means that we have no
+ ** chance of ever doing a copy command. Just output a single
+ ** literal segment for the entire target and exit.
+ */
+ if( lenSrc<=NHASH ){
+ putInt(lenOut, &zDelta);
+ *(zDelta++) = ':';
+ memcpy(zDelta, zOut, lenOut);
+ zDelta += lenOut;
+ putInt(checksum(zOut, lenOut), &zDelta);
+ *(zDelta++) = ';';
+ return zDelta - zOrigDelta;
+ }
+
+ /* Compute the hash table used to locate matching sections in the
+ ** source file.
+ */
+ nHash = lenSrc/NHASH;
+ collide = sqlite3_malloc( nHash*2*sizeof(int) );
+ landmark = &collide[nHash];
+ memset(landmark, -1, nHash*sizeof(int));
+ memset(collide, -1, nHash*sizeof(int));
+ for(i=0; i<lenSrc-NHASH; i+=NHASH){
+ int hv;
+ hash_init(&h, &zSrc[i]);
+ hv = hash_32bit(&h) % nHash;
+ collide[i/NHASH] = landmark[hv];
+ landmark[hv] = i/NHASH;
+ }
+
+ /* Begin scanning the target file and generating copy commands and
+ ** literal sections of the delta.
+ */
+ base = 0; /* We have already generated everything before zOut[base] */
+ while( base+NHASH<lenOut ){
+ int iSrc, iBlock;
+ int bestCnt, bestOfst=0, bestLitsz=0;
+ hash_init(&h, &zOut[base]);
+ i = 0; /* Trying to match a landmark against zOut[base+i] */
+ bestCnt = 0;
+ while( 1 ){
+ int hv;
+ int limit = 250;
+
+ hv = hash_32bit(&h) % nHash;
+ iBlock = landmark[hv];
+ while( iBlock>=0 && (limit--)>0 ){
+ /*
+ ** The hash window has identified a potential match against
+ ** landmark block iBlock. But we need to investigate further.
+ **
+ ** Look for a region in zOut that matches zSrc. Anchor the search
+ ** at zSrc[iSrc] and zOut[base+i]. Do not include anything prior to
+ ** zOut[base] or after zOut[outLen] nor anything after zSrc[srcLen].
+ **
+ ** Set cnt equal to the length of the match and set ofst so that
+ ** zSrc[ofst] is the first element of the match. litsz is the number
+ ** of characters between zOut[base] and the beginning of the match.
+ ** sz will be the overhead (in bytes) needed to encode the copy
+ ** command. Only generate copy command if the overhead of the
+ ** copy command is less than the amount of literal text to be copied.
+ */
+ int cnt, ofst, litsz;
+ int j, k, x, y;
+ int sz;
+
+ /* Beginning at iSrc, match forwards as far as we can. j counts
+ ** the number of characters that match */
+ iSrc = iBlock*NHASH;
+ for(
+ j=0, x=iSrc, y=base+i;
+ (unsigned int)x<lenSrc && (unsigned int)y<lenOut;
+ j++, x++, y++
+ ){
+ if( zSrc[x]!=zOut[y] ) break;
+ }
+ j--;
+
+ /* Beginning at iSrc-1, match backwards as far as we can. k counts
+ ** the number of characters that match */
+ for(k=1; k<iSrc && (unsigned int)k<=i; k++){
+ if( zSrc[iSrc-k]!=zOut[base+i-k] ) break;
+ }
+ k--;
+
+ /* Compute the offset and size of the matching region */
+ ofst = iSrc-k;
+ cnt = j+k+1;
+ litsz = i-k; /* Number of bytes of literal text before the copy */
+ /* sz will hold the number of bytes needed to encode the "insert"
+ ** command and the copy command, not counting the "insert" text */
+ sz = digit_count(i-k)+digit_count(cnt)+digit_count(ofst)+3;
+ if( cnt>=sz && cnt>bestCnt ){
+ /* Remember this match only if it is the best so far and it
+ ** does not increase the file size */
+ bestCnt = cnt;
+ bestOfst = iSrc-k;
+ bestLitsz = litsz;
+ }
+
+ /* Check the next matching block */
+ iBlock = collide[iBlock];
+ }
+
+ /* We have a copy command that does not cause the delta to be larger
+ ** than a literal insert. So add the copy command to the delta.
+ */
+ if( bestCnt>0 ){
+ if( bestLitsz>0 ){
+ /* Add an insert command before the copy */
+ putInt(bestLitsz,&zDelta);
+ *(zDelta++) = ':';
+ memcpy(zDelta, &zOut[base], bestLitsz);
+ zDelta += bestLitsz;
+ base += bestLitsz;
+ }
+ base += bestCnt;
+ putInt(bestCnt, &zDelta);
+ *(zDelta++) = '@';
+ putInt(bestOfst, &zDelta);
+ *(zDelta++) = ',';
+ if( bestOfst + bestCnt -1 > lastRead ){
+ lastRead = bestOfst + bestCnt - 1;
+ }
+ bestCnt = 0;
+ break;
+ }
+
+ /* If we reach this point, it means no match is found so far */
+ if( base+i+NHASH>=lenOut ){
+ /* We have reached the end of the file and have not found any
+ ** matches. Do an "insert" for everything that does not match */
+ putInt(lenOut-base, &zDelta);
+ *(zDelta++) = ':';
+ memcpy(zDelta, &zOut[base], lenOut-base);
+ zDelta += lenOut-base;
+ base = lenOut;
+ break;
+ }
+
+ /* Advance the hash by one character. Keep looking for a match */
+ hash_next(&h, zOut[base+i+NHASH]);
+ i++;
+ }
+ }
+ /* Output a final "insert" record to get all the text at the end of
+ ** the file that does not match anything in the source file.
+ */
+ if( base<lenOut ){
+ putInt(lenOut-base, &zDelta);
+ *(zDelta++) = ':';
+ memcpy(zDelta, &zOut[base], lenOut-base);
+ zDelta += lenOut-base;
+ }
+ /* Output the final checksum record. */
+ putInt(checksum(zOut, lenOut), &zDelta);
+ *(zDelta++) = ';';
+ sqlite3_free(collide);
+ return zDelta - zOrigDelta;
+}
+
+/*
+** End of code copied from fossil.
+**************************************************************************/
+
+static void strPrintfArray(
+ Str *pStr, /* String object to append to */
+ const char *zSep, /* Separator string */
+ const char *zFmt, /* Format for each entry */
+ char **az, int n /* Array of strings & its size (or -1) */
+){
+ int i;
+ for(i=0; az[i] && (i<n || n<0); i++){
+ if( i!=0 ) strPrintf(pStr, "%s", zSep);
+ strPrintf(pStr, zFmt, az[i], az[i], az[i]);
+ }
+}
+
+static void getRbudiffQuery(
+ const char *zTab,
+ char **azCol,
+ int nPK,
+ int bOtaRowid,
+ Str *pSql
+){
+ int i;
+
+ /* First the newly inserted rows: **/
+ strPrintf(pSql, "SELECT ");
+ strPrintfArray(pSql, ", ", "%s", azCol, -1);
+ strPrintf(pSql, ", 0, "); /* Set ota_control to 0 for an insert */
+ strPrintfArray(pSql, ", ", "NULL", azCol, -1);
+ strPrintf(pSql, " FROM aux.%Q AS n WHERE NOT EXISTS (\n", zTab);
+ strPrintf(pSql, " SELECT 1 FROM ", zTab);
+ strPrintf(pSql, " main.%Q AS o WHERE ", zTab);
+ strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK);
+ strPrintf(pSql, "\n)");
+
+ /* Deleted rows: */
+ strPrintf(pSql, "\nUNION ALL\nSELECT ");
+ strPrintfArray(pSql, ", ", "%s", azCol, nPK);
+ if( azCol[nPK] ){
+ strPrintf(pSql, ", ");
+ strPrintfArray(pSql, ", ", "NULL", &azCol[nPK], -1);
+ }
+ strPrintf(pSql, ", 1, "); /* Set ota_control to 1 for a delete */
+ strPrintfArray(pSql, ", ", "NULL", azCol, -1);
+ strPrintf(pSql, " FROM main.%Q AS n WHERE NOT EXISTS (\n", zTab);
+ strPrintf(pSql, " SELECT 1 FROM ", zTab);
+ strPrintf(pSql, " aux.%Q AS o WHERE ", zTab);
+ strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK);
+ strPrintf(pSql, "\n) ");
+
+ /* Updated rows. If all table columns are part of the primary key, there
+ ** can be no updates. In this case this part of the compound SELECT can
+ ** be omitted altogether. */
+ if( azCol[nPK] ){
+ strPrintf(pSql, "\nUNION ALL\nSELECT ");
+ strPrintfArray(pSql, ", ", "n.%s", azCol, nPK);
+ strPrintf(pSql, ",\n");
+ strPrintfArray(pSql, " ,\n",
+ " CASE WHEN n.%s IS o.%s THEN NULL ELSE n.%s END", &azCol[nPK], -1
+ );
+
+ if( bOtaRowid==0 ){
+ strPrintf(pSql, ", '");
+ strPrintfArray(pSql, "", ".", azCol, nPK);
+ strPrintf(pSql, "' ||\n");
+ }else{
+ strPrintf(pSql, ",\n");
+ }
+ strPrintfArray(pSql, " ||\n",
+ " CASE WHEN n.%s IS o.%s THEN '.' ELSE 'x' END", &azCol[nPK], -1
+ );
+ strPrintf(pSql, "\nAS ota_control, ");
+ strPrintfArray(pSql, ", ", "NULL", azCol, nPK);
+ strPrintf(pSql, ",\n");
+ strPrintfArray(pSql, " ,\n",
+ " CASE WHEN n.%s IS o.%s THEN NULL ELSE o.%s END", &azCol[nPK], -1
+ );
+
+ strPrintf(pSql, "\nFROM main.%Q AS o, aux.%Q AS n\nWHERE ", zTab, zTab);
+ strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK);
+ strPrintf(pSql, " AND ota_control LIKE '%%x%%'");
+ }
+
+ /* Now add an ORDER BY clause to sort everything by PK. */
+ strPrintf(pSql, "\nORDER BY ");
+ for(i=1; i<=nPK; i++) strPrintf(pSql, "%s%d", ((i>1)?", ":""), i);
+}
+
+static void rbudiff_one_table(const char *zTab, FILE *out){
+ int bOtaRowid; /* True to use an ota_rowid column */
+ int nPK; /* Number of primary key columns in table */
+ char **azCol; /* NULL terminated array of col names */
+ int i;
+ int nCol;
+ Str ct = {0, 0, 0}; /* The "CREATE TABLE data_xxx" statement */
+ Str sql = {0, 0, 0}; /* Query to find differences */
+ Str insert = {0, 0, 0}; /* First part of output INSERT statement */
+ sqlite3_stmt *pStmt = 0;
+
+ /* --rbu mode must use real primary keys. */
+ g.bSchemaPK = 1;
+
+ /* Check that the schemas of the two tables match. Exit early otherwise. */
+ checkSchemasMatch(zTab);
+
+ /* Grab the column names and PK details for the table(s). If no usable PK
+ ** columns are found, bail out early. */
+ azCol = columnNames("main", zTab, &nPK, &bOtaRowid);
+ if( azCol==0 ){
+ runtimeError("table %s has no usable PK columns", zTab);
+ }
+ for(nCol=0; azCol[nCol]; nCol++);
+
+ /* Build and output the CREATE TABLE statement for the data_xxx table */
+ strPrintf(&ct, "CREATE TABLE IF NOT EXISTS 'data_%q'(", zTab);
+ if( bOtaRowid ) strPrintf(&ct, "rbu_rowid, ");
+ strPrintfArray(&ct, ", ", "%s", &azCol[bOtaRowid], -1);
+ strPrintf(&ct, ", rbu_control);");
+
+ /* Get the SQL for the query to retrieve data from the two databases */
+ getRbudiffQuery(zTab, azCol, nPK, bOtaRowid, &sql);
+
+ /* Build the first part of the INSERT statement output for each row
+ ** in the data_xxx table. */
+ strPrintf(&insert, "INSERT INTO 'data_%q' (", zTab);
+ if( bOtaRowid ) strPrintf(&insert, "rbu_rowid, ");
+ strPrintfArray(&insert, ", ", "%s", &azCol[bOtaRowid], -1);
+ strPrintf(&insert, ", rbu_control) VALUES(");
+
+ pStmt = db_prepare("%s", sql.z);
+
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+
+ /* If this is the first row output, print out the CREATE TABLE
+ ** statement first. And then set ct.z to NULL so that it is not
+ ** printed again. */
+ if( ct.z ){
+ fprintf(out, "%s\n", ct.z);
+ strFree(&ct);
+ }
+
+ /* Output the first part of the INSERT statement */
+ fprintf(out, "%s", insert.z);
+
+ if( sqlite3_column_type(pStmt, nCol)==SQLITE_INTEGER ){
+ for(i=0; i<=nCol; i++){
+ if( i>0 ) fprintf(out, ", ");
+ printQuoted(out, sqlite3_column_value(pStmt, i));
+ }
+ }else{
+ char *zOtaControl;
+ int nOtaControl = sqlite3_column_bytes(pStmt, nCol);
+
+ zOtaControl = (char*)sqlite3_malloc(nOtaControl);
+ memcpy(zOtaControl, sqlite3_column_text(pStmt, nCol), nOtaControl+1);
+
+ for(i=0; i<nCol; i++){
+ int bDone = 0;
+ if( i>=nPK
+ && sqlite3_column_type(pStmt, i)==SQLITE_BLOB
+ && sqlite3_column_type(pStmt, nCol+1+i)==SQLITE_BLOB
+ ){
+ const char *aSrc = sqlite3_column_blob(pStmt, nCol+1+i);
+ int nSrc = sqlite3_column_bytes(pStmt, nCol+1+i);
+ const char *aFinal = sqlite3_column_blob(pStmt, i);
+ int nFinal = sqlite3_column_bytes(pStmt, i);
+ char *aDelta;
+ int nDelta;
+
+ aDelta = sqlite3_malloc(nFinal + 60);
+ nDelta = rbuDeltaCreate(aSrc, nSrc, aFinal, nFinal, aDelta);
+ if( nDelta<nFinal ){
+ int j;
+ fprintf(out, "x'");
+ for(j=0; j<nDelta; j++) fprintf(out, "%02x", (u8)aDelta[j]);
+ fprintf(out, "'");
+ zOtaControl[i-bOtaRowid] = 'f';
+ bDone = 1;
+ }
+ sqlite3_free(aDelta);
+ }
+
+ if( bDone==0 ){
+ printQuoted(out, sqlite3_column_value(pStmt, i));
+ }
+ fprintf(out, ", ");
+ }
+ fprintf(out, "'%s'", zOtaControl);
+ sqlite3_free(zOtaControl);
+ }
+
+ /* And the closing bracket of the insert statement */
+ fprintf(out, ");\n");
+ }
+
+ sqlite3_finalize(pStmt);
+
+ strFree(&ct);
+ strFree(&sql);
+ strFree(&insert);
+}
+
+/*
** Display a summary of differences between two versions of the same
** table table.
**
@@ -760,8 +1375,8 @@ static void summarize_one_table(const char *zTab, FILE *out){
goto end_summarize_one_table;
}
- az = columnNames("main", zTab, &nPk);
- az2 = columnNames("aux", zTab, &nPk2);
+ az = columnNames("main", zTab, &nPk, 0);
+ az2 = columnNames("aux", zTab, &nPk2, 0);
if( az && az2 ){
for(n=0; az[n]; n++){
if( sqlite3_stricmp(az[n],az2[n])!=0 ) break;
@@ -931,18 +1546,9 @@ static void changeset_one_table(const char *zTab, FILE *out){
int i, k; /* Loop counters */
const char *zSep; /* List separator */
- pStmt = db_prepare(
- "SELECT A.sql=B.sql FROM main.sqlite_master A, aux.sqlite_master B"
- " WHERE A.name=%Q AND B.name=%Q", zTab, zTab
- );
- if( SQLITE_ROW==sqlite3_step(pStmt) ){
- if( sqlite3_column_int(pStmt,0)==0 ){
- runtimeError("schema changes for table %s", safeId(zTab));
- }
- }else{
- runtimeError("table %s missing from one or both databases", safeId(zTab));
- }
- sqlite3_finalize(pStmt);
+ /* Check that the schemas of the two tables match. Exit early otherwise. */
+ checkSchemasMatch(zTab);
+
pStmt = db_prepare("PRAGMA main.table_info=%Q", zTab);
while( SQLITE_ROW==sqlite3_step(pStmt) ){
nCol++;
@@ -1118,6 +1724,7 @@ static void showHelp(void){
" --changeset FILE Write a CHANGESET into FILE\n"
" -L|--lib LIBRARY Load an SQLite extension library\n"
" --primarykey Use schema-defined PRIMARY KEYs\n"
+" --rbu Output SQL to create/populate RBU table(s)\n"
" --schema Show only differences in the schema\n"
" --summary Show only a summary of the differences\n"
" --table TAB Show only differences in table TAB\n"
@@ -1170,6 +1777,9 @@ int main(int argc, char **argv){
if( strcmp(z,"primarykey")==0 ){
g.bSchemaPK = 1;
}else
+ if( strcmp(z,"rbu")==0 ){
+ xDiff = rbudiff_one_table;
+ }else
if( strcmp(z,"schema")==0 ){
g.bSchemaOnly = 1;
}else
diff --git a/lib/libsqlite3/tool/tostr.awk b/lib/libsqlite3/tool/tostr.awk
index b4f48d3db55..83c6cc1a50a 100644
--- a/lib/libsqlite3/tool/tostr.awk
+++ b/lib/libsqlite3/tool/tostr.awk
@@ -3,6 +3,7 @@
# Convert input text into a C string
#
{
+ gsub(/\\/,"\\\\");
gsub(/\"/,"\\\"");
print "\"" $0 "\\n\"";
}