1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-08 03:22:21 +03:00

Add the {quote: StrAccum} object

for accumulating strings.  Revamp xprintf to use
the new object.  Rewrite the group_concat() function to use the new object.
Productize and test the group_concat() function. (CVS 4578)

FossilOrigin-Name: 221aee72be040769e8026b91648f03c6366a8821
This commit is contained in:
drh
2007-11-28 22:36:40 +00:00
parent 26b7994a97
commit ade8648301
8 changed files with 231 additions and 179 deletions

View File

@@ -166,6 +166,20 @@ static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
}
#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
@@ -203,9 +217,8 @@ static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
** 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 */
@@ -215,7 +228,6 @@ static int vxprintf(
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 */
@@ -234,9 +246,6 @@ static int vxprintf(
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 */
@@ -246,8 +255,7 @@ static int vxprintf(
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!='%' ){
@@ -255,14 +263,12 @@ static int vxprintf(
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 */
@@ -336,14 +342,14 @@ static int vxprintf(
if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
xtype = infop->type;
}else{
return -1;
return;
}
break;
}
}
zExtra = 0;
if( infop==0 ){
return -1;
return;
}
@@ -581,7 +587,7 @@ static int vxprintf(
#endif
break;
case etSIZE:
*(va_arg(ap,int*)) = count;
*(va_arg(ap,int*)) = pAccum->nChar;
length = width = 0;
break;
case etPERCENT:
@@ -627,7 +633,7 @@ static int vxprintf(
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;
}
@@ -647,7 +653,7 @@ static int vxprintf(
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;
@@ -658,10 +664,10 @@ static int vxprintf(
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;
}
@@ -675,143 +681,112 @@ static int vxprintf(
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.
/*
** Append N bytes of text from z to the StrAccum object.
*/
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".
*/
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;
}
}
if( nNewChar>0 ){
memcpy(&pM->zText[pM->nChar], zNewText, nNewChar);
pM->nChar += nNewChar;
}
pM->zText[pM->nChar] = 0;
}
}
/*
** This routine is a wrapper around xprintf() that invokes mout() as
** the consumer.
*/
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);
zNew = sqlite3_malloc( p->nAlloc );
if( zNew ){
sM.zText = zNew;
memcpy(zNew, p->zText, p->nChar);
sqlite3StrAccumReset(p);
p->zText = zNew;
}else{
p->mallocFailed = 1;
sqlite3StrAccumReset(p);
return;
}
}
}
return sM.zText;
memcpy(&p->zText[p->nChar], z, N);
p->nChar += N;
}
/*
** Realloc that is a real function, not a macro.
** 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 void *printf_realloc(void *old, int size){
return sqlite3_realloc(old, size);
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 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;
}
}
/*
** Initialize a string accumulator
*/
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;
}
/*
@@ -821,8 +796,11 @@ static void *printf_realloc(void *old, int size){
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;
@@ -835,13 +813,9 @@ char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
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;
}
@@ -850,8 +824,13 @@ char *sqlite3MPrintf(sqlite3 *db, const char *zFormat, ...){
** %-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;
}
/*
@@ -876,14 +855,17 @@ char *sqlite3_mprintf(const char *zFormat, ...){
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;
}
@@ -894,11 +876,13 @@ char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
** 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);