Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Ensure that LIMIT clauses may be passed through to virtual table implementations even if the WHERE clause uses operators that may only be optimized by virtual, not built-in, tables (!=, functions, MATCH etc.). |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
f38caab23bcef1df02618376de22d208 |
User & Date: | dan 2023-01-04 17:46:29 |
Context
2023-01-04
| ||
18:32 | Adjustments to the tool/warnings.sh script to account for compiler differences. (check-in: 863c03be user: drh tags: trunk) | |
17:46 | Ensure that LIMIT clauses may be passed through to virtual table implementations even if the WHERE clause uses operators that may only be optimized by virtual, not built-in, tables (!=, functions, MATCH etc.). (check-in: f38caab2 user: dan tags: trunk) | |
16:54 | Employ deliberate_fall_through macro to quiet some compilers. (check-in: 869635fb user: larrybr tags: trunk) | |
Changes
Changes to src/test_bestindex.c.
︙ | ︙ | |||
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | #if defined(INCLUDE_SQLITE_TCL_H) # include "sqlite_tcl.h" #else # include "tcl.h" #endif #ifndef SQLITE_OMIT_VIRTUALTABLE typedef struct tcl_vtab tcl_vtab; typedef struct tcl_cursor tcl_cursor; /* ** A fs virtual-table object */ struct tcl_vtab { sqlite3_vtab base; Tcl_Interp *interp; Tcl_Obj *pCmd; sqlite3 *db; }; /* A tcl cursor object */ struct tcl_cursor { sqlite3_vtab_cursor base; sqlite3_stmt *pStmt; /* Read data from here */ }; /* ** Dequote string z in place. */ static void tclDequote(char *z){ char q = z[0]; | > > > > > > > > > > | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | #if defined(INCLUDE_SQLITE_TCL_H) # include "sqlite_tcl.h" #else # include "tcl.h" #endif #ifndef SQLITE_OMIT_VIRTUALTABLE typedef struct tcl_vtab tcl_vtab; typedef struct tcl_cursor tcl_cursor; typedef struct TestFindFunction TestFindFunction; /* ** A fs virtual-table object */ struct tcl_vtab { sqlite3_vtab base; Tcl_Interp *interp; Tcl_Obj *pCmd; TestFindFunction *pFindFunctionList; sqlite3 *db; }; /* A tcl cursor object */ struct tcl_cursor { sqlite3_vtab_cursor base; sqlite3_stmt *pStmt; /* Read data from here */ }; struct TestFindFunction { tcl_vtab *pTab; const char *zName; TestFindFunction *pNext; }; /* ** Dequote string z in place. */ static void tclDequote(char *z){ char q = z[0]; |
︙ | ︙ | |||
219 220 221 222 223 224 225 226 227 228 229 230 231 232 | *ppVtab = &pTab->base; return rc; } /* The xDisconnect and xDestroy methods are also the same */ static int tclDisconnect(sqlite3_vtab *pVtab){ tcl_vtab *pTab = (tcl_vtab*)pVtab; Tcl_DecrRefCount(pTab->pCmd); sqlite3_free(pTab); return SQLITE_OK; } /* ** Open a new tcl cursor. | > > > > > | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | *ppVtab = &pTab->base; return rc; } /* The xDisconnect and xDestroy methods are also the same */ static int tclDisconnect(sqlite3_vtab *pVtab){ tcl_vtab *pTab = (tcl_vtab*)pVtab; while( pTab->pFindFunctionList ){ TestFindFunction *p = pTab->pFindFunctionList; pTab->pFindFunctionList = p->pNext; sqlite3_free(p); } Tcl_DecrRefCount(pTab->pCmd); sqlite3_free(pTab); return SQLITE_OK; } /* ** Open a new tcl cursor. |
︙ | ︙ | |||
394 395 396 397 398 399 400 | ){ int ii; Tcl_Obj *pRes = Tcl_NewObj(); Tcl_IncrRefCount(pRes); for(ii=0; ii<pIdxInfo->nConstraint; ii++){ struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; Tcl_Obj *pElem = Tcl_NewObj(); | | | 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 | ){ int ii; Tcl_Obj *pRes = Tcl_NewObj(); Tcl_IncrRefCount(pRes); for(ii=0; ii<pIdxInfo->nConstraint; ii++){ struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; Tcl_Obj *pElem = Tcl_NewObj(); const char *zOp = 0; Tcl_IncrRefCount(pElem); switch( pCons->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: zOp = "eq"; break; case SQLITE_INDEX_CONSTRAINT_GT: |
︙ | ︙ | |||
434 435 436 437 438 439 440 | case SQLITE_INDEX_CONSTRAINT_LIMIT: zOp = "limit"; break; case SQLITE_INDEX_CONSTRAINT_OFFSET: zOp = "offset"; break; } Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1)); | > | > > > | 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 | case SQLITE_INDEX_CONSTRAINT_LIMIT: zOp = "limit"; break; case SQLITE_INDEX_CONSTRAINT_OFFSET: zOp = "offset"; break; } Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1)); if( zOp ){ Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zOp, -1)); }else{ Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->op)); } Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->iColumn)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->usable)); Tcl_ListObjAppendElement(0, pRes, pElem); Tcl_DecrRefCount(pElem); |
︙ | ︙ | |||
688 689 690 691 692 693 694 695 696 697 698 699 700 701 | } } } } return rc; } /* ** A virtual table module that provides read-only access to a ** Tcl global variable namespace. */ static sqlite3_module tclModule = { 0, /* iVersion */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 | } } } } return rc; } static void tclFunction(sqlite3_context *pCtx, int nArg, sqlite3_value **apArg){ TestFindFunction *p = (TestFindFunction*)sqlite3_user_data(pCtx); Tcl_Interp *interp = p->pTab->interp; Tcl_Obj *pScript = 0; Tcl_Obj *pRet = 0; int ii; pScript = Tcl_DuplicateObj(p->pTab->pCmd); Tcl_IncrRefCount(pScript); Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("function", -1)); Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(p->zName, -1)); for(ii=0; ii<nArg; ii++){ const char *zArg = (const char*)sqlite3_value_text(apArg[ii]); Tcl_ListObjAppendElement(interp, pScript, (zArg ? Tcl_NewStringObj(zArg, -1) : Tcl_NewObj()) ); } Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL); Tcl_DecrRefCount(pScript); pRet = Tcl_GetObjResult(interp); sqlite3_result_text(pCtx, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT); } static int tclFindFunction( sqlite3_vtab *tab, int nArg, const char *zName, void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT */ void **ppArg /* OUT */ ){ int iRet = 0; tcl_vtab *pTab = (tcl_vtab*)tab; Tcl_Interp *interp = pTab->interp; Tcl_Obj *pScript = 0; int rc = SQLITE_OK; pScript = Tcl_DuplicateObj(pTab->pCmd); Tcl_IncrRefCount(pScript); Tcl_ListObjAppendElement( interp, pScript, Tcl_NewStringObj("xFindFunction", -1) ); Tcl_ListObjAppendElement(interp, pScript, Tcl_NewIntObj(nArg)); Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(zName, -1)); rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL); Tcl_DecrRefCount(pScript); if( rc==SQLITE_OK ){ Tcl_Obj *pObj = Tcl_GetObjResult(interp); if( Tcl_GetIntFromObj(interp, pObj, &iRet) ){ rc = SQLITE_ERROR; }else if( iRet>0 ){ int nName = strlen(zName); int nByte = nName + 1 + sizeof(TestFindFunction); TestFindFunction *pNew = 0; pNew = (TestFindFunction*)sqlite3_malloc(nByte); if( pNew==0 ){ iRet = 0; }else{ memset(pNew, 0, nByte); pNew->zName = (const char*)&pNew[1]; memcpy((char*)pNew->zName, zName, nName); pNew->pTab = pTab; pNew->pNext = pTab->pFindFunctionList; pTab->pFindFunctionList = pNew; *ppArg = (void*)pNew; *pxFunc = tclFunction; } } } return iRet; } /* ** A virtual table module that provides read-only access to a ** Tcl global variable namespace. */ static sqlite3_module tclModule = { 0, /* iVersion */ |
︙ | ︙ | |||
712 713 714 715 716 717 718 | tclColumn, /* xColumn - read data */ tclRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ | | | 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 | tclColumn, /* xColumn - read data */ tclRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ tclFindFunction, /* xFindFunction */ 0, /* xRename */ }; /* ** Decode a pointer to an sqlite3 object. */ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); |
︙ | ︙ |
Changes to src/whereexpr.c.
︙ | ︙ | |||
1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 | for(ii=0; ii<pWC->nTerm; ii++){ if( pWC->a[ii].wtFlags & TERM_CODED ){ /* This term is a vector operation that has been decomposed into ** other, subsequent terms. It can be ignored. See tag-20220128a */ assert( pWC->a[ii].wtFlags & TERM_VIRTUAL ); assert( pWC->a[ii].eOperator==WO_ROWVAL ); continue; } if( pWC->a[ii].leftCursor!=iCsr ) return; } /* Check condition (5). Return early if it is not met. */ if( pOrderBy ){ for(ii=0; ii<pOrderBy->nExpr; ii++){ | > > > > > > > | 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 | for(ii=0; ii<pWC->nTerm; ii++){ if( pWC->a[ii].wtFlags & TERM_CODED ){ /* This term is a vector operation that has been decomposed into ** other, subsequent terms. It can be ignored. See tag-20220128a */ assert( pWC->a[ii].wtFlags & TERM_VIRTUAL ); assert( pWC->a[ii].eOperator==WO_ROWVAL ); continue; } if( pWC->a[ii].nChild ){ /* If this term has child terms, then they are also part of the ** pWC->a[] array. So this term can be ignored, as a LIMIT clause ** will only be added if each of the child terms passes the ** (leftCursor==iCsr) test below. */ continue; } if( pWC->a[ii].leftCursor!=iCsr ) return; } /* Check condition (5). Return early if it is not met. */ if( pOrderBy ){ for(ii=0; ii<pOrderBy->nExpr; ii++){ |
︙ | ︙ |
Added test/bestindexA.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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | # 2020-01-29 # # 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. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix bestindexA ifcapable !vtab { finish_test return } proc vtab_command {method args} { switch -- $method { xConnect { return "CREATE TABLE x(a, b, c)" } xBestIndex { set hdl [lindex $args 0] set clist [$hdl constraints] foreach c $clist { array set C $c lappend ::vtab_constraints [list $C(op) $C(column)] } return [list] } xFilter { return "" } xFindFunction { foreach {nArg name} $args {} if {$nArg==2 && $name=="even"} { return 152 } return 0 } } return {} } register_tcl_module db do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING tcl(vtab_command); } proc do_xbestindex_test {tn sql res} { set script [subst { execsql "$sql" set ::vtab_constraints }] uplevel [list do_test $tn $script [list {*}$res]] set ::vtab_constraints [list] } do_xbestindex_test 1.1 { SELECT * FROM t1 WHERE a=? } { {eq 0} } do_xbestindex_test 1.2 { SELECT * FROM t1 WHERE a=? LIMIT 10 } { {eq 0} {limit 0} } do_xbestindex_test 1.3 { SELECT * FROM t1 WHERE a=? AND (b+1)=? LIMIT 10 } { {eq 0} } proc error_function {args} { error "not a function!" } db function even error_function do_xbestindex_test 1.4 { SELECT * FROM t1 WHERE even(a, ?) } { {152 0} } do_xbestindex_test 1.5 { SELECT * FROM t1 WHERE b=10 AND even(a, ?) } { {eq 1} {152 0} } do_xbestindex_test 1.6 { SELECT * FROM t1 WHERE b=10 LIMIT 10 } { {eq 1} {limit 0} } do_xbestindex_test 1.7 { SELECT * FROM t1 WHERE even(b,?) LIMIT 10 } { {152 1} {limit 0} } do_xbestindex_test 1.8 { SELECT * FROM t1 WHERE b!=? LIMIT 10 } { {ne 1} {limit 0} } do_xbestindex_test 1.9 { SELECT * FROM t1 WHERE ?=a LIMIT 10 } { {eq 0} {limit 0} } finish_test |