Aggregate window functions don't call xFinal
I just released a new API in my package to call sqlite3_create_window_function. Most everything works, but I'm noticing that the xFinal callback is not being called.
To reproduce:
git clone -b window-final https://github.com/zombiezen/go-sqlite.git &&
cd go-sqlite &&
go test -run=TestWindowFunc
I've stepped through with Delve to trace data passing and AFAICT, sqlite3_create_window_function is receiving the correct callback.
This doesn't appear to be an issue with upstream SQLite, so I'm suspecting this library. I used the following (roughly equivalent) C program to test with SQLite 3.38.05 and it behaves as I expect:
#include <assert.h>
#include <stdio.h>
#include "sqlite3.h"
static void sumintStep(
sqlite3_context *ctx,
int nArg,
sqlite3_value *apArg[]
){
sqlite3_int64 *pInt;
assert( nArg==1 );
if( sqlite3_value_type(apArg[0])!=SQLITE_INTEGER ){
sqlite3_result_error(ctx, "invalid argument", -1);
return;
}
fprintf(stderr, "STEP\n");
pInt = (sqlite3_int64*)sqlite3_aggregate_context(ctx, sizeof(sqlite3_int64));
if( pInt ){
*pInt += sqlite3_value_int64(apArg[0]);
}
}
static void sumintInverse(
sqlite3_context *ctx,
int nArg,
sqlite3_value *apArg[]
){
sqlite3_int64 *pInt;
assert( sqlite3_value_type(apArg[0])==SQLITE_INTEGER );
pInt = (sqlite3_int64*)sqlite3_aggregate_context(ctx, sizeof(sqlite3_int64));
*pInt -= sqlite3_value_int64(apArg[0]);
}
static void sumintFinal(sqlite3_context *ctx){
fprintf(stderr, "FINAL\n");
sqlite3_int64 res = 0;
sqlite3_int64 *pInt;
pInt = (sqlite3_int64*)sqlite3_aggregate_context(ctx, 0);
if( pInt ) res = *pInt;
sqlite3_result_int64(ctx, res);
}
static void sumintValue(sqlite3_context *ctx){
sqlite3_int64 res = 0;
sqlite3_int64 *pInt;
pInt = (sqlite3_int64*)sqlite3_aggregate_context(ctx, 0);
if( pInt ) res = *pInt;
sqlite3_result_int64(ctx, res);
}
static void sumintDestroy(void *pApp){
fprintf(stderr, "DESTROY %#x\n", pApp);
}
int register_sumint(sqlite3 *db){
return sqlite3_create_window_function(db, "sumint", 1, SQLITE_UTF8, 0,
sumintStep, sumintFinal, sumintValue, sumintInverse, sumintDestroy
);
}
static int callback(void *NotUsed, int argc, char **argv, char **azColName){
int i;
for(i=0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main() {
sqlite3* db;
if (sqlite3_open(":memory:", &db)) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
if (register_sumint(db)) {
fprintf(stderr, "Can't register function: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
if (sqlite3_exec(db, "CREATE TABLE t3(x, y);", NULL, 0, NULL)) {
fprintf(stderr, "CREATE TABLE: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
if (sqlite3_exec(db, "INSERT INTO t3 VALUES('a', 4), ('b', 5), ('c', 3), ('d', 8), ('e', 1);", NULL, 0, NULL)) {
fprintf(stderr, "INSERT: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
if (sqlite3_exec(db, "SELECT x, sumint(y) OVER ( ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_y FROM t3 ORDER BY x;", callback, 0, NULL)) {
fprintf(stderr, "SELECT: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
fprintf(stderr, "SELECT finished\n");
sqlite3_close(db);
return 0;
}
Edited by Roxy Light