SQLite

Changes On Branch multi-drop
Login

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 803481f250 to 1a49788afd

2024-03-06
20:59
Fix two -Werror=lto-type-mismatch warnings reported in forum post ef62b57bd5. (check-in: d4e423f3a4 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: 1a49788afd 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: 5d392c16bb 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: 6d4f1ae2fc 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: b606c09657 user: drh tags: month-truncate)
12:17
Remove an unused line of code. (check-in: 2627259069 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: 2266086cf0 user: drh tags: multi-drop)
10:55
Fix two -Werror=lto-type-mismatch warnings reported in forum post ef62b57bd5. (check-in: 803481f250 user: stephan tags: trunk)
03:45
Fix two -Werror=lto-type-mismatch warnings reported in forum post ef62b57bd5. (Closed-Leaf check-in: 29f94610dc 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: d51c699ae4 user: drh tags: trunk)

Changes to ext/rtree/rtree.c.
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058



1059
1060
1061
1062
1063
1064
1065
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';"
    "DROP TABLE '%q'.'%q_rowid';"
    "DROP TABLE '%q'.'%q_parent';",
    "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
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
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

/*
** Write code to erase the table with root-page iTable from database iDb.
** Also write code to modify the sqlite_schema table and internal schema
** The RootStack object holds a list (really a Heap) of btree root pages
** and schema numbers that need to be deleted using OP_Destroy.
** if a root-page of another table is moved by the btree-layer whilst
** erasing iTable (this can happen with an auto-vacuum database).
*/
**
static void destroyRootPage(Parse *pParse, int iTable, int iDb){
  Vdbe *v = sqlite3GetVdbe(pParse);
  int r1 = sqlite3GetTempReg(pParse);
  if( iTable<2 ) sqlite3ErrorMsg(pParse, "corrupt schema");
  sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb);
** OP_Destroy opcodes must be issued in order of decreasing root page
  sqlite3MayAbort(pParse);
#ifndef SQLITE_OMIT_AUTOVACUUM
  /* OP_Destroy stores an in integer r1. If this integer
** numbers in order to avoid having auto-vacuum disrupt subsequent 
** OP_Destroy opcodes.  For that reason, all pending OP_Destroy calls
  ** is non-zero, then it is the root page number of a table moved to
  ** location iTable. The following code modifies the sqlite_schema table to
  ** reflect this.
  **
  ** The "#NNN" in the SQL is a special constant that means whatever value
  ** is in register NNN.  See grammar rules associated with the TK_REGISTER
  ** token for additional information.
  */
  sqlite3NestedParse(pParse,
     "UPDATE %Q." LEGACY_SCHEMA_TABLE
     " SET rootpage=%d WHERE #%d AND rootpage=#%d",
** 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 */
     pParse->db->aDb[iDb].zDbSName, iTable, r1, r1);
#endif
  sqlite3ReleaseTempReg(pParse, r1);
}
};

/*
** Write VDBE code to erase table pTab and all associated indices on disk.
** Code to update the sqlite_schema tables and internal schema definitions
** in case a root-page belonging to another table is moved by the btree layer
** is also added (this can happen with an auto-vacuum database).
*/
static void destroyTable(Parse *pParse, Table *pTab){
  /* If the database may be auto-vacuum capable (if SQLITE_OMIT_AUTOVACUUM
  ** is not defined), then it is important to call OP_Destroy on the
  ** table and index root-pages in order, starting with the numerically
  ** largest root-page number. This guarantees that none of the root-pages
  ** to be destroyed is relocated by an earlier OP_Destroy. i.e. if the
  ** following were coded:
  **
  ** OP_Destroy 4 0
  ** ...
** Add a new Pgno and iDb to the RootStack in question.
  ** OP_Destroy 5 0
  **
**
  ** and root page 5 happened to be the largest root-page number in the
  ** database, then root page 5 would be moved to page 4 by the
  ** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit
  ** a free-list page.
  */
  Pgno iTab = pTab->tnum;
  Pgno iDestroyed = 0;

  while( 1 ){
** 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 ){
    Index *pIdx;
    Pgno iLargest = 0;

    p->nAlloc = p->nAlloc*2 + 12;
    p->a = sqlite3DbRealloc(pParse->db, p->a, sizeof(Pgno)*p->nAlloc);
    if( iDestroyed==0 || iTab<iDestroyed ){
      iLargest = iTab;
    }
    for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
      Pgno iIdx = pIdx->tnum;
      assert( pIdx->pSchema==pTab->pSchema );
      if( (iDestroyed==0 || (iIdx<iDestroyed)) && iIdx>iLargest ){
        iLargest = iIdx;
      }
    }
    if( iLargest==0 ){
    if( p->a==0 ){
      p->nAlloc = p->nUsed = 0;
      return;
    }
  }
    }else{
  assert( pgno>0 || CORRUPT_DB );
      int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
      assert( iDb>=0 && iDb<pParse->db->nDb );
      destroyRootPage(pParse, iLargest, iDb);
      iDestroyed = iLargest;
  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
3370






3371
3372
3373
3374
3375
3376
3377
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.
*/
void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
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
3423
3424







3425
3426
3427
3428
3429
3430
3431
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( !isView && !IsVirtual(pTab) ){
    destroyTable(pParse, pTab);
  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
3480
3481
3482
3483

3484
3485
3486
3487
3488
3489
3490
3491
3492
3493











3494
3495
3496
3497
3498
3499
3500
3501
3502
3503










3504
3505
3506
3507
3508




3509
3510


3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
















3526
3527
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
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;
  Vdbe *v;
  sqlite3 *db = pParse->db;
  int iDb;

  int ii, jj;
  if( db->mallocFailed ){
    goto exit_drop_table;
  }
  assert( pParse->nErr==0 );
  assert( pName->nSrc==1 );
  if( sqlite3ReadSchema(pParse) ) goto exit_drop_table;
  if( noErr ) db->suppressErr++;
  assert( isView==0 || isView==LOCATE_VIEW );
  pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]);
  if( noErr ) db->suppressErr--;
  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[0].zDatabase);
      sqlite3ForceNotReadOnly(pParse);
    }
    goto exit_drop_table;
  }
  iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
  assert( iDb>=0 && iDb<db->nDb );
    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) ){
    /* If pTab is a virtual table, call ViewGetColumnNames() to ensure
    ** it is initialized.
    */
    if( IsVirtual(pTab) && sqlite3ViewGetColumnNames(pParse, pTab) ){
    goto exit_drop_table;
  }
      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)){
      goto exit_drop_table;
    }
    if( isView ){
      if( !OMIT_TEMPDB && iDb==1 ){
        code = SQLITE_DROP_TEMP_VIEW;
      }else{
        code = SQLITE_DROP_VIEW;
      }
    {
      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;
      }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) ){
      goto exit_drop_table;
    }
    if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
      }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) ){
      goto exit_drop_table;
    }
  }
        testcase( ii>0 );
        testcase( ii+1<pName->nSrc );
        break;
      }
    }
#endif
  if( tableMayNotBeDropped(db, pTab) ){
    sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
    if( tableMayNotBeDropped(db, pTab) ){
      testcase( ii>0 );
      testcase( ii+1<pName->nSrc );
      sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
    goto exit_drop_table;
  }
      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) ){
    sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName);
    /* 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);
    goto exit_drop_table;
  }
  if( !isView && IsView(pTab) ){
    sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %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);
    goto exit_drop_table;
  }
      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;

  /* Generate code to remove the table from the schema table
  ** on disk.
  */
    /* 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.
    */
  v = sqlite3GetVdbe(pParse);
  if( v ){
    sqlite3BeginWriteOperation(pParse, 1, iDb);
    if( !isView ){
    if( IsOrdinaryTable(pTab) ){
      sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName);
      sqlite3FkDropTable(pParse, pName, pTab);
    }
    sqlite3CodeDropTable(pParse, pTab, iDb, isView);
  }

exit_drop_table:
  /* 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
4579
4580

4581
4582

4583
4584
4585
4586




4587
4588
4589



4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604

















4605
4606
4607





4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618












4619
4620
4621





4622
4623







4624
4625


4626
4627
4628
4629


4630
4631
4632


4633
4634
4635
4636
4637
4638
4639

4640
4641
4642
4643
4644
4645
4646
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;
  Vdbe *v;
  sqlite3 *db = pParse->db;
  Vdbe *v;
  int iDb;

  int ii, jj;
  if( db->mallocFailed ){
    goto exit_drop_index;
  }
  assert( pParse->nErr==0 );   /* Never called with prior non-OOM errors */
  RootStack rootStack;

  memset(&rootStack, 0, sizeof(rootStack));
  v = sqlite3GetVdbe(pParse);
  assert( pName->nSrc==1 );
  if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
    goto exit_drop_index;
  sqlite3ReadSchema(pParse);
  assert( pName!=0 || pParse->nErr!=0 );
  for(ii=0; pParse->nErr==0 && ii<pName->nSrc; ii++){
  }
  pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase);
  if( pIndex==0 ){
    if( !ifExists ){
      sqlite3ErrorMsg(pParse, "no such index: %S", pName->a);
    }else{
      sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
      sqlite3ForceNotReadOnly(pParse);
    }
    pParse->checkSchema = 1;
    goto exit_drop_index;
  }
  if( pIndex->idxType!=SQLITE_IDXTYPE_APPDEF ){
    sqlite3ErrorMsg(pParse, "index associated with UNIQUE "
      "or PRIMARY KEY constraint cannot be dropped", 0);
    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);
    goto exit_drop_index;
  }
  iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
      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) ){
      goto exit_drop_index;
    }
    if( !OMIT_TEMPDB && iDb==1 ) code = SQLITE_DROP_TEMP_INDEX;
    if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){
    {
      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) ){
      goto exit_drop_index;
    }
  }
        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 */
  v = sqlite3GetVdbe(pParse);
    /* Generate code to remove the index and from the schema table and
    ** from sqlite_statN tables */
  if( v ){
    sqlite3BeginWriteOperation(pParse, 1, iDb);
    sqlite3NestedParse(pParse,
       "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='index'",
       "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);
    destroyRootPage(pParse, pIndex->tnum, iDb);
    sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0);
  }

exit_drop_index:
  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
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
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 a single entry guaranteed to resolve to
** table pTab.
** 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 before dropping
** the table from the database. Triggers are disabled while running this
** DELETE, but foreign key actions are not.
** 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, sqlite3SrcListDup(db, pName, 0), 0, 0, 0);
        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
474

475
476
477
478
479
480
481
482
483
484
485
486
487
488

489
490
491
492
493
494
495
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) fullname(X). {
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) fullname(X). {
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
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
1507

1508
1509
1510
1511
1512
1513
1514
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) fullname(X).   {sqlite3DropIndex(pParse, X, E);}
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
1664

1665
1666
1667
1668
1669
1670
1671
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) fullname(X). {
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
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
4929
4930
4931
4932
4933
4934
4935
4936
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 sqlite3CodeDropTable(Parse*, Table*, 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
5577

5578
5579
5580
5581
5582
5583
5584
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 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
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
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;

  if( db->mallocFailed ) goto drop_trigger_cleanup;
  if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
  sqlite3ReadSchema(pParse);
    goto drop_trigger_cleanup;
  }

  assert( pName->nSrc==1 );
  zDb = pName->a[0].zDatabase;
  zName = pName->a[0].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;
  }
  if( !pTrigger ){
    if( !noErr ){
      sqlite3ErrorMsg(pParse, "no such trigger: %S", pName->a);
    }else{
      sqlite3CodeVerifyNamedSchema(pParse, zDb);
    }
    pParse->checkSchema = 1;
    goto drop_trigger_cleanup;
  }
  sqlite3DropTriggerPtr(pParse, pTrigger);

  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);
  }
drop_trigger_cleanup:
  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
120

121
122
123
124
125
126
127
128
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;
    DROP TABLE IF EXISTS t1, t2;
    DROP TABLE IF EXISTS 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
153

154
155
156
157
158
159
160
161
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;
    DROP TABLE IF EXISTS t1, t2;
    DROP TABLE IF EXISTS 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
181

182
183
184
185
186
187
188
189
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;
    DROP TABLE IF EXISTS t1, t2;
    DROP TABLE IF EXISTS 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
252

253
254
255
256
257
258
259
260
261
262

263
264
265
266
267
268
269
270
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;
  DROP TABLE IF EXISTS aa, bb;
  DROP TABLE IF EXISTS 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;
  DROP TABLE IF EXISTS tx, ty;
  DROP TABLE IF EXISTS 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
480

481
482
483
484
485
486
487
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;
  DROP TABLE IF EXISTS t0, t1;
  DROP TABLE IF EXISTS 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
63

64
65
66
67
68
69
70
71
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;
  DROP TABLE IF EXISTS t1, t3;
  DROP TABLE IF EXISTS 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
108

109
110
111
112
113
114
115
116
117
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; 
    DROP TABLE abc, t1, t3; 
    DROP TABLE t1; 
    DROP TABLE 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
299

300
301
302
303
304
305
306
307
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;
      DROP TABLE aux.t1, main.t1;
      DROP TABLE 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
117

118
119
120
121
122
123
124
125
126
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; 
    DROP TABLE abc, t1, t3; 
    DROP TABLE t1; 
    DROP TABLE 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
198

199
200
201
202
203
204
205
206
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;
    DROP TABLE t1, t2;
    DROP TABLE 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
219

220
221
222
223
224
225
226
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 t2}
  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
486

487
488
489
490
491
492
493
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 t2}
  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
729

730
731
732
733
734
735
736
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 v2}
  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
1093

1094
1095
1096
1097
1098
1099
1100
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 r2}
  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
1392

1393
1394
1395
1396
1397
1398
1399
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 i2}
  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
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,