/ 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 Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/alter.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains C code routines that used to generate VDBE code
    13     13   ** that implements the ALTER TABLE command.
    14     14   **
    15         -** $Id: alter.c,v 1.52 2009/01/20 16:53:40 danielk1977 Exp $
           15  +** $Id: alter.c,v 1.53 2009/02/13 03:43:32 drh Exp $
    16     16   */
    17     17   #include "sqliteInt.h"
    18     18   
    19     19   /*
    20     20   ** The code in this file only exists if we are not omitting the
    21     21   ** ALTER TABLE logic from the build.
    22     22   */
................................................................................
   449    449     if( pParse->nErr || db->mallocFailed ) return;
   450    450     pNew = pParse->pNewTable;
   451    451     assert( pNew );
   452    452   
   453    453     assert( sqlite3BtreeHoldsAllMutexes(db) );
   454    454     iDb = sqlite3SchemaToIndex(db, pNew->pSchema);
   455    455     zDb = db->aDb[iDb].zName;
   456         -  zTab = pNew->zName;
          456  +  zTab = &pNew->zName[16];  /* Skip the "sqlite_altertab_" prefix on the name */
   457    457     pCol = &pNew->aCol[pNew->nCol-1];
   458    458     pDflt = pCol->pDflt;
   459    459     pTab = sqlite3FindTable(db, zTab, zDb);
   460    460     assert( pTab );
   461    461   
   462    462   #ifndef SQLITE_OMIT_AUTHORIZATION
   463    463     /* Invoke the authorization callback. */
................................................................................
   579    579       goto exit_begin_add_column;
   580    580     }
   581    581   
   582    582     assert( pTab->addColOffset>0 );
   583    583     iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
   584    584   
   585    585     /* Put a copy of the Table struct in Parse.pNewTable for the
   586         -  ** sqlite3AddColumn() function and friends to modify.
          586  +  ** sqlite3AddColumn() function and friends to modify.  But modify
          587  +  ** the name by adding an "sqlite_altertab_" prefix.  By adding this
          588  +  ** prefix, we insure that the name will not collide with an existing
          589  +  ** table because user table are not allowed to have the "sqlite_"
          590  +  ** prefix on their name.
   587    591     */
   588    592     pNew = (Table*)sqlite3DbMallocZero(db, sizeof(Table));
   589    593     if( !pNew ) goto exit_begin_add_column;
   590    594     pParse->pNewTable = pNew;
   591    595     pNew->nRef = 1;
   592    596     pNew->db = db;
   593    597     pNew->nCol = pTab->nCol;
   594    598     assert( pNew->nCol>0 );
   595    599     nAlloc = (((pNew->nCol-1)/8)*8)+8;
   596    600     assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 );
   597    601     pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc);
   598         -  pNew->zName = sqlite3DbStrDup(db, pTab->zName);
          602  +  pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
   599    603     if( !pNew->aCol || !pNew->zName ){
   600    604       db->mallocFailed = 1;
   601    605       goto exit_begin_add_column;
   602    606     }
   603    607     memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
   604    608     for(i=0; i<pNew->nCol; i++){
   605    609       Column *pCol = &pNew->aCol[i];

Changes to src/build.c.

    18     18   **     CREATE INDEX
    19     19   **     DROP INDEX
    20     20   **     creating ID lists
    21     21   **     BEGIN TRANSACTION
    22     22   **     COMMIT
    23     23   **     ROLLBACK
    24     24   **
    25         -** $Id: build.c,v 1.517 2009/02/10 10:44:42 danielk1977 Exp $
           25  +** $Id: build.c,v 1.518 2009/02/13 03:43:32 drh Exp $
    26     26   */
    27     27   #include "sqliteInt.h"
    28     28   
    29     29   /*
    30     30   ** This routine is called when a new SQL statement is beginning to
    31     31   ** be parsed.  Initialize the pParse structure as needed.
    32     32   */
................................................................................
   361    361   ** Remove the given index from the index hash table, and free
   362    362   ** its memory structures.
   363    363   **
   364    364   ** The index is removed from the database hash tables but
   365    365   ** it is not unlinked from the Table that it indexes.
   366    366   ** Unlinking from the Table must be done by the calling function.
   367    367   */
   368         -static void sqliteDeleteIndex(Index *p){
          368  +static void sqlite3DeleteIndex(Index *p){
   369    369     Index *pOld;
   370    370     const char *zName = p->zName;
   371    371   
   372    372     pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName,
   373    373                              sqlite3Strlen30(zName)+1, 0);
   374    374     assert( pOld==0 || pOld==p );
   375    375     freeIndex(p);
................................................................................
   521    521     assert( pTable->nRef==0 );
   522    522   
   523    523     /* Delete all indices associated with this table
   524    524     */
   525    525     for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
   526    526       pNext = pIndex->pNext;
   527    527       assert( pIndex->pSchema==pTable->pSchema );
   528         -    sqliteDeleteIndex(pIndex);
          528  +    sqlite3DeleteIndex(pIndex);
   529    529     }
   530    530   
   531    531   #ifndef SQLITE_OMIT_FOREIGN_KEY
   532    532     /* Delete all foreign keys associated with this table.  The keys
   533    533     ** should have already been unlinked from the pSchema->aFKey hash table 
   534    534     */
   535    535     for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){
................................................................................
  2424   2424       pTab = pParse->pNewTable;
  2425   2425       if( !pTab ) goto exit_create_index;
  2426   2426       iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
  2427   2427     }
  2428   2428     pDb = &db->aDb[iDb];
  2429   2429   
  2430   2430     if( pTab==0 || pParse->nErr ) goto exit_create_index;
  2431         -  if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
         2431  +  if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 
         2432  +       && memcmp(&pTab->zName[7],"altertab_",9)!=0 ){
  2432   2433       sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
  2433   2434       goto exit_create_index;
  2434   2435     }
  2435   2436   #ifndef SQLITE_OMIT_VIEW
  2436   2437     if( pTab->pSelect ){
  2437   2438       sqlite3ErrorMsg(pParse, "views may not be indexed");
  2438   2439       goto exit_create_index;

Changes to test/alter.test.

     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #*************************************************************************
    11     11   # This file implements regression tests for SQLite library.  The
    12     12   # focus of this script is testing the ALTER TABLE statement.
    13     13   #
    14         -# $Id: alter.test,v 1.30 2008/05/09 14:17:52 drh Exp $
           14  +# $Id: alter.test,v 1.31 2009/02/13 03:43:32 drh Exp $
    15     15   #
    16     16   
    17     17   set testdir [file dirname $argv0]
    18     18   source $testdir/tester.tcl
    19     19   
    20     20   # If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
    21     21   ifcapable !altertable {
................................................................................
   806    806   } {t3102a_rename t3102b t3102c}
   807    807   do_test alter-13.3 {
   808    808     execsql {
   809    809       ALTER TABLE t3102b RENAME TO t3102b_rename;
   810    810       SELECT name FROM sqlite_master WHERE name LIKE 't3102%' ORDER BY 1;
   811    811     }
   812    812   } {t3102a_rename t3102b_rename t3102c}
          813  +
          814  +# Ticket #3651
          815  +do_test alter-14.1 {
          816  +  catchsql {
          817  +    CREATE TABLE t3651(a UNIQUE);
          818  +    ALTER TABLE t3651 ADD COLUMN b UNIQUE;
          819  +  }
          820  +} {1 {Cannot add a UNIQUE column}}
          821  +do_test alter-14.2 {
          822  +  catchsql {
          823  +    ALTER TABLE t3651 ADD COLUMN b PRIMARY KEY;
          824  +  }
          825  +} {1 {Cannot add a PRIMARY KEY column}}
          826  +
   813    827   
   814    828   finish_test