Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix the OP_OpenDup opcode so that it is able to duplicate a cursor that was itself opened by OP_OpenDup. Add additional verification of ephemeral tables. Fix for ticket [bb8a9fd4a9b7fce5]. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
bcbe5308f3a3b94f965b0f5627cb29cc |
User & Date: | drh 2021-03-18 16:47:24 |
Context
2021-03-18
| ||
16:52 | Increase the patch level to 3.35.3. (check-in: 259b7c3e user: drh tags: trunk) | |
16:47 | Fix the OP_OpenDup opcode so that it is able to duplicate a cursor that was itself opened by OP_OpenDup. Add additional verification of ephemeral tables. Fix for ticket [bb8a9fd4a9b7fce5]. (check-in: bcbe5308 user: drh tags: trunk) | |
16:36 | Remove unnecessary code from sqlite3BtreeClose() and add test cases. (Closed-Leaf check-in: edbfdcae user: drh tags: opendup-fix) | |
14:36 | Fix the dbsqlfuzz-derived authorizer in fuzzcheck to avoid creating junk files when running rogue tests. (check-in: eca21a62 user: drh tags: trunk) | |
Changes
Changes to src/btree.c.
︙ | ︙ | |||
2733 2734 2735 2736 2737 2738 2739 | } /* ** Close an open database and invalidate all cursors. */ int sqlite3BtreeClose(Btree *p){ BtShared *pBt = p->pBt; | < > > > > | | | | | | > | 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 | } /* ** Close an open database and invalidate all cursors. */ int sqlite3BtreeClose(Btree *p){ BtShared *pBt = p->pBt; /* Close all cursors opened via this handle. */ assert( sqlite3_mutex_held(p->db->mutex) ); sqlite3BtreeEnter(p); /* Verify that no other cursors have this Btree open */ #ifdef SQLITE_DEBUG { BtCursor *pCur = pBt->pCursor; while( pCur ){ BtCursor *pTmp = pCur; pCur = pCur->pNext; assert( pTmp->pBtree!=p ); } } #endif /* Rollback any active transaction and free the handle structure. ** The call to sqlite3BtreeRollback() drops any table-locks held by ** this handle. */ sqlite3BtreeRollback(p, SQLITE_OK, 0); sqlite3BtreeLeave(p); |
︙ | ︙ | |||
4537 4538 4539 4540 4541 4542 4543 | pPrev = pPrev->pNext; }while( ALWAYS(pPrev) ); } btreeReleaseAllCursorPages(pCur); unlockBtreeIfUnused(pBt); sqlite3_free(pCur->aOverflow); sqlite3_free(pCur->pKey); | > > > > > > | > | 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 | pPrev = pPrev->pNext; }while( ALWAYS(pPrev) ); } btreeReleaseAllCursorPages(pCur); unlockBtreeIfUnused(pBt); sqlite3_free(pCur->aOverflow); sqlite3_free(pCur->pKey); if( (pBt->openFlags & BTREE_SINGLE) && pBt->pCursor==0 ){ /* Since the BtShared is not sharable, there is no need to ** worry about the missing sqlite3BtreeLeave() call here. */ assert( pBtree->sharable==0 ); sqlite3BtreeClose(pBtree); }else{ sqlite3BtreeLeave(pBtree); } pCur->pBtree = 0; } return SQLITE_OK; } /* ** Make sure the BtCursor* given in the argument has a valid |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
268 269 270 271 272 273 274 | VdbeCursor *pCx = 0; nByte = ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0); assert( iCur>=0 && iCur<p->nCursor ); if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ | < < < < < | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | VdbeCursor *pCx = 0; nByte = ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0); assert( iCur>=0 && iCur<p->nCursor ); if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); p->apCsr[iCur] = 0; } if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){ p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); pCx->eCurType = eCurType; |
︙ | ︙ | |||
3869 3870 3871 3872 3873 3874 3875 | */ case OP_OpenDup: { VdbeCursor *pOrig; /* The original cursor to be duplicated */ VdbeCursor *pCx; /* The new cursor */ pOrig = p->apCsr[pOp->p2]; assert( pOrig ); | | > > > | | 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 | */ case OP_OpenDup: { VdbeCursor *pOrig; /* The original cursor to be duplicated */ VdbeCursor *pCx; /* The new cursor */ pOrig = p->apCsr[pOp->p2]; assert( pOrig ); assert( pOrig->isEphemeral ); /* Only ephemeral cursors can be duplicated */ pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; pCx->isEphemeral = 1; pCx->pKeyInfo = pOrig->pKeyInfo; pCx->isTable = pOrig->isTable; pCx->pgnoRoot = pOrig->pgnoRoot; pCx->isOrdered = pOrig->isOrdered; pCx->pBtx = pOrig->pBtx; pCx->hasBeenDuped = 1; pOrig->hasBeenDuped = 1; rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, pCx->pKeyInfo, pCx->uc.pCursor); /* The sqlite3BtreeCursor() routine can only fail for the first cursor ** opened for a database. Since there is already an open cursor when this ** opcode is run, the sqlite3BtreeCursor() cannot fail */ assert( rc==SQLITE_OK ); break; } |
︙ | ︙ | |||
3945 3946 3947 3948 3949 3950 3951 | assert( pOp->p2==0 ); /* Only used when number of columns is zero */ assert( pOp->opcode==OP_OpenEphemeral ); assert( aMem[pOp->p3].flags & MEM_Null ); aMem[pOp->p3].n = 0; aMem[pOp->p3].z = ""; } pCx = p->apCsr[pOp->p1]; | | | > | < | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > | 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 | assert( pOp->p2==0 ); /* Only used when number of columns is zero */ assert( pOp->opcode==OP_OpenEphemeral ); assert( aMem[pOp->p3].flags & MEM_Null ); aMem[pOp->p3].n = 0; aMem[pOp->p3].z = ""; } pCx = p->apCsr[pOp->p1]; if( pCx && !pCx->hasBeenDuped ){ /* If the ephermeral table is already open and has no duplicates from ** OP_OpenDup, then erase all existing content so that the table is ** empty again, rather than creating a new table. */ assert( pCx->isEphemeral ); pCx->seqCount = 0; pCx->cacheStatus = CACHE_STALE; rc = sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0); }else{ pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); if( pCx==0 ) goto no_mem; pCx->isEphemeral = 1; rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); if( rc==SQLITE_OK ){ rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0); if( rc==SQLITE_OK ){ /* If a transient index is required, create it by calling ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before ** opening it. If a transient table is required, just use the ** automatically created table with root-page 1 (an BLOB_INTKEY table). */ if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ assert( pOp->p4type==P4_KEYINFO ); rc = sqlite3BtreeCreateTable(pCx->pBtx, &pCx->pgnoRoot, BTREE_BLOBKEY | pOp->p5); if( rc==SQLITE_OK ){ assert( pCx->pgnoRoot==SCHEMA_ROOT+1 ); assert( pKeyInfo->db==db ); assert( pKeyInfo->enc==ENC(db) ); rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, pKeyInfo, pCx->uc.pCursor); } pCx->isTable = 0; }else{ pCx->pgnoRoot = SCHEMA_ROOT; rc = sqlite3BtreeCursor(pCx->pBtx, SCHEMA_ROOT, BTREE_WRCSR, 0, pCx->uc.pCursor); pCx->isTable = 1; } } pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); if( rc ){ sqlite3BtreeClose(pCx->pBtx); } } } if( rc ) goto abort_due_to_error; pCx->nullRow = 1; break; } /* Opcode: SorterOpen P1 P2 P3 P4 * |
︙ | ︙ |
Changes to src/vdbeInt.h.
︙ | ︙ | |||
82 83 84 85 86 87 88 89 90 91 92 93 94 95 | #ifdef SQLITE_DEBUG u8 seekOp; /* Most recent seek operation on this cursor */ u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */ #endif Bool isEphemeral:1; /* True for an ephemeral table */ Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ Btree *pBtx; /* Separate file holding temporary table */ i64 seqCount; /* Sequence counter */ u32 *aAltMap; /* Mapping from table to index column numbers */ /* Cached OP_Column parse information is only valid if cacheStatus matches ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of | > | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | #ifdef SQLITE_DEBUG u8 seekOp; /* Most recent seek operation on this cursor */ u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */ #endif Bool isEphemeral:1; /* True for an ephemeral table */ Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ Bool hasBeenDuped:1; /* This cursor was source or target of OP_OpenDup */ u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ Btree *pBtx; /* Separate file holding temporary table */ i64 seqCount; /* Sequence counter */ u32 *aAltMap; /* Mapping from table to index column numbers */ /* Cached OP_Column parse information is only valid if cacheStatus matches ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
2475 2476 2477 2478 2479 2480 2481 | assert( pCx->pBtx==0 || pCx->isEphemeral ); switch( pCx->eCurType ){ case CURTYPE_SORTER: { sqlite3VdbeSorterClose(p->db, pCx); break; } case CURTYPE_BTREE: { | < < < < < | | < | 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 | assert( pCx->pBtx==0 || pCx->isEphemeral ); switch( pCx->eCurType ){ case CURTYPE_SORTER: { sqlite3VdbeSorterClose(p->db, pCx); break; } case CURTYPE_BTREE: { assert( pCx->uc.pCursor!=0 ); sqlite3BtreeCloseCursor(pCx->uc.pCursor); break; } #ifndef SQLITE_OMIT_VIRTUALTABLE case CURTYPE_VTAB: { sqlite3_vtab_cursor *pVCur = pCx->uc.pVCur; const sqlite3_module *pModule = pVCur->pVtab->pModule; assert( pVCur->pVtab->nRef>0 ); |
︙ | ︙ |
Changes to test/with2.test.
︙ | ︙ | |||
410 411 412 413 414 415 416 417 418 | SELECT 1 UNION ALL SELECT a+1 FROM q, v WHERE a<5 ) SELECT * FROM q; } {1 2 3 4 5} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 | SELECT 1 UNION ALL SELECT a+1 FROM q, v WHERE a<5 ) SELECT * FROM q; } {1 2 3 4 5} # 2021-03-18 # Ticket bb8a9fd4a9b7fce5 reset_db do_execsql_test 9.1 { WITH xyz(a) AS ( WITH abc AS ( SELECT 1234 ) SELECT * FROM abc ) SELECT * FROM xyz AS one, xyz AS two, ( SELECT * FROM xyz UNION ALL SELECT * FROM xyz ); } {1234 1234 1234 1234 1234 1234} load_static_extension db series do_execsql_test 9.2 { WITH cst(rsx, rsy) AS ( SELECT 100, 100 ), cst2(minx, maxx, stepx, miny, maxy, stepy, qualitativex, qualitativey) AS ( SELECT NULL, NULL, NULL, NULL, NULL, NULL, 0, 0 ), ds0(m, n, x, y, x2, y2, title, size, mark, label, markmode) AS ( SELECT 1, 2, 3, 4, 5, 6, 7 , 8, 9, 10, 11 ), ds(m, n, x, y, x2, y2, title, size, mark, label, markmode) AS ( SELECT m, n, x, y, x2, y2, title, size, mark, label, markmode FROM ds0 WINDOW w AS (PARTITION BY m, x ORDER BY n) ), d(m, n, x, y, x2, y2, labelx,labely,title,size,mark,label,markmode) AS ( SELECT m, n, x, y, x2, y2, x, y, title, size, mark, label, markmode FROM ds, cst2 ), ylabels(y, label) AS ( SELECT y, MIN(labely) FROM d GROUP BY y ), yaxis(maxy, miny, stepy , minstepy) AS ( WITH xt0(minx, maxx) AS ( SELECT coalesce(miny, min(min(y2), min(y))), coalesce(maxy, max(max(y2), max(y))) + qualitativey FROM d, cst2 ), xt1(mx, mn) AS (SELECT maxx, minx FROM xt0), xt2(mx, mn, step) AS (SELECT mx, mn, (mx-mn) FROM xt1), xt3(mx, mn, ms) AS ( SELECT mx, mn, first_value(rs) OVER (order by x desc) AS ms FROM (SELECT mx, mn, step, f,(mx-mn) as rng, 1.0*step/f as rs, 1.0*(mx-mn)/(step/f) AS x FROM xt2, (SELECT 1 AS f UNION ALL SELECT 2 UNION ALL SELECT 4 UNION ALL SELECT 5)) AS src WHERE x < 10 limit 1), xt4(minstepy) AS ( SELECT MIN(abs(y2-y)) FROM d WHERE y2 != y ) SELECT (mx/ms)*ms, (mn/ms)*ms, coalesce(stepy, ms), coalesce(minstepy, ms, stepy) FROM xt3, cst2,xt4 ), distinct_mark_n_m(mark, ze, zem, title) AS ( SELECT DISTINCT mark, n AS ze, m AS zem, title FROM ds0 ), facet0(m, mi, title, radial) AS ( SELECT md, row_number() OVER () - 1, title, 'radial' IN (SELECT mark FROM distinct_mark_n_m WHERE zem = md) FROM (SELECT DISTINCT zem AS md, title AS title FROM distinct_mark_n_m ORDER BY 2, 1) ), facet(m, mi, xorigin, yorigin, title, radial) AS ( SELECT m, mi, rsx * 1.2 * IFNULL(CASE WHEN ( 0 ) > 0 THEN mi / ( 0 ) ELSE mi % ( 2 ) END, mi), rsy * 1.2 * IFNULL(CASE WHEN ( 2 ) > 0 THEN mi / ( 2 ) ELSE mi / ( 0 ) END, 0), title, radial FROM facet0, cst ), radygrid(m, mi, tty, wty, ttx, ttx2, xorigin, yorigin) AS ( SELECT m, mi, rsy / 2 / ((maxy-miny)/stepy) * (value-1) AS tty, coalesce(NULL, miny + stepy * (value-1)) AS wty, xorigin, xorigin+rsx, xorigin + rsx / 2, yorigin + rsy / 2 FROM generate_series(1), yaxis, cst, facet LEFT JOIN ylabels ON ylabels.y = (miny + (value-1) * stepy) WHERE radial AND stop = 1+1.0*(maxy-miny)/stepy ), ypos(m, mi, pcx, pcy, radial) AS ( SELECT m, mi, xorigin, yorigin + CASE WHEN 0 BETWEEN miny AND maxy THEN rsy - (0 - miny) * rsy / (maxy-miny) WHEN 0 >= maxy THEN 0 ELSE rsy END, radial FROM yaxis, cst, facet WHERE NOT radial UNION ALL SELECT m, mi, xorigin + rsx / 2, yorigin + (CASE WHEN 0 BETWEEN miny AND maxy THEN rsy - (0 - miny) * rsy / 2 / (maxy-miny) WHEN 0 >= maxy THEN 0 ELSE rsy END ) / 2, radial FROM yaxis, cst, facet WHERE radial ) SELECT * FROM radygrid , ypos; } {} finish_test |