Fix and test the processing of sqlite3_result_error() withing aggregate
functions. Allow errors to come from the step function (a new capability). Ticket #1632. (CVS 2981) FossilOrigin-Name: fd4a6bb1ac94d085dda247799c0a5c64aaeec046
This commit is contained in:
+29
-3
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test1.c,v 1.200 2006/01/20 10:55:05 danielk1977 Exp $
|
||||
** $Id: test1.c,v 1.201 2006/01/20 15:45:36 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
@@ -555,6 +555,14 @@ static int test_create_function(
|
||||
|
||||
/*
|
||||
** Routines to implement the x_count() aggregate function.
|
||||
**
|
||||
** x_count() counts the number of non-null arguments. But there are
|
||||
** some twists for testing purposes.
|
||||
**
|
||||
** If the argument to x_count() is 40 then a UTF-8 error is reported
|
||||
** on the step function. If x_count(41) is seen, then a UTF-16 error
|
||||
** is reported on the step function. If the total count is 42, then
|
||||
** a UTF-8 error is reported on the finalize function.
|
||||
*/
|
||||
typedef struct CountCtx CountCtx;
|
||||
struct CountCtx {
|
||||
@@ -566,11 +574,26 @@ static void countStep(sqlite3_context *context, int argc, sqlite3_value **argv){
|
||||
if( (argc==0 || SQLITE_NULL!=sqlite3_value_type(argv[0]) ) && p ){
|
||||
p->n++;
|
||||
}
|
||||
if( argc>0 ){
|
||||
int v = sqlite3_value_int(argv[0]);
|
||||
if( v==40 ){
|
||||
sqlite3_result_error(context, "value of 40 handed to x_count", -1);
|
||||
}else if( v==41 ){
|
||||
const char zUtf16ErrMsg[] = { 0, 0x61, 0, 0x62, 0, 0x63, 0, 0, 0};
|
||||
sqlite3_result_error16(context, &zUtf16ErrMsg[1-SQLITE_BIGENDIAN], -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
static void countFinalize(sqlite3_context *context){
|
||||
CountCtx *p;
|
||||
p = sqlite3_aggregate_context(context, sizeof(*p));
|
||||
sqlite3_result_int(context, p ? p->n : 0);
|
||||
if( p ){
|
||||
if( p->n==42 ){
|
||||
sqlite3_result_error(context, "x_count totals to 42", -1);
|
||||
}else{
|
||||
sqlite3_result_int(context, p ? p->n : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -582,7 +605,10 @@ static void countFinalize(sqlite3_context *context){
|
||||
**
|
||||
** The original motivation for this routine was to be able to call the
|
||||
** sqlite3_create_aggregate function while a query is in progress in order
|
||||
** to test the SQLITE_MISUSE detection logic.
|
||||
** to test the SQLITE_MISUSE detection logic. See misuse.test.
|
||||
**
|
||||
** This routine was later extended to test the use of sqlite3_result_error()
|
||||
** within aggregate functions.
|
||||
*/
|
||||
static int test_create_aggregate(
|
||||
void *NotUsed,
|
||||
|
||||
+16
-13
@@ -43,7 +43,7 @@
|
||||
** in this file for details. If in doubt, do not deviate from existing
|
||||
** commenting and indentation practices when changing or adding code.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.533 2006/01/18 18:22:43 danielk1977 Exp $
|
||||
** $Id: vdbe.c,v 1.534 2006/01/20 15:45:36 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
@@ -1202,22 +1202,17 @@ case OP_Function: {
|
||||
pOp->p3type = P3_VDBEFUNC;
|
||||
}
|
||||
|
||||
/* If the function returned an error, throw an exception */
|
||||
if( ctx.isError ){
|
||||
sqlite3SetString(&p->zErrMsg, sqlite3_value_text(&ctx.s), (char*)0);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Copy the result of the function to the top of the stack */
|
||||
sqlite3VdbeChangeEncoding(&ctx.s, encoding);
|
||||
pTos++;
|
||||
pTos->flags = 0;
|
||||
sqlite3VdbeMemMove(pTos, &ctx.s);
|
||||
|
||||
/* If the function returned an error, throw an exception */
|
||||
if( ctx.isError ){
|
||||
if( !(pTos->flags&MEM_Str) ){
|
||||
sqlite3SetString(&p->zErrMsg, "user function error", (char*)0);
|
||||
}else{
|
||||
sqlite3SetString(&p->zErrMsg, sqlite3_value_text(pTos), (char*)0);
|
||||
sqlite3VdbeChangeEncoding(pTos, encoding);
|
||||
}
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4437,6 +4432,9 @@ case OP_AggStep: { /* no-push */
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nMem );
|
||||
ctx.pMem = pMem = &p->aMem[pOp->p1];
|
||||
pMem->n++;
|
||||
ctx.s.flags = MEM_Null;
|
||||
ctx.s.z = 0;
|
||||
ctx.s.xDel = 0;
|
||||
ctx.isError = 0;
|
||||
ctx.pColl = 0;
|
||||
if( ctx.pFunc->needCollSeq ){
|
||||
@@ -4448,8 +4446,10 @@ case OP_AggStep: { /* no-push */
|
||||
(ctx.pFunc->xStep)(&ctx, n, apVal);
|
||||
popStack(&pTos, n);
|
||||
if( ctx.isError ){
|
||||
sqlite3SetString(&p->zErrMsg, sqlite3_value_text(&ctx.s), (char*)0);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
sqlite3VdbeMemRelease(&ctx.s);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4470,7 +4470,10 @@ case OP_AggFinal: { /* no-push */
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nMem );
|
||||
pMem = &p->aMem[pOp->p1];
|
||||
assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
|
||||
sqlite3VdbeMemFinalize(pMem, (FuncDef*)pOp->p3);
|
||||
rc = sqlite3VdbeMemFinalize(pMem, (FuncDef*)pOp->p3);
|
||||
if( rc==SQLITE_ERROR ){
|
||||
sqlite3SetString(&p->zErrMsg, sqlite3_value_text(pMem), (char*)0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -374,7 +374,7 @@ int sqlite3VdbeMemRealify(Mem*);
|
||||
int sqlite3VdbeMemNumerify(Mem*);
|
||||
int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
|
||||
void sqlite3VdbeMemRelease(Mem *p);
|
||||
void sqlite3VdbeMemFinalize(Mem*, FuncDef*);
|
||||
int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
|
||||
#ifndef NDEBUG
|
||||
void sqlite3VdbeMemSanity(Mem*, u8);
|
||||
int sqlite3VdbeOpcodeNoPush(u8);
|
||||
|
||||
+10
-1
@@ -201,8 +201,12 @@ int sqlite3VdbeMemStringify(Mem *pMem, int enc){
|
||||
** Memory cell pMem contains the context of an aggregate function.
|
||||
** This routine calls the finalize method for that function. The
|
||||
** result of the aggregate is stored back into pMem.
|
||||
**
|
||||
** Return SQLITE_ERROR if the finalizer reports an error. SQLITE_OK
|
||||
** otherwise.
|
||||
*/
|
||||
void sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
|
||||
int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
|
||||
int rc = SQLITE_OK;
|
||||
if( pFunc && pFunc->xFinalize ){
|
||||
sqlite3_context ctx;
|
||||
assert( (pMem->flags & MEM_Null)!=0 || pFunc==*(FuncDef**)&pMem->i );
|
||||
@@ -210,6 +214,7 @@ void sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
|
||||
ctx.s.z = pMem->zShort;
|
||||
ctx.pMem = pMem;
|
||||
ctx.pFunc = pFunc;
|
||||
ctx.isError = 0;
|
||||
pFunc->xFinalize(&ctx);
|
||||
if( pMem->z && pMem->z!=pMem->zShort ){
|
||||
sqliteFree( pMem->z );
|
||||
@@ -218,7 +223,11 @@ void sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
|
||||
if( pMem->flags & MEM_Short ){
|
||||
pMem->z = pMem->zShort;
|
||||
}
|
||||
if( ctx.isError ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user