/ Check-in [dd179ff2]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Correctly handle attempts to add a UNIQUE or PRIMARY KEY column using the ALTER TABLE statement. Ticket #3651. (CVS 6291)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:dd179ff2986bc2a86d70bbe927fd0e123e17d398
User & Date: drh 2009-02-13 03:43:32
Context
2009-02-13
16:59
Make sure OOM errors in the ANALYSIS loader get reported back out to high-level layers. Strange behavior can result otherwise. (CVS 6292) check-in: 88a6355c user: drh tags: trunk
03:43
Correctly handle attempts to add a UNIQUE or PRIMARY KEY column using the ALTER TABLE statement. Ticket #3651. (CVS 6291) check-in: dd179ff2 user: drh tags: trunk
2009-02-12
17:07
Add additional cross-references from API documentation to other documents. Comment changes only; no changes to code. (CVS 6290) check-in: 97203a0a user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/alter.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
...
579
580
581
582
583
584
585
586




587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that used to generate VDBE code
** that implements the ALTER TABLE command.
**
** $Id: alter.c,v 1.52 2009/01/20 16:53:40 danielk1977 Exp $
*/
#include "sqliteInt.h"

/*
** The code in this file only exists if we are not omitting the
** ALTER TABLE logic from the build.
*/
................................................................................
  if( pParse->nErr || db->mallocFailed ) return;
  pNew = pParse->pNewTable;
  assert( pNew );

  assert( sqlite3BtreeHoldsAllMutexes(db) );
  iDb = sqlite3SchemaToIndex(db, pNew->pSchema);
  zDb = db->aDb[iDb].zName;
  zTab = pNew->zName;
  pCol = &pNew->aCol[pNew->nCol-1];
  pDflt = pCol->pDflt;
  pTab = sqlite3FindTable(db, zTab, zDb);
  assert( pTab );

#ifndef SQLITE_OMIT_AUTHORIZATION
  /* Invoke the authorization callback. */
................................................................................
    goto exit_begin_add_column;
  }

  assert( pTab->addColOffset>0 );
  iDb = sqlite3SchemaToIndex(db, pTab->pSchema);

  /* Put a copy of the Table struct in Parse.pNewTable for the
  ** sqlite3AddColumn() function and friends to modify.




  */
  pNew = (Table*)sqlite3DbMallocZero(db, sizeof(Table));
  if( !pNew ) goto exit_begin_add_column;
  pParse->pNewTable = pNew;
  pNew->nRef = 1;
  pNew->db = db;
  pNew->nCol = pTab->nCol;
  assert( pNew->nCol>0 );
  nAlloc = (((pNew->nCol-1)/8)*8)+8;
  assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 );
  pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc);
  pNew->zName = sqlite3DbStrDup(db, pTab->zName);
  if( !pNew->aCol || !pNew->zName ){
    db->mallocFailed = 1;
    goto exit_begin_add_column;
  }
  memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
  for(i=0; i<pNew->nCol; i++){
    Column *pCol = &pNew->aCol[i];







|







 







|







 







|
>
>
>
>











|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
...
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that used to generate VDBE code
** that implements the ALTER TABLE command.
**
** $Id: alter.c,v 1.53 2009/02/13 03:43:32 drh Exp $
*/
#include "sqliteInt.h"

/*
** The code in this file only exists if we are not omitting the
** ALTER TABLE logic from the build.
*/
................................................................................
  if( pParse->nErr || db->mallocFailed ) return;
  pNew = pParse->pNewTable;
  assert( pNew );

  assert( sqlite3BtreeHoldsAllMutexes(db) );
  iDb = sqlite3SchemaToIndex(db, pNew->pSchema);
  zDb = db->aDb[iDb].zName;
  zTab = &pNew->zName[16];  /* Skip the "sqlite_altertab_" prefix on the name */
  pCol = &pNew->aCol[pNew->nCol-1];
  pDflt = pCol->pDflt;
  pTab = sqlite3FindTable(db, zTab, zDb);
  assert( pTab );

#ifndef SQLITE_OMIT_AUTHORIZATION
  /* Invoke the authorization callback. */
................................................................................
    goto exit_begin_add_column;
  }

  assert( pTab->addColOffset>0 );
  iDb = sqlite3SchemaToIndex(db, pTab->pSchema);

  /* Put a copy of the Table struct in Parse.pNewTable for the
  ** sqlite3AddColumn() function and friends to modify.  But modify
  ** the name by adding an "sqlite_altertab_" prefix.  By adding this
  ** prefix, we insure that the name will not collide with an existing
  ** table because user table are not allowed to have the "sqlite_"
  ** prefix on their name.
  */
  pNew = (Table*)sqlite3DbMallocZero(db, sizeof(Table));
  if( !pNew ) goto exit_begin_add_column;
  pParse->pNewTable = pNew;
  pNew->nRef = 1;
  pNew->db = db;
  pNew->nCol = pTab->nCol;
  assert( pNew->nCol>0 );
  nAlloc = (((pNew->nCol-1)/8)*8)+8;
  assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 );
  pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc);
  pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
  if( !pNew->aCol || !pNew->zName ){
    db->mallocFailed = 1;
    goto exit_begin_add_column;
  }
  memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
  for(i=0; i<pNew->nCol; i++){
    Column *pCol = &pNew->aCol[i];

Changes to src/build.c.

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
...
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
...
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
....
2424
2425
2426
2427
2428
2429
2430
2431

2432
2433
2434
2435
2436
2437
2438
**     CREATE INDEX
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**
** $Id: build.c,v 1.517 2009/02/10 10:44:42 danielk1977 Exp $
*/
#include "sqliteInt.h"

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Initialize the pParse structure as needed.
*/
................................................................................
** Remove the given index from the index hash table, and free
** its memory structures.
**
** The index is removed from the database hash tables but
** it is not unlinked from the Table that it indexes.
** Unlinking from the Table must be done by the calling function.
*/
static void sqliteDeleteIndex(Index *p){
  Index *pOld;
  const char *zName = p->zName;

  pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName,
                           sqlite3Strlen30(zName)+1, 0);
  assert( pOld==0 || pOld==p );
  freeIndex(p);
................................................................................
  assert( pTable->nRef==0 );

  /* Delete all indices associated with this table
  */
  for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
    pNext = pIndex->pNext;
    assert( pIndex->pSchema==pTable->pSchema );
    sqliteDeleteIndex(pIndex);
  }

#ifndef SQLITE_OMIT_FOREIGN_KEY
  /* Delete all foreign keys associated with this table.  The keys
  ** should have already been unlinked from the pSchema->aFKey hash table 
  */
  for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){
................................................................................
    pTab = pParse->pNewTable;
    if( !pTab ) goto exit_create_index;
    iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
  }
  pDb = &db->aDb[iDb];

  if( pTab==0 || pParse->nErr ) goto exit_create_index;
  if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){

    sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
    goto exit_create_index;
  }
#ifndef SQLITE_OMIT_VIEW
  if( pTab->pSelect ){
    sqlite3ErrorMsg(pParse, "views may not be indexed");
    goto exit_create_index;







|







 







|







 







|







 







|
>







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
...
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
...
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
....
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
**     CREATE INDEX
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**
** $Id: build.c,v 1.518 2009/02/13 03:43:32 drh Exp $
*/
#include "sqliteInt.h"

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Initialize the pParse structure as needed.
*/
................................................................................
** Remove the given index from the index hash table, and free
** its memory structures.
**
** The index is removed from the database hash tables but
** it is not unlinked from the Table that it indexes.
** Unlinking from the Table must be done by the calling function.
*/
static void sqlite3DeleteIndex(Index *p){
  Index *pOld;
  const char *zName = p->zName;

  pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName,
                           sqlite3Strlen30(zName)+1, 0);
  assert( pOld==0 || pOld==p );
  freeIndex(p);
................................................................................
  assert( pTable->nRef==0 );

  /* Delete all indices associated with this table
  */
  for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
    pNext = pIndex->pNext;
    assert( pIndex->pSchema==pTable->pSchema );
    sqlite3DeleteIndex(pIndex);
  }

#ifndef SQLITE_OMIT_FOREIGN_KEY
  /* Delete all foreign keys associated with this table.  The keys
  ** should have already been unlinked from the pSchema->aFKey hash table 
  */
  for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){
................................................................................
    pTab = pParse->pNewTable;
    if( !pTab ) goto exit_create_index;
    iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
  }
  pDb = &db->aDb[iDb];

  if( pTab==0 || pParse->nErr ) goto exit_create_index;
  if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 
       && memcmp(&pTab->zName[7],"altertab_",9)!=0 ){
    sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
    goto exit_create_index;
  }
#ifndef SQLITE_OMIT_VIEW
  if( pTab->pSelect ){
    sqlite3ErrorMsg(pParse, "views may not be indexed");
    goto exit_create_index;

Changes to test/alter.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
806
807
808
809
810
811
812
813














814
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#*************************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ALTER TABLE statement.
#
# $Id: alter.test,v 1.30 2008/05/09 14:17:52 drh Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
................................................................................
} {t3102a_rename t3102b t3102c}
do_test alter-13.3 {
  execsql {
    ALTER TABLE t3102b RENAME TO t3102b_rename;
    SELECT name FROM sqlite_master WHERE name LIKE 't3102%' ORDER BY 1;
  }
} {t3102a_rename t3102b_rename t3102c}















finish_test







|







 








>
>
>
>
>
>
>
>
>
>
>
>
>
>

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#*************************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the ALTER TABLE statement.
#
# $Id: alter.test,v 1.31 2009/02/13 03:43:32 drh Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
ifcapable !altertable {
................................................................................
} {t3102a_rename t3102b t3102c}
do_test alter-13.3 {
  execsql {
    ALTER TABLE t3102b RENAME TO t3102b_rename;
    SELECT name FROM sqlite_master WHERE name LIKE 't3102%' ORDER BY 1;
  }
} {t3102a_rename t3102b_rename t3102c}

# Ticket #3651
do_test alter-14.1 {
  catchsql {
    CREATE TABLE t3651(a UNIQUE);
    ALTER TABLE t3651 ADD COLUMN b UNIQUE;
  }
} {1 {Cannot add a UNIQUE column}}
do_test alter-14.2 {
  catchsql {
    ALTER TABLE t3651 ADD COLUMN b PRIMARY KEY;
  }
} {1 {Cannot add a PRIMARY KEY column}}


finish_test