Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | 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) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
fd4a6bb1ac94d085dda247799c0a5c64 |
User & Date: | drh 2006-01-20 15:45:36.000 |
Context
2006-01-20
| ||
16:32 | Fix another couple of IO or malloc() failure problems in a shared-cache context. (CVS 2982) (check-in: 7e34163a65 user: danielk1977 tags: trunk) | |
15:45 | 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) (check-in: fd4a6bb1ac user: drh tags: trunk) | |
10:55 | Handle some of the IO error conditions that may occur in a shared-cache context. (CVS 2980) (check-in: 97491d4eb5 user: danielk1977 tags: trunk) | |
Changes
Changes to src/test1.c.
︙ | ︙ | |||
9 10 11 12 13 14 15 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** Code for testing the printf() interface to SQLite. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ** May you share freely, never taking more than you give. ** ************************************************************************* ** Code for testing the printf() interface to SQLite. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** ** $Id: test1.c,v 1.201 2006/01/20 15:45:36 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include "os.h" #include <stdlib.h> #include <string.h> |
︙ | ︙ | |||
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 | if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR; Tcl_SetResult(interp, (char *)errorName(rc), 0); return TCL_OK; } /* ** Routines to implement the x_count() aggregate function. */ typedef struct CountCtx CountCtx; struct CountCtx { int n; }; static void countStep(sqlite3_context *context, int argc, sqlite3_value **argv){ CountCtx *p; p = sqlite3_aggregate_context(context, sizeof(*p)); if( (argc==0 || SQLITE_NULL!=sqlite3_value_type(argv[0]) ) && p ){ p->n++; } } static void countFinalize(sqlite3_context *context){ CountCtx *p; p = sqlite3_aggregate_context(context, sizeof(*p)); | > > > > > > > > > > > > > > > > > > > > > | > > | > > > | 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 | if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR; Tcl_SetResult(interp, (char *)errorName(rc), 0); return TCL_OK; } /* ** 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 { int n; }; static void countStep(sqlite3_context *context, int argc, sqlite3_value **argv){ CountCtx *p; p = sqlite3_aggregate_context(context, sizeof(*p)); 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)); 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); } } } /* ** Usage: sqlite_test_create_aggregate DB ** ** Call the sqlite3_create_function API on the given database in order ** to create a function named "x_count". This function does the same thing ** as the "md5sum" function. ** ** 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. 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, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
39 40 41 42 43 44 45 | ** ** Various scripts scan this source file in order to generate HTML ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | ** ** Various scripts scan this source file in order to generate HTML ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** 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.534 2006/01/20 15:45:36 drh Exp $ */ #include "sqliteInt.h" #include "os.h" #include <ctype.h> #include "vdbeInt.h" /* |
︙ | ︙ | |||
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 | ** immediately call the destructor for any non-static values. */ if( ctx.pVdbeFunc ){ sqlite3VdbeDeleteAuxData(ctx.pVdbeFunc, pOp->p1); pOp->p3 = (char *)ctx.pVdbeFunc; pOp->p3type = P3_VDBEFUNC; } /* Copy the result of the function to the top of the stack */ sqlite3VdbeChangeEncoding(&ctx.s, encoding); pTos++; pTos->flags = 0; sqlite3VdbeMemMove(pTos, &ctx.s); | > > > > > > < < < < < < < < < < < | 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 | ** immediately call the destructor for any non-static values. */ if( ctx.pVdbeFunc ){ sqlite3VdbeDeleteAuxData(ctx.pVdbeFunc, pOp->p1); pOp->p3 = (char *)ctx.pVdbeFunc; 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); break; } /* Opcode: BitAnd * * * ** ** Pop the top two elements from the stack. Convert both elements ** to integers. Push back onto the stack the bit-wise AND of the |
︙ | ︙ | |||
4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 | apVal[i] = pRec; storeTypeInfo(pRec, encoding); } ctx.pFunc = (FuncDef*)pOp->p3; assert( pOp->p1>=0 && pOp->p1<p->nMem ); ctx.pMem = pMem = &p->aMem[pOp->p1]; pMem->n++; ctx.isError = 0; ctx.pColl = 0; if( ctx.pFunc->needCollSeq ){ assert( pOp>p->aOp ); assert( pOp[-1].p3type==P3_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); ctx.pColl = (CollSeq *)pOp[-1].p3; } (ctx.pFunc->xStep)(&ctx, n, apVal); popStack(&pTos, n); if( ctx.isError ){ rc = SQLITE_ERROR; } break; } /* Opcode: AggFinal P1 P2 P3 ** ** Execute the finalizer function for an aggregate. P1 is ** the memory location that is the accumulator for the aggregate. ** ** P2 is the number of arguments that the step function takes and ** P3 is a pointer to the FuncDef for this function. The P2 ** argument is not used by this opcode. It is only there to disambiguate ** functions that can take varying numbers of arguments. The ** P3 argument is only needed for the degenerate case where ** the step function was not previously called. */ case OP_AggFinal: { /* no-push */ Mem *pMem; assert( pOp->p1>=0 && pOp->p1<p->nMem ); pMem = &p->aMem[pOp->p1]; assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); | > > > > > | > > > | 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 | apVal[i] = pRec; storeTypeInfo(pRec, encoding); } ctx.pFunc = (FuncDef*)pOp->p3; 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 ){ assert( pOp>p->aOp ); assert( pOp[-1].p3type==P3_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); ctx.pColl = (CollSeq *)pOp[-1].p3; } (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; } /* Opcode: AggFinal P1 P2 P3 ** ** Execute the finalizer function for an aggregate. P1 is ** the memory location that is the accumulator for the aggregate. ** ** P2 is the number of arguments that the step function takes and ** P3 is a pointer to the FuncDef for this function. The P2 ** argument is not used by this opcode. It is only there to disambiguate ** functions that can take varying numbers of arguments. The ** P3 argument is only needed for the degenerate case where ** the step function was not previously called. */ case OP_AggFinal: { /* no-push */ Mem *pMem; assert( pOp->p1>=0 && pOp->p1<p->nMem ); pMem = &p->aMem[pOp->p1]; assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); rc = sqlite3VdbeMemFinalize(pMem, (FuncDef*)pOp->p3); if( rc==SQLITE_ERROR ){ sqlite3SetString(&p->zErrMsg, sqlite3_value_text(pMem), (char*)0); } break; } /* Opcode: Vacuum * * * ** ** Vacuum the entire database. This opcode will cause other virtual |
︙ | ︙ |
Changes to src/vdbeInt.h.
︙ | ︙ | |||
370 371 372 373 374 375 376 | int sqlite3VdbeMemIntegerify(Mem*); double sqlite3VdbeRealValue(Mem*); void sqlite3VdbeIntegerAffinity(Mem*); int sqlite3VdbeMemRealify(Mem*); int sqlite3VdbeMemNumerify(Mem*); int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*); void sqlite3VdbeMemRelease(Mem *p); | | | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | int sqlite3VdbeMemIntegerify(Mem*); double sqlite3VdbeRealValue(Mem*); void sqlite3VdbeIntegerAffinity(Mem*); int sqlite3VdbeMemRealify(Mem*); int sqlite3VdbeMemNumerify(Mem*); int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*); void sqlite3VdbeMemRelease(Mem *p); int sqlite3VdbeMemFinalize(Mem*, FuncDef*); #ifndef NDEBUG void sqlite3VdbeMemSanity(Mem*, u8); int sqlite3VdbeOpcodeNoPush(u8); #endif int sqlite3VdbeMemTranslate(Mem*, u8); void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf, int nBuf); int sqlite3VdbeMemHandleBom(Mem *pMem); |
︙ | ︙ |
Changes to src/vdbemem.c.
︙ | ︙ | |||
197 198 199 200 201 202 203 204 | return rc; } /* ** 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. */ | > > > | > > > > | > > | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | return rc; } /* ** 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. */ 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 ); ctx.s.flags = MEM_Null; 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 ); } *pMem = ctx.s; if( pMem->flags & MEM_Short ){ pMem->z = pMem->zShort; } if( ctx.isError ){ rc = SQLITE_ERROR; } } return rc; } /* ** Release any memory held by the Mem. This may leave the Mem in an ** inconsistent state, for example with (Mem.z==0) and ** (Mem.type==SQLITE_TEXT). */ |
︙ | ︙ |
Added test/aggerror.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 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 |
Changes to test/func.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 2001 September 15 # # 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. The # focus of this file is testing built-in functions. # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # 2001 September 15 # # 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. The # focus of this file is testing built-in functions. # # $Id: func.test,v 1.44 2006/01/20 15:45:37 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Create a table to work with. # do_test func-0.0 { |
︙ | ︙ | |||
463 464 465 466 467 468 469 | } } {1} do_test func-15.1 { catchsql { select test_error(NULL); } | | | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 | } } {1} do_test func-15.1 { catchsql { select test_error(NULL); } } {1 {}} # Test the quote function for BLOB and NULL values. do_test func-16.1 { execsql { CREATE TABLE tbl2(a, b); } set STMT [sqlite3_prepare $::DB "INSERT INTO tbl2 VALUES(?, ?)" -1 TAIL] |
︙ | ︙ |