aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.c2
-rw-r--r--src/sqlite.h.in5
-rw-r--r--src/sqliteInt.h2
-rw-r--r--src/window.c47
4 files changed, 52 insertions, 4 deletions
diff --git a/src/main.c b/src/main.c
index a8c1d4dc6..3ef3a3d9f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1719,7 +1719,7 @@ int sqlite3CreateFunc(
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY );
- extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY);
+ extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|SQLITE_SUBTYPE);
enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
#ifndef SQLITE_OMIT_UTF16
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 521ddffdb..8136889ee 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -4987,9 +4987,14 @@ int sqlite3_create_window_function(
**
** The SQLITE_DIRECTONLY flag means that the function may only be invoked
** from top-level SQL, and cannot be used in VIEWs or TRIGGERs.
+**
+** The SQLITE_SUBTYPE flag indicates to SQLite that the function may call
+** [sqlite3_result_subtype()] in order to configure its return value with
+** a sub-type.
*/
#define SQLITE_DETERMINISTIC 0x000000800
#define SQLITE_DIRECTONLY 0x000080000
+#define SQLITE_SUBTYPE 0x000100000
/*
** CAPI3REF: Deprecated Functions
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index d3f7ffe26..e7c1d9c1f 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1686,6 +1686,7 @@ struct FuncDestructor {
#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
#define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */
+#define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */
/*
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
@@ -3611,6 +3612,7 @@ struct Window {
int regOne; /* Register containing constant value 1 */
int regStartRowid;
int regEndRowid;
+ u8 bExprArgs;
};
#ifndef SQLITE_OMIT_WINDOWFUNC
diff --git a/src/window.c b/src/window.c
index ed2e32079..b88993044 100644
--- a/src/window.c
+++ b/src/window.c
@@ -869,6 +869,32 @@ static void selectWindowRewriteEList(
}
/*
+** Return true if the top-level of list pList contains an SQL function
+** with the SQLITE_FUNC_SUBTYPE flag set. Return false otherwise.
+*/
+int exprListContainsSubtype(Parse *pParse, ExprList *pList){
+ if( pList ){
+ sqlite3 *db = pParse->db;
+ int i;
+ for(i=0; i<pList->nExpr; i++){
+ Expr *p = pList->a[i].pExpr;
+ if( p->op==TK_FUNCTION ){
+ FuncDef *pDef;
+ int nArg = 0;
+ if( !ExprHasProperty(p, EP_TokenOnly) && p->x.pList ){
+ nArg = p->x.pList->nExpr;
+ }
+ pDef = sqlite3FindFunction(db, p->u.zToken, nArg, db->enc, 0);
+ if( pDef && (pDef->funcFlags & SQLITE_FUNC_SUBTYPE) ){
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*
** Append a copy of each expression in expression-list pAppend to
** expression list pList. Return a pointer to the result list.
*/
@@ -965,8 +991,15 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){
** window function - one for the accumulator, another for interim
** results. */
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
- pSublist = exprListAppendList(pParse, pSublist, pWin->pOwner->x.pList, 0);
+ ExprList *pArgs = pWin->pOwner->x.pList;
+ if( exprListContainsSubtype(pParse, pArgs) ){
+ selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist);
+ pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
+ pWin->bExprArgs = 1;
+ }else{
+ pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
+ pSublist = exprListAppendList(pParse, pSublist, pArgs, 0);
+ }
if( pWin->pFilter ){
Expr *pFilter = sqlite3ExprDup(db, pWin->pFilter, 0);
pSublist = sqlite3ExprListAppend(pParse, pSublist, pFilter);
@@ -1432,7 +1465,7 @@ static void windowAggStep(
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
FuncDef *pFunc = pWin->pFunc;
int regArg;
- int nArg = windowArgCount(pWin);
+ int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin);
int i;
assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED );
@@ -1482,6 +1515,11 @@ static void windowAggStep(
VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, regTmp);
}
+ if( pWin->bExprArgs ){
+ nArg = pWin->pOwner->x.pList->nExpr;
+ regArg = sqlite3GetTempRange(pParse, nArg);
+ sqlite3ExprCodeExprList(pParse, pWin->pOwner->x.pList, regArg, 0, 0);
+ }
if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
CollSeq *pColl;
assert( nArg>0 );
@@ -1492,6 +1530,9 @@ static void windowAggStep(
bInverse, regArg, pWin->regAccum);
sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nArg);
+ if( pWin->bExprArgs ){
+ sqlite3ReleaseTempRange(pParse, regArg, nArg);
+ }
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
}
}