diff options
| author | Scott MacVicar <scottmac@php.net> | 2008-03-07 10:55:14 +0000 |
|---|---|---|
| committer | Scott MacVicar <scottmac@php.net> | 2008-03-07 10:55:14 +0000 |
| commit | 31dade5280849135b00fd1c5e53d057732a72776 (patch) | |
| tree | 564b9f0f9d8cf89d7df9a9c12147ba8a5da6506f /ext/pdo_sqlite/sqlite/tool | |
| parent | 7abf0787ad9fd613ddde880c9bc163161d7bf4ff (diff) | |
| download | php-git-31dade5280849135b00fd1c5e53d057732a72776.tar.gz | |
MFB: Update bundled SQLite to 3.5.6
Diffstat (limited to 'ext/pdo_sqlite/sqlite/tool')
| -rw-r--r-- | ext/pdo_sqlite/sqlite/tool/fragck.tcl | 149 | ||||
| -rw-r--r-- | ext/pdo_sqlite/sqlite/tool/lemon.c | 295 | ||||
| -rw-r--r-- | ext/pdo_sqlite/sqlite/tool/lempar.c | 147 | ||||
| -rw-r--r-- | ext/pdo_sqlite/sqlite/tool/mkkeywordhash.c | 120 | ||||
| -rw-r--r-- | ext/pdo_sqlite/sqlite/tool/mksqlite3c.tcl | 278 | ||||
| -rw-r--r-- | ext/pdo_sqlite/sqlite/tool/mksqlite3internalh.tcl | 145 | ||||
| -rw-r--r-- | ext/pdo_sqlite/sqlite/tool/omittest.tcl | 176 | ||||
| -rw-r--r-- | ext/pdo_sqlite/sqlite/tool/soak1.tcl | 103 | ||||
| -rw-r--r-- | ext/pdo_sqlite/sqlite/tool/spaceanal.tcl | 65 |
9 files changed, 1264 insertions, 214 deletions
diff --git a/ext/pdo_sqlite/sqlite/tool/fragck.tcl b/ext/pdo_sqlite/sqlite/tool/fragck.tcl new file mode 100644 index 0000000000..35e76f482b --- /dev/null +++ b/ext/pdo_sqlite/sqlite/tool/fragck.tcl @@ -0,0 +1,149 @@ +# Run this TCL script using "testfixture" to get a report that shows +# the sequence of database pages used by a particular table or index. +# This information is used for fragmentation analysis. +# + +# Get the name of the database to analyze +# + +if {[llength $argv]!=2} { + puts stderr "Usage: $argv0 database-name table-or-index-name" + exit 1 +} +set file_to_analyze [lindex $argv 0] +if {![file exists $file_to_analyze]} { + puts stderr "No such file: $file_to_analyze" + exit 1 +} +if {![file readable $file_to_analyze]} { + puts stderr "File is not readable: $file_to_analyze" + exit 1 +} +if {[file size $file_to_analyze]<512} { + puts stderr "Empty or malformed database: $file_to_analyze" + exit 1 +} +set objname [lindex $argv 1] + +# Open the database +# +sqlite3 db [lindex $argv 0] +set DB [btree_open [lindex $argv 0] 1000 0] + +# This proc is a wrapper around the btree_cursor_info command. The +# second argument is an open btree cursor returned by [btree_cursor]. +# The first argument is the name of an array variable that exists in +# the scope of the caller. If the third argument is non-zero, then +# info is returned for the page that lies $up entries upwards in the +# tree-structure. (i.e. $up==1 returns the parent page, $up==2 the +# grandparent etc.) +# +# The following entries in that array are filled in with information retrieved +# using [btree_cursor_info]: +# +# $arrayvar(page_no) = The page number +# $arrayvar(entry_no) = The entry number +# $arrayvar(page_entries) = Total number of entries on this page +# $arrayvar(cell_size) = Cell size (local payload + header) +# $arrayvar(page_freebytes) = Number of free bytes on this page +# $arrayvar(page_freeblocks) = Number of free blocks on the page +# $arrayvar(payload_bytes) = Total payload size (local + overflow) +# $arrayvar(header_bytes) = Header size in bytes +# $arrayvar(local_payload_bytes) = Local payload size +# $arrayvar(parent) = Parent page number +# +proc cursor_info {arrayvar csr {up 0}} { + upvar $arrayvar a + foreach [list a(page_no) \ + a(entry_no) \ + a(page_entries) \ + a(cell_size) \ + a(page_freebytes) \ + a(page_freeblocks) \ + a(payload_bytes) \ + a(header_bytes) \ + a(local_payload_bytes) \ + a(parent) \ + a(first_ovfl) ] [btree_cursor_info $csr $up] break +} + +# Determine the page-size of the database. This global variable is used +# throughout the script. +# +set pageSize [db eval {PRAGMA page_size}] + +# Find the root page of table or index to be analyzed. Also find out +# if the object is a table or an index. +# +if {$objname=="sqlite_master"} { + set rootpage 1 + set type table +} else { + db eval { + SELECT rootpage, type FROM sqlite_master + WHERE name=$objname + } break + if {![info exists rootpage]} { + puts stderr "no such table or index: $objname" + exit 1 + } + if {$type!="table" && $type!="index"} { + puts stderr "$objname is something other than a table or index" + exit 1 + } + if {![string is integer -strict $rootpage]} { + puts stderr "invalid root page for $objname: $rootpage" + exit 1 + } +} + +# The cursor $csr is pointing to an entry. Print out information +# about the page that $up levels above that page that contains +# the entry. If $up==0 use the page that contains the entry. +# +# If information about the page has been printed already, then +# this is a no-op. +# +proc page_info {csr up} { + global seen + cursor_info ci $csr $up + set pg $ci(page_no) + if {[info exists seen($pg)]} return + set seen($pg) 1 + + # Do parent pages first + # + if {$ci(parent)} { + page_info $csr [expr {$up+1}] + } + + # Find the depth of this page + # + set depth 1 + set i $up + while {$ci(parent)} { + incr i + incr depth + cursor_info ci $csr $i + } + + # print the results + # + puts [format {LEVEL %d: %6d} $depth $pg] +} + + + + +# Loop through the object and print out page numbers +# +set csr [btree_cursor $DB $rootpage 0] +for {btree_first $csr} {![btree_eof $csr]} {btree_next $csr} { + page_info $csr 0 + set i 1 + foreach pg [btree_ovfl_info $DB $csr] { + puts [format {OVFL %3d: %6d} $i $pg] + incr i + } +} +exit 0 diff --git a/ext/pdo_sqlite/sqlite/tool/lemon.c b/ext/pdo_sqlite/sqlite/tool/lemon.c index 759e1c3786..e613f77581 100644 --- a/ext/pdo_sqlite/sqlite/tool/lemon.c +++ b/ext/pdo_sqlite/sqlite/tool/lemon.c @@ -11,6 +11,7 @@ #include <string.h> #include <ctype.h> #include <stdlib.h> +#include <assert.h> #ifndef __WIN32__ # if defined(_WIN32) || defined(WIN32) @@ -18,6 +19,12 @@ # endif #endif +#ifdef __WIN32__ +extern int access(); +#else +#include <unistd.h> +#endif + /* #define PRIVATE static */ #define PRIVATE @@ -27,20 +34,10 @@ #define MAXRHS 1000 #endif -char *msort(); -extern void *malloc(); +static char *msort(char*,char**,int(*)(const char*,const char*)); -/******** From the file "action.h" *************************************/ -struct action *Action_new(); -struct action *Action_sort(); - -/********* From the file "assert.h" ************************************/ -void myassert(); -#ifndef NDEBUG -# define assert(X) if(!(X))myassert(__FILE__,__LINE__) -#else -# define assert(X) -#endif +static struct action *Action_new(void); +static struct action *Action_sort(struct action *); /********** From the file "build.h" ************************************/ void FindRulePrecedences(); @@ -111,7 +108,7 @@ int SetUnion(/* char *A,char *B */); /* A <- A U B, thru element N */ ** Principal data structures for the LEMON parser generator. */ -typedef enum {B_FALSE=0, B_TRUE} Boolean; +typedef enum {LEMON_FALSE=0, LEMON_TRUE} Boolean; /* Symbols (terminals and nonterminals) of the grammar are stored ** in the following: */ @@ -134,6 +131,7 @@ struct symbol { } assoc; /* Associativity if predecence is defined */ char *firstset; /* First-set for all rules of this symbol */ Boolean lambda; /* True if NT and can generate an empty string */ + int useCnt; /* Number of times used */ char *destructor; /* Code which executes whenever this symbol is ** popped from the stack during error processing */ int destructorln; /* Line number of destructor code */ @@ -152,6 +150,7 @@ struct symbol { struct rule { struct symbol *lhs; /* Left-hand side of the rule */ char *lhsalias; /* Alias for the LHS (NULL if none) */ + int lhsStart; /* True if left-hand side is the start symbol */ int ruleline; /* Line number for the rule */ int nrhs; /* Number of RHS symbols */ struct symbol **rhs; /* The RHS symbols */ @@ -193,7 +192,9 @@ struct action { ACCEPT, REDUCE, ERROR, - CONFLICT, /* Was a reduce, but part of a conflict */ + SSCONFLICT, /* A shift/shift conflict */ + SRCONFLICT, /* Was a reduce, but part of a conflict */ + 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 */ @@ -332,14 +333,14 @@ void Configtable_clear(/* int(*)(struct config *) */); */ /* Allocate a new parser action */ -struct action *Action_new(){ +static struct action *Action_new(void){ static struct action *freelist = 0; struct action *new; if( freelist==0 ){ int i; int amt = 100; - freelist = (struct action *)malloc( sizeof(struct action)*amt ); + freelist = (struct action *)calloc(amt, sizeof(struct action)); if( freelist==0 ){ fprintf(stderr,"Unable to allocate memory for a new parser action."); exit(1); @@ -352,27 +353,31 @@ struct action *Action_new(){ return new; } -/* Compare two actions */ -static int actioncmp(ap1,ap2) -struct action *ap1; -struct action *ap2; -{ +/* Compare two actions for sorting purposes. Return negative, zero, or +** positive if the first action is less than, equal to, or greater than +** the first +*/ +static int actioncmp( + struct action *ap1, + struct action *ap2 +){ int rc; rc = ap1->sp->index - ap2->sp->index; - if( rc==0 ) rc = (int)ap1->type - (int)ap2->type; if( rc==0 ){ - assert( ap1->type==REDUCE || ap1->type==RD_RESOLVED || ap1->type==CONFLICT); - assert( ap2->type==REDUCE || ap2->type==RD_RESOLVED || ap2->type==CONFLICT); + rc = (int)ap1->type - (int)ap2->type; + } + if( rc==0 && ap1->type==REDUCE ){ rc = ap1->x.rp->index - ap2->x.rp->index; } return rc; } /* Sort parser actions */ -struct action *Action_sort(ap) -struct action *ap; -{ - ap = (struct action *)msort((char *)ap,(char **)&ap->next,actioncmp); +static struct action *Action_sort( + struct action *ap +){ + ap = (struct action *)msort((char *)ap,(char **)&ap->next, + (int(*)(const char*,const char*))actioncmp); return ap; } @@ -437,7 +442,7 @@ void acttab_free(acttab *p){ /* Allocate a new acttab structure */ acttab *acttab_alloc(void){ - acttab *p = malloc( sizeof(*p) ); + acttab *p = calloc( 1, sizeof(*p) ); if( p==0 ){ fprintf(stderr,"Unable to allocate memory for a new acttab."); exit(1); @@ -558,17 +563,6 @@ int acttab_insert(acttab *p){ return i - p->mnLookahead; } -/********************** From the file "assert.c" ****************************/ -/* -** A more efficient way of handling assertions. -*/ -void myassert(file,line) -char *file; -int line; -{ - fprintf(stderr,"Assertion failed on line %d of file \"%s\"\n",line,file); - exit(1); -} /********************** From the file "build.c" *****************************/ /* ** Routines to construction the finite state machine for the LEMON @@ -622,7 +616,7 @@ struct lemon *lemp; int progress; for(i=0; i<lemp->nsymbol; i++){ - lemp->symbols[i]->lambda = B_FALSE; + lemp->symbols[i]->lambda = LEMON_FALSE; } for(i=lemp->nterminal; i<lemp->nsymbol; i++){ lemp->symbols[i]->firstset = SetNew(); @@ -635,10 +629,10 @@ struct lemon *lemp; if( rp->lhs->lambda ) continue; for(i=0; i<rp->nrhs; i++){ struct symbol *sp = rp->rhs[i]; - if( sp->type!=TERMINAL || sp->lambda==B_FALSE ) break; + if( sp->type!=TERMINAL || sp->lambda==LEMON_FALSE ) break; } if( i==rp->nrhs ){ - rp->lhs->lambda = B_TRUE; + rp->lhs->lambda = LEMON_TRUE; progress = 1; } } @@ -661,10 +655,10 @@ struct lemon *lemp; } break; }else if( s1==s2 ){ - if( s1->lambda==B_FALSE ) break; + if( s1->lambda==LEMON_FALSE ) break; }else{ progress += SetUnion(s1->firstset,s2->firstset); - if( s2->lambda==B_FALSE ) break; + if( s2->lambda==LEMON_FALSE ) break; } } } @@ -721,6 +715,7 @@ does not work properly.",sp->name); ** left-hand side */ for(rp=sp->rule; rp; rp=rp->nextlhs){ struct config *newcfp; + rp->lhsStart = 1; newcfp = Configlist_addbasis(rp,0); SetAdd(newcfp->fws,0); } @@ -972,7 +967,7 @@ struct lemon *lemp; struct action *ap, *nap; struct state *stp; stp = lemp->sorted[i]; - assert( stp->ap ); + /* assert( stp->ap ); */ stp->ap = Action_sort(stp->ap); for(ap=stp->ap; ap && ap->next; ap=ap->next){ for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){ @@ -984,11 +979,11 @@ struct lemon *lemp; } /* Report an error for each rule that can never be reduced. */ - for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = B_FALSE; + for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = LEMON_FALSE; for(i=0; i<lemp->nstate; i++){ struct action *ap; for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ - if( ap->type==REDUCE ) ap->x.rp->canReduce = B_TRUE; + if( ap->type==REDUCE ) ap->x.rp->canReduce = LEMON_TRUE; } } for(rp=lemp->rule; rp; rp=rp->next){ @@ -1019,12 +1014,16 @@ struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */ struct symbol *spx, *spy; int errcnt = 0; assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */ + if( apx->type==SHIFT && apy->type==SHIFT ){ + apy->type = SSCONFLICT; + errcnt++; + } if( apx->type==SHIFT && apy->type==REDUCE ){ spx = apx->sp; spy = apy->x.rp->precsym; if( spy==0 || spx->prec<0 || spy->prec<0 ){ /* Not enough precedence information. */ - apy->type = CONFLICT; + apy->type = SRCONFLICT; errcnt++; }else if( spx->prec>spy->prec ){ /* Lower precedence wins */ apy->type = RD_RESOLVED; @@ -1036,7 +1035,7 @@ struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */ apx->type = SH_RESOLVED; }else{ assert( spx->prec==spy->prec && spx->assoc==NONE ); - apy->type = CONFLICT; + apy->type = SRCONFLICT; errcnt++; } }else if( apx->type==REDUCE && apy->type==REDUCE ){ @@ -1044,7 +1043,7 @@ struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */ spy = apy->x.rp->precsym; if( spx==0 || spy==0 || spx->prec<0 || spy->prec<0 || spx->prec==spy->prec ){ - apy->type = CONFLICT; + apy->type = RRCONFLICT; errcnt++; }else if( spx->prec>spy->prec ){ apy->type = RD_RESOLVED; @@ -1055,10 +1054,14 @@ struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */ assert( apx->type==SH_RESOLVED || apx->type==RD_RESOLVED || - apx->type==CONFLICT || + apx->type==SSCONFLICT || + apx->type==SRCONFLICT || + apx->type==RRCONFLICT || apy->type==SH_RESOLVED || apy->type==RD_RESOLVED || - apy->type==CONFLICT + apy->type==SSCONFLICT || + apy->type==SRCONFLICT || + apy->type==RRCONFLICT ); /* The REDUCE/SHIFT case cannot happen because SHIFTs come before ** REDUCEs on the list. If we reach this point it must be because @@ -1084,7 +1087,7 @@ PRIVATE struct config *newconfig(){ if( freelist==0 ){ int i; int amt = 3; - freelist = (struct config *)malloc( sizeof(struct config)*amt ); + freelist = (struct config *)calloc( amt, sizeof(struct config) ); if( freelist==0 ){ fprintf(stderr,"Unable to allocate memory for a new configuration."); exit(1); @@ -1218,7 +1221,7 @@ struct lemon *lemp; break; }else{ SetUnion(newcfp->fws,xsp->firstset); - if( xsp->lambda==B_FALSE ) break; + if( xsp->lambda==LEMON_FALSE ) break; } } if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp); @@ -1435,6 +1438,7 @@ char **argv; lem.basisflag = basisflag; Symbol_new("$"); lem.errsym = Symbol_new("error"); + lem.errsym->useCnt = 0; /* Parse the input file */ Parse(&lem); @@ -1460,7 +1464,7 @@ char **argv; Reprint(&lem); }else{ /* Initialize the size for all follow and first sets */ - SetSize(lem.nterminal); + SetSize(lem.nterminal+1); /* Find the precedence for every production rule (that has one) */ FindRulePrecedences(&lem); @@ -1558,12 +1562,12 @@ char **argv; ** The "next" pointers for elements in the lists a and b are ** changed. */ -static char *merge(a,b,cmp,offset) -char *a; -char *b; -int (*cmp)(); -int offset; -{ +static char *merge( + char *a, + char *b, + int (*cmp)(const char*,const char*), + int offset +){ char *ptr, *head; if( a==0 ){ @@ -1610,11 +1614,11 @@ int offset; ** The "next" pointers for elements in list are changed. */ #define LISTSIZE 30 -char *msort(list,next,cmp) -char *list; -char **next; -int (*cmp)(); -{ +static char *msort( + char *list, + char **next, + int (*cmp)(const char*,const char*) +){ unsigned long offset; char *ep; char *set[LISTSIZE]; @@ -2097,8 +2101,8 @@ to follow the previous rule."); case IN_RHS: if( x[0]=='.' ){ struct rule *rp; - rp = (struct rule *)malloc( sizeof(struct rule) + - sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs ); + rp = (struct rule *)calloc( sizeof(struct rule) + + sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs, 1); if( rp==0 ){ ErrorMsg(psp->filename,psp->tokenlineno, "Can't allocate enough memory for this rule."); @@ -2134,7 +2138,7 @@ to follow the previous rule."); }else if( isalpha(x[0]) ){ if( psp->nrhs>=MAXRHS ){ ErrorMsg(psp->filename,psp->tokenlineno, - "Too many symbols on RHS or rule beginning at \"%s\".", + "Too many symbols on RHS of rule beginning at \"%s\".", x); psp->errorcnt++; psp->state = RESYNC_AFTER_RULE_ERROR; @@ -2147,11 +2151,11 @@ to follow the previous rule."); struct symbol *msp = psp->rhs[psp->nrhs-1]; if( msp->type!=MULTITERMINAL ){ struct symbol *origsp = msp; - msp = malloc(sizeof(*msp)); + msp = calloc(1,sizeof(*msp)); memset(msp, 0, sizeof(*msp)); msp->type = MULTITERMINAL; msp->nsubsym = 1; - msp->subsym = malloc(sizeof(struct symbol*)); + msp->subsym = calloc(1,sizeof(struct symbol*)); msp->subsym[0] = origsp; msp->name = origsp->name; psp->rhs[psp->nrhs-1] = msp; @@ -2396,9 +2400,9 @@ to follow the previous rule."); static void preprocess_input(char *z){ int i, j, k, n; int exclude = 0; - int start; + int start = 0; int lineno = 1; - int start_lineno; + int start_lineno = 1; for(i=0; z[i]; i++){ if( z[i]=='\n' ) lineno++; if( z[i]!='%' || (i>0 && z[i-1]!='\n') ) continue; @@ -2456,6 +2460,7 @@ struct lemon *gp; char *cp, *nextcp; int startline = 0; + memset(&ps, '\0', sizeof(ps)); ps.gp = gp; ps.filename = gp->filename; ps.errorcnt = 0; @@ -2603,7 +2608,7 @@ struct plink *Plink_new(){ if( plink_freelist==0 ){ int i; int amt = 100; - plink_freelist = (struct plink *)malloc( sizeof(struct plink)*amt ); + plink_freelist = (struct plink *)calloc( amt, sizeof(struct plink) ); if( plink_freelist==0 ){ fprintf(stderr, "Unable to allocate memory for a new follow-set propagation link.\n"); @@ -2829,10 +2834,15 @@ int PrintAction(struct action *ap, FILE *fp, int indent){ case ERROR: fprintf(fp,"%*s error",indent,ap->sp->name); break; - case CONFLICT: + case SRCONFLICT: + case RRCONFLICT: fprintf(fp,"%*s reduce %-3d ** Parsing conflict **", indent,ap->sp->name,ap->x.rp->index); break; + case SSCONFLICT: + fprintf(fp,"%*s shift %d ** Parsing conflict **", + indent,ap->sp->name,ap->x.stp->statenum); + break; case SH_RESOLVED: case RD_RESOLVED: case NOT_USED: @@ -2854,7 +2864,6 @@ struct lemon *lemp; fp = file_open(lemp,".out","wb"); if( fp==0 ) return; - fprintf(fp," \b"); for(i=0; i<lemp->nstate; i++){ stp = lemp->sorted[i]; fprintf(fp,"State %d:\n",stp->statenum); @@ -2884,6 +2893,27 @@ struct lemon *lemp; } fprintf(fp,"\n"); } + fprintf(fp, "----------------------------------------------------\n"); + fprintf(fp, "Symbols:\n"); + for(i=0; i<lemp->nsymbol; i++){ + int j; + struct symbol *sp; + + sp = lemp->symbols[i]; + fprintf(fp, " %3d: %s", i, sp->name); + if( sp->type==NONTERMINAL ){ + fprintf(fp, ":"); + if( sp->lambda ){ + fprintf(fp, " <lambda>"); + } + for(j=0; j<lemp->nterminal; j++){ + if( sp->firstset && SetFind(sp->firstset, j) ){ + fprintf(fp, " %s", lemp->symbols[j]->name); + } + } + } + fprintf(fp, "\n"); + } fclose(fp); return; } @@ -2898,7 +2928,6 @@ int modemask; char *pathlist; char *path,*cp; char c; - extern int access(); #ifdef __WIN32__ cp = strrchr(argv0,'\\'); @@ -3166,7 +3195,7 @@ PRIVATE char *append_str(char *zText, int n, int p1, int p2){ if( z==0 ) return ""; while( n-- > 0 ){ c = *(zText++); - if( c=='%' && zText[0]=='d' ){ + if( c=='%' && n>0 && zText[0]=='d' ){ sprintf(zInt, "%d", p1); p1 = p2; strcpy(&z[used], zInt); @@ -3195,6 +3224,11 @@ PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ for(i=0; i<rp->nrhs; i++) used[i] = 0; lhsused = 0; + if( rp->code==0 ){ + rp->code = "\n"; + rp->line = rp->ruleline; + } + append_str(0,0,0,0); for(cp=rp->code; *cp; cp++){ if( isalpha(*cp) && (cp==rp->code || (!isalnum(cp[-1]) && cp[-1]!='_')) ){ @@ -3259,8 +3293,10 @@ PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ } } } - cp = append_str(0,0,0,0); - rp->code = Strsafe(cp); + if( rp->code ){ + cp = append_str(0,0,0,0); + rp->code = Strsafe(cp?cp:""); + } } /* @@ -3315,7 +3351,7 @@ int mhflag; /* True if generating makeheaders output */ /* Allocate and initialize types[] and allocate stddt[] */ arraysize = lemp->nsymbol * 2; - types = (char**)malloc( arraysize * sizeof(char*) ); + types = (char**)calloc( arraysize, sizeof(char*) ); for(i=0; i<arraysize; i++) types[i] = 0; maxdtlength = 0; if( lemp->vartype ){ @@ -3396,7 +3432,9 @@ int mhflag; /* True if generating makeheaders output */ fprintf(out," %s yy%d;\n",types[i],i+1); lineno++; free(types[i]); } - fprintf(out," int yy%d;\n",lemp->errsym->dtnum); lineno++; + if( lemp->errsym->useCnt ){ + fprintf(out," int yy%d;\n",lemp->errsym->dtnum); lineno++; + } free(stddt); free(types); fprintf(out,"} YYMINORTYPE;\n"); lineno++; @@ -3446,6 +3484,25 @@ static int axset_compare(const void *a, const void *b){ return p2->nAction - p1->nAction; } +/* +** Write text on "out" that describes the rule "rp". +*/ +static void writeRuleText(FILE *out, struct rule *rp){ + int j; + fprintf(out,"%s ::=", rp->lhs->name); + for(j=0; j<rp->nrhs; j++){ + struct symbol *sp = rp->rhs[j]; + fprintf(out," %s", sp->name); + if( sp->type==MULTITERMINAL ){ + int k; + for(k=1; k<sp->nsubsym; k++){ + fprintf(out,"|%s",sp->subsym[k]->name); + } + } + } +} + + /* Generate C source code for the parser */ void ReportTable(lemp, mhflag) struct lemon *lemp; @@ -3508,18 +3565,13 @@ int mhflag; /* Output in makeheaders format if true */ lemp->wildcard->index); lineno++; } print_stack_union(out,lemp,&lineno,mhflag); + fprintf(out, "#ifndef YYSTACKDEPTH\n"); lineno++; if( lemp->stacksize ){ - if( atoi(lemp->stacksize)<=0 ){ - ErrorMsg(lemp->filename,0, -"Illegal stack size: [%s]. The stack size should be an integer constant.", - lemp->stacksize); - lemp->errorcnt++; - lemp->stacksize = "100"; - } fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++; }else{ fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++; } + fprintf(out, "#endif\n"); lineno++; if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; } @@ -3546,8 +3598,10 @@ int mhflag; /* Output in makeheaders format if true */ } fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++; fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++; - fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++; - fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); 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++; + } if( lemp->has_fallback ){ fprintf(out,"#define YYFALLBACK 1\n"); lineno++; } @@ -3566,7 +3620,7 @@ int mhflag; /* Output in makeheaders format if true */ */ /* Compute the actions on all states and count them up */ - ax = malloc( sizeof(ax[0])*lemp->nstate*2 ); + ax = calloc(lemp->nstate*2, sizeof(ax[0])); if( ax==0 ){ fprintf(stderr,"malloc failed\n"); exit(1); @@ -3623,7 +3677,7 @@ int mhflag; /* Output in makeheaders format if true */ n = acttab_size(pActtab); for(i=j=0; i<n; i++){ int action = acttab_yyaction(pActtab, i); - if( action<0 ) action = lemp->nsymbol + lemp->nrule + 2; + if( action<0 ) action = lemp->nstate + lemp->nrule + 2; if( j==0 ) fprintf(out," /* %5d */ ", i); fprintf(out, " %4d,", action); if( j==9 || i==n-1 ){ @@ -3746,17 +3800,8 @@ int mhflag; /* Output in makeheaders format if true */ */ for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ assert( rp->index==i ); - fprintf(out," /* %3d */ \"%s ::=", i, rp->lhs->name); - for(j=0; j<rp->nrhs; j++){ - struct symbol *sp = rp->rhs[j]; - fprintf(out," %s", sp->name); - if( sp->type==MULTITERMINAL ){ - int k; - for(k=1; k<sp->nsubsym; k++){ - fprintf(out,"|%s",sp->subsym[k]->name); - } - } - } + fprintf(out," /* %3d */ \"", i); + writeRuleText(out, rp); fprintf(out,"\",\n"); lineno++; } tplt_xfer(lemp->name,in,out,&lineno); @@ -3769,7 +3814,8 @@ int mhflag; /* Output in makeheaders format if true */ for(i=0; i<lemp->nsymbol; i++){ struct symbol *sp = lemp->symbols[i]; if( sp==0 || sp->type!=TERMINAL ) continue; - fprintf(out," case %d:\n",sp->index); lineno++; + fprintf(out," case %d: /* %s */\n", + sp->index, sp->name); lineno++; } for(i=0; i<lemp->nsymbol && lemp->symbols[i]->type!=TERMINAL; i++); if( i<lemp->nsymbol ){ @@ -3783,7 +3829,8 @@ int mhflag; /* Output in makeheaders format if true */ struct symbol *sp = lemp->symbols[i]; if( sp==0 || sp->type==TERMINAL || sp->index<=0 || sp->destructor!=0 ) continue; - fprintf(out," case %d:\n",sp->index); lineno++; + fprintf(out," case %d: /* %s */\n", + sp->index, sp->name); lineno++; dflt_sp = sp; } if( dflt_sp!=0 ){ @@ -3794,7 +3841,8 @@ int mhflag; /* Output in makeheaders format if true */ for(i=0; i<lemp->nsymbol; i++){ struct symbol *sp = lemp->symbols[i]; if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue; - fprintf(out," case %d:\n",sp->index); lineno++; + fprintf(out," case %d: /* %s */\n", + sp->index, sp->name); lineno++; /* Combine duplicate destructors into a single case */ for(j=i+1; j<lemp->nsymbol; j++){ @@ -3802,7 +3850,8 @@ int mhflag; /* Output in makeheaders format if true */ if( sp2 && sp2->type!=TERMINAL && sp2->destructor && sp2->dtnum==sp->dtnum && strcmp(sp->destructor,sp2->destructor)==0 ){ - fprintf(out," case %d:\n",sp2->index); lineno++; + fprintf(out," case %d: /* %s */\n", + sp2->index, sp2->name); lineno++; sp2->destructor = 0; } } @@ -3828,15 +3877,19 @@ int mhflag; /* Output in makeheaders format if true */ /* Generate code which execution during each REDUCE action */ for(rp=lemp->rule; rp; rp=rp->next){ - if( rp->code ) translate_code(lemp, rp); + translate_code(lemp, rp); } for(rp=lemp->rule; rp; rp=rp->next){ struct rule *rp2; if( rp->code==0 ) continue; - fprintf(out," case %d:\n",rp->index); lineno++; + fprintf(out," case %d: /* ", rp->index); + writeRuleText(out, rp); + fprintf(out, " */\n"); lineno++; for(rp2=rp->next; rp2; rp2=rp2->next){ if( rp2->code==rp->code ){ - fprintf(out," case %d:\n",rp2->index); lineno++; + fprintf(out," case %d: /* ", rp2->index); + writeRuleText(out, rp2); + fprintf(out," */\n"); lineno++; rp2->code = 0; } } @@ -3928,6 +3981,7 @@ struct lemon *lemp; } if( ap->type!=REDUCE ) continue; rp = ap->x.rp; + if( rp->lhsStart ) continue; if( rp==rbest ) continue; n = 1; for(ap2=ap->next; ap2; ap2=ap2->next){ @@ -4036,13 +4090,11 @@ int n; /* Allocate a new set */ char *SetNew(){ char *s; - int i; - s = (char*)malloc( size ); + s = (char*)calloc( size, 1); if( s==0 ){ extern void memory_error(); memory_error(); } - for(i=0; i<size; i++) s[i] = 0; return s; } @@ -4060,6 +4112,7 @@ char *s; int e; { int rv; + assert( e>=0 && e<size ); rv = s[e]; s[e] = 1; return !rv; @@ -4249,7 +4302,7 @@ char *x; sp = Symbol_find(x); if( sp==0 ){ - sp = (struct symbol *)malloc( sizeof(struct symbol) ); + sp = (struct symbol *)calloc(1, sizeof(struct symbol) ); MemoryCheck(sp); sp->name = Strsafe(x); sp->type = isupper(*x) ? TERMINAL : NONTERMINAL; @@ -4258,11 +4311,13 @@ char *x; sp->prec = -1; sp->assoc = UNK; sp->firstset = 0; - sp->lambda = B_FALSE; + sp->lambda = LEMON_FALSE; sp->destructor = 0; sp->datatype = 0; + sp->useCnt = 0; Symbol_insert(sp,sp->name); } + sp->useCnt++; return sp; } @@ -4432,7 +4487,7 @@ struct symbol **Symbol_arrayof() int i,size; if( x2a==0 ) return 0; size = x2a->count; - array = (struct symbol **)malloc( sizeof(struct symbol *)*size ); + array = (struct symbol **)calloc(size, sizeof(struct symbol *)); if( array ){ for(i=0; i<size; i++) array[i] = x2a->tbl[i].data; } @@ -4483,7 +4538,7 @@ struct config *a; struct state *State_new() { struct state *new; - new = (struct state *)malloc( sizeof(struct state) ); + new = (struct state *)calloc(1, sizeof(struct state) ); MemoryCheck(new); return new; } diff --git a/ext/pdo_sqlite/sqlite/tool/lempar.c b/ext/pdo_sqlite/sqlite/tool/lempar.c index a18c43a24d..cca91ad252 100644 --- a/ext/pdo_sqlite/sqlite/tool/lempar.c +++ b/ext/pdo_sqlite/sqlite/tool/lempar.c @@ -44,7 +44,8 @@ ** This is typically a union of many types, one of ** which is ParseTOKENTYPE. The entry in the union ** for base tokens is called "yy0". -** YYSTACKDEPTH is the maximum depth of the parser's stack. +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() ** ParseARG_SDECL A static variable declaration for the %extra_argument ** ParseARG_PDECL A parameter declaration for the %extra_argument ** ParseARG_STORE Code to store %extra_argument into yypParser @@ -152,7 +153,12 @@ struct yyParser { int yyidx; /* Index of top element in stack */ int yyerrcnt; /* Shifts left before out of the error */ ParseARG_SDECL /* A place to hold %extra_argument */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ +#else yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +#endif }; typedef struct yyParser yyParser; @@ -204,21 +210,29 @@ static const char *const yyRuleName[] = { }; #endif /* NDEBUG */ + +#if YYSTACKDEPTH<=0 /* -** This function returns the symbolic name associated with a token -** value. +** Try to increase the size of the parser stack. */ -const char *ParseTokenName(int tokenType){ +static void yyGrowStack(yyParser *p){ + int newSize; + yyStackEntry *pNew; + + newSize = p->yystksz*2 + 100; + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + if( pNew ){ + p->yystack = pNew; + p->yystksz = newSize; #ifndef NDEBUG - if( tokenType>0 && tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ - return yyTokenName[tokenType]; - }else{ - return "Unknown"; - } -#else - return ""; + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", + yyTracePrompt, p->yystksz); + } #endif + } } +#endif /* ** This function allocates a new parser. @@ -237,6 +251,9 @@ void *ParseAlloc(void *(*mallocProc)(size_t)){ pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); if( pParser ){ pParser->yyidx = -1; +#if YYSTACKDEPTH<=0 + yyGrowStack(pParser); +#endif } return pParser; } @@ -308,6 +325,9 @@ void ParseFree( yyParser *pParser = (yyParser*)p; if( pParser==0 ) return; while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + free(pParser->yystack); +#endif (*freeProc)((void*)pParser); } @@ -329,9 +349,7 @@ static int yy_find_shift_action( if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ return yy_default[stateno]; } - if( iLookAhead==YYNOCODE ){ - return YY_NO_ACTION; - } + assert( iLookAhead!=YYNOCODE ); i += iLookAhead; if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ if( iLookAhead>0 ){ @@ -382,21 +400,32 @@ static int yy_find_reduce_action( YYCODETYPE iLookAhead /* The look-ahead token */ ){ int i; - /* int stateno = pParser->yystack[pParser->yyidx].stateno; */ - - if( stateno>YY_REDUCE_MAX || - (i = yy_reduce_ofst[stateno])==YY_REDUCE_USE_DFLT ){ - return yy_default[stateno]; - } - if( iLookAhead==YYNOCODE ){ - return YY_NO_ACTION; - } + assert( stateno<=YY_REDUCE_MAX ); + i = yy_reduce_ofst[stateno]; + assert( i!=YY_REDUCE_USE_DFLT ); + assert( iLookAhead!=YYNOCODE ); i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ - return yy_default[stateno]; - }else{ - return yy_action[i]; - } + assert( i>=0 && i<YY_SZ_ACTTAB ); + assert( yy_lookahead[i]==iLookAhead ); + return yy_action[i]; +} + +/* +** The following routine is called if the stack overflows. +*/ +static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){ + ParseARG_FETCH; + yypParser->yyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ } /* @@ -410,21 +439,20 @@ static void yy_shift( ){ yyStackEntry *yytos; yypParser->yyidx++; +#if YYSTACKDEPTH>0 if( yypParser->yyidx>=YYSTACKDEPTH ){ - ParseARG_FETCH; - yypParser->yyidx--; -#ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); - } -#endif - while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); - /* Here code is inserted which will execute if the parser - ** stack every overflows */ -%% - ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ - return; + yyStackOverflow(yypParser, yypMinor); + return; } +#else + if( yypParser->yyidx>=yypParser->yystksz ){ + yyGrowStack(yypParser); + if( yypParser->yyidx>=yypParser->yystksz ){ + yyStackOverflow(yypParser, yypMinor); + return; + } + } +#endif yytos = &yypParser->yystack[yypParser->yyidx]; yytos->stateno = yyNewState; yytos->major = yyMajor; @@ -476,7 +504,6 @@ static void yy_reduce( } #endif /* NDEBUG */ -#ifndef NDEBUG /* Silence complaints from purify about yygotominor being uninitialized ** in some cases when it is copied into the stack after the following ** switch. yygotominor is uninitialized when a rule reduces that does @@ -484,9 +511,15 @@ static void yy_reduce( ** value of the nonterminal uninitialized is utterly harmless as long ** as the value is never used. So really the only thing this code ** accomplishes is to quieten purify. + ** + ** 2007-01-16: The wireshark project (www.wireshark.org) reports that + ** without this code, their parser segfaults. I'm not sure what there + ** parser is doing to make this happen. This is the second bug report + ** from wireshark this week. Clearly they are stressing Lemon in ways + ** that it has not been previously stressed... (SQLite ticket #2172) */ memset(&yygotominor, 0, sizeof(yygotominor)); -#endif + switch( yyruleno ){ /* Beginning here are the reduction cases. A typical example @@ -520,7 +553,8 @@ static void yy_reduce( { yy_shift(yypParser,yyact,yygoto,&yygotominor); } - }else if( yyact == YYNSTATE + YYNRULE + 1 ){ + }else{ + assert( yyact == YYNSTATE + YYNRULE + 1 ); yy_accept(yypParser); } } @@ -605,13 +639,21 @@ void Parse( YYMINORTYPE yyminorunion; int yyact; /* The parser action. */ int yyendofinput; /* True if we are at the end of input */ +#ifdef YYERRORSYMBOL int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif yyParser *yypParser; /* The parser */ /* (re)initialize the parser, if necessary */ yypParser = (yyParser*)yyp; if( yypParser->yyidx<0 ){ - /* if( yymajor==0 ) return; // not sure why this was here... */ +#if YYSTACKDEPTH<=0 + if( yypParser->yystksz <=0 ){ + memset(&yyminorunion, 0, sizeof(yyminorunion)); + yyStackOverflow(yypParser, &yyminorunion); + return; + } +#endif yypParser->yyidx = 0; yypParser->yyerrcnt = -1; yypParser->yystack[0].stateno = 0; @@ -630,17 +672,17 @@ void Parse( do{ yyact = yy_find_shift_action(yypParser,yymajor); if( yyact<YYNSTATE ){ + assert( !yyendofinput ); /* Impossible to shift the $ token */ yy_shift(yypParser,yyact,yymajor,&yyminorunion); yypParser->yyerrcnt--; - if( yyendofinput && yypParser->yyidx>=0 ){ - yymajor = 0; - }else{ - yymajor = YYNOCODE; - } + yymajor = YYNOCODE; }else if( yyact < YYNSTATE + YYNRULE ){ yy_reduce(yypParser,yyact-YYNSTATE); - }else if( yyact == YY_ERROR_ACTION ){ + }else{ + assert( yyact == YY_ERROR_ACTION ); +#ifdef YYERRORSYMBOL int yymx; +#endif #ifndef NDEBUG if( yyTraceFILE ){ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); @@ -721,9 +763,6 @@ void Parse( } yymajor = YYNOCODE; #endif - }else{ - yy_accept(yypParser); - yymajor = YYNOCODE; } }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); return; diff --git a/ext/pdo_sqlite/sqlite/tool/mkkeywordhash.c b/ext/pdo_sqlite/sqlite/tool/mkkeywordhash.c index 3301a40e97..e5adb82731 100644 --- a/ext/pdo_sqlite/sqlite/tool/mkkeywordhash.c +++ b/ext/pdo_sqlite/sqlite/tool/mkkeywordhash.c @@ -8,6 +8,25 @@ #include <stdlib.h> /* +** A header comment placed at the beginning of generated code. +*/ +static const char zHdr[] = + "/***** This file contains automatically generated code ******\n" + "**\n" + "** The code in this file has been automatically generated by\n" + "**\n" + "** $Header$\n" + "**\n" + "** The code in this file implements a function that determines whether\n" + "** or not a given identifier is really an SQL keyword. The same thing\n" + "** might be implemented more directly using a hand-written hash table.\n" + "** But by using this automatically generated code, the size of the code\n" + "** is substantially reduced. This is important for embedded applications\n" + "** on platforms with limited memory.\n" + "*/\n" +; + +/* ** All the keywords of the SQL language are stored as in a hash ** table composed of instances of the following structure. */ @@ -21,6 +40,7 @@ struct Keyword { int offset; /* Offset to start of name string */ int len; /* Length of this keyword, not counting final \000 */ int prefix; /* Number of characters in prefix */ + int longestSuffix; /* Longest suffix that is a prefix on another word */ int iNext; /* Index in aKeywordTable[] of next with same hash */ int substrId; /* Id to another keyword this keyword is embedded in */ int substrOffset; /* Offset into substrId for start of this keyword */ @@ -95,7 +115,8 @@ struct Keyword { #else # define TRIGGER 0x00002000 #endif -#ifdef SQLITE_OMIT_VACUUM +#if defined(SQLITE_OMIT_AUTOVACUUM) && \ + (defined(SQLITE_OMIT_VACUUM) || defined(SQLITE_OMIT_ATTACH)) # define VACUUM 0 #else # define VACUUM 0x00004000 @@ -110,6 +131,11 @@ struct Keyword { #else # define VTAB 0x00010000 #endif +#ifdef SQLITE_OMIT_AUTOVACUUM +# define AUTOVACUUM 0 +#else +# define AUTOVACUUM 0x00020000 +#endif /* ** These are the keywords @@ -198,8 +224,10 @@ static Keyword aKeywordTable[] = { { "OR", "TK_OR", ALWAYS }, { "ORDER", "TK_ORDER", ALWAYS }, { "OUTER", "TK_JOIN_KW", ALWAYS }, + { "PLAN", "TK_PLAN", EXPLAIN }, { "PRAGMA", "TK_PRAGMA", PRAGMA }, { "PRIMARY", "TK_PRIMARY", ALWAYS }, + { "QUERY", "TK_QUERY", EXPLAIN }, { "RAISE", "TK_RAISE", TRIGGER }, { "REFERENCES", "TK_REFERENCES", FKEY }, { "REGEXP", "TK_LIKE_KW", ALWAYS }, @@ -212,7 +240,6 @@ static Keyword aKeywordTable[] = { { "ROW", "TK_ROW", TRIGGER }, { "SELECT", "TK_SELECT", ALWAYS }, { "SET", "TK_SET", ALWAYS }, - { "STATEMENT", "TK_STATEMENT", TRIGGER }, { "TABLE", "TK_TABLE", ALWAYS }, { "TEMP", "TK_TEMP", ALWAYS }, { "TEMPORARY", "TK_TEMP", ALWAYS }, @@ -233,7 +260,7 @@ static Keyword aKeywordTable[] = { }; /* Number of keywords */ -static int NKEYWORD = (sizeof(aKeywordTable)/sizeof(aKeywordTable[0])); +static int nKeyword = (sizeof(aKeywordTable)/sizeof(aKeywordTable[0])); /* An array to map all upper-case characters into their corresponding ** lower-case character. @@ -272,7 +299,10 @@ static int keywordCompare1(const void *a, const void *b){ static int keywordCompare2(const void *a, const void *b){ const Keyword *pA = (Keyword*)a; const Keyword *pB = (Keyword*)b; - int n = strcmp(pA->zName, pB->zName); + int n = pB->longestSuffix - pA->longestSuffix; + if( n==0 ){ + n = strcmp(pA->zName, pB->zName); + } return n; } static int keywordCompare3(const void *a, const void *b){ @@ -287,7 +317,7 @@ static int keywordCompare3(const void *a, const void *b){ */ static Keyword *findById(int id){ int i; - for(i=0; i<NKEYWORD; i++){ + for(i=0; i<nKeyword; i++){ if( aKeywordTable[i].id==id ) break; } return &aKeywordTable[i]; @@ -302,34 +332,36 @@ int main(int argc, char **argv){ int bestSize, bestCount; int count; int nChar; - int aHash[1000]; /* 1000 is much bigger than NKEYWORD */ + int totalLen = 0; + int aHash[1000]; /* 1000 is much bigger than nKeyword */ /* Remove entries from the list of keywords that have mask==0 */ - for(i=j=0; i<NKEYWORD; i++){ + for(i=j=0; i<nKeyword; i++){ if( aKeywordTable[i].mask==0 ) continue; if( j<i ){ aKeywordTable[j] = aKeywordTable[i]; } j++; } - NKEYWORD = j; + nKeyword = j; /* Fill in the lengths of strings and hashes for all entries. */ - for(i=0; i<NKEYWORD; i++){ + for(i=0; i<nKeyword; i++){ Keyword *p = &aKeywordTable[i]; p->len = strlen(p->zName); - p->hash = (UpperToLower[p->zName[0]]*4) ^ - (UpperToLower[p->zName[p->len-1]]*3) ^ p->len; + totalLen += p->len; + p->hash = (UpperToLower[(int)p->zName[0]]*4) ^ + (UpperToLower[(int)p->zName[p->len-1]]*3) ^ p->len; p->id = i+1; } /* Sort the table from shortest to longest keyword */ - qsort(aKeywordTable, NKEYWORD, sizeof(aKeywordTable[0]), keywordCompare1); + qsort(aKeywordTable, nKeyword, sizeof(aKeywordTable[0]), keywordCompare1); /* Look for short keywords embedded in longer keywords */ - for(i=NKEYWORD-2; i>=0; i--){ + for(i=nKeyword-2; i>=0; i--){ Keyword *p = &aKeywordTable[i]; - for(j=NKEYWORD-1; j>i && p->substrId==0; j--){ + for(j=nKeyword-1; j>i && p->substrId==0; j--){ Keyword *pOther = &aKeywordTable[j]; if( pOther->substrId ) continue; if( pOther->len<=p->len ) continue; @@ -343,18 +375,35 @@ int main(int argc, char **argv){ } } - /* Sort the table into alphabetical order */ - qsort(aKeywordTable, NKEYWORD, sizeof(aKeywordTable[0]), keywordCompare2); + /* Compute the longestSuffix value for every word */ + for(i=0; i<nKeyword; i++){ + Keyword *p = &aKeywordTable[i]; + if( p->substrId ) continue; + for(j=0; j<nKeyword; j++){ + Keyword *pOther; + if( j==i ) continue; + pOther = &aKeywordTable[j]; + if( pOther->substrId ) continue; + for(k=p->longestSuffix+1; k<p->len && k<pOther->len; k++){ + if( memcmp(&p->zName[p->len-k], pOther->zName, k)==0 ){ + p->longestSuffix = k; + } + } + } + } + + /* Sort the table into reverse order by length */ + qsort(aKeywordTable, nKeyword, sizeof(aKeywordTable[0]), keywordCompare2); /* Fill in the offset for all entries */ nChar = 0; - for(i=0; i<NKEYWORD; i++){ + for(i=0; i<nKeyword; i++){ Keyword *p = &aKeywordTable[i]; if( p->offset>0 || p->substrId ) continue; p->offset = nChar; nChar += p->len; for(k=p->len-1; k>=1; k--){ - for(j=i+1; j<NKEYWORD; j++){ + for(j=i+1; j<nKeyword; j++){ Keyword *pOther = &aKeywordTable[j]; if( pOther->offset>0 || pOther->substrId ) continue; if( pOther->len<=k ) continue; @@ -371,7 +420,7 @@ int main(int argc, char **argv){ } } } - for(i=0; i<NKEYWORD; i++){ + for(i=0; i<nKeyword; i++){ Keyword *p = &aKeywordTable[i]; if( p->substrId ){ p->offset = findById(p->substrId)->offset + p->substrOffset; @@ -379,15 +428,15 @@ int main(int argc, char **argv){ } /* Sort the table by offset */ - qsort(aKeywordTable, NKEYWORD, sizeof(aKeywordTable[0]), keywordCompare3); + qsort(aKeywordTable, nKeyword, sizeof(aKeywordTable[0]), keywordCompare3); /* Figure out how big to make the hash table in order to minimize the ** number of collisions */ - bestSize = NKEYWORD; - bestCount = NKEYWORD*NKEYWORD; - for(i=NKEYWORD/2; i<=2*NKEYWORD; i++){ + bestSize = nKeyword; + bestCount = nKeyword*nKeyword; + for(i=nKeyword/2; i<=2*nKeyword; i++){ for(j=0; j<i; j++) aHash[j] = 0; - for(j=0; j<NKEYWORD; j++){ + for(j=0; j<nKeyword; j++){ h = aKeywordTable[j].hash % i; aHash[h] *= 2; aHash[h]++; @@ -401,18 +450,21 @@ int main(int argc, char **argv){ /* Compute the hash */ for(i=0; i<bestSize; i++) aHash[i] = 0; - for(i=0; i<NKEYWORD; i++){ + for(i=0; i<nKeyword; i++){ h = aKeywordTable[i].hash % bestSize; aKeywordTable[i].iNext = aHash[h]; aHash[h] = i+1; } /* Begin generating code */ + printf("%s", zHdr); printf("/* Hash score: %d */\n", bestCount); printf("static int keywordCode(const char *z, int n){\n"); + printf(" /* zText[] encodes %d bytes of keywords in %d bytes */\n", + totalLen + nKeyword, nChar+1 ); printf(" static const char zText[%d] =\n", nChar+1); - for(i=j=0; i<NKEYWORD; i++){ + for(i=j=0; i<nKeyword; i++){ Keyword *p = &aKeywordTable[i]; if( p->substrId ) continue; if( j==0 ) printf(" \""); @@ -437,8 +489,8 @@ int main(int argc, char **argv){ } printf("%s };\n", j==0 ? "" : "\n"); - printf(" static const unsigned char aNext[%d] = {\n", NKEYWORD); - for(i=j=0; i<NKEYWORD; i++){ + printf(" static const unsigned char aNext[%d] = {\n", nKeyword); + for(i=j=0; i<nKeyword; i++){ if( j==0 ) printf(" "); printf(" %3d,", aKeywordTable[i].iNext); j++; @@ -449,8 +501,8 @@ int main(int argc, char **argv){ } printf("%s };\n", j==0 ? "" : "\n"); - printf(" static const unsigned char aLen[%d] = {\n", NKEYWORD); - for(i=j=0; i<NKEYWORD; i++){ + printf(" static const unsigned char aLen[%d] = {\n", nKeyword); + for(i=j=0; i<nKeyword; i++){ if( j==0 ) printf(" "); printf(" %3d,", aKeywordTable[i].len+aKeywordTable[i].prefix); j++; @@ -461,8 +513,8 @@ int main(int argc, char **argv){ } printf("%s };\n", j==0 ? "" : "\n"); - printf(" static const unsigned short int aOffset[%d] = {\n", NKEYWORD); - for(i=j=0; i<NKEYWORD; i++){ + printf(" static const unsigned short int aOffset[%d] = {\n", nKeyword); + for(i=j=0; i<nKeyword; i++){ if( j==0 ) printf(" "); printf(" %3d,", aKeywordTable[i].offset); j++; @@ -473,8 +525,8 @@ int main(int argc, char **argv){ } printf("%s };\n", j==0 ? "" : "\n"); - printf(" static const unsigned char aCode[%d] = {\n", NKEYWORD); - for(i=j=0; i<NKEYWORD; i++){ + printf(" static const unsigned char aCode[%d] = {\n", nKeyword); + for(i=j=0; i<nKeyword; i++){ char *zToken = aKeywordTable[i].zTokenType; if( j==0 ) printf(" "); printf("%s,%*s", zToken, (int)(14-strlen(zToken)), ""); diff --git a/ext/pdo_sqlite/sqlite/tool/mksqlite3c.tcl b/ext/pdo_sqlite/sqlite/tool/mksqlite3c.tcl new file mode 100644 index 0000000000..e7befd8fc4 --- /dev/null +++ b/ext/pdo_sqlite/sqlite/tool/mksqlite3c.tcl @@ -0,0 +1,278 @@ +#!/usr/bin/tclsh +# +# To build a single huge source file holding all of SQLite (or at +# least the core components - the test harness, shell, and TCL +# interface are omitted.) first do +# +# make target_source +# +# The make target above moves all of the source code files into +# a subdirectory named "tsrc". (This script expects to find the files +# there and will not work if they are not found.) There are a few +# generated C code files that are also added to the tsrc directory. +# For example, the "parse.c" and "parse.h" files to implement the +# the parser are derived from "parse.y" using lemon. And the +# "keywordhash.h" files is generated by a program named "mkkeywordhash". +# +# After the "tsrc" directory has been created and populated, run +# this script: +# +# tclsh mksqlite3c.tcl +# +# The amalgamated SQLite code will be written into sqlite3.c +# + +# Begin by reading the "sqlite3.h" header file. Count the number of lines +# in this file and extract the version number. That information will be +# needed in order to generate the header of the amalgamation. +# +if {[lsearch $argv --nostatic]>=0} { + set addstatic 0 +} else { + set addstatic 1 +} +set in [open tsrc/sqlite3.h] +set cnt 0 +set VERSION ????? +while {![eof $in]} { + set line [gets $in] + if {$line=="" && [eof $in]} break + incr cnt + regexp {#define\s+SQLITE_VERSION\s+"(.*)"} $line all VERSION +} +close $in + +# Open the output file and write a header comment at the beginning +# of the file. +# +set out [open sqlite3.c w] +set today [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S UTC" -gmt 1] +puts $out [subst \ +{/****************************************************************************** +** This file is an amalgamation of many separate C source files from SQLite +** version $VERSION. By combining all the individual C code files into this +** single large file, the entire code can be compiled as a one translation +** unit. This allows many compilers to do optimizations that would not be +** possible if the files were compiled separately. Performance improvements +** of 5% are more are commonly seen when SQLite is compiled as a single +** translation unit. +** +** This file is all you need to compile SQLite. To use SQLite in other +** programs, you need this file and the "sqlite3.h" header file that defines +** the programming interface to the SQLite library. (If you do not have +** the "sqlite3.h" header file at hand, you will find a copy in the first +** $cnt lines past this header comment.) Additional code files may be +** needed if you want a wrapper to interface SQLite with your choice of +** programming language. The code for the "sqlite3" command-line shell +** is also in a separate file. This file contains only code for the core +** SQLite library. +** +** This amalgamation was generated on $today. +*/ +#define SQLITE_CORE 1 +#define SQLITE_AMALGAMATION 1}] +if {$addstatic} { + puts $out \ +{#ifndef SQLITE_PRIVATE +# define SQLITE_PRIVATE static +#endif +#ifndef SQLITE_API +# define SQLITE_API +#endif} +} + +# These are the header files used by SQLite. The first time any of these +# files are seen in a #include statement in the C code, include the complete +# text of the file in-line. The file only needs to be included once. +# +foreach hdr { + btree.h + btreeInt.h + fts3.h + fts3_hash.h + fts3_tokenizer.h + hash.h + keywordhash.h + mutex.h + opcodes.h + os_common.h + os.h + os_os2.h + pager.h + parse.h + sqlite3ext.h + sqlite3.h + sqliteInt.h + sqliteLimit.h + vdbe.h + vdbeInt.h +} { + set available_hdr($hdr) 1 +} +set available_hdr(sqliteInt.h) 0 + +# 78 stars used for comment formatting. +set s78 \ +{*****************************************************************************} + +# Insert a comment into the code +# +proc section_comment {text} { + global out s78 + set n [string length $text] + set nstar [expr {60 - $n}] + set stars [string range $s78 0 $nstar] + puts $out "/************** $text $stars/" +} + +# Read the source file named $filename and write it into the +# sqlite3.c output file. If any #include statements are seen, +# process them approprately. +# +proc copy_file {filename} { + global seen_hdr available_hdr out addstatic + set tail [file tail $filename] + section_comment "Begin file $tail" + set in [open $filename r] + 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]+)\(} + if {[file extension $filename]==".h"} { + set declpattern " *$declpattern" + } + set declpattern ^$declpattern + while {![eof $in]} { + set line [gets $in] + if {[regexp {^#\s*include\s+["<]([^">]+)[">]} $line all hdr]} { + if {[info exists available_hdr($hdr)]} { + if {$available_hdr($hdr)} { + if {$hdr!="os_common.h"} { + set available_hdr($hdr) 0 + } + section_comment "Include $hdr in the middle of $tail" + copy_file tsrc/$hdr + section_comment "Continuing where we left off in $tail" + } + } elseif {![info exists seen_hdr($hdr)]} { + set seen_hdr($hdr) 1 + puts $out $line + } + } elseif {[regexp {^#ifdef __cplusplus} $line]} { + puts $out "#if 0" + } elseif {[regexp {^#line} $line]} { + # Skip #line directives. + } elseif {$addstatic && ![regexp {^(static|typedef)} $line]} { + if {[regexp $declpattern $line all funcname]} { + # Add the SQLITE_PRIVATE or SQLITE_API keyword before functions. + # so that linkage can be modified at compile-time. + if {[regexp {^sqlite3_} $funcname]} { + puts $out "SQLITE_API $line" + } else { + puts $out "SQLITE_PRIVATE $line" + } + } elseif {[regexp $varpattern $line all varname]} { + # Add the SQLITE_PRIVATE before variable declarations or + # definitions for internal use + if {![regexp {^sqlite3_} $varname]} { + regsub {^extern } $line {} line + puts $out "SQLITE_PRIVATE $line" + } elseif {![regexp {^SQLITE_EXTERN} $line]} { + puts $out "SQLITE_API $line" + } else { + puts $out $line + } + } elseif {[regexp {^void \(\*sqlite3_io_trace\)} $line]} { + puts $out "SQLITE_API $line" + } else { + puts $out $line + } + } else { + puts $out $line + } + } + close $in + section_comment "End of $tail" +} + + +# Process the source files. Process files containing commonly +# used subroutines first in order to help the compiler find +# inlining opportunities. +# +foreach file { + sqliteInt.h + + date.c + os.c + + fault.c + mem1.c + mem2.c + mem3.c + mutex.c + mutex_os2.c + mutex_unix.c + mutex_w32.c + malloc.c + printf.c + random.c + utf.c + util.c + hash.c + opcodes.c + + os_os2.c + os_unix.c + os_win.c + + pager.c + + btmutex.c + btree.c + + vdbefifo.c + vdbemem.c + vdbeaux.c + vdbeapi.c + vdbe.c + vdbeblob.c + journal.c + + expr.c + alter.c + analyze.c + attach.c + auth.c + build.c + callback.c + delete.c + func.c + insert.c + legacy.c + loadext.c + pragma.c + prepare.c + select.c + table.c + trigger.c + update.c + vacuum.c + vtab.c + where.c + + parse.c + + tokenize.c + complete.c + + main.c + + fts3.c + fts3_hash.c + fts3_porter.c + fts3_tokenizer.c + fts3_tokenizer1.c +} { + copy_file tsrc/$file +} + +close $out diff --git a/ext/pdo_sqlite/sqlite/tool/mksqlite3internalh.tcl b/ext/pdo_sqlite/sqlite/tool/mksqlite3internalh.tcl new file mode 100644 index 0000000000..cacd7cbe18 --- /dev/null +++ b/ext/pdo_sqlite/sqlite/tool/mksqlite3internalh.tcl @@ -0,0 +1,145 @@ +#!/usr/bin/tclsh +# +# To build a single huge source file holding all of SQLite (or at +# least the core components - the test harness, shell, and TCL +# interface are omitted.) first do +# +# make target_source +# +# The make target above moves all of the source code files into +# a subdirectory named "tsrc". (This script expects to find the files +# there and will not work if they are not found.) There are a few +# generated C code files that are also added to the tsrc directory. +# For example, the "parse.c" and "parse.h" files to implement the +# the parser are derived from "parse.y" using lemon. And the +# "keywordhash.h" files is generated by a program named "mkkeywordhash". +# +# After the "tsrc" directory has been created and populated, run +# this script: +# +# tclsh mksqlite3c.tcl +# +# The amalgamated SQLite code will be written into sqlite3.c +# + +# Begin by reading the "sqlite3.h" header file. Count the number of lines +# in this file and extract the version number. That information will be +# needed in order to generate the header of the amalgamation. +# +set in [open tsrc/sqlite3.h] +set cnt 0 +set VERSION ????? +while {![eof $in]} { + set line [gets $in] + if {$line=="" && [eof $in]} break + incr cnt + regexp {#define\s+SQLITE_VERSION\s+"(.*)"} $line all VERSION +} +close $in + +# Open the output file and write a header comment at the beginning +# of the file. +# +set out [open sqlite3internal.h w] +set today [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S UTC" -gmt 1] +puts $out [subst \ +{/****************************************************************************** +** This file is an amalgamation of many private header files from SQLite +** version $VERSION. +*/}] + +# These are the header files used by SQLite. The first time any of these +# files are seen in a #include statement in the C code, include the complete +# text of the file in-line. The file only needs to be included once. +# +foreach hdr { + btree.h + btreeInt.h + hash.h + keywordhash.h + opcodes.h + os_common.h + os.h + os_os2.h + pager.h + parse.h + sqlite3ext.h + sqlite3.h + sqliteInt.h + sqliteLimit.h + vdbe.h + vdbeInt.h +} { + set available_hdr($hdr) 1 +} + +# 78 stars used for comment formatting. +set s78 \ +{*****************************************************************************} + +# Insert a comment into the code +# +proc section_comment {text} { + global out s78 + set n [string length $text] + set nstar [expr {60 - $n}] + set stars [string range $s78 0 $nstar] + puts $out "/************** $text $stars/" +} + +# Read the source file named $filename and write it into the +# sqlite3.c output file. If any #include statements are seen, +# process them approprately. +# +proc copy_file {filename} { + global seen_hdr available_hdr out + set tail [file tail $filename] + section_comment "Begin file $tail" + set in [open $filename r] + while {![eof $in]} { + set line [gets $in] + if {[regexp {^#\s*include\s+["<]([^">]+)[">]} $line all hdr]} { + if {[info exists available_hdr($hdr)]} { + if {$available_hdr($hdr)} { + section_comment "Include $hdr in the middle of $tail" + copy_file tsrc/$hdr + section_comment "Continuing where we left off in $tail" + } + } elseif {![info exists seen_hdr($hdr)]} { + set seen_hdr($hdr) 1 + puts $out $line + } + } elseif {[regexp {^#ifdef __cplusplus} $line]} { + puts $out "#if 0" + } elseif {[regexp {^#line} $line]} { + # Skip #line directives. + } else { + puts $out $line + } + } + close $in + section_comment "End of $tail" +} + + +# Process the source files. Process files containing commonly +# used subroutines first in order to help the compiler find +# inlining opportunities. +# +foreach file { + sqliteInt.h + sqlite3.h + btree.h + hash.h + os.h + pager.h + parse.h + sqlite3ext.h + vdbe.h +} { + if {$available_hdr($file)} { + copy_file tsrc/$file + } +} + +close $out diff --git a/ext/pdo_sqlite/sqlite/tool/omittest.tcl b/ext/pdo_sqlite/sqlite/tool/omittest.tcl new file mode 100644 index 0000000000..baf69c3151 --- /dev/null +++ b/ext/pdo_sqlite/sqlite/tool/omittest.tcl @@ -0,0 +1,176 @@ + +set rcsid {$Id: omittest.tcl,v 1.2 2008-03-07 10:55:14 scottmac Exp $} + +# Documentation for this script. This may be output to stderr +# if the script is invoked incorrectly. +set ::USAGE_MESSAGE { +This Tcl script is used to test the various compile time options +available for omitting code (the SQLITE_OMIT_xxx options). It +should be invoked as follows: + + <script> ?-makefile PATH-TO-MAKEFILE? + +The default value for ::MAKEFILE is "../Makefile.linux.gcc". + +This script builds the testfixture program and runs the SQLite test suite +once with each SQLITE_OMIT_ option defined and then once with all options +defined together. Each run is performed in a seperate directory created +as a sub-directory of the current directory by the script. The output +of the build is saved in <sub-directory>/build.log. The output of the +test-suite is saved in <sub-directory>/test.log. + +Almost any SQLite makefile (except those generated by configure - see below) +should work. The following properties are required: + + * The makefile should support the "testfixture" target. + * The makefile should support the "test" target. + * The makefile should support the variable "OPTS" as a way to pass + options from the make command line to lemon and the C compiler. + +More precisely, the following two invocations must be supported: + + make -f $::MAKEFILE testfixture OPTS="-DSQLITE_OMIT_ALTERTABLE=1" + make -f $::MAKEFILE test + +Makefiles generated by the sqlite configure program cannot be used as +they do not respect the OPTS variable. +} + + +# Build a testfixture executable and run quick.test using it. The first +# parameter is the name of the directory to create and use to run the +# test in. The second parameter is a list of OMIT symbols to define +# when doing so. For example: +# +# run_quick_test /tmp/testdir {SQLITE_OMIT_TRIGGER SQLITE_OMIT_VIEW} +# +# +proc run_quick_test {dir omit_symbol_list} { + # Compile the value of the OPTS Makefile variable. + set opts "-DSQLITE_MEMDEBUG=2 -DSQLITE_DEBUG -DOS_UNIX" + foreach sym $omit_symbol_list { + append opts " -D${sym}=1" + } + + # Create the directory and do the build. If an error occurs return + # early without attempting to run the test suite. + file mkdir $dir + puts -nonewline "Building $dir..." + flush stdout + set rc [catch { + exec make -C $dir -f $::MAKEFILE testfixture OPTS=$opts >& $dir/build.log + }] + if {$rc} { + puts "No good. See $dir/build.log." + return + } else { + puts "Ok" + } + + # Create an empty file "$dir/sqlite3". This is to trick the makefile out + # of trying to build the sqlite shell. The sqlite shell won't build + # with some of the OMIT options (i.e OMIT_COMPLETE). + if {![file exists $dir/sqlite3]} { + set wr [open $dir/sqlite3 w] + puts $wr "dummy" + close $wr + } + + # Run the test suite. + puts -nonewline "Testing $dir..." + flush stdout + set rc [catch { + exec make -C $dir -f $::MAKEFILE test OPTS=$opts >& $dir/test.log + }] + if {$rc} { + puts "No good. See $dir/test.log." + } else { + puts "Ok" + } +} + + +# This proc processes the command line options passed to this script. +# Currently the only option supported is "-makefile", default +# "../Makefile.linux-gcc". Set the ::MAKEFILE variable to the value of this +# option. +# +proc process_options {argv} { + set ::MAKEFILE ../Makefile.linux-gcc ;# Default value + for {set i 0} {$i < [llength $argv]} {incr i} { + switch -- [lindex $argv $i] { + -makefile { + incr i + set ::MAKEFILE [lindex $argv $i] + } + + default { + puts stderr [string trim $::USAGE_MESSAGE] + exit -1 + } + } + set ::MAKEFILE [file normalize $::MAKEFILE] + } +} + +# Main routine. +# + +proc main {argv} { + # List of SQLITE_OMIT_XXX symbols supported by SQLite. + set ::SYMBOLS [list \ + SQLITE_OMIT_ALTERTABLE \ + SQLITE_OMIT_AUTHORIZATION \ + SQLITE_OMIT_AUTOINCREMENT \ + SQLITE_OMIT_AUTOVACUUM \ + SQLITE_OMIT_BLOB_LITERAL \ + SQLITE_OMIT_COMPLETE \ + SQLITE_OMIT_COMPOUND_SELECT \ + SQLITE_OMIT_CONFLICT_CLAUSE \ + SQLITE_OMIT_DATETIME_FUNCS \ + SQLITE_OMIT_EXPLAIN \ + SQLITE_OMIT_FLOATING_POINT \ + SQLITE_OMIT_FOREIGN_KEY \ + SQLITE_OMIT_INCRBLOB \ + SQLITE_OMIT_INTEGRITY_CHECK \ + SQLITE_OMIT_MEMORYDB \ + SQLITE_OMIT_PAGER_PRAGMAS \ + SQLITE_OMIT_PRAGMA \ + SQLITE_OMIT_PROGRESS_CALLBACK \ + SQLITE_OMIT_REINDEX \ + SQLITE_OMIT_SCHEMA_PRAGMAS \ + SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS \ + SQLITE_OMIT_SUBQUERY \ + SQLITE_OMIT_TCL_VARIABLE \ + SQLITE_OMIT_TRIGGER \ + SQLITE_OMIT_UTF16 \ + SQLITE_OMIT_VACUUM \ + SQLITE_OMIT_VIEW \ + SQLITE_OMIT_VIRTUALTABLE \ + ] + + # Process any command line options. + process_options $argv + + # First try a test with all OMIT symbols except SQLITE_OMIT_FLOATING_POINT + # and SQLITE_OMIT_PRAGMA defined. The former doesn't work (causes segfaults) + # and the latter is currently incompatible with the test suite (this should + # be fixed, but it will be a lot of work). + set allsyms [list] + foreach s $::SYMBOLS { + if {$s!="SQLITE_OMIT_FLOATING_POINT" && $s!="SQLITE_OMIT_PRAGMA"} { + lappend allsyms $s + } + } + run_quick_test test_OMIT_EVERYTHING $allsyms + + # Now try one quick.test with each of the OMIT symbols defined. Included + # are the OMIT_FLOATING_POINT and OMIT_PRAGMA symbols, even though we + # know they will fail. It's good to be reminded of this from time to time. + foreach sym $::SYMBOLS { + set dirname "test_[string range $sym 7 end]" + run_quick_test $dirname $sym + } +} + +main $argv diff --git a/ext/pdo_sqlite/sqlite/tool/soak1.tcl b/ext/pdo_sqlite/sqlite/tool/soak1.tcl new file mode 100644 index 0000000000..7a78b8d7dd --- /dev/null +++ b/ext/pdo_sqlite/sqlite/tool/soak1.tcl @@ -0,0 +1,103 @@ +#!/usr/bin/tclsh +# +# Usage: +# +# tclsh soak1.tcl local-makefile.mk ?target? ?scenario? +# +# This generates many variations on local-makefile.mk (by modifing +# the OPT = lines) and runs them will fulltest, one by one. The +# constructed makefiles are named "soak1.mk". +# +# If ?target? is provided, that is the makefile target that is run. +# The default is "fulltest" +# +# If ?scenario? is provided, it is the name of a single scenario to +# be run. All other scenarios are skipped. +# +set localmake [lindex $argv 0] +set target [lindex $argv 1] +set scene [lindex $argv 2] +if {$target==""} {set target fulltest} +if {$scene==""} {set scene all} + +set in [open $localmake] +set maketxt [read $in] +close $in +regsub -all {\\\n} $maketxt {} maketxt +#set makefilename "soak1-[expr {int(rand()*1000000000)}].mk" +set makefilename "soak1.mk" + +# Generate a makefile +# +proc generate_makefile {pattern} { + global makefilename maketxt + set out [open $makefilename w] + set seen_opt 0 + foreach line [split $maketxt \n] { + if {[regexp {^ *#? *OPTS[ =+]} $line]} { + if {!$seen_opt} { + puts $out "OPTS += -DSQLITE_NO_SYNC=1" + foreach x $pattern { + puts $out "OPTS += -D$x" + } + set seen_opt 1 + } + } else { + puts $out $line + } + } + close $out +} + +# Run a test +# +proc scenario {id title pattern} { + global makefilename target scene + if {$scene!="all" && $scene!=$id && $scene!=$title} return + puts "**************** $title ***************" + generate_makefile $pattern + exec make -f $makefilename clean >@stdout 2>@stdout + exec make -f $makefilename $target >@stdout 2>@stdout +} + +############################################################################### +# Add new scenarios here +# +scenario 0 {Default} {} +scenario 1 {Debug} { + SQLITE_DEBUG=1 + SQLITE_MEMDEBUG=1 +} +scenario 2 {Everything} { + SQLITE_DEBUG=1 + SQLITE_MEMDEBUG=1 + SQLITE_ENABLE_MEMORY_MANAGEMENT=1 + SQLITE_ENABLE_COLUMN_METADATA=1 + SQLITE_ENABLE_LOAD_EXTENSION=1 HAVE_DLOPEN=1 + SQLITE_ENABLE_MEMORY_MANAGEMENT=1 +} +scenario 3 {Customer-1} { + SQLITE_DEBUG=1 SQLITE_MEMDEBUG=1 + THREADSAFE=1 OS_UNIX=1 + SQLITE_DISABLE_LFS=1 + SQLITE_DEFAULT_AUTOVACUUM=1 + SQLITE_DEFAULT_PAGE_SIZE=1024 + SQLITE_MAX_PAGE_SIZE=4096 + SQLITE_DEFAULT_CACHE_SIZE=64 + SQLITE_DEFAULT_TEMP_CACHE_SIZE=32 + TEMP_STORE=3 + SQLITE_OMIT_PROGRESS_CALLBACK=1 + SQLITE_OMIT_LOAD_EXTENSION=1 + SQLITE_OMIT_VIRTUALTABLE=1 + SQLITE_ENABLE_IOTRACE=1 +} +scenario 4 {Small-Cache} { + SQLITE_DEBUG=1 SQLITE_MEMDEBUG=1 + THREADSAFE=1 OS_UNIX=1 + SQLITE_DEFAULT_AUTOVACUUM=1 + SQLITE_DEFAULT_PAGE_SIZE=1024 + SQLITE_MAX_PAGE_SIZE=2048 + SQLITE_DEFAULT_CACHE_SIZE=13 + SQLITE_DEFAULT_TEMP_CACHE_SIZE=11 + TEMP_STORE=1 +} diff --git a/ext/pdo_sqlite/sqlite/tool/spaceanal.tcl b/ext/pdo_sqlite/sqlite/tool/spaceanal.tcl index c0fe5b5d87..3718357cbb 100644 --- a/ext/pdo_sqlite/sqlite/tool/spaceanal.tcl +++ b/ext/pdo_sqlite/sqlite/tool/spaceanal.tcl @@ -26,6 +26,10 @@ if {[file size $file_to_analyze]<512} { exit 1 } +# Maximum distance between pages before we consider it a "gap" +# +set MAXGAP 3 + # Open the database # sqlite3 db [lindex $argv 0] @@ -53,12 +57,17 @@ set tabledef\ ovfl_pages int, -- Number of overflow pages used int_unused int, -- Number of unused bytes on interior pages leaf_unused int, -- Number of unused bytes on primary pages - ovfl_unused int -- Number of unused bytes on overflow pages + ovfl_unused int, -- Number of unused bytes on overflow pages + gap_cnt int -- Number of gaps in the page layout );} mem eval $tabledef proc integerify {real} { - return [expr int($real)] + if {[string is double -strict $real]} { + return [expr {int($real)}] + } else { + return 0 + } } mem function int integerify @@ -105,7 +114,8 @@ proc cursor_info {arrayvar csr {up 0}} { a(payload_bytes) \ a(header_bytes) \ a(local_payload_bytes) \ - a(parent) ] [btree_cursor_info $csr $up] {} + a(parent) \ + a(first_ovfl) ] [btree_cursor_info $csr $up] break } # Determine the page-size of the database. This global variable is used @@ -119,7 +129,8 @@ set pageSize [db eval {PRAGMA page_size}] # database, including the sqlite_master table. # set sql { - SELECT name, rootpage FROM sqlite_master WHERE type='table' + SELECT name, rootpage FROM sqlite_master + WHERE type='table' AND rootpage>0 UNION ALL SELECT 'sqlite_master', 1 ORDER BY 1 @@ -144,6 +155,8 @@ foreach {name rootpage} [db eval $sql] { set ovfl_pages $wideZero ;# Number of overflow pages used set leaf_pages $wideZero ;# Number of leaf pages set int_pages $wideZero ;# Number of interior pages + set gap_cnt 0 ;# Number of holes in the page sequence + set prev_pgno 0 ;# Last page number seen # As the btree is traversed, the array variable $seen($pgno) is set to 1 # the first time page $pgno is encountered. @@ -179,6 +192,9 @@ foreach {name rootpage} [db eval $sql] { set n [expr {int(ceil($ovfl/($pageSize-4.0)))}] incr ovfl_pages $n incr unused_ovfl [expr {$n*($pageSize-4) - $ovfl}] + set pglist [btree_ovfl_info $DB $csr] + } else { + set pglist {} } # If this is the first table entry analyzed for the page, then update @@ -190,6 +206,7 @@ foreach {name rootpage} [db eval $sql] { set seen($ci(page_no)) 1 incr leaf_pages incr unused_leaf $ci(page_freebytes) + set pglist "$ci(page_no) $pglist" # Now check if the page has a parent that has not been analyzed. If # so, update the $int_pages, $cnt_int_entry and $unused_int statistics @@ -209,7 +226,19 @@ foreach {name rootpage} [db eval $sql] { incr int_pages incr cnt_int_entry $ci(page_entries) incr unused_int $ci(page_freebytes) + + # parent pages come before their first child + set pglist "$ci(page_no) $pglist" + } + } + + # Check the page list for fragmentation + # + foreach pg $pglist { + if {$pg!=$prev_pgno+1 && $prev_pgno>0} { + incr gap_cnt } + set prev_pgno $pg } } btree_close_cursor $csr @@ -249,6 +278,7 @@ foreach {name rootpage} [db eval $sql] { append sql ",$unused_int" append sql ",$unused_leaf" append sql ",$unused_ovfl" + append sql ",$gap_cnt" append sql ); mem eval $sql } @@ -278,6 +308,8 @@ foreach {name tbl_name rootpage} [db eval $sql] { set mx_payload $wideZero ;# Maximum payload size set ovfl_pages $wideZero ;# Number of overflow pages used set leaf_pages $wideZero ;# Number of leaf pages + set gap_cnt 0 ;# Number of holes in the page sequence + set prev_pgno 0 ;# Last page number seen # As the btree is traversed, the array variable $seen($pgno) is set to 1 # the first time page $pgno is encountered. @@ -323,6 +355,11 @@ foreach {name tbl_name rootpage} [db eval $sql] { set seen($ci(page_no)) 1 incr leaf_pages incr unused_leaf $ci(page_freebytes) + set pg $ci(page_no) + if {$prev_pgno>0 && $pg!=$prev_pgno+1} { + incr gap_cnt + } + set prev_pgno $ci(page_no) } } btree_close_cursor $csr @@ -354,6 +391,7 @@ foreach {name tbl_name rootpage} [db eval $sql] { append sql ",0" append sql ",$unused_leaf" append sql ",$unused_ovfl" + append sql ",$gap_cnt" append sql ); mem eval $sql } @@ -419,7 +457,8 @@ proc subreport {title where} { int(sum(ovfl_pages)) AS ovfl_pages, int(sum(leaf_unused)) AS leaf_unused, int(sum(int_unused)) AS int_unused, - int(sum(ovfl_unused)) AS ovfl_unused + int(sum(ovfl_unused)) AS ovfl_unused, + int(sum(gap_cnt)) AS gap_cnt FROM space_used WHERE $where" {} {} # Output the sub-report title, nicely decorated with * characters. @@ -475,6 +514,10 @@ proc subreport {title where} { if {[info exists avg_fanout]} { statline {Average fanout} $avg_fanout } + if {$total_pages>1} { + set fragmentation [percent $gap_cnt [expr {$total_pages-1}] {fragmentation}] + statline {Fragmentation} $fragmentation + } statline {Maximum payload per entry} $mx_payload statline {Entries that use overflow} $ovfl_cnt $ovfl_cnt_percent if {$int_pages>0} { @@ -582,7 +625,9 @@ set user_percent [percent $user_payload $file_bytes] # Output the summary statistics calculated above. # puts "/** Disk-Space Utilization Report For $file_to_analyze" -puts "*** As of [clock format [clock seconds] -format {%Y-%b-%d %H:%M:%S}]" +catch { + puts "*** As of [clock format [clock seconds] -format {%Y-%b-%d %H:%M:%S}]" +} puts "" statline {Page size in bytes} $pageSize statline {Pages in the whole file (measured)} $file_pgcnt @@ -728,6 +773,14 @@ Average unused bytes per entry category on a per-entry basis. This is the number of unused bytes on all pages divided by the number of entries. +Fragmentation + + The percentage of pages in the table or index that are not + consecutive in the disk file. Many filesystems are optimized + for sequential file access so smaller fragmentation numbers + sometimes result in faster queries, especially for larger + database files that do not fit in the disk cache. + Maximum payload per entry The largest payload size of any entry. |
