mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-16 23:02:26 +03:00
Get group_concat() to handle varying separator lengths when windowing
FossilOrigin-Name: 98e0f2bf67cdee1da1edadeb54ff8564728b3f28fc821e46e8de201247c3fc87
This commit is contained in:
116
src/func.c
116
src/func.c
@@ -1716,23 +1716,36 @@ static void minMaxFinalize(sqlite3_context *context){
|
||||
/*
|
||||
** group_concat(EXPR, ?SEPARATOR?)
|
||||
*/
|
||||
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 */
|
||||
/* 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.
|
||||
*/
|
||||
int *pnSepLengths;
|
||||
#endif
|
||||
} GroupConcatCtx;
|
||||
|
||||
static void groupConcatStep(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const char *zVal;
|
||||
StrAccum *pAccum;
|
||||
GroupConcatCtx *pGCC;
|
||||
const char *zSep;
|
||||
int nVal, nSep;
|
||||
assert( argc==1 || argc==2 );
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum));
|
||||
|
||||
if( pAccum ){
|
||||
pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC));
|
||||
if( pGCC ){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
int firstTerm = pAccum->mxAlloc==0;
|
||||
pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH];
|
||||
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]);
|
||||
@@ -1741,49 +1754,92 @@ static void groupConcatStep(
|
||||
zSep = ",";
|
||||
nSep = 1;
|
||||
}
|
||||
if( zSep ) sqlite3_str_append(pAccum, zSep, nSep);
|
||||
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);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
else{
|
||||
pGCC->nFirstSepLength = (argc==2)? sqlite3_value_bytes(argv[1]) : 1;
|
||||
}
|
||||
pGCC->nAccum += 1;
|
||||
#endif
|
||||
zVal = (char*)sqlite3_value_text(argv[0]);
|
||||
nVal = sqlite3_value_bytes(argv[0]);
|
||||
if( zVal ) sqlite3_str_append(pAccum, zVal, nVal);
|
||||
if( zVal ) sqlite3_str_append(&pGCC->str, zVal, nVal);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
static void groupConcatInverse(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
int n;
|
||||
StrAccum *pAccum;
|
||||
GroupConcatCtx *pGCC;
|
||||
assert( argc==1 || argc==2 );
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum));
|
||||
/* pAccum is always non-NULL since groupConcatStep() will have always
|
||||
pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC));
|
||||
/* pGCC is always non-NULL since groupConcatStep() will have always
|
||||
** run frist to initialize it */
|
||||
if( ALWAYS(pAccum) ){
|
||||
n = sqlite3_value_bytes(argv[0]);
|
||||
if( argc==2 ){
|
||||
n += sqlite3_value_bytes(argv[1]);
|
||||
if( ALWAYS(pGCC) ){
|
||||
int nVS = sqlite3_value_bytes(argv[0]);
|
||||
pGCC->nAccum -= 1;
|
||||
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));
|
||||
}
|
||||
}else{
|
||||
n++;
|
||||
/* If removing single accumulated string, harmlessly over-do. */
|
||||
nVS += pGCC->nFirstSepLength;
|
||||
}
|
||||
if( n>=(int)pAccum->nChar ){
|
||||
pAccum->nChar = 0;
|
||||
if( nVS>=(int)pGCC->str.nChar ){
|
||||
pGCC->str.nChar = 0;
|
||||
}else{
|
||||
pAccum->nChar -= n;
|
||||
memmove(pAccum->zText, &pAccum->zText[n], pAccum->nChar);
|
||||
pGCC->str.nChar -= nVS;
|
||||
memmove(pGCC->str.zText, &pGCC->str.zText[nVS], pGCC->str.nChar);
|
||||
}
|
||||
if( pGCC->str.nChar==0 ){
|
||||
pGCC->str.mxAlloc = 0;
|
||||
sqlite3_free(pGCC->pnSepLengths);
|
||||
pGCC->pnSepLengths = 0;
|
||||
}
|
||||
if( pAccum->nChar==0 ) pAccum->mxAlloc = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define groupConcatInverse 0
|
||||
#endif /* SQLITE_OMIT_WINDOWFUNC */
|
||||
static void groupConcatFinalize(sqlite3_context *context){
|
||||
StrAccum *pAccum;
|
||||
pAccum = sqlite3_aggregate_context(context, 0);
|
||||
if( pAccum ){
|
||||
GroupConcatCtx *pGCC
|
||||
= (GroupConcatCtx*)sqlite3_aggregate_context(context, 0);
|
||||
if( pGCC ){
|
||||
StrAccum *pAccum = &pGCC->str;
|
||||
if( pAccum->accError==SQLITE_TOOBIG ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
}else if( pAccum->accError==SQLITE_NOMEM ){
|
||||
@@ -1792,13 +1848,17 @@ static void groupConcatFinalize(sqlite3_context *context){
|
||||
sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1,
|
||||
sqlite3_free);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
sqlite3_free(pGCC->pnSepLengths);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
static void groupConcatValue(sqlite3_context *context){
|
||||
sqlite3_str *pAccum;
|
||||
pAccum = (sqlite3_str*)sqlite3_aggregate_context(context, 0);
|
||||
if( pAccum ){
|
||||
GroupConcatCtx *pGCC
|
||||
= (GroupConcatCtx*)sqlite3_aggregate_context(context, 0);
|
||||
if( pGCC ){
|
||||
StrAccum *pAccum = &pGCC->str;
|
||||
if( pAccum->accError==SQLITE_TOOBIG ){
|
||||
sqlite3_result_error_toobig(context);
|
||||
}else if( pAccum->accError==SQLITE_NOMEM ){
|
||||
|
||||
Reference in New Issue
Block a user