-C Change\sthe\sBTree\sso\sthat\sit\suses\sthe\sPagers\stemporary\spage\sspace\swhen\nreorganizing\sthe\srows\son\sa\spage,\srather\sthan\smallocing\sfor\sspace\sof\nits\sown.\s\sIn\sthis\sway,\swe\savoid\shaving\sto\sdeal\swith\sa\smalloc\sfailure\ndeep\sdown\sinside\sthe\spage\sreorganizer.\s\sTicket\s#2806\s(CVS\s4577)
-D 2007-11-28T16:19:56
+C Add\sthe\s{quote:\sStrAccum}\sobject\r\n\sfor\saccumulating\sstrings.\s\sRevamp\sxprintf\sto\suse\r\nthe\snew\sobject.\s\sRewrite\sthe\sgroup_concat()\sfunction\sto\suse\sthe\snew\sobject.\r\nProductize\sand\stest\sthe\sgroup_concat()\sfunction.\s(CVS\s4578)
+D 2007-11-28T22:36:41
F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7
F Makefile.in 35396fd58890420b29edcf27b6c0e2d054862a6b
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
F src/delete.c 034b87768c4135a22038a86a205f9d2d5f68a143
F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b
F src/expr.c 7977bb9680ebeeabfa3214d936778baaa26dcc0c
-F src/func.c 73b4974e5ff03cc71345cc3a33b0022f7b99974a
+F src/func.c 49f98cfe26b9ab507b96a34404295c4d89dfc620
F src/hash.c 45a7005aac044b6c86bd7e49c44bc15d30006d6c
F src/hash.h 031cd9f915aff27e12262cb9eb570ac1b8326b53
F src/insert.c a090c7258f2be707cca8f0cf376142f141621241
F src/parse.y a780b33ef45dd7b3272319cf91e609d6f109a31c
F src/pragma.c cb1486e76dbcad757968afc4083d3472032e62b5
F src/prepare.c f811fdb6fd4a82cca673a6e1d5b041d6caf567f1
-F src/printf.c 96c8d55315a13fc53cb3754cb15046f3ff891ea2
+F src/printf.c 0d7ad185914c952bfa6235ac8f5f019db5ad37e5
F src/random.c 4a22746501bf36b0a088c66e38dde5daba6a35da
F src/select.c 7c0ab94b8f287eb94fdb1eb101be603832ecfc34
F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
F src/shell.c c97be281cfc3dcb14902f45e4b16f20038eb83ff
F src/sqlite.h.in 75ae0863db3a0b074868a6157e34b646dbe143dd
F src/sqlite3ext.h a93f59cdee3638dc0c9c086f80df743a4e68c3cb
-F src/sqliteInt.h 22fb7a7271dbabca410d71c6ad80897f063356b1
+F src/sqliteInt.h c724e97dcc7d9c3881ce6443f243afccfe6b10a8
F src/sqliteLimit.h 15ffe2116746c27ace2b428a26a4fcd6dba6fa65
F src/table.c 1aeb9eab57b4235db86fe15a35dec76fb445a9c4
F src/tclsqlite.c 9923abeffc9b3d7dad58e92b319661521f60debf
F test/fts3atoken.test 25c2070e1e8755d414bf9c8200427b277a9f99fa
F test/fts3b.test b3a25180a633873d37d86e1ccd00ed690d37237a
F test/fts3near.test 2d4dadcaac5025ab65bb87e66c45f39e92966194
-F test/func.test fd05232dffa77492c473f5a71d2cde6cb0ccfb1a
+F test/func.test 4d54202f6a1c8498444d9efe460851b02a1e8e4f
F test/fuzz.test 62fc19dd36a427777fd671b569df07166548628a
F test/fuzz2.test ea38692ce2da99ad79fe0be5eb1a452c1c4d37bb
F test/fuzz_common.tcl ff4bc2dfc465f6878f8e2d819620914365382731
F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9
F test/lock4.test f358fa835dff485d462072eee991111f09e87441
F test/main.test 05f585bb70c05caac3e047903b517cbb319ed204
-F test/malloc.test 93fca57780e221192c74e51c59c4291768f33897
+F test/malloc.test debc0cb41b7031b316353b949960a70e444fd301
F test/malloc2.test 850471731efad72af5a7748e366a371933ff0b64
F test/malloc3.test 3d690cbd66c93a3d41606ed8cfcbe1c9853e9d83
F test/malloc4.test f0e5e0f639f61e2776a6c3f5308f836b3ad8b3c7
F test/speed2.test 53177056baf6556dcbdcf032bbdfc41c1aa74ded
F test/speed3.test e312d7e442a5047d730569fdae2ba99bc94e1a13
F test/speed4.test 20d8ea20bea3ca09c3ef3b5ec820a17e58e132cb
-F test/sqllimits1.test 3b08a538c9828041a5c1454293594d922602044d
+F test/sqllimits1.test e7a6c34f6915c334a66db8065484d686dbabece0
F test/subquery.test 8203f85db56ba022a57a0589890090c8feed4e59
F test/subselect.test 974e87f8fc91c5f00dd565316d396a5a6c3106c4
F test/substr.test 4be572ac017143e59b4058dc75c91a0d0dc6d4e0
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 542e11f954983ae26fef4ea850c8b2a20f738edd
-R 21c1d3a78e0cb66c070fb743696cb747
+P 98960132dc082da61652201f4bd2b559725350c0
+R 099384e3de985a880eea8f610db48bc7
U drh
-Z 6091ee38d7896a754234ddd4a74396ad
+Z 25fbe708392dfcc01a46aeda08531786
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
** All other code has file scope.
**
-** $Id: func.c,v 1.176 2007/11/01 17:38:31 drh Exp $
+** $Id: func.c,v 1.177 2007/11/28 22:36:41 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
}
}
-#ifdef SQLITE_GROUP_CONCAT
/*
** group_concat(EXPR, ?SEPARATOR?)
*/
sqlite3_value **argv
){
const char *zVal;
- char **pzAccumulator;
+ StrAccum *pAccum;
const char *zSep;
+ int nVal, nSep;
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
- zVal = sqlite3_value_text(argv[0]);
- pzAccumulator = (char**)sqlite3_aggregate_context(context, sizeof(char*));
- if( pzAccumulator ){
- if( *pzAccumulator==0 ){
- *pzAccumulator = sqlite3_mprintf("%s", zVal);
- }else{
+ pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum));
+
+ if( pAccum ){
+ pAccum->useMalloc = 1;
+ if( pAccum->nChar ){
if( argc==2 ){
- zSep = sqlite3_value_text(argv[1]);
+ zSep = (char*)sqlite3_value_text(argv[1]);
+ nSep = sqlite3_value_bytes(argv[1]);
}else{
zSep = ",";
+ nSep = 1;
}
- *pzAccumulator = sqlite3_mprintf("%z%s%s", *pzAccumulator, zSep, zVal);
+ sqlite3StrAccumAppend(pAccum, zSep, nSep);
}
+ zVal = (char*)sqlite3_value_text(argv[0]);
+ nVal = sqlite3_value_bytes(argv[0]);
+ sqlite3StrAccumAppend(pAccum, zVal, nVal);
}
}
static void groupConcatFinalize(sqlite3_context *context){
- char **pzAccum;
- pzAccum = sqlite3_aggregate_context(context, 0);
- if( pzAccum ){
- sqlite3_result_text(context, *pzAccum, -1, sqlite3_free);
+ StrAccum *pAccum;
+ pAccum = sqlite3_aggregate_context(context, 0);
+ if( pAccum ){
+ if( pAccum->tooBig ){
+ sqlite3_result_error_toobig(context);
+ }else if( pAccum->mallocFailed ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1,
+ sqlite3_free);
+ }
}
}
-#endif /*SQLITE_GROUP_CONCAT*/
/*
** This function registered all of the above C functions as SQL
{ "avg", 1, 0, 0, sumStep, avgFinalize },
{ "count", 0, 0, 0, countStep, countFinalize },
{ "count", 1, 0, 0, countStep, countFinalize },
-#ifdef SQLITE_GROUP_CONCAT
{ "group_concat", 1, 0, 0, groupConcatStep, groupConcatFinalize },
{ "group_concat", 2, 0, 0, groupConcatStep, groupConcatFinalize },
-#endif
};
int i;
}
#endif /* SQLITE_OMIT_FLOATING_POINT */
+/*
+** Append N space characters to the given string buffer.
+*/
+static void appendSpace(StrAccum *pAccum, int N){
+ static const char zSpaces[] = " ";
+ while( N>=sizeof(zSpaces)-1 ){
+ sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
+ N -= sizeof(zSpaces)-1;
+ }
+ if( N>0 ){
+ sqlite3StrAccumAppend(pAccum, zSpaces, N);
+ }
+}
+
/*
** On machines with a small stack size, you can redefine the
** SQLITE_PRINT_BUF_SIZE to be less than 350. But beware - for
** seems to make a big difference in determining how fast this beast
** will run.
*/
-static int vxprintf(
- void (*func)(void*,const char*,int), /* Consumer of text */
- void *arg, /* First argument to the consumer */
+static void vxprintf(
+ StrAccum *pAccum, /* Accumulate results here */
int useExtended, /* Allow extended %-conversions */
const char *fmt, /* Format string */
va_list ap /* arguments */
int precision; /* Precision of the current field */
int length; /* Length of the field */
int idx; /* A general purpose loop counter */
- int count; /* Total number of characters output */
int width; /* Width of the current field */
etByte flag_leftjustify; /* True if "-" flag is present */
etByte flag_plussign; /* True if "+" flag is present */
etByte errorflag = 0; /* True if an error is encountered */
etByte xtype; /* Conversion paradigm */
char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
- static const char spaces[] =
- " ";
-#define etSPACESIZE (sizeof(spaces)-1)
#ifndef SQLITE_OMIT_FLOATING_POINT
int exp, e2; /* exponent of real numbers */
double rounder; /* Used for rounding floating point values */
int nsd; /* Number of significant digits returned */
#endif
- func(arg,"",0);
- count = length = 0;
+ length = 0;
bufpt = 0;
for(; (c=(*fmt))!=0; ++fmt){
if( c!='%' ){
bufpt = (char *)fmt;
amt = 1;
while( (c=(*++fmt))!='%' && c!=0 ) amt++;
- (*func)(arg,bufpt,amt);
- count += amt;
+ sqlite3StrAccumAppend(pAccum, bufpt, amt);
if( c==0 ) break;
}
if( (c=(*++fmt))==0 ){
errorflag = 1;
- (*func)(arg,"%",1);
- count++;
+ sqlite3StrAccumAppend(pAccum, "%", 1);
break;
}
/* Find out what flags are present */
if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
xtype = infop->type;
}else{
- return -1;
+ return;
}
break;
}
}
zExtra = 0;
if( infop==0 ){
- return -1;
+ return;
}
#endif
break;
case etSIZE:
- *(va_arg(ap,int*)) = count;
+ *(va_arg(ap,int*)) = pAccum->nChar;
length = width = 0;
break;
case etPERCENT:
n += i + 1 + needQuote*2;
if( n>etBUFSIZE ){
bufpt = zExtra = sqlite3_malloc( n );
- if( bufpt==0 ) return -1;
+ if( bufpt==0 ) return;
}else{
bufpt = buf;
}
case etTOKEN: {
Token *pToken = va_arg(ap, Token*);
if( pToken && pToken->z ){
- (*func)(arg, (char*)pToken->z, pToken->n);
+ sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
}
length = width = 0;
break;
struct SrcList_item *pItem = &pSrc->a[k];
assert( k>=0 && k<pSrc->nSrc );
if( pItem->zDatabase && pItem->zDatabase[0] ){
- (*func)(arg, pItem->zDatabase, strlen(pItem->zDatabase));
- (*func)(arg, ".", 1);
+ sqlite3StrAccumAppend(pAccum, pItem->zDatabase, -1);
+ sqlite3StrAccumAppend(pAccum, ".", 1);
}
- (*func)(arg, pItem->zName, strlen(pItem->zName));
+ sqlite3StrAccumAppend(pAccum, pItem->zName, -1);
length = width = 0;
break;
}
register int nspace;
nspace = width-length;
if( nspace>0 ){
- count += nspace;
- while( nspace>=etSPACESIZE ){
- (*func)(arg,spaces,etSPACESIZE);
- nspace -= etSPACESIZE;
- }
- if( nspace>0 ) (*func)(arg,spaces,nspace);
+ appendSpace(pAccum, nspace);
}
}
if( length>0 ){
- (*func)(arg,bufpt,length);
- count += length;
+ sqlite3StrAccumAppend(pAccum, bufpt, length);
}
if( flag_leftjustify ){
register int nspace;
nspace = width-length;
if( nspace>0 ){
- count += nspace;
- while( nspace>=etSPACESIZE ){
- (*func)(arg,spaces,etSPACESIZE);
- nspace -= etSPACESIZE;
- }
- if( nspace>0 ) (*func)(arg,spaces,nspace);
+ appendSpace(pAccum, nspace);
}
}
if( zExtra ){
sqlite3_free(zExtra);
}
}/* End for loop over the format string */
- return errorflag ? -1 : count;
} /* End of function */
-
-/* This structure is used to store state information about the
-** write to memory that is currently in progress.
-*/
-struct sgMprintf {
- char *zBase; /* A base allocation */
- char *zText; /* The string collected so far */
- int nChar; /* Length of the string so far */
- int nTotal; /* Output size if unconstrained */
- int nAlloc; /* Amount of space allocated in zText */
- void *(*xRealloc)(void*,int); /* Function used to realloc memory */
- int iMallocFailed; /* True if xRealloc() has failed */
-};
-
-/*
-** This function implements the callback from vxprintf.
-**
-** This routine add nNewChar characters of text in zNewText to
-** the sgMprintf structure pointed to by "arg".
+/*
+** Append N bytes of text from z to the StrAccum object.
*/
-static void mout(void *arg, const char *zNewText, int nNewChar){
- struct sgMprintf *pM = (struct sgMprintf*)arg;
- if( pM->iMallocFailed ) return;
- pM->nTotal += nNewChar;
- if( pM->zText ){
- if( pM->nChar + nNewChar + 1 > pM->nAlloc ){
- if( pM->xRealloc==0 ){
- nNewChar = pM->nAlloc - pM->nChar - 1;
- }else{
- int nAlloc = pM->nChar + nNewChar*2 + 1;
- if( pM->zText==pM->zBase ){
- pM->zText = pM->xRealloc(0, nAlloc);
- if( pM->zText==0 ){
- pM->nAlloc = 0;
- pM->iMallocFailed = 1;
- return;
- }else if( pM->nChar ){
- memcpy(pM->zText, pM->zBase, pM->nChar);
- }
- }else{
- char *zNew;
- zNew = pM->xRealloc(pM->zText, nAlloc);
- if( zNew ){
- pM->zText = zNew;
- }else{
- pM->iMallocFailed = 1;
- pM->xRealloc(pM->zText, 0);
- pM->zText = 0;
- pM->nAlloc = 0;
- return;
- }
+void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
+ if( p->tooBig | p->mallocFailed ){
+ return;
+ }
+ if( N<0 ){
+ N = strlen(z);
+ }
+ if( N==0 ){
+ return;
+ }
+ if( p->nChar+N >= p->nAlloc ){
+ char *zNew;
+ if( !p->useMalloc ){
+ p->tooBig = 1;
+ N = p->nAlloc - p->nChar - 1;
+ if( N<=0 ){
+ return;
+ }
+ }else{
+ p->nAlloc += p->nAlloc + N + 1;
+ if( p->nAlloc > SQLITE_MAX_LENGTH ){
+ p->nAlloc = SQLITE_MAX_LENGTH;
+ if( p->nChar+N >= p->nAlloc ){
+ sqlite3StrAccumReset(p);
+ p->tooBig = 1;
+ return;
}
- pM->nAlloc = nAlloc;
+ }
+ zNew = sqlite3_malloc( p->nAlloc );
+ if( zNew ){
+ memcpy(zNew, p->zText, p->nChar);
+ sqlite3StrAccumReset(p);
+ p->zText = zNew;
+ }else{
+ p->mallocFailed = 1;
+ sqlite3StrAccumReset(p);
+ return;
}
}
- if( nNewChar>0 ){
- memcpy(&pM->zText[pM->nChar], zNewText, nNewChar);
- pM->nChar += nNewChar;
- }
- pM->zText[pM->nChar] = 0;
}
+ memcpy(&p->zText[p->nChar], z, N);
+ p->nChar += N;
}
/*
-** This routine is a wrapper around xprintf() that invokes mout() as
-** the consumer.
+** Finish off a string by making sure it is zero-terminated.
+** Return a pointer to the resulting string. Return a NULL
+** pointer if any kind of error was encountered.
*/
-static char *base_vprintf(
- void *(*xRealloc)(void*, int), /* realloc() function. May be NULL */
- int useInternal, /* Use internal %-conversions if true */
- char *zInitBuf, /* Initially write here, before mallocing */
- int nInitBuf, /* Size of zInitBuf[] */
- const char *zFormat, /* format string */
- va_list ap /* arguments */
-){
- struct sgMprintf sM;
- sM.zBase = sM.zText = zInitBuf;
- sM.nChar = sM.nTotal = 0;
- sM.nAlloc = nInitBuf;
- sM.xRealloc = xRealloc;
- sM.iMallocFailed = 0;
- vxprintf(mout, &sM, useInternal, zFormat, ap);
- assert(sM.iMallocFailed==0 || sM.zText==0);
- if( xRealloc && !sM.iMallocFailed ){
- if( sM.zText==sM.zBase ){
- sM.zText = xRealloc(0, sM.nChar+1);
- if( sM.zText ){
- memcpy(sM.zText, sM.zBase, sM.nChar+1);
- }
- }else if( sM.nAlloc>sM.nChar+10 ){
- char *zNew;
- sqlite3MallocBenignFailure(1);
- zNew = xRealloc(sM.zText, sM.nChar+1);
- if( zNew ){
- sM.zText = zNew;
+char *sqlite3StrAccumFinish(StrAccum *p){
+ if( p->zText ){
+ p->zText[p->nChar] = 0;
+ if( p->useMalloc && p->zText==p->zBase ){
+ p->zText = sqlite3_malloc( p->nChar+1 );
+ if( p->zText ){
+ memcpy(p->zText, p->zBase, p->nChar+1);
+ }else{
+ p->mallocFailed = 1;
}
}
}
- return sM.zText;
+ return p->zText;
+}
+
+/*
+** Reset an StrAccum string. Reclaim all malloced memory.
+*/
+void sqlite3StrAccumReset(StrAccum *p){
+ if( p->zText!=p->zBase ){
+ sqlite3_free(p->zText);
+ p->zText = 0;
+ }
}
/*
-** Realloc that is a real function, not a macro.
+** Initialize a string accumulator
*/
-static void *printf_realloc(void *old, int size){
- return sqlite3_realloc(old, size);
+static void sqlite3StrAccumInit(StrAccum *p, char *zBase, int n){
+ p->zText = p->zBase = zBase;
+ p->nChar = 0;
+ p->nAlloc = n;
+ p->useMalloc = 1;
+ p->tooBig = 0;
+ p->mallocFailed = 0;
}
/*
char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
char *z;
char zBase[SQLITE_PRINT_BUF_SIZE];
- z = base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap);
- if( z==0 && db!=0 ){
+ StrAccum acc;
+ sqlite3StrAccumInit(&acc, zBase, sizeof(zBase));
+ vxprintf(&acc, 1, zFormat, ap);
+ z = sqlite3StrAccumFinish(&acc);
+ if( acc.mallocFailed && db ){
db->mallocFailed = 1;
}
return z;
char *sqlite3MPrintf(sqlite3 *db, const char *zFormat, ...){
va_list ap;
char *z;
- char zBase[SQLITE_PRINT_BUF_SIZE];
va_start(ap, zFormat);
- z = base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap);
+ z = sqlite3VMPrintf(db, zFormat, ap);
va_end(ap);
- if( z==0 && db!=0 ){
- db->mallocFailed = 1;
- }
return z;
}
** %-conversion extensions.
*/
char *sqlite3_vmprintf(const char *zFormat, va_list ap){
+ char *z;
char zBase[SQLITE_PRINT_BUF_SIZE];
- return base_vprintf(sqlite3_realloc, 0, zBase, sizeof(zBase), zFormat, ap);
+ StrAccum acc;
+ sqlite3StrAccumInit(&acc, zBase, sizeof(zBase));
+ vxprintf(&acc, 0, zFormat, ap);
+ z = sqlite3StrAccumFinish(&acc);
+ return z;
}
/*
char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
char *z;
va_list ap;
+ StrAccum acc;
if( n<=0 ){
return zBuf;
}
- zBuf[0] = 0;
+ sqlite3StrAccumInit(&acc, zBuf, n);
+ acc.useMalloc = 0;
va_start(ap,zFormat);
- z = base_vprintf(0, 0, zBuf, n, zFormat, ap);
+ vxprintf(&acc, 0, zFormat, ap);
va_end(ap);
+ z = sqlite3StrAccumFinish(&acc);
return z;
}
** and segfaults if you give it a long long int.
*/
void sqlite3DebugPrintf(const char *zFormat, ...){
- extern int getpid(void);
va_list ap;
+ StrAccum acc;
char zBuf[500];
- va_start(ap, zFormat);
- base_vprintf(0, 0, zBuf, sizeof(zBuf), zFormat, ap);
+ sqlite3StrAccumInit(&acc, zBuf, sizeof(zBuf));
+ acc.useMalloc = 0;
+ va_start(ap,zFormat);
+ vxprintf(&acc, 0, zFormat, ap);
va_end(ap);
fprintf(stdout,"%s", zBuf);
fflush(stdout);