mirror of
https://github.com/sqlite/sqlite.git
synced 2025-10-21 11:13:54 +03:00
Fixes to the version of "varsep" group_concat so that (1) it builds under
separate compilation and (2) omits tabs in source code and (3) runs faster than trunk. This variant of the group_concat_varsep branch might be preferred over the tip because it preserves (undocumented) legacy behavior about the position of separators relative to terms. FossilOrigin-Name: 04399cf9645e04b171090ff8a3c27752929c10d2cd8778e26f8f3337aa902ab6
This commit is contained in:
94
src/func.c
94
src/func.c
@@ -1715,17 +1715,23 @@ static void minMaxFinalize(sqlite3_context *context){
|
||||
|
||||
/*
|
||||
** group_concat(EXPR, ?SEPARATOR?)
|
||||
**
|
||||
** The SEPARATOR goes before the EXPR string. This is tragic. The
|
||||
** groupConcatInverse() implementation would have been easier if the
|
||||
** SEPARATOR were appended after EXPR. And the order is undocumented,
|
||||
** so we could change it, in theory. But the old behavior has been
|
||||
** around for so long that we dare not, for fear of breaking something.
|
||||
*/
|
||||
typedef struct {
|
||||
StrAccum str; /* The accumulated concatenation */
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
int nAccum; /* Number of strings presently concatenated */
|
||||
int nFirstSepLength; /* Used to detect separator length change */
|
||||
int nFirstSepLength; /* Used to detect separator length change */
|
||||
/* If pnSepLengths!=0, refs an array of inter-string separator lengths,
|
||||
* stored as actually incorporated into presently accumulated result.
|
||||
* (Hence, its slots in use number nAccum-1 between method calls.)
|
||||
* If pnSepLengths==0, nFirstSepLength is the length used throughout.
|
||||
*/
|
||||
** stored as actually incorporated into presently accumulated result.
|
||||
** (Hence, its slots in use number nAccum-1 between method calls.)
|
||||
** If pnSepLengths==0, nFirstSepLength is the length used throughout.
|
||||
*/
|
||||
int *pnSepLengths;
|
||||
#endif
|
||||
} GroupConcatCtx;
|
||||
@@ -1746,38 +1752,45 @@ static void groupConcatStep(
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
int firstTerm = pGCC->str.mxAlloc==0;
|
||||
pGCC->str.mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH];
|
||||
if( !firstTerm ){
|
||||
if( argc==2 ){
|
||||
zSep = (char*)sqlite3_value_text(argv[1]);
|
||||
nSep = sqlite3_value_bytes(argv[1]);
|
||||
}else{
|
||||
zSep = ",";
|
||||
nSep = 1;
|
||||
if( argc==1 ){
|
||||
if( !firstTerm ){
|
||||
sqlite3_str_appendchar(&pGCC->str, 1, ',');
|
||||
}
|
||||
if( zSep )
|
||||
sqlite3_str_append(&pGCC->str, zSep, nSep);
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
else
|
||||
nSep = 0;
|
||||
else{
|
||||
pGCC->nFirstSepLength = 1;
|
||||
}
|
||||
#endif
|
||||
}else if( !firstTerm ){
|
||||
zSep = (char*)sqlite3_value_text(argv[1]);
|
||||
nSep = sqlite3_value_bytes(argv[1]);
|
||||
if( zSep ){
|
||||
sqlite3_str_append(&pGCC->str, zSep, nSep);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
else{
|
||||
nSep = 0;
|
||||
}
|
||||
if( nSep != pGCC->nFirstSepLength || pGCC->pnSepLengths != 0 ){
|
||||
int * pnsl = pGCC->pnSepLengths;
|
||||
if( pnsl == 0 ){
|
||||
/* First separator length variation seen, start tracking them. */
|
||||
pnsl = (int*)sqlite3_malloc64((pGCC->nAccum+1) * sizeof(int));
|
||||
if( pnsl!=0 ){
|
||||
int i = 0, nA = pGCC->nAccum-1;
|
||||
while( i<nA ) pnsl[i++] = pGCC->nFirstSepLength;
|
||||
}
|
||||
}else{
|
||||
pnsl = (int*)sqlite3_realloc64(pnsl, pGCC->nAccum * sizeof(int));
|
||||
}
|
||||
if( pnsl!=0 ){
|
||||
if( pGCC->nAccum>0 )
|
||||
pnsl[pGCC->nAccum-1] = nSep;
|
||||
pGCC->pnSepLengths = pnsl;
|
||||
}else{
|
||||
setStrAccumError(&pGCC->str, SQLITE_NOMEM);
|
||||
}
|
||||
int *pnsl = pGCC->pnSepLengths;
|
||||
if( pnsl == 0 ){
|
||||
/* First separator length variation seen, start tracking them. */
|
||||
pnsl = (int*)sqlite3_malloc64((pGCC->nAccum+1) * sizeof(int));
|
||||
if( pnsl!=0 ){
|
||||
int i = 0, nA = pGCC->nAccum-1;
|
||||
while( i<nA ) pnsl[i++] = pGCC->nFirstSepLength;
|
||||
}
|
||||
}else{
|
||||
pnsl = (int*)sqlite3_realloc64(pnsl, pGCC->nAccum * sizeof(int));
|
||||
}
|
||||
if( pnsl!=0 ){
|
||||
if( pGCC->nAccum>0 ){
|
||||
pnsl[pGCC->nAccum-1] = nSep;
|
||||
}
|
||||
pGCC->pnSepLengths = pnsl;
|
||||
}else{
|
||||
sqlite3StrAccumSetError(&pGCC->str, SQLITE_NOMEM);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1811,9 +1824,9 @@ static void groupConcatInverse(
|
||||
if( pGCC->pnSepLengths!=0 ){
|
||||
assert(pGCC->nAccum >= 0);
|
||||
if( pGCC->nAccum>0 ){
|
||||
nVS += *pGCC->pnSepLengths;
|
||||
memmove(pGCC->pnSepLengths, pGCC->pnSepLengths+1,
|
||||
(pGCC->nAccum-1)*sizeof(int));
|
||||
nVS += *pGCC->pnSepLengths;
|
||||
memmove(pGCC->pnSepLengths, pGCC->pnSepLengths+1,
|
||||
(pGCC->nAccum-1)*sizeof(int));
|
||||
}
|
||||
}else{
|
||||
/* If removing single accumulated string, harmlessly over-do. */
|
||||
@@ -1844,8 +1857,9 @@ static void groupConcatFinalize(sqlite3_context *context){
|
||||
sqlite3_result_error_toobig(context);
|
||||
}else if( pAccum->accError==SQLITE_NOMEM ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1,
|
||||
}else{
|
||||
int n = pAccum->nChar;
|
||||
sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), n,
|
||||
sqlite3_free);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
@@ -1865,7 +1879,7 @@ static void groupConcatValue(sqlite3_context *context){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
const char *zText = sqlite3_str_value(pAccum);
|
||||
sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user