Add the ALTER TABLE ... ADD COLUMN command. (CVS 2393)

FossilOrigin-Name: 94185dd4f7e2e941c74a521488d1212a75927218
diff --git a/src/alter.c b/src/alter.c
index 2ed4a7a..c01219f 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -12,9 +12,10 @@
 ** This file contains C code routines that used to generate VDBE code
 ** that implements the ALTER TABLE command.
 **
-** $Id: alter.c,v 1.2 2005/02/15 21:36:18 drh Exp $
+** $Id: alter.c,v 1.3 2005/03/17 05:03:39 danielk1977 Exp $
 */
 #include "sqliteInt.h"
+#include <ctype.h>
 
 /*
 ** The code in this file only exists if we are not omitting the
@@ -167,6 +168,78 @@
 }
 
 /*
+** Generate the text of a WHERE expression which can be used to select all
+** temporary triggers on table pTab from the sqlite_temp_master table. If
+** table pTab has no temporary triggers, or is itself stored in the 
+** temporary database, NULL is returned.
+*/
+static char *whereTempTriggers(Parse *pParse, Table *pTab){
+  Trigger *pTrig;
+  char *zWhere = 0;
+  char *tmp = 0;
+  if( pTab->iDb!=1 ){
+    for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){
+      if( pTrig->iDb==1 ){
+        if( !zWhere ){
+          zWhere = sqlite3MPrintf("name=%Q", pTrig->name);
+        }else{
+          tmp = zWhere;
+          zWhere = sqlite3MPrintf("%s OR name=%Q", zWhere, pTrig->name);
+          sqliteFree(tmp);
+        }
+      }
+    }
+  }
+  return zWhere;
+}
+
+/*
+** Generate code to drop and reload the internal representation of table
+** pTab from the database, including triggers and temporary triggers.
+** Argument zName is the name of the table in the database schema at
+** the time the generated code is executed. This can be different from
+** pTab->zName if this function is being called to code part of an 
+** "ALTER TABLE RENAME TO" statement.
+*/
+static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
+  Vdbe *v;
+  char *zWhere;
+  int iDb;
+#ifndef SQLITE_OMIT_TRIGGER
+  Trigger *pTrig;
+#endif
+
+  v = sqlite3GetVdbe(pParse);
+  if( !v ) return;
+  iDb = pTab->iDb;
+
+#ifndef SQLITE_OMIT_TRIGGER
+  /* Drop any table triggers from the internal schema. */
+  for(pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext){
+    assert( pTrig->iDb==iDb || pTrig->iDb==1 );
+    sqlite3VdbeOp3(v, OP_DropTrigger, pTrig->iDb, 0, pTrig->name, 0);
+  }
+#endif
+
+  /* Drop the table and index from the internal schema */
+  sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0);
+
+  /* Reload the table, index and permanent trigger schemas. */
+  zWhere = sqlite3MPrintf("tbl_name=%Q", zName);
+  if( !zWhere ) return;
+  sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, zWhere, P3_DYNAMIC);
+
+#ifndef SQLITE_OMIT_TRIGGER
+  /* Now, if the table is not stored in the temp database, reload any temp 
+  ** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined. 
+  */
+  if( (zWhere=whereTempTriggers(pParse, pTab)) ){
+    sqlite3VdbeOp3(v, OP_ParseSchema, 1, 0, zWhere, P3_DYNAMIC);
+  }
+#endif
+}
+
+/*
 ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" 
 ** command. 
 */
@@ -179,11 +252,10 @@
   char *zDb;                /* Name of database iDb */
   Table *pTab;              /* Table being renamed */
   char *zName = 0;          /* NULL-terminated version of pName */ 
-  char *zWhere = 0;         /* Where clause of schema elements to reparse */
   sqlite3 *db = pParse->db; /* Database connection */
   Vdbe *v;
 #ifndef SQLITE_OMIT_TRIGGER
-  char *zTempTrig = 0;      /* Where clause to locate temp triggers */
+  char *zWhere = 0;         /* Where clause to locate temp triggers */
 #endif
   
   assert( pSrc->nSrc==1 );
@@ -255,7 +327,7 @@
           "(type='table' OR type='index' OR type='trigger');", 
       zDb, SCHEMA_TABLE(iDb), zName, zName, zName, 
 #ifndef SQLITE_OMIT_TRIGGER
-zName,
+      zName,
 #endif
       zName, strlen(pTab->zName), pTab->zName
   );
@@ -276,59 +348,204 @@
   ** table. Don't do this if the table being ALTERed is itself located in
   ** the temp database.
   */
-  if( iDb!=1 ){
-    Trigger *pTrig;
-    char *tmp = 0;
-    for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){
-      if( pTrig->iDb==1 ){
-        if( !zTempTrig ){
-          zTempTrig = 
-              sqlite3MPrintf("type = 'trigger' AND (name=%Q", pTrig->name);
-        }else{
-          tmp = zTempTrig;
-          zTempTrig = sqlite3MPrintf("%s OR name=%Q", zTempTrig, pTrig->name);
-          sqliteFree(tmp);
-        }
-      }
-    }
-    if( zTempTrig ){
-      tmp = zTempTrig;
-      zTempTrig = sqlite3MPrintf("%s)", zTempTrig);
-      sqliteFree(tmp);
-      sqlite3NestedParse(pParse, 
-          "UPDATE sqlite_temp_master SET "
-              "sql = sqlite_rename_trigger(sql, %Q), "
-              "tbl_name = %Q "
-              "WHERE %s;", zName, zName, zTempTrig);
-    }
+  if( (zWhere=whereTempTriggers(pParse, pTab)) ){
+    sqlite3NestedParse(pParse, 
+        "UPDATE sqlite_temp_master SET "
+            "sql = sqlite_rename_trigger(sql, %Q), "
+            "tbl_name = %Q "
+            "WHERE %s;", zName, zName, zWhere);
+    sqliteFree(zWhere);
   }
 #endif
 
-  /* Drop the elements of the in-memory schema that refered to the table
-  ** renamed and load the new versions from the database.
-  */
-  if( pParse->nErr==0 ){
-#ifndef SQLITE_OMIT_TRIGGER
-    Trigger *pTrig;
-    for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){
-      assert( pTrig->iDb==iDb || pTrig->iDb==1 );
-      sqlite3VdbeOp3(v, OP_DropTrigger, pTrig->iDb, 0, pTrig->name, 0);
-    }
-#endif
-    sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0);
-    zWhere = sqlite3MPrintf("tbl_name=%Q", zName);
-    sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, zWhere, P3_DYNAMIC);
-#ifndef SQLITE_OMIT_TRIGGER
-    if( zTempTrig ){
-      sqlite3VdbeOp3(v, OP_ParseSchema, 1, 0, zTempTrig, P3_DYNAMIC);
-    }
-  }else{
-    sqliteFree(zTempTrig);
-#endif
-  }
+  /* Drop and reload the internal table schema. */
+  reloadTableSchema(pParse, pTab, zName);
 
 exit_rename_table:
   sqlite3SrcListDelete(pSrc);
   sqliteFree(zName);
 }
+
+
+/*
+** This function is called after an "ALTER TABLE ... ADD" statement
+** has been parsed. Argument pColDef contains the text of the new
+** column definition.
+**
+** The Table structure pParse->pNewTable was extended to include
+** the new column during parsing.
+*/
+void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
+  Table *pNew;              /* Copy of pParse->pNewTable */
+  Table *pTab;              /* Table being altered */
+  int iDb;                  /* Database number */
+  const char *zDb;          /* Database name */
+  const char *zTab;         /* Table name */
+  char *zCol;               /* Null-terminated column definition */
+  Column *pCol;             /* The new column */
+  Expr *pDflt;              /* Default value for the new column */
+  Vdbe *v;
+
+  if( pParse->nErr ) return;
+  pNew = pParse->pNewTable;
+  assert( pNew );
+
+  iDb = pNew->iDb;
+  zDb = pParse->db->aDb[iDb].zName;
+  zTab = pNew->zName;
+  pCol = &pNew->aCol[pNew->nCol-1];
+  pDflt = pCol->pDflt;
+  pTab = sqlite3FindTable(pParse->db, zTab, zDb);
+  assert( pTab );
+
+  /* If the default value for the new column was specified with a 
+  ** literal NULL, then set pDflt to 0. This simplifies checking
+  ** for an SQL NULL default below.
+  */
+  if( pDflt && pDflt->op==TK_NULL ){
+    pDflt = 0;
+  }
+
+  /* Check that the new column is not specified as PRIMARY KEY or UNIQUE.
+  ** If there is a NOT NULL constraint, then the default value for the
+  ** column must not be NULL.
+  */
+  if( pCol->isPrimKey ){
+    sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column");
+    return;
+  }
+  if( pNew->pIndex ){
+    sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column");
+    return;
+  }
+  if( pCol->notNull && !pDflt ){
+    sqlite3ErrorMsg(pParse, 
+        "Cannot add a NOT NULL column with default value NULL");
+    return;
+  }
+
+  /* Ensure the default expression is something that sqlite3ValueFromExpr()
+  ** can handle (i.e. not CURRENT_TIME etc.)
+  */
+  if( pDflt ){
+    sqlite3_value *pVal;
+    if( sqlite3ValueFromExpr(pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){
+      /* malloc() has failed */
+      return;
+    }
+    if( !pVal ){
+      sqlite3ErrorMsg(pParse, "Cannot add a column with non-constant default");
+      return;
+    }
+    sqlite3ValueFree(pVal);
+  }
+
+  /* Modify the CREATE TABLE statement. */
+  zCol = sqliteStrNDup(pColDef->z, pColDef->n);
+  if( zCol ){
+    char *zEnd = &zCol[pColDef->n-1];
+    while( zEnd>zCol && *zEnd==';' || isspace(*(unsigned char *)zEnd) ){
+      *zEnd-- = '\0';
+    }
+    sqlite3NestedParse(pParse, 
+        "UPDATE %Q.%s SET "
+          "sql = substr(sql,0,%d) || ', ' || %Q || substr(sql,%d,length(sql)) "
+        "WHERE type = 'table' AND name = %Q", 
+      zDb, SCHEMA_TABLE(iDb), pNew->addColOffset, zCol, pNew->addColOffset+1,
+      zTab
+    );
+    sqliteFree(zCol);
+  }
+
+  /* If the default value of the new column is NULL, then set the file
+  ** format to 2. If the default value of the new column is not NULL,
+  ** the file format becomes 3.
+  */
+  if( (v=sqlite3GetVdbe(pParse)) ){
+    int f = (pDflt?3:2);
+
+    /* Only set the file format to $f if it is currently less than $f. */
+    sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 1);
+    sqlite3VdbeAddOp(v, OP_Integer, f, 0);
+    sqlite3VdbeAddOp(v, OP_Ge, 0, sqlite3VdbeCurrentAddr(v)+3);
+    sqlite3VdbeAddOp(v, OP_Integer, f, 0);
+    sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 1);
+  }
+
+  /* Reload the schema of the modified table. */
+  reloadTableSchema(pParse, pTab, pTab->zName);
+}
+
+
+/*
+** This function is called by the parser after the table-name in
+** an "ALTER TABLE <table-name> ADD" statement is parsed. Argument 
+** pSrc is the full-name of the table being altered.
+**
+** This routine makes a (partial) copy of the Table structure
+** for the table being altered and sets Parse.pNewTable to point
+** to it. Routines called by the parser as the column definition
+** is parsed (i.e. sqlite3AddColumn()) add the new Column data to 
+** the copy. The copy of the Table structure is deleted by tokenize.c 
+** after parsing is finished.
+**
+** Routine sqlite3AlterFinishAddColumn() will be called to complete
+** coding the "ALTER TABLE ... ADD" statement.
+*/
+void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
+  Table *pNew;
+  Table *pTab;
+  Vdbe *v;
+  int iDb;
+  int i;
+  int nAlloc;
+
+  /* Look up the table being altered. */
+  assert( !pParse->pNewTable );
+  pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase);
+  if( !pTab ) goto exit_begin_add_column;
+
+  /* Make sure this is not an attempt to ALTER a view. */
+  if( pTab->pSelect ){
+    sqlite3ErrorMsg(pParse, "Cannot add a column to a view");
+    goto exit_begin_add_column;
+  }
+
+  assert( pTab->addColOffset>0 );
+  iDb = pTab->iDb;
+
+  /* Put a copy of the Table struct in Parse.pNewTable for the
+  ** sqlite3AddColumn() function and friends to modify.
+  */
+  pNew = (Table *)sqliteMalloc(sizeof(Table));
+  if( !pNew ) goto exit_begin_add_column;
+  pParse->pNewTable = pNew;
+  pNew->nCol = pTab->nCol;
+  nAlloc = ((pNew->nCol)/8)+8;
+  pNew->aCol = (Column *)sqliteMalloc(sizeof(Column)*nAlloc);
+  pNew->zName = sqliteStrDup(pTab->zName);
+  if( !pNew->aCol || !pNew->zName ){
+    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];
+    pCol->zName = sqliteStrDup(pCol->zName);
+    pCol->zType = 0;
+    pCol->pDflt = 0;
+  }
+  pNew->iDb = iDb;
+  pNew->addColOffset = pTab->addColOffset;
+
+  /* Begin a transaction and increment the schema cookie.  */
+  sqlite3BeginWriteOperation(pParse, 0, iDb);
+  v = sqlite3GetVdbe(pParse);
+  if( !v ) goto exit_begin_add_column;
+  sqlite3ChangeCookie(pParse->db, v, iDb);
+
+exit_begin_add_column:
+  sqlite3SrcListDelete(pSrc);
+  return;
+}
 #endif  /* SQLITE_ALTER_TABLE */
+
diff --git a/src/build.c b/src/build.c
index 15fc2b6..d1f2fef 100644
--- a/src/build.c
+++ b/src/build.c
@@ -22,7 +22,7 @@
 **     COMMIT
 **     ROLLBACK
 **
-** $Id: build.c,v 1.313 2005/03/16 12:15:21 danielk1977 Exp $
+** $Id: build.c,v 1.314 2005/03/17 05:03:39 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -1413,7 +1413,12 @@
 ** "CREATE TABLE ... AS SELECT ..." statement.  The column names of
 ** the new table will match the result set of the SELECT.
 */
-void sqlite3EndTable(Parse *pParse, Token *pEnd, Select *pSelect){
+void sqlite3EndTable(
+  Parse *pParse,          /* Parse context */
+  Token *pCons,           /* The ',' token after the last column defn. */
+  Token *pEnd,            /* The final ')' token in the CREATE TABLE */
+  Select *pSelect         /* Select from a "CREATE ... AS SELECT" */
+){
   Table *p;
   sqlite3 *db = pParse->db;
 
@@ -1567,6 +1572,14 @@
     pParse->pNewTable = 0;
     db->nTable++;
     db->flags |= SQLITE_InternChanges;
+
+#ifndef SQLITE_OMIT_ALTERTABLE
+    if( !p->pSelect ){
+      assert( !pSelect && pCons && pEnd );
+      if( pCons->z==0 ) pCons = pEnd;
+      p->addColOffset = 13 + (pCons->z - pParse->sNameToken.z);
+    }
+#endif
   }
 }
 
@@ -1629,7 +1642,7 @@
   sEnd.n = 1;
 
   /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
-  sqlite3EndTable(pParse, &sEnd, 0);
+  sqlite3EndTable(pParse, 0, &sEnd, 0);
   return;
 }
 #endif /* SQLITE_OMIT_VIEW */
diff --git a/src/parse.y b/src/parse.y
index 2157f15..ad7fb2f 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -14,7 +14,7 @@
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.168 2005/03/16 12:15:21 danielk1977 Exp $
+** @(#) $Id: parse.y,v 1.169 2005/03/17 05:03:40 danielk1977 Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -114,11 +114,11 @@
 %type temp {int}
 temp(A) ::= TEMP.  {A = 1;}
 temp(A) ::= .      {A = 0;}
-create_table_args ::= LP columnlist conslist_opt RP(X). {
-  sqlite3EndTable(pParse,&X,0);
+create_table_args ::= LP columnlist conslist_opt(X) RP(Y). {
+  sqlite3EndTable(pParse,&X,&Y,0);
 }
 create_table_args ::= AS select(S). {
-  sqlite3EndTable(pParse,0,S);
+  sqlite3EndTable(pParse,0,0,S);
   sqlite3SelectDelete(S);
 }
 columnlist ::= columnlist COMMA column.
@@ -128,8 +128,15 @@
 // column.  The type is always just "text".  But the code will accept
 // an elaborate typename.  Perhaps someday we'll do something with it.
 //
-column ::= columnid type carglist. 
-columnid ::= nm(X).                {sqlite3AddColumn(pParse,&X);}
+column(A) ::= columnid(X) type carglist. {
+  A.z = X.z;
+  A.n = (pParse->sLastToken.z-X.z) + pParse->sLastToken.n;
+}
+columnid(A) ::= nm(X). {
+  sqlite3AddColumn(pParse,&X);
+  A = X;
+}
+
 
 // An IDENTIFIER can be a generic identifier, or one of several
 // keywords.  Any non-standard keyword can also be an identifier.
@@ -223,7 +230,7 @@
 ccons ::= PRIMARY KEY sortorder onconf(R) autoinc(I).
                                      {sqlite3AddPrimaryKey(pParse,0,R,I);}
 ccons ::= UNIQUE onconf(R).          {sqlite3CreateIndex(pParse,0,0,0,0,R,0,0);}
-ccons ::= CHECK LP expr RP onconf.
+ccons ::= CHECK LP expr(X) RP onconf. {sqlite3ExprDelete(X);}
 ccons ::= REFERENCES nm(T) idxlist_opt(TA) refargs(R).
                                 {sqlite3CreateForeignKey(pParse,0,&T,TA,R);}
 ccons ::= defer_subclause(D).   {sqlite3DeferForeignKey(pParse,D);}
@@ -263,8 +270,8 @@
 // For the time being, the only constraint we care about is the primary
 // key and UNIQUE.  Both create indices.
 //
-conslist_opt ::= .
-conslist_opt ::= COMMA conslist.
+conslist_opt(A) ::= .                   {A.n = 0; A.z = 0;}
+conslist_opt(A) ::= COMMA(X) conslist.  {A = X;}
 conslist ::= conslist COMMA tcons.
 conslist ::= conslist tcons.
 conslist ::= tcons.
@@ -975,4 +982,12 @@
 cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). {
   sqlite3AlterRenameTable(pParse,X,&Z);
 }
+cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column(Y). {
+  sqlite3AlterFinishAddColumn(pParse, &Y);
+}
+add_column_fullname ::= fullname(X). {
+  sqlite3AlterBeginAddColumn(pParse, X);
+}
+kwcolumn_opt ::= .
+kwcolumn_opt ::= COLUMNKW.
 %endif
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 174c0d2..f5e79a9 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.372 2005/03/09 12:26:51 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.373 2005/03/17 05:03:40 danielk1977 Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -611,6 +611,9 @@
   Trigger *pTrigger; /* List of SQL triggers on this table */
   FKey *pFKey;       /* Linked list of all foreign keys in this table */
   char *zColAff;     /* String defining the affinity of each column */
+#ifndef SQLITE_OMIT_ALTERTABLE
+  int addColOffset;  /* Offset in CREATE TABLE statement to add a new column */
+#endif
 };
 
 /*
@@ -1367,7 +1370,7 @@
 void sqlite3AddColumnType(Parse*,Token*,Token*);
 void sqlite3AddDefaultValue(Parse*,Expr*);
 void sqlite3AddCollateType(Parse*, const char*, int);
-void sqlite3EndTable(Parse*,Token*,Select*);
+void sqlite3EndTable(Parse*,Token*,Token*,Select*);
 
 #ifndef SQLITE_OMIT_VIEW
   void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int);
@@ -1554,5 +1557,7 @@
 void sqlite3CodeSubselect(Parse *, Expr *);
 int sqlite3SelectResolve(Parse *, Select *, NameContext *);
 void sqlite3ColumnDefault(Vdbe *, Table *, int);
+void sqlite3AlterFinishAddColumn(Parse *, Token *);
+void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
 
 #endif