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 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
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';",
    pRtree->zDb, pRtree->zName, 
    pRtree->zDb, pRtree->zName,
    pRtree->zDb, pRtree->zName
  );
  if( !zCreate ){
    rc = SQLITE_NOMEM;
  }else{







|
|
|







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
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
      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
** 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);
  sqlite3MayAbort(pParse);
#ifndef SQLITE_OMIT_AUTOVACUUM

  /* OP_Destroy stores an in integer r1. If this integer
  ** 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",
     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
  ** ...
  ** 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 ){
    Index *pIdx;
    Pgno iLargest = 0;

    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 ){

      return;


    }else{
      int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
      assert( iDb>=0 && iDb<pParse->db->nDb );










      destroyRootPage(pParse, iLargest, iDb);























      iDestroyed = iLargest;































    }





  }





}

/*
** Remove entries from the sqlite_statN tables (for N in (1,2,3))
** after a DROP INDEX or DROP TABLE command.
*/
static void sqlite3ClearStatTables(







|
|
<
<
|
<
<
<
<
|
<
<
>
|
<
<
>
>
|
|
|
|
>
>
>
>
|
|
>
>
>
|
|
|
<
<
<
|


<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
|
<
<
<
>
|
|
|
|
|
|
<
|
|
<
<
<
<
<
<
<
<
<
<
|
>

>
>
|
<
|
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>

>
>
>
>
>







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
3370





3371
3372
3373
3374
3375
3376
3377
    }
  }
}

/*
** Generate code to drop a table.
*/
void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){





  Vdbe *v;
  sqlite3 *db = pParse->db;
  Trigger *pTrigger;
  Db *pDb = &db->aDb[iDb];

  v = sqlite3GetVdbe(pParse);
  assert( v!=0 );







|
>
>
>
>
>







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
3423

3424




3425
3426
3427
3428
3429
3430
3431
  ** 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);




  }

  /* 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);







|
>
|
>
>
>
>







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
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

/*
** 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;

  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--;

  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 is a virtual table, call ViewGetColumnNames() to ensure
  ** it is initialized.
  */
  if( IsVirtual(pTab) && sqlite3ViewGetColumnNames(pParse, pTab) ){
    goto exit_drop_table;

  }
#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;
      }
#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) ){
      goto exit_drop_table;


    }
    if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
      goto exit_drop_table;



    }
  }
#endif
  if( tableMayNotBeDropped(db, pTab) ){


    sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
    goto exit_drop_table;

  }

#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);
    goto exit_drop_table;

  }
  if( !isView && IsView(pTab) ){


    sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName);
    goto exit_drop_table;

  }
#endif






  /* Generate code to remove the table from the schema table


  ** on disk.


  */
  v = sqlite3GetVdbe(pParse);
  if( v ){
    sqlite3BeginWriteOperation(pParse, 1, iDb);
    if( !isView ){
      sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName);
      sqlite3FkDropTable(pParse, pName, pTab);
    }
    sqlite3CodeDropTable(pParse, pTab, iDb, isView);
  }

exit_drop_table:








  sqlite3SrcListDelete(db, pName);

}

/*
** 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







<


|
<
|
|
>
|
>
|
|
|
|
|
|

|
|
|
|
|
|
>
|
|
|

|
|
|
|
<
>
|

|
|
|
|
|
|
|
>
>
|
|
|
|
|
|
|

|
|
|

|
|
|
|
|
|
|
|
|
>
>
|
|
<
>
>
>
|
|

|
>
>
|
<
>
|


|
|
|
|
>
>
|
<
>
|
|
>
>
|
<
>
|

>
>
>
>
>

|
>
>
|
>
>
|
<
<

|



<


|
>
>
>
>
>
>
>
>

>







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
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

/*
** 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;

  int iDb;

  if( db->mallocFailed ){
    goto exit_drop_index;
  }

  assert( pParse->nErr==0 );   /* Never called with prior non-OOM errors */
  assert( pName->nSrc==1 );
  if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){

    goto exit_drop_index;
  }

  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);
    goto exit_drop_index;



  }
  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) ){
      goto exit_drop_index;



    }
  }
#endif








  /* Generate code to remove the index and from the schema table */
  v = sqlite3GetVdbe(pParse);
  if( v ){
    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);


    sqlite3ChangeCookie(pParse, iDb);
    destroyRootPage(pParse, pIndex->tnum, iDb);
    sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0);
  }

exit_drop_index:
  sqlite3SrcListDelete(db, pName);

}

/*
** 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.
**







<

>

|
<
|
|
>
|
<
|
>
|
<
>
|
|
|
|
|
|
|
>
>
|
|
|
|
|
|
|
<
>
>
>
|
|

|
|
|
|
|
|
|
>
>
|
|
|
<
>
>
>
|
|


>
>
>
>
>
>
>
|
|
<


|
>



>
>

<


<
<

>







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
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
    }
  }
}

/*
** 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.
**
** 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.




*/
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);



    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;








    sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 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.  
    **







|
>
|








|
>
>
|
|
>
>
>
>






>
>



















>
>
>
>
>
>
>
>
|
>
>







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
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). {
  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). {
  sqlite3DropTable(pParse, X, 1, E);
}
%endif  SQLITE_OMIT_VIEW

//////////////////////// The SELECT statement /////////////////////////////////
//
cmd ::= select(X).  {







|













|







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
1507
1508
1509
1510
1511
1512
1513
1514
%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);}

///////////////////////////// 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);}







|







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
1664
1665
1666
1667
1668
1669
1670
1671
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). {
  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). {







|







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
4929
4930
4931
4932
4933
4934
4935
4936
# 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







<







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
5577
5578
5579
5580
5581
5582
5583
5584
** 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)







|







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
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
**/
void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){
  Trigger *pTrigger = 0;
  int i;
  const char *zDb;
  const char *zName;
  sqlite3 *db = pParse->db;


  if( db->mallocFailed ) goto drop_trigger_cleanup;
  if( SQLITE_OK!=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);

drop_trigger_cleanup:
  sqlite3SrcListDelete(db, pName);
}

/*
** Return a pointer to the Table structure for the table that a trigger
** is set on.
*/







>

<
|
<
<
<
|
>
|
|
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
>
>
|
>
>
>
|
|
>
|
|
<







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
120
121
122
123
124
125
126
127
128
  }
} {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 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,







|
<







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
153
154
155
156
157
158
159
160
161
        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 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 (







|
<







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
181
182
183
184
185
186
187
188
189
     (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 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;







|
<







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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
  }
} {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 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 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 {







|
<








|
<







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
480
481
482
483
484
485
486
487
} {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 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







|
<






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
63
64
65
66
67
68
69
70
71
} 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 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}







|
<







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
108
109
110
111
112
113
114
115
116
117
} {{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 t1; 
    DROP TABLE t3; 
  }
} {}

do_test alter3-2.1 {
  execsql {
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1,2);







|
<
<







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
299
300
301
302
303
304
305
306
307
  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 t1;
    }
  } {}
}

#----------------------------------------------------------------
# Test that the table schema is correctly reloaded when a column
# is added to a table.







|
<







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
117
118
119
120
121
122
123
124
125
126
} {{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 t1; 
    DROP TABLE t3; 
  }
} {}

do_test alter4-2.1 {
  execsql {
    CREATE TABLE temp.t1(a, b);
    INSERT INTO t1 VALUES(1,2);







|
<
<







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
198
199
200
201
202
203
204
205
206
} {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 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;







|
<







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
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}
} {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}







|







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
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}
} {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"} {







|







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
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}
} {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}







|







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
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}
} {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}







|







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
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}
} {1 {not authorized}}
do_test auth-1.205a {
  set ::authargs
} {i2 t2 main {}}
db eval {
  ATTACH ':memory:' as di205;
  CREATE TABLE di205.t1(x);







|







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,