From: drh Date: Fri, 20 Jan 2006 15:45:36 +0000 (+0000) Subject: Fix and test the processing of sqlite3_result_error() withing aggregate X-Git-Tag: version-3.6.10~3186 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=90669c1dca02e5dc52d979f7a47ee2d27200e165;p=thirdparty%2Fsqlite.git 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 --- diff --git a/manifest b/manifest index 49283d5ac0..7d13cd8bd5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Handle\ssome\sof\sthe\sIO\serror\sconditions\sthat\smay\soccur\sin\sa\sshared-cache\scontext.\s(CVS\s2980) -D 2006-01-20T10:55:05 +C Fix\sand\stest\sthe\sprocessing\sof\ssqlite3_result_error()\swithing\saggregate\nfunctions.\s\sAllow\serrors\sto\scome\sfrom\sthe\sstep\sfunction\s(a\snew\ncapability).\s\sTicket\s#1632.\s(CVS\s2981) +D 2006-01-20T15:45:36 F Makefile.in ab3ffd8d469cef4477257169b82810030a6bb967 F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -73,7 +73,7 @@ F src/sqlite.h.in 492580f7e3ff71eb43193eb7bb98e2d549889ce3 F src/sqliteInt.h 7ddd6141a57748363fe42119f165d06260996aa3 F src/table.c 486dcfce532685b53b5a2b5da8bba0ded6fb2316 F src/tclsqlite.c d650bea0248fc0a310ddc2cb94273a3a5021fddf -F src/test1.c 44e8f194ee7d95e25dc421b10ced2ec9d89632a0 +F src/test1.c bcdd1978cb4f576611917948728acd09ede9c85d F src/test2.c ca74a1d8aeb7d9606e8f6b762c5daf85c1a3f92b F src/test3.c 86e99724ee898b119ed575ef9f98618afe7e5e5d F src/test4.c 6633cb7b4a7429e938804a34f688d118c8b7f8e1 @@ -87,15 +87,16 @@ F src/update.c 14be4ba2f438919b4217085c02feff569e6cf1f2 F src/utf.c 5ab8ca05d4e9ec81174b010f01ab12a232f0087d F src/util.c 14f1f842caede231c8f72308c40d8e1f1cbec59e F src/vacuum.c 3865673cc66acd0717ecd517f6b8fdb2a5e7924b -F src/vdbe.c 2a3d5ea76ab166bd77de9e3a7dbeb4a9354a1603 +F src/vdbe.c 9eceb866b8197d25d07f700e16b1a50638d4bd6e F src/vdbe.h 8729a4ee16ff9aeab2af9667df3cf300ff978e13 -F src/vdbeInt.h 5451cf71f229e366ac543607c0a17f36e5737ea9 +F src/vdbeInt.h eb3f86ab08ef11635bc78eb88c3ff13f923c233b F src/vdbeapi.c b5a3eacce266a657cdc0fc740b60ba480fb88649 F src/vdbeaux.c d9a757ed4e3eefc54408226cb781694059fe2b39 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5 -F src/vdbemem.c 53f25c5c537e4ded24549d6c8537e65d4efc42d1 +F src/vdbemem.c 2034e93b32c14bda6e306bb54e3a8e930b963027 F src/where.c 5215507b232e718606e0014f999912d53de32a70 F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42 +F test/aggerror.test b2223511ec37fcdeca023b5a261900f18261aad8 F test/all.test 5df90d015ca63fcef2a4b62c24f7316b66c4bfd4 F test/alter.test d2569ed639946be29cfcae9117942b4a6d01f719 F test/alter2.test cc0b8832e4e98605dbc26910efd4bb89abe59cb2 @@ -160,7 +161,7 @@ F test/enc3.test 890508efff6677345e93bf2a8adb0489b30df030 F test/expr.test a513aceb5d89042232e0d07ac5a1591965cf3963 F test/fkey1.test 153004438d51e6769fb1ce165f6313972d6263ce F test/format4.test 9f31d41d4f926cab97b2ebe6be00a6ab12dece87 -F test/func.test a7119afcc16abdf24b24486684fb888279008f75 +F test/func.test 337888172c054c2de1aa3bacad6115522943dc2c F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a F test/in.test cead6165aebbe0d451bb2263a307173acfeb6240 F test/index.test c478459611ded74745fee57f99f424da8a5f5fbd @@ -343,7 +344,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P 0208e4221a2d90b5ae0755061c345d3351a30da8 -R 1b27118630fddde079e3c9acefda5965 -U danielk1977 -Z 68c504492dfd4d0600ae3c8ff35cbb3d +P 97491d4eb5fc24d8f5cc7605db844359ecc6a818 +R 0ac1c3b7951d8503eabc2e4f37bf1aac +U drh +Z 64f4b8a9886c6294a59c365b44cd61fd diff --git a/manifest.uuid b/manifest.uuid index d56f17b21e..103b23698e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -97491d4eb5fc24d8f5cc7605db844359ecc6a818 \ No newline at end of file +fd4a6bb1ac94d085dda247799c0a5c64aaeec046 \ No newline at end of file diff --git a/src/test1.c b/src/test1.c index f3444d08a7..c563be8493 100644 --- a/src/test1.c +++ b/src/test1.c @@ -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, diff --git a/src/vdbe.c b/src/vdbe.c index ccc2ea1d70..f87eea08db 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -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->p1nMem ); 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->p1nMem ); 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; } diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 10c5be7871..88867b9761 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -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); diff --git a/src/vdbemem.c b/src/vdbemem.c index 2596418a9e..64416083d1 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -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; } /* diff --git a/test/aggerror.test b/test/aggerror.test new file mode 100644 index 0000000000..0179844673 --- /dev/null +++ b/test/aggerror.test @@ -0,0 +1,76 @@ +# 2006 January 20 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# +# This file implements tests for calling sqlite3_result_error() +# from within an aggregate function implementation. +# +# $Id: aggerror.test,v 1.1 2006/01/20 15:45:37 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + + +# Add the x_count aggregate function to the database handle. +# x_count will error out if its input is 40 or 41 or if its +# final results is 42. Make sure that such errors are handled +# appropriately. +# +do_test aggfunc-1.1 { + set DB [sqlite3_connection_pointer db] + sqlite3_create_aggregate $DB + execsql { + CREATE TABLE t1(a); + INSERT INTO t1 VALUES(1); + INSERT INTO t1 VALUES(2); + INSERT INTO t1 SELECT a+2 FROM t1; + INSERT INTO t1 SELECT a+4 FROM t1; + INSERT INTO t1 SELECT a+8 FROM t1; + INSERT INTO t1 SELECT a+16 FROM t1; + INSERT INTO t1 SELECT a+32 FROM t1 ORDER BY a LIMIT 7; + SELECT x_count(*) FROM t1; + } +} {39} +do_test aggfunc-1.2 { + execsql { + INSERT INTO t1 VALUES(40); + SELECT x_count(*) FROM t1; + } +} {40} +do_test aggfunc-1.3 { + catchsql { + SELECT x_count(a) FROM t1; + } +} {1 {value of 40 handed to x_count}} +do_test aggfunc-1.4 { + execsql { + UPDATE t1 SET a=41 WHERE a=40 + } + catchsql { + SELECT x_count(a) FROM t1; + } +} {1 abc} +do_test aggfunc-1.5 { + execsql { + SELECT x_count(*) FROM t1 + } +} 40 +do_test aggfunc-1.6 { + execsql { + INSERT INTO t1 VALUES(40); + INSERT INTO t1 VALUES(42); + } + catchsql { + SELECT x_count(*) FROM t1; + } +} {1 {x_count totals to 42}} + +finish_test diff --git a/test/func.test b/test/func.test index e0428b998f..4af372c397 100644 --- a/test/func.test +++ b/test/func.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing built-in functions. # -# $Id: func.test,v 1.43 2006/01/03 00:33:50 drh Exp $ +# $Id: func.test,v 1.44 2006/01/20 15:45:37 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -467,7 +467,7 @@ do_test func-15.1 { catchsql { select test_error(NULL); } -} {1 {user function error}} +} {1 {}} # Test the quote function for BLOB and NULL values. do_test func-16.1 {