]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the {quote: StrAccum} object
authordrh <drh@noemail.net>
Wed, 28 Nov 2007 22:36:40 +0000 (22:36 +0000)
committerdrh <drh@noemail.net>
Wed, 28 Nov 2007 22:36:40 +0000 (22:36 +0000)
 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

manifest
manifest.uuid
src/func.c
src/printf.c
src/sqliteInt.h
test/func.test
test/malloc.test
test/sqllimits1.test

index b754e50c48d32fda3d877fab75e8da1c3553df34..e7e04da2cb96ba20000d22c0286f150aa0fdbed1 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-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
@@ -93,7 +93,7 @@ F src/date.c 49c5a6d2de6c12000905b4d36868b07d3011bbf6
 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
@@ -128,14 +128,14 @@ F src/pager.h f504f7ae84060fee0416a853e368d3d113c3d6fa
 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
@@ -312,7 +312,7 @@ F test/fts3ao.test 0aa29dd4fc1c8d46b1f7cfe5926f7ac97551bea9
 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
@@ -356,7 +356,7 @@ F test/lock2.test 5f9557b775662c2a5ee435378f39e10d64f65cb3
 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
@@ -429,7 +429,7 @@ F test/speed1.test 22e1b27af0683ed44dcd2f93ed817a9c3e65084a
 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
@@ -593,7 +593,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130
 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
index e680c75f3b4d382fe9ac29a4ffd7c42e1318fc2a..93f868d506129a5907d054e3e17d04c0ed279db5 100644 (file)
@@ -1 +1 @@
-98960132dc082da61652201f4bd2b559725350c0
\ No newline at end of file
+221aee72be040769e8026b91648f03c6366a8821
\ No newline at end of file
index fcad3aadf0c7806cfac7ffab27e1165528fb080d..6bf4ed1f8d2c566fdecafc1908a9debdadb20dcb 100644 (file)
@@ -16,7 +16,7 @@
 ** 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>
@@ -1312,7 +1312,6 @@ static void minMaxFinalize(sqlite3_context *context){
   }
 }
 
-#ifdef SQLITE_GROUP_CONCAT
 /*
 ** group_concat(EXPR, ?SEPARATOR?)
 */
@@ -1322,32 +1321,43 @@ static void groupConcatStep(
   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
@@ -1427,10 +1437,8 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
     { "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;
 
index 839c24bbe5ba250a8016f91c972d5adf224cc748..260b3c2267bbea3180a51d1f5cd28dc2ba704b86 100644 (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.
-*/
-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;
 }
 
 /*
@@ -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);
index c27b632c8234558b7e8773b0b4d5f9fa148aa85a..f1142493e85274a07450f689141ecda431a3decb 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.620 2007/11/27 02:38:01 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.621 2007/11/28 22:36:41 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -363,6 +363,7 @@ typedef struct NameContext NameContext;
 typedef struct Parse Parse;
 typedef struct Select Select;
 typedef struct SrcList SrcList;
+typedef struct StrAccum StrAccum;
 typedef struct Table Table;
 typedef struct TableLock TableLock;
 typedef struct Token Token;
@@ -1573,6 +1574,20 @@ struct DbFixer {
   const Token *pName; /* Name of the container - used for error messages */
 };
 
+/*
+** An objected used to accumulate the text of a string where we
+** do not necessarily know how big the string will be in the end.
+*/
+struct StrAccum {
+  char *zBase;     /* A base allocation.  Not from malloc. */
+  char *zText;     /* The string collected so far */
+  int  nChar;      /* Length of the string so far */
+  int  nAlloc;     /* Amount of space allocated in zText */
+  u8   mallocFailed;   /* Becomes true if any memory allocation fails */
+  u8   useMalloc;      /* True if zText is enlargable using realloc */
+  u8   tooBig;         /* Becomes true if string size exceeds limits */
+};
+
 /*
 ** A pointer to this structure is used to communicate information
 ** from sqlite3Init and OP_ParseSchema into the sqlite3InitCallback.
@@ -1879,6 +1894,10 @@ int sqlite3ApiExit(sqlite3 *db, int);
 void sqlite3AbortOtherActiveVdbes(sqlite3 *, Vdbe *);
 int sqlite3OpenTempDatabase(Parse *);
 
+void sqlite3StrAccumAppend(StrAccum*,const char*,int);
+char *sqlite3StrAccumFinish(StrAccum*);
+void sqlite3StrAccumReset(StrAccum*);
+
 
 /*
 ** The interface to the LEMON-generated parser
index 8af5a88d2f729deb6533e33587d215c0bf799552..8ae7edd70285468e30f417cbfda56b63b8ef4633 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing built-in functions.
 #
-# $Id: func.test,v 1.70 2007/10/20 15:41:58 drh Exp $
+# $Id: func.test,v 1.71 2007/11/28 22:36:41 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -884,4 +884,32 @@ do_test func-23.1 {
   }
 } {3}
 
+# The group_concat() function.
+#
+do_test func-24.1 {
+  execsql {
+    SELECT group_concat(t1) FROM tbl1
+  }
+} {this,program,is,free,software}
+do_test func-24.2 {
+  execsql {
+    SELECT group_concat(t1,' ') FROM tbl1
+  }
+} {{this program is free software}}
+do_test func-24.3 {
+  execsql {
+    SELECT group_concat(t1,' ' || rowid || ' ') FROM tbl1
+  }
+} {{this 2 program 3 is 4 free 5 software}}
+do_test func-24.4 {
+  execsql {
+    SELECT group_concat(NULL,t1) FROM tbl1
+  }
+} {{}}
+do_test func-24.5 {
+  execsql {
+    SELECT group_concat(t1,NULL) FROM tbl1
+  }
+} {thisprogramisfreesoftware}
+
 finish_test
index fbef0df7f9039ce00618715577b62322a9b81a1c..454601934f2cdf72603a45a6e79d93ed36c10286 100644 (file)
@@ -16,7 +16,7 @@
 # to see what happens in the library if a malloc were to really fail
 # due to an out-of-memory situation.
 #
-# $Id: malloc.test,v 1.52 2007/11/16 14:55:46 danielk1977 Exp $
+# $Id: malloc.test,v 1.53 2007/11/28 22:36:41 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -51,7 +51,7 @@ ifcapable bloblit&&subquery {
     SELECT * FROM t1;
     SELECT avg(b) FROM t1 GROUP BY a HAVING b>20.0;
     DELETE FROM t1 WHERE a IN (SELECT min(a) FROM t1);
-    SELECT count(*) FROM t1;
+    SELECT count(*), group_concat(e) FROM t1;
   } 
 }
 
index 81e10c89591f684d97626bf6e79c3860a868b415..27f5dadd31a227a961fee26631324d291fdf4f24 100644 (file)
@@ -12,7 +12,7 @@
 # This file contains tests to verify that the limits defined in
 # sqlite source file limits.h are enforced.
 #
-# $Id: sqllimits1.test,v 1.19 2007/10/09 08:29:33 danielk1977 Exp $
+# $Id: sqllimits1.test,v 1.20 2007/11/28 22:36:41 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -128,6 +128,19 @@ do_test sqllimits-1.14.3 {
   sqlite3_finalize $::STMT 
 } {SQLITE_TOOBIG}
 
+do_test sqllimits-1.15 {
+  execsql {
+    CREATE TABLE t4(x);
+    INSERT INTO t4 VALUES(1);
+    INSERT INTO t4 VALUES(2);
+    INSERT INTO t4 SELECT 2+x FROM t4;
+  }
+  catchsql {
+    SELECT group_concat(hex(randomblob(20000))) FROM t4;
+  }
+} {1 {string or blob too big}}
+db eval {DROP TABLE t4}
+
 #--------------------------------------------------------------------
 # Test cases sqllimits-2.* test that the SQLITE_MAX_SQL_LENGTH limit
 # is enforced.