Good news, everyone! I've taught SQLite to feel love!
No, wait, that's not it. I spotted the thread 'how to use group_concat
uniquely' today, and felt inspired, so I wrote a quick patch.
The patch allows a multi-argument aggregate function to be passed
DISTINCT, under the restriction that every other argument must be
constant.
This allows for, among other things, the following syntax:
sqlite> select group_concat(distinct type, ';') from sqlite_master;
table;index
But you still can't do it if there is more than one column referenced:
sqlite> select group_concat(distinct type, tbl_name) from sqlite_master;
Error: DISTINCT aggregates must have at most one non-constant argument
I know how I would implement that, but that's way more effort than I'm
willing to expend right now.
Here's a quick diff. If people (in particular, DRH) think it's worth
it, I'll write up the test cases and submit a more formal patch.
Index: src/select.c
===================================================================
--- src/select.c
+++ src/select.c
@@ -3487,10 +3487,74 @@
if( pParse->nErr || db->mallocFailed ) return;
sqlite3SelectAddTypeInfo(pParse, p);
}
/*
+** Set up and validate an aggregate being called with a DISTINCT argument.
+**
+** Verifies that one of the following is true:
+**
+** * The aggregate takes exactly one argument.
+** * The aggregate is being passed at most one non-constant argument.
+**
+** If neither of these is satisfied, the return value is zero.
+** Otherwise, the return value is nonzero, and the following are performed:
+**
+** * pFunc->iDistinctArg is set to the index of the argument that is
+** DISTINCT.
+** * keyExprList is filled out to refer to only the distinct column.
+** Note that all pointers are aliased to the original expression list;
+** no freeing must be done.
+**
+*/
+
+static int setupDistinctAggregate(Parse *pParse, struct AggInfo_func
*pFunc, ExprList *keyExprList){
+ int i;
+ ExprList *pEL = pFunc->pExpr->x.pList;
+ struct ExprList_item *pELi;
+ pFunc->iDistinctArg = -1;
+ if( pEL==0 ){
+ return 0;
+ }
+ if( pEL->nExpr==1 ){
+ /* If there is only one argument to the aggregate, that argument
must be the
+ ** DISTINCT one.
+ */
+
+ pFunc->iDistinctArg = 0;
+ }else{
+ /* Okay, there is more than one argument to this aggregate.
+ ** Require that at most one is non-constant.
+ */
+ for(i=0, pELi=pEL->a; i<pEL->nExpr; i++, pELi++){
+ if( !sqlite3ExprIsConstantOrFunction(pELi->pExpr) )
+ {
+ if( pFunc->iDistinctArg>=0 ){
+ return 0;
+ }
+ pFunc->iDistinctArg = i;
+ }
+ }
+
+ if( pFunc->iDistinctArg<0 ){
+ /* All of the arguments passed to this function are constant.
+ ** Arbitrarily choose the first argument for the DISTINCTness.
+ ** TODO: somehow degrade this to a non-DISTINCT for this case.
+ */
+ pFunc->iDistinctArg = 0;
+ }
+ }
+
+ keyExprList->nExpr = 1;
+ keyExprList->nAlloc = 0;
+ keyExprList->iECursor = pEL->iECursor;
+ keyExprList->a = &pEL->a[pFunc->iDistinctArg];
+
+ return 1;
+}
+
+/*
** Reset the aggregate accumulator.
**
** The aggregate accumulator is a set of memory cells that hold
** intermediate results while calculating an aggregate. This
** routine simply stores NULLs in all of those memory cells.
@@ -3507,17 +3571,18 @@
}
for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){
sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem);
if( pFunc->iDistinct>=0 ){
Expr *pE = pFunc->pExpr;
+ ExprList keyEL;
assert( !ExprHasProperty(pE, EP_xIsSelect) );
- if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){
- sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one "
- "argument");
+ if( !setupDistinctAggregate(pParse, pFunc, &keyEL) ){
+ sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have at most one "
+ "non-constant argument");
pFunc->iDistinct = -1;
}else{
- KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList);
+ KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, &keyEL);
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
(char*)pKeyInfo, P4_KEYINFO_HANDOFF);
}
}
}
@@ -3565,12 +3630,12 @@
nArg = 0;
regAgg = 0;
}
if( pF->iDistinct>=0 ){
addrNext = sqlite3VdbeMakeLabel(v);
- assert( nArg==1 );
- codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg);
+ //assert( nArg==1 );
+ codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg +
pF->iDistinctArg);
}
if( pF->pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
CollSeq *pColl = 0;
struct ExprList_item *pItem;
int j;
Index: src/sqliteInt.h
===================================================================
--- src/sqliteInt.h
+++ src/sqliteInt.h
@@ -1530,10 +1530,11 @@
struct AggInfo_func { /* For each aggregate function */
Expr *pExpr; /* Expression encoding the function */
FuncDef *pFunc; /* The aggregate function implementation */
int iMem; /* Memory location that acts as accumulator */
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
+ int iDistinctArg; /* Index of argument that is DISTINCT */
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
int nFuncAlloc; /* Number of slots allocated for aFunc[] */
};
--
-- Stevie-O
Real programmers use COPY CON PROGRAM.EXE
_______________________________________________
sqlite-users mailing list
[email protected]
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users