Index: src/func.c ================================================================== --- src/func.c +++ src/func.c @@ -14,11 +14,11 @@ ** ** There is only one exported symbol in this file - the function ** 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 #include #include @@ -1310,46 +1310,56 @@ } sqlite3VdbeMemRelease(pRes); } } -#ifdef SQLITE_GROUP_CONCAT /* ** group_concat(EXPR, ?SEPARATOR?) */ static void groupConcatStep( sqlite3_context *context, int argc, 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 ** functions. This should be the only routine in this file with ** external linkage. @@ -1425,14 +1435,12 @@ { "sum", 1, 0, 0, sumStep, sumFinalize }, { "total", 1, 0, 0, sumStep, totalFinalize }, { "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; for(i=0; i=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 ** smaller values some %f conversions may go into an infinite loop. @@ -201,23 +215,21 @@ ** ** Note that the order in which automatic variables are declared below ** 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 c; /* Next character in the format string */ char *bufpt; /* Pointer to the conversion buffer */ 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 flag_blanksign; /* True if " " flag is present */ etByte flag_alternateform; /* True if "#" flag is present */ @@ -232,39 +244,33 @@ char buf[etBUFSIZE]; /* Conversion buffer */ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ 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 */ etByte flag_dp; /* True if decimal point should be shown */ etByte flag_rtz; /* True if trailing zeros should be removed */ etByte flag_exp; /* True to force display of the exponent */ 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!='%' ){ int amt; 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 */ flag_leftjustify = flag_plussign = flag_blanksign = flag_alternateform = flag_altform2 = flag_zeropad = 0; @@ -334,18 +340,18 @@ if( c==fmtinfo[idx].fmttype ){ infop = &fmtinfo[idx]; if( useExtended || (infop->flags & FLAG_INTERN)==0 ){ xtype = infop->type; }else{ - return -1; + return; } break; } } zExtra = 0; if( infop==0 ){ - return -1; + return; } /* Limit the precision to prevent overflowing buf[] during conversion */ if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){ @@ -579,11 +585,11 @@ length = width; } #endif break; case etSIZE: - *(va_arg(ap,int*)) = count; + *(va_arg(ap,int*)) = pAccum->nChar; length = width = 0; break; case etPERCENT: buf[0] = '%'; bufpt = buf; @@ -625,11 +631,11 @@ } needQuote = !isnull && xtype==etSQLESCAPE2; 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; } j = 0; if( needQuote ) bufpt[j++] = q; @@ -645,11 +651,11 @@ break; } 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; } case etSRCLIST: { @@ -656,14 +662,14 @@ SrcList *pSrc = va_arg(ap, SrcList*); int k = va_arg(ap, int); struct SrcList_item *pItem = &pSrc->a[k]; assert( k>=0 && knSrc ); 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; } }/* End switch over the format type */ /* @@ -673,158 +679,130 @@ */ 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( 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". -*/ -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; - } - } - 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); - if( zNew ){ - sM.zText = zNew; - } - } - } - return sM.zText; -} - -/* -** Realloc that is a real function, not a macro. -*/ -static void *printf_realloc(void *old, int size){ - return sqlite3_realloc(old, size); +} /* End of function */ + +/* +** Append N bytes of text from z to the StrAccum object. +*/ +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; + } + } + 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; + } + } + } + memcpy(&p->zText[p->nChar], z, N); + p->nChar += N; +} + +/* +** 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. +*/ +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; } /* ** Print into memory obtained from sqliteMalloc(). Use the internal ** %-conversion extensions. */ 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; } @@ -833,27 +811,28 @@ ** %-conversion extensions. */ 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; } /* ** Print into memory obtained from sqlite3_malloc(). Omit the internal ** %-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; } /* ** Print into memory obtained from sqlite3_malloc()(). Omit the internal ** %-conversion extensions. @@ -874,18 +853,21 @@ ** specified by some locales. */ 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; } #if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) || defined(SQLITE_MEMDEBUG) /* @@ -892,15 +874,17 @@ ** A version of printf() that understands %lld. Used for debugging. ** The printf() built into some versions of windows does not understand %lld ** 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); } #endif Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -9,11 +9,11 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** 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_ /* @@ -361,10 +361,11 @@ typedef struct Module Module; 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; typedef struct TriggerStack TriggerStack; typedef struct TriggerStep TriggerStep; @@ -1571,10 +1572,24 @@ const char *zDb; /* Make sure all objects are contained in this database */ const char *zType; /* Type of the container - used for error messages */ 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. */ typedef struct { @@ -1876,10 +1891,14 @@ void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*)); 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: test/func.test ================================================================== --- test/func.test +++ test/func.test @@ -9,11 +9,11 @@ # #*********************************************************************** # 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 # Create a table to work with. @@ -881,7 +881,35 @@ sqlite3_create_aggregate db execsql { SELECT legacy_count() FROM t6; } } {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: test/malloc.test ================================================================== --- test/malloc.test +++ test/malloc.test @@ -14,11 +14,11 @@ # the SQLite library accepts a special command (sqlite3_memdebug_fail N C) # which causes the N-th malloc to fail. This special feature is used # 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 # Only run these tests if memory debugging is turned on. @@ -49,11 +49,11 @@ INSERT INTO t1 VALUES(1,2.3,4.5,'hi',x'746865726500'); INSERT INTO t1 VALUES(6,7.0,0.8,'hello','out yonder'); 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; } } # Ensure that no file descriptors were leaked. do_test malloc-1.X { Index: test/sqllimits1.test ================================================================== --- test/sqllimits1.test +++ test/sqllimits1.test @@ -10,11 +10,11 @@ #*********************************************************************** # # 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 # Test organization: @@ -125,10 +125,23 @@ sqlite3_step $::STMT } {SQLITE_ERROR} 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. #