Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch multi-drop Excluding Merge-Ins
This is equivalent to a diff from 803481f2 to 1a49788a
2024-03-06
| ||
20:59 | Fix two -Werror=lto-type-mismatch warnings reported in forum post ef62b57bd5. (check-in: d4e423f3 user: mistachkin tags: branch-3.45) | |
2024-03-04
| ||
18:02 | The ability to DROP multiple tables (or views or indexes or triggers) at once, as implemented by this branch, appears to work. However, this branch adds over 1000 bytes to the code footprint. And the changes is significant, adding risk. The benefit of being able to drop multiple tables in a single statement is not seen to be enough to overcome those downsides, and so there are no immediate plans to merge this branch. (Leaf check-in: 1a49788a user: drh tags: multi-drop) | |
2024-03-02
| ||
21:02 | New date/time modifiers "mnth" and "yr" work like "month" and "year" but resolve day-of-month overflow by truncating rather than rolling over into the next month. Forum thread 232d1abb5d (check-in: 5d392c16 user: drh tags: trunk) | |
20:39 | Attempt to use less memory when handling a large VALUES clause attached to an INSERT statement. This branch is buggy. (check-in: 6d4f1ae2 user: dan tags: exp-values-clause) | |
13:38 | Proof-of-concept for new time-interval operator "pg-month" and "pg-year" that use the truncate-to-month algorithm for month overflow instead of the wrap-to-next-month algorithm that is used by SQLite by default. (check-in: b606c096 user: drh tags: month-truncate) | |
12:17 | Remove an unused line of code. (check-in: 26272590 user: drh tags: multi-drop) | |
2024-02-29
| ||
13:44 | Add the ability to DROP one or more objects of the same class in a single statement by listing the objects as multiple arguments to the DROP command. (check-in: 2266086c user: drh tags: multi-drop) | |
10:55 | Fix two -Werror=lto-type-mismatch warnings reported in forum post ef62b57bd5. (check-in: 803481f2 user: stephan tags: trunk) | |
03:45 | Fix two -Werror=lto-type-mismatch warnings reported in forum post ef62b57bd5. (Closed-Leaf check-in: 29f94610 user: stephan tags: lto-type-mismatch) | |
2024-02-28
| ||
15:32 | Add in a VdbeCoverage() macro needed by STAT4 that should have been part of check-in [63ef234e88857a65]. (check-in: d51c699a user: drh tags: trunk) | |
Changes to ext/rtree/rtree.c.
︙ | ︙ | |||
1049 1050 1051 1052 1053 1054 1055 | /* ** Rtree virtual table module xDestroy method. */ static int rtreeDestroy(sqlite3_vtab *pVtab){ Rtree *pRtree = (Rtree *)pVtab; int rc; char *zCreate = sqlite3_mprintf( | | | | | 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 | /* ** Rtree virtual table module xDestroy method. */ static int rtreeDestroy(sqlite3_vtab *pVtab){ Rtree *pRtree = (Rtree *)pVtab; int rc; char *zCreate = sqlite3_mprintf( "DROP TABLE '%q'.'%q_node'," "'%q'.'%q_rowid'," "'%q'.'%q_parent';", pRtree->zDb, pRtree->zName, pRtree->zDb, pRtree->zName, pRtree->zDb, pRtree->zName ); if( !zCreate ){ rc = SQLITE_NOMEM; }else{ |
︙ | ︙ |
Changes to src/build.c.
︙ | ︙ | |||
3256 3257 3258 3259 3260 3261 3262 | pIdx->tnum = iTo; } } } #endif /* | | | < < | < < < < | < < > | < < > > | | | | > > > > | | > > > | | | < < < | < < < < < < < < < < < < < < | < | < < < > | | | | | | < | | < < < < < < < < < < | > > > | < | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 | pIdx->tnum = iTo; } } } #endif /* ** The RootStack object holds a list (really a Heap) of btree root pages ** and schema numbers that need to be deleted using OP_Destroy. ** ** OP_Destroy opcodes must be issued in order of decreasing root page ** numbers in order to avoid having auto-vacuum disrupt subsequent ** OP_Destroy opcodes. For that reason, all pending OP_Destroy calls ** are accumulated in an instance of this object. Then at the end of ** code generation, this object is used to generate the OP_Destroy opcodes ** in decreasing order. ** ** The data structure is a max-heap. Each rootpage/schema-number combo ** is stored as a 64-bit integer, with the schema-number in the upper 32 ** bits and the page number in the lower 32-bits. The root of the heap ** (RootStack.a[0]) is the largest entry in the heap. The children of ** heap entry i are i*2+1 and i*2+2. The heap always stays balanced by ** ensuring that a parent entry is larger than both children. ** */ typedef struct RootStack RootStack; struct RootStack { u32 nAlloc; /* Slots allocated for a[] */ u32 nUsed; /* Slots used for in a[] */ u64 *a; /* Sorting heap. Each entry has iDb in the upper 32 bits and ** a page number in the lower 32 bits */ }; /* ** Add a new Pgno and iDb to the RootStack in question. ** ** The new entry is inserted at the of the heap (most distant child) ** and then the heap is rebalanced. */ static void rootStackPush(Parse *pParse, RootStack *p, Pgno pgno, int iDb){ u64 iNew; int i, j; if( p->nAlloc<p->nUsed+1 ){ p->nAlloc = p->nAlloc*2 + 12; p->a = sqlite3DbRealloc(pParse->db, p->a, sizeof(Pgno)*p->nAlloc); if( p->a==0 ){ p->nAlloc = p->nUsed = 0; return; } } assert( pgno>0 || CORRUPT_DB ); assert( iDb>=0 && iDb<pParse->db->nDb ); iNew = (((u64)iDb)<<32) | pgno; i = p->nUsed++; p->a[i] = iNew; while( i>=1 && p->a[j = (i-1)/2]<p->a[i] ){ u64 tmp = p->a[i]; p->a[i] = p->a[j]; p->a[j] = tmp; i = j; } } /* ** Remove the largest entry from the RootStack heap. Rebalance the ** heap and then return that largest entry. */ static u64 rootStackPop(RootStack *p){ u64 iMax; int i, j; assert( p->nUsed>0 ); assert( p->a!=0 ); iMax = p->a[0]; p->a[0] = p->a[--p->nUsed]; i = 0; while( (j = i*2+1)<p->nUsed ){ u64 tmp; if( j+1<p->nUsed && p->a[j+1]>p->a[j] ) j++; if( p->a[i]>p->a[j] ) break; tmp = p->a[i]; p->a[i] = p->a[j]; p->a[j] = tmp; i = j; } return iMax; } /* ** Generate OP_Destroy opcodes for every btree named in the given ** RootStack object. Issue these OP_Destroy opcodes in order of decreasing ** root page number. Then clean up any memory used by the RootStack. */ static void rootStackCode(Parse *pParse, RootStack *p){ int iLastDb = -1; int r1 = sqlite3GetTempReg(pParse); int r2 = sqlite3GetTempReg(pParse); int regReturn = sqlite3GetTempReg(pParse); Vdbe *v = pParse->pVdbe; int addrSub = 0; while( pParse->nErr==0 && p->nUsed>0 ){ u64 iNext = rootStackPop(p); Pgno pgno = iNext & 0xffffffff; int iDb = (iNext>>32)&0xffff; if( pgno<2 ) sqlite3ErrorMsg(pParse, "corrupt schema"); sqlite3MayAbort(pParse); if( iDb!=iLastDb ){ /* Code a subroutine to that will update the schema table when ** a root page number changes. The old root page is in register r1. ** Root page is moved to the value in register r2. */ iLastDb = iDb; sqlite3VdbeAddOp0(v, OP_Goto); addrSub = sqlite3VdbeCurrentAddr(v); sqlite3NestedParse(pParse, "UPDATE %Q." LEGACY_SCHEMA_TABLE " SET rootpage=#%d WHERE rootpage=#%d", pParse->db->aDb[iDb].zDbSName, r2, r1); sqlite3VdbeAddOp1(v, OP_Return, regReturn); sqlite3VdbeJumpHere(v, addrSub-1); } sqlite3VdbeAddOp3(v, OP_Destroy, pgno, r1, iDb); sqlite3VdbeAddOp2(v, OP_IfNot, r1, sqlite3VdbeCurrentAddr(v)+3); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Integer, pgno, r2); sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, addrSub); } if( pParse->nErr==0 ){ sqlite3ReleaseTempReg(pParse, r1); sqlite3ReleaseTempReg(pParse, regReturn); } sqlite3DbFree(pParse->db, p->a); } /* ** Remove entries from the sqlite_statN tables (for N in (1,2,3)) ** after a DROP INDEX or DROP TABLE command. */ static void sqlite3ClearStatTables( |
︙ | ︙ | |||
3363 3364 3365 3366 3367 3368 3369 | } } } /* ** Generate code to drop a table. */ | | > > > > > | 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 | } } } /* ** Generate code to drop a table. */ static void sqlite3CodeDropTable( Parse *pParse, /* Parsing context */ RootStack *pStack, /* List of pending OP_Destroys */ Table *pTab, /* Table to be dropped */ int iDb /* Schema holding pTab */ ){ Vdbe *v; sqlite3 *db = pParse->db; Trigger *pTrigger; Db *pDb = &db->aDb[iDb]; v = sqlite3GetVdbe(pParse); assert( v!=0 ); |
︙ | ︙ | |||
3416 3417 3418 3419 3420 3421 3422 | ** created in the temp database that refers to a table in another ** database. */ sqlite3NestedParse(pParse, "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE tbl_name=%Q and type!='trigger'", pDb->zDbSName, pTab->zName); | | > | > > > > | 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 | ** created in the temp database that refers to a table in another ** database. */ sqlite3NestedParse(pParse, "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE tbl_name=%Q and type!='trigger'", pDb->zDbSName, pTab->zName); if( IsOrdinaryTable(pTab) ){ Index *pIdx; rootStackPush(pParse, pStack, pTab->tnum, iDb); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->tnum==pTab->tnum ) continue; rootStackPush(pParse, pStack, pIdx->tnum, iDb); } } /* Remove the table entry from SQLite's internal schema and modify ** the schema cookie. */ if( IsVirtual(pTab) ){ sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0); |
︙ | ︙ | |||
3473 3474 3475 3476 3477 3478 3479 | /* ** This routine is called to do the work of a DROP TABLE statement. ** pName is the name of the table to be dropped. */ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ Table *pTab; | < | < | | > | > | | | | | | | | | | | | > | | | | | | | < > | | | | | | | | > > | | | | | | | | | | | | | | | | | | | > > | | < > > > | | | > > | < > | | | | | > > | < > | | > > | < > | > > > > > | > > | > > | < < | < | > > > > > > > > > | 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 | /* ** This routine is called to do the work of a DROP TABLE statement. ** pName is the name of the table to be dropped. */ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ Table *pTab; sqlite3 *db = pParse->db; int iDb; int ii, jj; RootStack rootStack; memset(&rootStack, 0, sizeof(rootStack)); (void)sqlite3GetVdbe(pParse); sqlite3ReadSchema(pParse); assert( pName!=0 || pParse->nErr!=0 ); for(ii=0; pParse->nErr==0 && ii<pName->nSrc; ii++){ if( noErr ) db->suppressErr++; assert( isView==0 || isView==LOCATE_VIEW ); pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[ii]); if( noErr ) db->suppressErr--; if( pTab==0 ){ if( noErr ){ sqlite3CodeVerifyNamedSchema(pParse, pName->a[ii].zDatabase); sqlite3ForceNotReadOnly(pParse); } testcase( ii+1<pName->nSrc ); continue; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 && iDb<db->nDb ); /* If pTab is a virtual table, call ViewGetColumnNames() to ensure ** it is initialized. */ if( IsVirtual(pTab) && sqlite3ViewGetColumnNames(pParse, pTab) ){ break; } #ifndef SQLITE_OMIT_AUTHORIZATION { int code; const char *zTab = SCHEMA_TABLE(iDb); const char *zDb = db->aDb[iDb].zDbSName; const char *zArg2 = 0; if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){ testcase( ii>0 ); testcase( ii+1<pName->nSrc ); break; } if( isView ){ if( !OMIT_TEMPDB && iDb==1 ){ code = SQLITE_DROP_TEMP_VIEW; }else{ code = SQLITE_DROP_VIEW; } #ifndef SQLITE_OMIT_VIRTUALTABLE }else if( IsVirtual(pTab) ){ code = SQLITE_DROP_VTABLE; zArg2 = sqlite3GetVTable(db, pTab)->pMod->zName; #endif }else{ if( !OMIT_TEMPDB && iDb==1 ){ code = SQLITE_DROP_TEMP_TABLE; }else{ code = SQLITE_DROP_TABLE; } } if( sqlite3AuthCheck(pParse, code, pTab->zName, zArg2, zDb) ){ testcase( ii>0 ); testcase( ii+1<pName->nSrc ); break; } if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ testcase( ii>0 ); testcase( ii+1<pName->nSrc ); break; } } #endif if( tableMayNotBeDropped(db, pTab) ){ testcase( ii>0 ); testcase( ii+1<pName->nSrc ); sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName); break; } #ifndef SQLITE_OMIT_VIEW /* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used ** on a table. */ if( isView && !IsView(pTab) ){ testcase( ii>0 ); testcase( ii+1<pName->nSrc ); sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName); break; } if( !isView && IsView(pTab) ){ testcase( ii>0 ); testcase( ii+1<pName->nSrc ); sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName); break; } #endif /* If this table or view has appeared previously in the list of tables ** or views to be dropped, then the prior appearance is sufficient so ** skip this one. */ for(jj=ii-1; jj>=0 && pName->a[jj].pTab!=pTab; jj--){} if( jj>=0 ) continue; /* Remember the table for use in the second pass */ pName->a[ii].pTab = pTab; pTab->nTabRef++; /* Generate code to clear this table from sqlite_statN and to ** cascade foreign key constraints. */ sqlite3BeginWriteOperation(pParse, 1, iDb); if( IsOrdinaryTable(pTab) ){ sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName); sqlite3FkDropTable(pParse, pName, pTab); } } /* Generate code to actually delete the tables/views in a second pass. ** Btrees must be deleted largest root page first, to avoid problems ** caused by autovacuum page reordering. */ for(ii=0; pParse->nErr==0 && ii<pName->nSrc; ii++){ pTab = pName->a[ii].pTab; if( pTab==0 ) continue; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3CodeDropTable(pParse, &rootStack, pTab, iDb); } sqlite3SrcListDelete(db, pName); rootStackCode(pParse, &rootStack); } /* ** This routine is called to create a new foreign key on the table ** currently under construction. pFromCol determines which columns ** in the current table point to the foreign key. If pFromCol==0 then ** connect the key to the last column inserted. pTo is the name of |
︙ | ︙ | |||
4572 4573 4574 4575 4576 4577 4578 | /* ** This routine will drop an existing named index. This routine ** implements the DROP INDEX statement. */ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){ Index *pIndex; | < > | < | | > | < | > | < > | | | | | | | > > | | | | | | | < > > > | | | | | | | | | > > | | | < > > > | | > > > > > > > | | < | > > > < < < > | 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 | /* ** This routine will drop an existing named index. This routine ** implements the DROP INDEX statement. */ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){ Index *pIndex; sqlite3 *db = pParse->db; Vdbe *v; int iDb; int ii, jj; RootStack rootStack; memset(&rootStack, 0, sizeof(rootStack)); v = sqlite3GetVdbe(pParse); sqlite3ReadSchema(pParse); assert( pName!=0 || pParse->nErr!=0 ); for(ii=0; pParse->nErr==0 && ii<pName->nSrc; ii++){ pName->a[ii].regReturn = 0; pIndex = sqlite3FindIndex(db, pName->a[ii].zName, pName->a[ii].zDatabase); if( pIndex==0 ){ if( !ifExists ){ sqlite3ErrorMsg(pParse, "no such index: %S", pName->a+ii); }else{ sqlite3CodeVerifyNamedSchema(pParse, pName->a[ii].zDatabase); sqlite3ForceNotReadOnly(pParse); testcase( ii>0 ); testcase( ii+1<pName->nSrc ); } pParse->checkSchema = 1; continue; } if( pIndex->idxType!=SQLITE_IDXTYPE_APPDEF ){ sqlite3ErrorMsg(pParse, "index associated with UNIQUE " "or PRIMARY KEY constraint cannot be dropped", 0); testcase( ii>0 ); testcase( ii+1<pName->nSrc ); break; } iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); #ifndef SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_DROP_INDEX; Table *pTab = pIndex->pTable; const char *zDb = db->aDb[iDb].zDbSName; const char *zTab = SCHEMA_TABLE(iDb); if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ testcase( ii>0 ); testcase( ii+1<pName->nSrc ); break; } if( !OMIT_TEMPDB && iDb==1 ) code = SQLITE_DROP_TEMP_INDEX; if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ testcase( ii>0 ); testcase( ii+1<pName->nSrc ); break; } } #endif /* Skip over redundant DROP INDEXes */ for(jj=ii-1; jj>=0 && pName->a[jj].u2.pIdx!=pIndex; jj--){} if( jj>=0 ) continue; /* Record that this index needs to be dropped. */ pName->a[ii].u2.pIdx = pIndex; /* Generate code to remove the index and from the schema table and ** from sqlite_statN tables */ sqlite3BeginWriteOperation(pParse, 1, iDb); sqlite3NestedParse(pParse, "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='index'", db->aDb[iDb].zDbSName, pIndex->zName ); sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName); rootStackPush(pParse, &rootStack, pIndex->tnum, iDb); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0); } sqlite3SrcListDelete(db, pName); rootStackCode(pParse, &rootStack); } /* ** pArray is a pointer to an array of objects. Each object in the ** array is szEntry bytes in size. This routine uses sqlite3DbRealloc() ** to extend the array so that there is space for a new object at the end. ** |
︙ | ︙ |
Changes to src/fkey.c.
︙ | ︙ | |||
714 715 716 717 718 719 720 | } } } /* ** This function is called to generate code that runs when table pTab is ** being dropped from the database. The SrcList passed as the second argument | | > | | > > | | > > > > > > > > > > > > > > | > > | 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 | } } } /* ** This function is called to generate code that runs when table pTab is ** being dropped from the database. The SrcList passed as the second argument ** to this function contains entries for every table that is being dropped, ** with the SrcItem.pTab line being set to the table being dropped. pTab ** will be one of those tables. ** ** Normally, no code is required. However, if either ** ** (a) The table is the parent table of a FK constraint, or ** (b) The table is the child table of a deferred FK constraint and it is ** determined at runtime that there are outstanding deferred FK ** constraint violations in the database, ** ** then the equivalent of "DELETE FROM <tbl>" is executed for every table ** being dropped, in the order specified in the DROP TABLE statement, which ** is the same as the order in which these tables appear in pName. ** Triggers are disabled while running these DELETEs, but foreign key ** actions are not. ** ** This routine sets the SrcList.addrFillSub value for all pName entries ** for which DELETE FROM has been run. This prevents the DELETE FROM ** from being run multiple times. */ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){ sqlite3 *db = pParse->db; if( (db->flags&SQLITE_ForeignKeys) && IsOrdinaryTable(pTab) ){ int iSkip = 0; Vdbe *v = sqlite3GetVdbe(pParse); SrcList *pSrc; int ii; assert( v ); /* VDBE has already been allocated */ assert( IsOrdinaryTable(pTab) ); if( sqlite3FkReferences(pTab)==0 ){ /* Search for a deferred foreign key constraint for which this table ** is the child table. If one cannot be found, return without ** generating any VDBE code. If one can be found, then jump over ** the entire DELETE if there are no outstanding deferred constraints ** when this statement is run. */ FKey *p; for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){ if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break; } if( !p ) return; iSkip = sqlite3VdbeMakeLabel(pParse); sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); VdbeCoverage(v); } pParse->disableTriggers = 1; for(ii=0; ii<pName->nSrc; ii++){ if( pName->a[ii].pTab==0 ) continue; if( pName->a[ii].addrFillSub ) continue; pName->a[ii].addrFillSub = 1; pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); if( pSrc ){ pSrc->a[0].zDatabase = sqlite3DbStrDup(db, pName->a[ii].zDatabase); pSrc->a[0].zName = sqlite3DbStrDup(db, pName->a[ii].zName); sqlite3DeleteFrom(pParse, pSrc, 0, 0, 0); } } pParse->disableTriggers = 0; /* If the DELETE has generated immediate foreign key constraint ** violations, halt the VDBE and return an error at this point, before ** any modifications to the schema are made. This is because statement ** transactions are not able to rollback schema changes. ** |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
467 468 469 470 471 472 473 | orconf(A) ::= OR resolvetype(X). {A = X;} resolvetype(A) ::= raisetype(A). resolvetype(A) ::= IGNORE. {A = OE_Ignore;} resolvetype(A) ::= REPLACE. {A = OE_Replace;} ////////////////////////// The DROP TABLE ///////////////////////////////////// // | | | | 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 | orconf(A) ::= OR resolvetype(X). {A = X;} resolvetype(A) ::= raisetype(A). resolvetype(A) ::= IGNORE. {A = OE_Ignore;} resolvetype(A) ::= REPLACE. {A = OE_Replace;} ////////////////////////// The DROP TABLE ///////////////////////////////////// // cmd ::= DROP TABLE ifexists(E) fullnamelist(X). { sqlite3DropTable(pParse, X, 0, E); } %type ifexists {int} ifexists(A) ::= IF EXISTS. {A = 1;} ifexists(A) ::= . {A = 0;} ///////////////////// The CREATE VIEW statement ///////////////////////////// // %ifndef SQLITE_OMIT_VIEW cmd ::= createkw(X) temp(T) VIEW ifnotexists(E) nm(Y) dbnm(Z) eidlist_opt(C) AS select(S). { sqlite3CreateView(pParse, &X, &Y, &Z, C, S, T, E); } cmd ::= DROP VIEW ifexists(E) fullnamelist(X). { sqlite3DropTable(pParse, X, 1, E); } %endif SQLITE_OMIT_VIEW //////////////////////// The SELECT statement ///////////////////////////////// // cmd ::= select(X). { |
︙ | ︙ | |||
769 770 771 772 773 774 775 776 777 778 779 780 781 782 | A = sqlite3SrcListAppend(pParse,0,&X,0); if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &X); } fullname(A) ::= nm(X) DOT nm(Y). { A = sqlite3SrcListAppend(pParse,0,&X,&Y); if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &Y); } %type xfullname {SrcList*} %destructor xfullname {sqlite3SrcListDelete(pParse->db, $$);} xfullname(A) ::= nm(X). {A = sqlite3SrcListAppend(pParse,0,&X,0); /*A-overwrites-X*/} xfullname(A) ::= nm(X) DOT nm(Y). {A = sqlite3SrcListAppend(pParse,0,&X,&Y); /*A-overwrites-X*/} | > > > > > > > > > > > > | 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 | A = sqlite3SrcListAppend(pParse,0,&X,0); if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &X); } fullname(A) ::= nm(X) DOT nm(Y). { A = sqlite3SrcListAppend(pParse,0,&X,&Y); if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &Y); } %type fullnamelist {SrcList*} %destructor fullnamelist {sqlite3SrcListDelete(pParse->db, $$);} fullnamelist(A) ::= fullname(A). fullnamelist(A) ::= fullnamelist(L) COMMA nm(X). { A = sqlite3SrcListAppend(pParse,L,&X,0); assert( !IN_RENAME_OBJECT ); /* Used only by DROP, which cannot be part of schema */ } fullnamelist(A) ::= fullnamelist(L) COMMA nm(X) DOT nm(Y). { A = sqlite3SrcListAppend(pParse,L,&X,&Y); assert( !IN_RENAME_OBJECT ); /* Used only by DROP, which cannot be part of schema */ } %type xfullname {SrcList*} %destructor xfullname {sqlite3SrcListDelete(pParse->db, $$);} xfullname(A) ::= nm(X). {A = sqlite3SrcListAppend(pParse,0,&X,0); /*A-overwrites-X*/} xfullname(A) ::= nm(X) DOT nm(Y). {A = sqlite3SrcListAppend(pParse,0,&X,&Y); /*A-overwrites-X*/} |
︙ | ︙ | |||
1500 1501 1502 1503 1504 1505 1506 | %type collate {int} collate(C) ::= . {C = 0;} collate(C) ::= COLLATE ids. {C = 1;} ///////////////////////////// The DROP INDEX command ///////////////////////// // | | | 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 | %type collate {int} collate(C) ::= . {C = 0;} collate(C) ::= COLLATE ids. {C = 1;} ///////////////////////////// The DROP INDEX command ///////////////////////// // cmd ::= DROP INDEX ifexists(E) fullnamelist(X). {sqlite3DropIndex(pParse, X, E);} ///////////////////////////// The VACUUM command ///////////////////////////// // %if !SQLITE_OMIT_VACUUM && !SQLITE_OMIT_ATTACH %type vinto {Expr*} %destructor vinto {sqlite3ExprDelete(pParse->db, $$);} cmd ::= VACUUM vinto(Y). {sqlite3Vacuum(pParse,0,Y);} |
︙ | ︙ | |||
1657 1658 1659 1660 1661 1662 1663 | raisetype(A) ::= ROLLBACK. {A = OE_Rollback;} raisetype(A) ::= ABORT. {A = OE_Abort;} raisetype(A) ::= FAIL. {A = OE_Fail;} //////////////////////// DROP TRIGGER statement ////////////////////////////// %ifndef SQLITE_OMIT_TRIGGER | | | 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 | raisetype(A) ::= ROLLBACK. {A = OE_Rollback;} raisetype(A) ::= ABORT. {A = OE_Abort;} raisetype(A) ::= FAIL. {A = OE_Fail;} //////////////////////// DROP TRIGGER statement ////////////////////////////// %ifndef SQLITE_OMIT_TRIGGER cmd ::= DROP TRIGGER ifexists(NOERR) fullnamelist(X). { sqlite3DropTrigger(pParse,X,NOERR); } %endif !SQLITE_OMIT_TRIGGER //////////////////////// ATTACH DATABASE file AS name ///////////////////////// %ifndef SQLITE_OMIT_ATTACH cmd ::= ATTACH database_kw_opt expr(F) AS expr(D) key_opt(K). { |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 | union { char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */ ExprList *pFuncArg; /* Arguments to table-valued-function */ } u1; union { Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */ } u2; }; /* ** The OnOrUsing object represents either an ON clause or a USING clause. ** It can never be both at the same time, but it can be neither. */ | > > | 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 | union { char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */ ExprList *pFuncArg; /* Arguments to table-valued-function */ } u1; union { Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */ Trigger *pTrig; /* Trigger in argument list of DROP TRIGGER */ Index *pIdx; /* Index in argument list to DROP INDEX */ } u2; }; /* ** The OnOrUsing object represents either an ON clause or a USING clause. ** It can never be both at the same time, but it can be neither. */ |
︙ | ︙ | |||
4922 4923 4924 4925 4926 4927 4928 | # define sqlite3ViewGetColumnNames(A,B) 0 #endif #if SQLITE_MAX_ATTACHED>30 int sqlite3DbMaskAllZero(yDbMask); #endif void sqlite3DropTable(Parse*, SrcList*, int, int); | < | 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 | # define sqlite3ViewGetColumnNames(A,B) 0 #endif #if SQLITE_MAX_ATTACHED>30 int sqlite3DbMaskAllZero(yDbMask); #endif void sqlite3DropTable(Parse*, SrcList*, int, int); void sqlite3DeleteTable(sqlite3*, Table*); void sqlite3DeleteTableGeneric(sqlite3*, void*); void sqlite3FreeIndex(sqlite3*, Index*); #ifndef SQLITE_OMIT_AUTOINCREMENT void sqlite3AutoincrementBegin(Parse *pParse); void sqlite3AutoincrementEnd(Parse *pParse); #else |
︙ | ︙ | |||
5570 5571 5572 5573 5574 5575 5576 | ** key functionality is available. If OMIT_TRIGGER is defined but ** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In ** this case foreign keys are parsed, but no other functionality is ** provided (enforcement of FK constraints requires the triggers sub-system). */ #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) void sqlite3FkCheck(Parse*, Table*, int, int, int*, int); | | | 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 | ** key functionality is available. If OMIT_TRIGGER is defined but ** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In ** this case foreign keys are parsed, but no other functionality is ** provided (enforcement of FK constraints requires the triggers sub-system). */ #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) void sqlite3FkCheck(Parse*, Table*, int, int, int*, int); void sqlite3FkDropTable(Parse*, SrcList*, Table*); void sqlite3FkActions(Parse*, Table*, ExprList*, int, int*, int); int sqlite3FkRequired(Parse*, Table*, int*, int); u32 sqlite3FkOldmask(Parse*, Table*); FKey *sqlite3FkReferences(Table *); void sqlite3FkClearTriggerCache(sqlite3*,int); #else #define sqlite3FkActions(a,b,c,d,e,f) |
︙ | ︙ |
Changes to src/trigger.c.
︙ | ︙ | |||
620 621 622 623 624 625 626 627 | **/ void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){ Trigger *pTrigger = 0; int i; const char *zDb; const char *zName; sqlite3 *db = pParse->db; | > < | < < < | > | | | | | | | | | | > | | | | | | > > | > > > | | > | | < | 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 | **/ void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){ Trigger *pTrigger = 0; int i; const char *zDb; const char *zName; sqlite3 *db = pParse->db; int ii, jj; sqlite3ReadSchema(pParse); assert( pName!=0 || pParse->nErr!=0 ); for(ii=0; pParse->nErr==0 && ii<pName->nSrc; ii++){ zDb = pName->a[ii].zDatabase; zName = pName->a[ii].zName; assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; i<db->nDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDb && sqlite3DbIsNamed(db, j, zDb)==0 ) continue; assert( sqlite3SchemaMutexHeld(db, j, 0) ); pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName); if( pTrigger ) break; } pName->a[ii].u2.pTrig = pTrigger; if( !pTrigger ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "no such trigger: %S", pName->a+ii); }else{ sqlite3CodeVerifyNamedSchema(pParse, zDb); } testcase( ii>0 ); testcase( ii+1<pName->nSrc ); pParse->checkSchema = 1; continue; } for(jj=ii-1; jj>=0; jj--){ if( pName->a[jj].u2.pTrig==pTrigger ) break; } if( jj>=0 ) continue; sqlite3DropTriggerPtr(pParse, pTrigger); } sqlite3SrcListDelete(db, pName); } /* ** Return a pointer to the Table structure for the table that a trigger ** is set on. */ |
︙ | ︙ |
Changes to test/aggnested.test.
︙ | ︙ | |||
113 114 115 116 117 118 119 | } } {2 1} # Further variants of the test case, as found in the ticket # do_test aggnested-3.1 { db eval { | | < | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | } } {2 1} # Further variants of the test case, as found in the ticket # do_test aggnested-3.1 { db eval { DROP TABLE IF EXISTS t1, t2; CREATE TABLE t1 ( id1 INTEGER PRIMARY KEY AUTOINCREMENT, value1 INTEGER ); INSERT INTO t1 VALUES(4469,2),(4476,1); CREATE TABLE t2 ( id2 INTEGER PRIMARY KEY AUTOINCREMENT, |
︙ | ︙ | |||
146 147 148 149 150 151 152 | FROM t1 AS other RIGHT JOIN t1 AS curr GROUP BY curr.id1); } } {1 1} do_test aggnested-3.2 { db eval { | | < | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | FROM t1 AS other RIGHT JOIN t1 AS curr GROUP BY curr.id1); } } {1 1} do_test aggnested-3.2 { db eval { DROP TABLE IF EXISTS t1, t2; CREATE TABLE t1 ( id1 INTEGER, value1 INTEGER, x1 INTEGER ); INSERT INTO t1 VALUES(4469,2,98),(4469,1,99),(4469,3,97); CREATE TABLE t2 ( |
︙ | ︙ | |||
174 175 176 177 178 179 180 | (SELECT value1 as xyz, max(x1) AS pqr FROM t1 GROUP BY id1); } } {1 0} do_test aggnested-3.3 { db eval { | | < | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | (SELECT value1 as xyz, max(x1) AS pqr FROM t1 GROUP BY id1); } } {1 0} do_test aggnested-3.3 { db eval { DROP TABLE IF EXISTS t1, t2; CREATE TABLE t1(id1, value1); INSERT INTO t1 VALUES(4469,2),(4469,1); CREATE TABLE t2 (value2); INSERT INTO t2 VALUES(1); SELECT (SELECT sum(value2=value1) FROM t2), max(value1) FROM t1 GROUP BY id1; |
︙ | ︙ | |||
245 246 247 248 249 250 251 | } } {12 2 34 4} # 2019-08-31 # Problem found by dbsqlfuzz # do_execsql_test aggnested-4.1 { | | < | < | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | } } {12 2 34 4} # 2019-08-31 # Problem found by dbsqlfuzz # do_execsql_test aggnested-4.1 { DROP TABLE IF EXISTS aa, bb; CREATE TABLE aa(x INT); INSERT INTO aa(x) VALUES(123); CREATE TABLE bb(y INT); INSERT INTO bb(y) VALUES(456); SELECT (SELECT sum(x+(SELECT y)) FROM bb) FROM aa; } {579} do_execsql_test aggnested-4.2 { SELECT (SELECT sum(x+y) FROM bb) FROM aa; } {579} do_execsql_test aggnested-4.3 { DROP TABLE IF EXISTS tx, ty; CREATE TABLE tx(x INT); INSERT INTO tx VALUES(1),(2),(3),(4),(5); CREATE TABLE ty(y INT); INSERT INTO ty VALUES(91),(92),(93); SELECT min((SELECT count(y) FROM ty)) FROM tx; } {3} do_execsql_test aggnested-4.4 { |
︙ | ︙ | |||
473 474 475 476 477 478 479 | } {6} # 2023-12-16 # New test case for check-in [4470f657d2069972] from 2023-11-02 # https://bugs.chromium.org/p/chromium/issues/detail?id=1511689 # do_execsql_test 10.1 { | | < | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 | } {6} # 2023-12-16 # New test case for check-in [4470f657d2069972] from 2023-11-02 # https://bugs.chromium.org/p/chromium/issues/detail?id=1511689 # do_execsql_test 10.1 { DROP TABLE IF EXISTS t0, t1; CREATE TABLE t0(c1, c2); INSERT INTO t0 VALUES(1,2); CREATE TABLE t1(c3, c4); INSERT INTO t1 VALUES(3,4); SELECT * FROM t0 WHERE EXISTS (SELECT 1 FROM t1 GROUP BY c3 HAVING ( SELECT count(*) FROM (SELECT 1 UNION ALL SELECT sum(DISTINCT c1) ) ) ) BETWEEN 1 AND 1; } {1 2} finish_test |
Changes to test/aggorderby.test.
︙ | ︙ | |||
56 57 58 59 60 61 62 | } 20 do_execsql_test aggorderby-4.1 { SELECT c, max(a ORDER BY a) FROM t1; } {7 9} do_execsql_test aggorderby-5.0 { | | < | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | } 20 do_execsql_test aggorderby-4.1 { SELECT c, max(a ORDER BY a) FROM t1; } {7 9} do_execsql_test aggorderby-5.0 { DROP TABLE IF EXISTS t1, t3; CREATE TABLE t1(a TEXT); INSERT INTO t1 VALUES('aaa'),('bbb'); CREATE TABLE t3(d TEXT); INSERT INTO t3 VALUES('/'),('-'); SELECT (SELECT string_agg(a,d) FROM t3) FROM t1; } {aaa-aaa bbb-bbb} do_execsql_test aggorderby-5.1 { SELECT (SELECT group_concat(a,d ORDER BY d) FROM t3) FROM t1; } {aaa/aaa bbb/bbb} |
︙ | ︙ |
Changes to test/alter3.test.
︙ | ︙ | |||
101 102 103 104 105 106 107 | } {{CREATE TABLE t3(a, b, c VARCHAR(10, 20), UNIQUE(a, b))}} do_test alter3-1.99 { catchsql { # May not exist if foriegn-keys are omitted at compile time. DROP TABLE t2; } execsql { | | < < | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | } {{CREATE TABLE t3(a, b, c VARCHAR(10, 20), UNIQUE(a, b))}} do_test alter3-1.99 { catchsql { # May not exist if foriegn-keys are omitted at compile time. DROP TABLE t2; } execsql { DROP TABLE abc, t1, t3; } } {} do_test alter3-2.1 { execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1,2); |
︙ | ︙ | |||
292 293 294 295 296 297 298 | do_test alter3-5.9 { execsql { SELECT * FROM t1; } } {1 one 2 two} do_test alter3-5.99 { execsql { | | < | 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | do_test alter3-5.9 { execsql { SELECT * FROM t1; } } {1 one 2 two} do_test alter3-5.99 { execsql { DROP TABLE aux.t1, main.t1; } } {} } #---------------------------------------------------------------- # Test that the table schema is correctly reloaded when a column # is added to a table. |
︙ | ︙ |
Changes to test/alter4.test.
︙ | ︙ | |||
110 111 112 113 114 115 116 | } {{CREATE TABLE t3(a, b, c VARCHAR(10, 20), UNIQUE(a, b))}} do_test alter4-1.99 { catchsql { # May not exist if foriegn-keys are omitted at compile time. DROP TABLE t2; } execsql { | | < < | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | } {{CREATE TABLE t3(a, b, c VARCHAR(10, 20), UNIQUE(a, b))}} do_test alter4-1.99 { catchsql { # May not exist if foriegn-keys are omitted at compile time. DROP TABLE t2; } execsql { DROP TABLE abc, t1, t3; } } {} do_test alter4-2.1 { execsql { CREATE TABLE temp.t1(a, b); INSERT INTO t1 VALUES(1,2); |
︙ | ︙ |
Changes to test/analyze.test.
︙ | ︙ | |||
191 192 193 194 195 196 197 | } {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3}} do_test analyze-3.8 { execsql { CREATE TABLE t3 AS SELECT a, b, rowid AS c, 'hi' AS d FROM t1; CREATE INDEX t3i1 ON t3(a); CREATE INDEX t3i2 ON t3(a,b,c,d); CREATE INDEX t3i3 ON t3(d,b,c,a); | | < | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | } {t1i1 {5 3} t1i2 {5 2} t1i3 {5 3 1} t2i1 {5 3}} do_test analyze-3.8 { execsql { CREATE TABLE t3 AS SELECT a, b, rowid AS c, 'hi' AS d FROM t1; CREATE INDEX t3i1 ON t3(a); CREATE INDEX t3i2 ON t3(a,b,c,d); CREATE INDEX t3i3 ON t3(d,b,c,a); DROP TABLE t1, t2; SELECT idx, stat FROM sqlite_stat1 ORDER BY idx; } } {} do_test analyze-3.9 { execsql { ANALYZE; SELECT idx, stat FROM sqlite_stat1 ORDER BY idx; |
︙ | ︙ |
Changes to test/auth.test.
︙ | ︙ | |||
212 213 214 215 216 217 218 | proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_TABLE"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } return SQLITE_OK } | | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_TABLE"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } return SQLITE_OK } catchsql {DROP TABLE IF EXISTS none1, none2, t2, none3} } {1 {not authorized}} do_test auth-1.21.2 { set ::authargs } {t2 {} main {}} do_test auth-1.22 { execsql {SELECT name FROM sqlite_master} } {t2} |
︙ | ︙ | |||
479 480 481 482 483 484 485 | do_test auth-1.63 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DELETE" && $arg1=="sqlite_master"} { return SQLITE_DENY } return SQLITE_OK } | | | 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 | do_test auth-1.63 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DELETE" && $arg1=="sqlite_master"} { return SQLITE_DENY } return SQLITE_OK } catchsql {DROP TABLE IF EXISTS none1, t2, none2} } {1 {not authorized}} do_test auth-1.64 { execsql {SELECT name FROM sqlite_master} } {t2} do_test auth-1.65 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DELETE" && $arg1=="t2"} { |
︙ | ︙ | |||
722 723 724 725 726 727 728 | proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_VIEW"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } return SQLITE_OK } | | | 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 | proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_VIEW"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } return SQLITE_OK } catchsql {DROP VIEW IF EXISTS none1, v2, none2} } {1 {not authorized}} do_test auth-1.102 { set ::authargs } {v2 {} main {}} do_test auth-1.103 { execsql {SELECT name FROM sqlite_master} } {t2 v2} |
︙ | ︙ | |||
1086 1087 1088 1089 1090 1091 1092 | proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_TRIGGER"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } return SQLITE_OK } | | | 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 | proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_TRIGGER"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } return SQLITE_OK } catchsql {DROP TRIGGER IF EXISTS none1, r2, none2} } {1 {not authorized}} do_test auth-1.154 { set ::authargs } {r2 t2 main {}} do_test auth-1.155 { execsql {SELECT name FROM sqlite_master} } {t2 tx r2} |
︙ | ︙ | |||
1385 1386 1387 1388 1389 1390 1391 | proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_INDEX"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } return SQLITE_OK } | | | 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 | proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_DROP_INDEX"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } return SQLITE_OK } catchsql {DROP INDEX IF EXISTS none1, i2, none2} } {1 {not authorized}} do_test auth-1.205a { set ::authargs } {i2 t2 main {}} db eval { ATTACH ':memory:' as di205; CREATE TABLE di205.t1(x); |
︙ | ︙ |
Added test/drop-many.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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | # 2024-02-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. # #*********************************************************************** # # Test cases for DROP TABLE, DROP INDEX, DROP TRIGGER, and DROP VIEW that # list multiple objects to be dropped. set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix drop-many do_execsql_test 1.1 { CREATE TABLE t1(a); CREATE TABLE t2(b, c UNIQUE); CREATE TABLE t3(d TEXT PRIMARY KEY, e); CREATE INDEX t3e ON t3(e); ATTACH ':memory:' AS aux1; CREATE TABLE aux1.t4(f INT, g INT, h INT PRIMARY KEY) WITHOUT ROWID; CREATE INDEX aux1.t4g ON t4(g); CREATE INDEX t2b ON t2(b); CREATE VIEW v5 AS SELECT 1,2,3; CREATE VIEW v6 AS SELECT * FROM t3; CREATE VIEW aux1.v7 AS SELECT 'hello'; CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN SELECT 'this is trigger r1'; END; CREATE TRIGGER r2 BEFORE DELETE ON t2 BEGIN INSERT INTO t1 VALUES(old.b); END; CREATE TRIGGER aux1.r3 AFTER UPDATE ON t4 BEGIN SELECT 'trigger r3'; END; CREATE TRIGGER aux1.r4 INSTEAD OF UPDATE ON v7 BEGIN SELECT NULL; END; } {} do_execsql_test 1.2 { BEGIN; DROP TABLE t2, t4, t3, t1; SELECT name FROM sqlite_schema WHERE type='table' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='table' ORDER BY name; ROLLBACK; } {} do_catchsql_test 1.3.1 { DROP TABLE t2, t4, t3, t05, t1; } {1 {no such table: t05}} do_execsql_test 1.3.2 { SELECT name FROM sqlite_schema WHERE type='table' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='table' ORDER BY name; } {t1 t2 t3 t4} do_execsql_test 1.4 { BEGIN; DROP TABLE IF EXISTS t01, t2, t02, t4, t03, t3, t04, t1, t05; SELECT name FROM sqlite_schema WHERE type='table' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='table' ORDER BY name; ROLLBACK; } {} do_catchsql_test 1.5.1 { DROP TABLE IF EXISTS t2, t4, t3, v7, t1; } {1 {use DROP VIEW to delete view v7}} do_execsql_test 1.5.2 { SELECT name FROM sqlite_schema WHERE type='table' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='table' ORDER BY name; } {t1 t2 t3 t4} do_execsql_test 2.1 { BEGIN; DROP VIEW v5, v6, v7; SELECT name FROM sqlite_schema WHERE type='view' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='view' ORDER BY name; ROLLBACK; } {} do_catchsql_test 2.2.1 { DROP VIEW v5, v6, v8, v7; } {1 {no such view: v8}} do_execsql_test 2.2.2 { SELECT name FROM sqlite_schema WHERE type='view' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='view' ORDER BY name; } {v5 v6 v7} do_catchsql_test 2.3.1 { DROP VIEW v5, v6, t1, v7; } {1 {use DROP TABLE to delete table t1}} do_execsql_test 2.3.2 { SELECT name FROM sqlite_schema WHERE type='view' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='view' ORDER BY name; } {v5 v6 v7} do_execsql_test 3.1 { BEGIN; DROP INDEX t2b, aux1.t4g, main.t3e; SELECT name FROM sqlite_schema WHERE type='index' AND name NOT LIKE 'sqlite%' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='index' ORDER BY name; ROLLBACK; } {} do_catchsql_test 3.2.1 { DROP INDEX t2b, aux1.t4g, t1, main.t3e; } {1 {no such index: t1}} do_execsql_test 3.2.2 { SELECT name FROM sqlite_schema WHERE type='index' AND name NOT LIKE 'sqlite%' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='index' ORDER BY name; } {t2b t3e t4g} do_execsql_test 3.3 { BEGIN; DROP INDEX IF EXISTS aux1.none, t2b, none2, aux1.t4g, main.t3e, none3; SELECT name FROM sqlite_schema WHERE type='index' AND name NOT LIKE 'sqlite%' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='index' ORDER BY name; ROLLBACK; } {} do_execsql_test 4.1 { BEGIN; DROP TRIGGER main.r1, r2, r3, aux1.r4; SELECT name FROM sqlite_schema WHERE type='trigger' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='trigger' ORDER BY name; ROLLBACK; } {} do_catchsql_test 4.2.1 { DROP TRIGGER main.r1, r2, r3, main.t1, aux1.r4; } {1 {no such trigger: main.t1}} do_execsql_test 4.2.2 { SELECT name FROM sqlite_schema WHERE type='trigger' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='trigger' ORDER BY name; } {r1 r2 r3 r4} do_execsql_test 4.3 { BEGIN; DROP TRIGGER IF EXISTS none1, main.r1, r2, aux1.none2, r3, aux1.r4; SELECT name FROM sqlite_schema WHERE type='trigger' UNION ALL SELECT name from aux1.sqlite_schema WHERE type='trigger' ORDER BY name; ROLLBACK; } {} finish_test |
Changes to test/fkey1.test.
︙ | ︙ | |||
66 67 68 69 70 71 72 73 74 75 76 77 78 79 | DROP TABLE t7; DROP TABLE t9; DROP TABLE t5; DROP TABLE t8; DROP TABLE t6; DROP TABLE t10; } } {} do_test fkey1-3.1 { execsql { CREATE TABLE t5(a PRIMARY KEY, b, c); CREATE TABLE t6( d REFERENCES t5, | > > > > > > > > > > > | 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 | DROP TABLE t7; DROP TABLE t9; DROP TABLE t5; DROP TABLE t8; DROP TABLE t6; DROP TABLE t10; } } {} do_test fkey1-2.2 { execsql { CREATE TABLE t5(x references t4); CREATE TABLE t6(x references t4); CREATE TABLE t7(x references t4); CREATE TABLE t8(x references t4); CREATE TABLE t9(x references t4); CREATE TABLE t10(x references t4); DROP TABLE t7, t9, t5, t8, t6, t10; } } {} do_test fkey1-3.1 { execsql { CREATE TABLE t5(a PRIMARY KEY, b, c); CREATE TABLE t6( d REFERENCES t5, |
︙ | ︙ |