mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
Add the ALTER TABLE ... ADD COLUMN command. (CVS 2393)
FossilOrigin-Name: 94185dd4f7e2e941c74a521488d1212a75927218
This commit is contained in:
23
manifest
23
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Version\s3.1.6\s(CVS\s2392)
|
C Add\sthe\sALTER\sTABLE\s...\sADD\sCOLUMN\scommand.\s(CVS\s2393)
|
||||||
D 2005-03-17T04:01:24
|
D 2005-03-17T05:03:39
|
||||||
F Makefile.in 5c00d0037104de2a50ac7647a5f12769795957a3
|
F Makefile.in 5c00d0037104de2a50ac7647a5f12769795957a3
|
||||||
F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7
|
F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7
|
||||||
F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
|
F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
|
||||||
@@ -27,12 +27,12 @@ F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
|
|||||||
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
|
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
|
||||||
F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863
|
F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863
|
||||||
F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
|
F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
|
||||||
F src/alter.c 6dab3d91aa4bf5c24e874145a2a547070c8c1883
|
F src/alter.c 85a23fc9a565c0e065d05d1d89744fcc6dee1c65
|
||||||
F src/attach.c 3615dbe960cbee4aa5ea300b8a213dad36527b0f
|
F src/attach.c 3615dbe960cbee4aa5ea300b8a213dad36527b0f
|
||||||
F src/auth.c 18c5a0befe20f3a58a41e3ddd78f372faeeefe1f
|
F src/auth.c 18c5a0befe20f3a58a41e3ddd78f372faeeefe1f
|
||||||
F src/btree.c 1d9b2179ccac13970c883da6ae3758cc72978bb0
|
F src/btree.c 1d9b2179ccac13970c883da6ae3758cc72978bb0
|
||||||
F src/btree.h 2e2cc923224649337d7217df0dd32b06673ca180
|
F src/btree.h 2e2cc923224649337d7217df0dd32b06673ca180
|
||||||
F src/build.c 66bf13d2d478d43b9b490bebf07ea62524d80993
|
F src/build.c a5688630ba792987dca4daa62aae5ea30d1fad67
|
||||||
F src/date.c f3d1f5cd1503dabf426a198f3ebef5afbc122a7f
|
F src/date.c f3d1f5cd1503dabf426a198f3ebef5afbc122a7f
|
||||||
F src/delete.c d70d54a84695de92efc05b9db7d3684cd21d9094
|
F src/delete.c d70d54a84695de92efc05b9db7d3684cd21d9094
|
||||||
F src/experimental.c 50c1e3b34f752f4ac10c36f287db095c2b61766d
|
F src/experimental.c 50c1e3b34f752f4ac10c36f287db095c2b61766d
|
||||||
@@ -54,14 +54,14 @@ F src/os_win.c bddeae1c3299be0fbe47077dd4e98b786a067f71
|
|||||||
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
|
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
|
||||||
F src/pager.c 26a642c1238615bdea53d458ab6a4df7ca070a08
|
F src/pager.c 26a642c1238615bdea53d458ab6a4df7ca070a08
|
||||||
F src/pager.h 70d496f372163abb6340f474288c4bb9ea962cf7
|
F src/pager.h 70d496f372163abb6340f474288c4bb9ea962cf7
|
||||||
F src/parse.y 5c8336e7df0d843976c2aa0f63010d479d9cdba5
|
F src/parse.y 10c0ace9efce31d5a06e03488a4284b9d97abc56
|
||||||
F src/pragma.c 4b20dbc0f4b97f412dc511853d3d0c2e0d4adedc
|
F src/pragma.c 4b20dbc0f4b97f412dc511853d3d0c2e0d4adedc
|
||||||
F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357
|
F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357
|
||||||
F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
|
F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
|
||||||
F src/select.c 69e6093d52e871a243477e9746f820456538dd03
|
F src/select.c 69e6093d52e871a243477e9746f820456538dd03
|
||||||
F src/shell.c 25b3217d7c64e6497225439d261a253a23efff26
|
F src/shell.c 25b3217d7c64e6497225439d261a253a23efff26
|
||||||
F src/sqlite.h.in c85f6bad9ca7de29f505fe886646cfff7df4c55e
|
F src/sqlite.h.in c85f6bad9ca7de29f505fe886646cfff7df4c55e
|
||||||
F src/sqliteInt.h b59243adc43f0326ca7d8ce0b7ebd3cc70bd670d
|
F src/sqliteInt.h 9b2aa887e79b2ecadc24f0b30363b9ec1e5b51e3
|
||||||
F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
|
F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
|
||||||
F src/tclsqlite.c 29e56ecdb94c4066dbe6b088d12cc2404bc9597e
|
F src/tclsqlite.c 29e56ecdb94c4066dbe6b088d12cc2404bc9597e
|
||||||
F src/test1.c dd3e4961f3b9b235a68d4af5d77a06eb7be73184
|
F src/test1.c dd3e4961f3b9b235a68d4af5d77a06eb7be73184
|
||||||
@@ -86,6 +86,7 @@ F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42
|
|||||||
F test/all.test 7f0988442ab811dfa41793b5b550f5828ce316f3
|
F test/all.test 7f0988442ab811dfa41793b5b550f5828ce316f3
|
||||||
F test/alter.test 3a20ce14c3989f7e2e75da50797065c2e56f838b
|
F test/alter.test 3a20ce14c3989f7e2e75da50797065c2e56f838b
|
||||||
F test/alter2.test 60ba0a7057dc71ad630a1cc7c487104346849d50
|
F test/alter2.test 60ba0a7057dc71ad630a1cc7c487104346849d50
|
||||||
|
F test/alter3.test ed4b8d4f1ce429d313221cc827d9a18392e51620
|
||||||
F test/attach.test e6bda19cc954fd84836fadbd70d80134cb17918a
|
F test/attach.test e6bda19cc954fd84836fadbd70d80134cb17918a
|
||||||
F test/attach2.test 6f3a3a3a7f5be40388dd4d805e0e0712718dca9d
|
F test/attach2.test 6f3a3a3a7f5be40388dd4d805e0e0712718dca9d
|
||||||
F test/attach3.test c05c70b933afbde0901dab9da3e66ee842c09f38
|
F test/attach3.test c05c70b933afbde0901dab9da3e66ee842c09f38
|
||||||
@@ -259,7 +260,7 @@ F www/faq.tcl 1e348dec52dc0f21f4216fd6918c69c56daa4cfd
|
|||||||
F www/fileformat.tcl 900c95b9633abc3dcfc384d9ddd8eb4876793059
|
F www/fileformat.tcl 900c95b9633abc3dcfc384d9ddd8eb4876793059
|
||||||
F www/formatchng.tcl bfbf14dbf5181e771d06da7797767b0200b36d8a
|
F www/formatchng.tcl bfbf14dbf5181e771d06da7797767b0200b36d8a
|
||||||
F www/index.tcl a53c9b092ab528cc4b9755ccd78e23ca796bafe0
|
F www/index.tcl a53c9b092ab528cc4b9755ccd78e23ca796bafe0
|
||||||
F www/lang.tcl 0e5aeb09864b9ae0746d8afaa6377193f1553486
|
F www/lang.tcl 21cb28bee6a522ab9a376e33f15a048638d6db97
|
||||||
F www/lockingv3.tcl f59b19d6c8920a931f096699d6faaf61c05db55f
|
F www/lockingv3.tcl f59b19d6c8920a931f096699d6faaf61c05db55f
|
||||||
F www/mingw.tcl d96b451568c5d28545fefe0c80bee3431c73f69c
|
F www/mingw.tcl d96b451568c5d28545fefe0c80bee3431c73f69c
|
||||||
F www/nulls.tcl ec35193f92485b87b90a994a01d0171b58823fcf
|
F www/nulls.tcl ec35193f92485b87b90a994a01d0171b58823fcf
|
||||||
@@ -276,7 +277,7 @@ F www/tclsqlite.tcl e73f8f8e5f20e8277619433f7970060ab01088fc
|
|||||||
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
|
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
|
||||||
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
|
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
|
||||||
F www/whentouse.tcl 3e522a06ad41992023c80ca29a048ae2331ca5bd
|
F www/whentouse.tcl 3e522a06ad41992023c80ca29a048ae2331ca5bd
|
||||||
P 3f45cf3516be9919fe2c0673d6f445fa83d42126
|
P 6a3f4e4be6f294f756ed882f220f579252735f3f
|
||||||
R ecdf32cee3676fed33ab83ee414d13b7
|
R 6d3cd1539c542074a3e7518c7b23a719
|
||||||
U drh
|
U danielk1977
|
||||||
Z f31b75bb74fc3f7add0a909524577db3
|
Z c3ddc11776f2cb70a8848ec00f552873
|
||||||
|
@@ -1 +1 @@
|
|||||||
6a3f4e4be6f294f756ed882f220f579252735f3f
|
94185dd4f7e2e941c74a521488d1212a75927218
|
311
src/alter.c
311
src/alter.c
@@ -12,9 +12,10 @@
|
|||||||
** This file contains C code routines that used to generate VDBE code
|
** This file contains C code routines that used to generate VDBE code
|
||||||
** that implements the ALTER TABLE command.
|
** 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 "sqliteInt.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** The code in this file only exists if we are not omitting the
|
** The code in this file only exists if we are not omitting the
|
||||||
@@ -166,6 +167,78 @@ void sqlite3AlterFunctions(sqlite3 *db){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** 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"
|
** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy"
|
||||||
** command.
|
** command.
|
||||||
@@ -179,11 +252,10 @@ void sqlite3AlterRenameTable(
|
|||||||
char *zDb; /* Name of database iDb */
|
char *zDb; /* Name of database iDb */
|
||||||
Table *pTab; /* Table being renamed */
|
Table *pTab; /* Table being renamed */
|
||||||
char *zName = 0; /* NULL-terminated version of pName */
|
char *zName = 0; /* NULL-terminated version of pName */
|
||||||
char *zWhere = 0; /* Where clause of schema elements to reparse */
|
|
||||||
sqlite3 *db = pParse->db; /* Database connection */
|
sqlite3 *db = pParse->db; /* Database connection */
|
||||||
Vdbe *v;
|
Vdbe *v;
|
||||||
#ifndef SQLITE_OMIT_TRIGGER
|
#ifndef SQLITE_OMIT_TRIGGER
|
||||||
char *zTempTrig = 0; /* Where clause to locate temp triggers */
|
char *zWhere = 0; /* Where clause to locate temp triggers */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
assert( pSrc->nSrc==1 );
|
assert( pSrc->nSrc==1 );
|
||||||
@@ -255,7 +327,7 @@ void sqlite3AlterRenameTable(
|
|||||||
"(type='table' OR type='index' OR type='trigger');",
|
"(type='table' OR type='index' OR type='trigger');",
|
||||||
zDb, SCHEMA_TABLE(iDb), zName, zName, zName,
|
zDb, SCHEMA_TABLE(iDb), zName, zName, zName,
|
||||||
#ifndef SQLITE_OMIT_TRIGGER
|
#ifndef SQLITE_OMIT_TRIGGER
|
||||||
zName,
|
zName,
|
||||||
#endif
|
#endif
|
||||||
zName, strlen(pTab->zName), pTab->zName
|
zName, strlen(pTab->zName), pTab->zName
|
||||||
);
|
);
|
||||||
@@ -276,59 +348,204 @@ zName,
|
|||||||
** table. Don't do this if the table being ALTERed is itself located in
|
** table. Don't do this if the table being ALTERed is itself located in
|
||||||
** the temp database.
|
** the temp database.
|
||||||
*/
|
*/
|
||||||
if( iDb!=1 ){
|
if( (zWhere=whereTempTriggers(pParse, pTab)) ){
|
||||||
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,
|
sqlite3NestedParse(pParse,
|
||||||
"UPDATE sqlite_temp_master SET "
|
"UPDATE sqlite_temp_master SET "
|
||||||
"sql = sqlite_rename_trigger(sql, %Q), "
|
"sql = sqlite_rename_trigger(sql, %Q), "
|
||||||
"tbl_name = %Q "
|
"tbl_name = %Q "
|
||||||
"WHERE %s;", zName, zName, zTempTrig);
|
"WHERE %s;", zName, zName, zWhere);
|
||||||
}
|
sqliteFree(zWhere);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Drop the elements of the in-memory schema that refered to the table
|
/* Drop and reload the internal table schema. */
|
||||||
** renamed and load the new versions from the database.
|
reloadTableSchema(pParse, pTab, zName);
|
||||||
*/
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
exit_rename_table:
|
exit_rename_table:
|
||||||
sqlite3SrcListDelete(pSrc);
|
sqlite3SrcListDelete(pSrc);
|
||||||
sqliteFree(zName);
|
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 */
|
#endif /* SQLITE_ALTER_TABLE */
|
||||||
|
|
||||||
|
19
src/build.c
19
src/build.c
@@ -22,7 +22,7 @@
|
|||||||
** COMMIT
|
** COMMIT
|
||||||
** ROLLBACK
|
** 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 "sqliteInt.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@@ -1413,7 +1413,12 @@ static char *createTableStmt(Table *p){
|
|||||||
** "CREATE TABLE ... AS SELECT ..." statement. The column names of
|
** "CREATE TABLE ... AS SELECT ..." statement. The column names of
|
||||||
** the new table will match the result set of the SELECT.
|
** 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;
|
Table *p;
|
||||||
sqlite3 *db = pParse->db;
|
sqlite3 *db = pParse->db;
|
||||||
|
|
||||||
@@ -1567,6 +1572,14 @@ void sqlite3EndTable(Parse *pParse, Token *pEnd, Select *pSelect){
|
|||||||
pParse->pNewTable = 0;
|
pParse->pNewTable = 0;
|
||||||
db->nTable++;
|
db->nTable++;
|
||||||
db->flags |= SQLITE_InternChanges;
|
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 @@ void sqlite3CreateView(
|
|||||||
sEnd.n = 1;
|
sEnd.n = 1;
|
||||||
|
|
||||||
/* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
|
/* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
|
||||||
sqlite3EndTable(pParse, &sEnd, 0);
|
sqlite3EndTable(pParse, 0, &sEnd, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif /* SQLITE_OMIT_VIEW */
|
#endif /* SQLITE_OMIT_VIEW */
|
||||||
|
33
src/parse.y
33
src/parse.y
@@ -14,7 +14,7 @@
|
|||||||
** the parser. Lemon will also generate a header file containing
|
** the parser. Lemon will also generate a header file containing
|
||||||
** numeric codes for all of the tokens.
|
** 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_prefix TK_
|
||||||
%token_type {Token}
|
%token_type {Token}
|
||||||
@@ -114,11 +114,11 @@ create_table ::= CREATE(X) temp(T) TABLE nm(Y) dbnm(Z). {
|
|||||||
%type temp {int}
|
%type temp {int}
|
||||||
temp(A) ::= TEMP. {A = 1;}
|
temp(A) ::= TEMP. {A = 1;}
|
||||||
temp(A) ::= . {A = 0;}
|
temp(A) ::= . {A = 0;}
|
||||||
create_table_args ::= LP columnlist conslist_opt RP(X). {
|
create_table_args ::= LP columnlist conslist_opt(X) RP(Y). {
|
||||||
sqlite3EndTable(pParse,&X,0);
|
sqlite3EndTable(pParse,&X,&Y,0);
|
||||||
}
|
}
|
||||||
create_table_args ::= AS select(S). {
|
create_table_args ::= AS select(S). {
|
||||||
sqlite3EndTable(pParse,0,S);
|
sqlite3EndTable(pParse,0,0,S);
|
||||||
sqlite3SelectDelete(S);
|
sqlite3SelectDelete(S);
|
||||||
}
|
}
|
||||||
columnlist ::= columnlist COMMA column.
|
columnlist ::= columnlist COMMA column.
|
||||||
@@ -128,8 +128,15 @@ columnlist ::= column.
|
|||||||
// column. The type is always just "text". But the code will accept
|
// column. The type is always just "text". But the code will accept
|
||||||
// an elaborate typename. Perhaps someday we'll do something with it.
|
// an elaborate typename. Perhaps someday we'll do something with it.
|
||||||
//
|
//
|
||||||
column ::= columnid type carglist.
|
column(A) ::= columnid(X) type carglist. {
|
||||||
columnid ::= nm(X). {sqlite3AddColumn(pParse,&X);}
|
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
|
// An IDENTIFIER can be a generic identifier, or one of several
|
||||||
// keywords. Any non-standard keyword can also be an identifier.
|
// keywords. Any non-standard keyword can also be an identifier.
|
||||||
@@ -223,7 +230,7 @@ ccons ::= NOT NULL onconf(R). {sqlite3AddNotNull(pParse, R);}
|
|||||||
ccons ::= PRIMARY KEY sortorder onconf(R) autoinc(I).
|
ccons ::= PRIMARY KEY sortorder onconf(R) autoinc(I).
|
||||||
{sqlite3AddPrimaryKey(pParse,0,R,I);}
|
{sqlite3AddPrimaryKey(pParse,0,R,I);}
|
||||||
ccons ::= UNIQUE onconf(R). {sqlite3CreateIndex(pParse,0,0,0,0,R,0,0);}
|
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).
|
ccons ::= REFERENCES nm(T) idxlist_opt(TA) refargs(R).
|
||||||
{sqlite3CreateForeignKey(pParse,0,&T,TA,R);}
|
{sqlite3CreateForeignKey(pParse,0,&T,TA,R);}
|
||||||
ccons ::= defer_subclause(D). {sqlite3DeferForeignKey(pParse,D);}
|
ccons ::= defer_subclause(D). {sqlite3DeferForeignKey(pParse,D);}
|
||||||
@@ -263,8 +270,8 @@ init_deferred_pred_opt(A) ::= INITIALLY IMMEDIATE. {A = 0;}
|
|||||||
// For the time being, the only constraint we care about is the primary
|
// For the time being, the only constraint we care about is the primary
|
||||||
// key and UNIQUE. Both create indices.
|
// key and UNIQUE. Both create indices.
|
||||||
//
|
//
|
||||||
conslist_opt ::= .
|
conslist_opt(A) ::= . {A.n = 0; A.z = 0;}
|
||||||
conslist_opt ::= COMMA conslist.
|
conslist_opt(A) ::= COMMA(X) conslist. {A = X;}
|
||||||
conslist ::= conslist COMMA tcons.
|
conslist ::= conslist COMMA tcons.
|
||||||
conslist ::= conslist tcons.
|
conslist ::= conslist tcons.
|
||||||
conslist ::= tcons.
|
conslist ::= tcons.
|
||||||
@@ -975,4 +982,12 @@ cmd ::= REINDEX nm(X) dbnm(Y). {sqlite3Reindex(pParse, &X, &Y);}
|
|||||||
cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). {
|
cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). {
|
||||||
sqlite3AlterRenameTable(pParse,X,&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
|
%endif
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** Internal interface definitions for SQLite.
|
** 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_
|
#ifndef _SQLITEINT_H_
|
||||||
#define _SQLITEINT_H_
|
#define _SQLITEINT_H_
|
||||||
@@ -611,6 +611,9 @@ struct Table {
|
|||||||
Trigger *pTrigger; /* List of SQL triggers on this table */
|
Trigger *pTrigger; /* List of SQL triggers on this table */
|
||||||
FKey *pFKey; /* Linked list of all foreign keys in this table */
|
FKey *pFKey; /* Linked list of all foreign keys in this table */
|
||||||
char *zColAff; /* String defining the affinity of each column */
|
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 sqlite3AddPrimaryKey(Parse*, ExprList*, int, int);
|
|||||||
void sqlite3AddColumnType(Parse*,Token*,Token*);
|
void sqlite3AddColumnType(Parse*,Token*,Token*);
|
||||||
void sqlite3AddDefaultValue(Parse*,Expr*);
|
void sqlite3AddDefaultValue(Parse*,Expr*);
|
||||||
void sqlite3AddCollateType(Parse*, const char*, int);
|
void sqlite3AddCollateType(Parse*, const char*, int);
|
||||||
void sqlite3EndTable(Parse*,Token*,Select*);
|
void sqlite3EndTable(Parse*,Token*,Token*,Select*);
|
||||||
|
|
||||||
#ifndef SQLITE_OMIT_VIEW
|
#ifndef SQLITE_OMIT_VIEW
|
||||||
void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int);
|
void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int);
|
||||||
@@ -1554,5 +1557,7 @@ void sqlite3ExpirePreparedStatements(sqlite3*);
|
|||||||
void sqlite3CodeSubselect(Parse *, Expr *);
|
void sqlite3CodeSubselect(Parse *, Expr *);
|
||||||
int sqlite3SelectResolve(Parse *, Select *, NameContext *);
|
int sqlite3SelectResolve(Parse *, Select *, NameContext *);
|
||||||
void sqlite3ColumnDefault(Vdbe *, Table *, int);
|
void sqlite3ColumnDefault(Vdbe *, Table *, int);
|
||||||
|
void sqlite3AlterFinishAddColumn(Parse *, Token *);
|
||||||
|
void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
333
test/alter3.test
Normal file
333
test/alter3.test
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
# 2005 February 19
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
#*************************************************************************
|
||||||
|
# This file implements regression tests for SQLite library. The
|
||||||
|
# focus of this script is testing that SQLite can handle a subtle
|
||||||
|
# file format change that may be used in the future to implement
|
||||||
|
# "ALTER TABLE ... ADD COLUMN".
|
||||||
|
#
|
||||||
|
# $Id: alter3.test,v 1.1 2005/03/17 05:03:41 danielk1977 Exp $
|
||||||
|
#
|
||||||
|
|
||||||
|
set testdir [file dirname $argv0]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
|
||||||
|
# Test Organisation:
|
||||||
|
# ------------------
|
||||||
|
#
|
||||||
|
# alter3-1.*: Test that ALTER TABLE correctly modifies the CREATE TABLE sql.
|
||||||
|
# alter3-2.*: Test error messages.
|
||||||
|
# alter3-3.*: Test adding columns with default value NULL.
|
||||||
|
# alter3-4.*: Test adding columns with default values other than NULL.
|
||||||
|
# alter3-5.*: Test adding columns to tables in ATTACHed databases.
|
||||||
|
# alter3-6.*: Test that temp triggers are not accidentally dropped.
|
||||||
|
# alter3-7.*: Test that VACUUM resets the file-format.
|
||||||
|
#
|
||||||
|
|
||||||
|
# This procedure returns the value of the file-format in file 'test.db'.
|
||||||
|
#
|
||||||
|
proc get_file_format {{fname test.db}} {
|
||||||
|
set bt [btree_open $fname 10 0]
|
||||||
|
set meta [btree_get_meta $bt]
|
||||||
|
btree_close $bt
|
||||||
|
lindex $meta 2
|
||||||
|
}
|
||||||
|
|
||||||
|
do_test alter3-1.1 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE abc(a, b, c);
|
||||||
|
SELECT sql FROM sqlite_master;
|
||||||
|
}
|
||||||
|
} {{CREATE TABLE abc(a, b, c)}}
|
||||||
|
do_test alter3-1.2 {
|
||||||
|
execsql {ALTER TABLE abc ADD d INTEGER;}
|
||||||
|
execsql {
|
||||||
|
SELECT sql FROM sqlite_master;
|
||||||
|
}
|
||||||
|
} {{CREATE TABLE abc(a, b, c, d INTEGER)}}
|
||||||
|
do_test alter3-1.3 {
|
||||||
|
execsql {ALTER TABLE abc ADD e}
|
||||||
|
execsql {
|
||||||
|
SELECT sql FROM sqlite_master;
|
||||||
|
}
|
||||||
|
} {{CREATE TABLE abc(a, b, c, d INTEGER, e)}}
|
||||||
|
do_test alter3-1.4 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE main.t1(a, b);
|
||||||
|
ALTER TABLE t1 ADD c;
|
||||||
|
SELECT sql FROM sqlite_master WHERE tbl_name = 't1';
|
||||||
|
}
|
||||||
|
} {{CREATE TABLE t1(a, b, c)}}
|
||||||
|
do_test alter3-1.5 {
|
||||||
|
execsql {
|
||||||
|
ALTER TABLE t1 ADD d CHECK (a>d);
|
||||||
|
SELECT sql FROM sqlite_master WHERE tbl_name = 't1';
|
||||||
|
}
|
||||||
|
} {{CREATE TABLE t1(a, b, c, d CHECK (a>d))}}
|
||||||
|
do_test alter3-1.6 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE t2(a, b, UNIQUE(a, b));
|
||||||
|
ALTER TABLE t2 ADD c REFERENCES t1(c) ;
|
||||||
|
SELECT sql FROM sqlite_master WHERE tbl_name = 't2' AND type = 'table';
|
||||||
|
}
|
||||||
|
} {{CREATE TABLE t2(a, b, c REFERENCES t1(c), UNIQUE(a, b))}}
|
||||||
|
do_test alter3-1.7 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE t3(a, b, UNIQUE(a, b));
|
||||||
|
ALTER TABLE t3 ADD COLUMN c VARCHAR(10, 20);
|
||||||
|
SELECT sql FROM sqlite_master WHERE tbl_name = 't3' AND type = 'table';
|
||||||
|
}
|
||||||
|
} {{CREATE TABLE t3(a, b, c VARCHAR(10, 20), UNIQUE(a, b))}}
|
||||||
|
do_test alter3-1.99 {
|
||||||
|
execsql {
|
||||||
|
DROP TABLE abc;
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP TABLE t3;
|
||||||
|
}
|
||||||
|
} {}
|
||||||
|
|
||||||
|
do_test alter3-2.1 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE t1(a, b);
|
||||||
|
}
|
||||||
|
catchsql {
|
||||||
|
ALTER TABLE t1 ADD c PRIMARY KEY;
|
||||||
|
}
|
||||||
|
} {1 {Cannot add a PRIMARY KEY column}}
|
||||||
|
do_test alter3-2.2 {
|
||||||
|
catchsql {
|
||||||
|
ALTER TABLE t1 ADD c UNIQUE
|
||||||
|
}
|
||||||
|
} {1 {Cannot add a UNIQUE column}}
|
||||||
|
do_test alter3-2.3 {
|
||||||
|
catchsql {
|
||||||
|
ALTER TABLE t1 ADD b VARCHAR(10)
|
||||||
|
}
|
||||||
|
} {1 {duplicate column name: b}}
|
||||||
|
do_test alter3-2.3 {
|
||||||
|
catchsql {
|
||||||
|
ALTER TABLE t1 ADD c NOT NULL;
|
||||||
|
}
|
||||||
|
} {1 {Cannot add a NOT NULL column with default value NULL}}
|
||||||
|
do_test alter3-2.4 {
|
||||||
|
catchsql {
|
||||||
|
ALTER TABLE t1 ADD c NOT NULL DEFAULT 10;
|
||||||
|
}
|
||||||
|
} {0 {}}
|
||||||
|
do_test alter3-2.5 {
|
||||||
|
execsql {
|
||||||
|
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||||
|
}
|
||||||
|
catchsql {
|
||||||
|
alter table v1 add column d;
|
||||||
|
}
|
||||||
|
} {1 {Cannot add a column to a view}}
|
||||||
|
do_test alter3-2.6 {
|
||||||
|
catchsql {
|
||||||
|
alter table t1 add column d DEFAULT CURRENT_TIME;
|
||||||
|
}
|
||||||
|
} {1 {Cannot add a column with non-constant default}}
|
||||||
|
do_test alter3-2.99 {
|
||||||
|
execsql {
|
||||||
|
DROP TABLE t1;
|
||||||
|
}
|
||||||
|
} {}
|
||||||
|
|
||||||
|
do_test alter3-3.1 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE t1(a, b);
|
||||||
|
INSERT INTO t1 VALUES(1, 100);
|
||||||
|
INSERT INTO t1 VALUES(2, 300);
|
||||||
|
SELECT * FROM t1;
|
||||||
|
}
|
||||||
|
} {1 100 2 300}
|
||||||
|
do_test alter3-3.1 {
|
||||||
|
execsql {
|
||||||
|
PRAGMA schema_version = 10;
|
||||||
|
}
|
||||||
|
} {}
|
||||||
|
do_test alter3-3.2 {
|
||||||
|
execsql {
|
||||||
|
ALTER TABLE t1 ADD c;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
}
|
||||||
|
} {1 100 {} 2 300 {}}
|
||||||
|
do_test alter3-3.3 {
|
||||||
|
get_file_format
|
||||||
|
} {3}
|
||||||
|
do_test alter3-3.4 {
|
||||||
|
execsql {
|
||||||
|
PRAGMA schema_version;
|
||||||
|
}
|
||||||
|
} {11}
|
||||||
|
|
||||||
|
do_test alter3-4.1 {
|
||||||
|
db close
|
||||||
|
file delete -force test.db
|
||||||
|
set ::DB [sqlite3 db test.db]
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE t1(a, b);
|
||||||
|
INSERT INTO t1 VALUES(1, 100);
|
||||||
|
INSERT INTO t1 VALUES(2, 300);
|
||||||
|
SELECT * FROM t1;
|
||||||
|
}
|
||||||
|
} {1 100 2 300}
|
||||||
|
do_test alter3-4.1 {
|
||||||
|
execsql {
|
||||||
|
PRAGMA schema_version = 20;
|
||||||
|
}
|
||||||
|
} {}
|
||||||
|
do_test alter3-4.2 {
|
||||||
|
execsql {
|
||||||
|
ALTER TABLE t1 ADD c DEFAULT 'hello world';
|
||||||
|
SELECT * FROM t1;
|
||||||
|
}
|
||||||
|
} {1 100 {hello world} 2 300 {hello world}}
|
||||||
|
do_test alter3-4.3 {
|
||||||
|
get_file_format
|
||||||
|
} {3}
|
||||||
|
do_test alter3-4.4 {
|
||||||
|
execsql {
|
||||||
|
PRAGMA schema_version;
|
||||||
|
}
|
||||||
|
} {21}
|
||||||
|
do_test alter3-4.99 {
|
||||||
|
execsql {
|
||||||
|
DROP TABLE t1;
|
||||||
|
}
|
||||||
|
} {}
|
||||||
|
|
||||||
|
do_test alter3-5.1 {
|
||||||
|
file delete -force test2.db
|
||||||
|
file delete -force test2.db-journal
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE t1(a, b);
|
||||||
|
INSERT INTO t1 VALUES(1, 'one');
|
||||||
|
INSERT INTO t1 VALUES(2, 'two');
|
||||||
|
ATTACH 'test2.db' AS aux;
|
||||||
|
CREATE TABLE aux.t1 AS SELECT * FROM t1;
|
||||||
|
PRAGMA aux.schema_version = 30;
|
||||||
|
SELECT sql FROM aux.sqlite_master;
|
||||||
|
}
|
||||||
|
} {{CREATE TABLE t1(a,b)}}
|
||||||
|
do_test alter3-5.2 {
|
||||||
|
execsql {
|
||||||
|
ALTER TABLE aux.t1 ADD COLUMN c VARCHAR(128);
|
||||||
|
SELECT sql FROM aux.sqlite_master;
|
||||||
|
}
|
||||||
|
} {{CREATE TABLE t1(a,b, c VARCHAR(128))}}
|
||||||
|
do_test alter3-5.3 {
|
||||||
|
execsql {
|
||||||
|
SELECT * FROM aux.t1;
|
||||||
|
}
|
||||||
|
} {1 one {} 2 two {}}
|
||||||
|
do_test alter3-5.4 {
|
||||||
|
execsql {
|
||||||
|
PRAGMA aux.schema_version;
|
||||||
|
}
|
||||||
|
} {31}
|
||||||
|
do_test alter3-5.5 {
|
||||||
|
list [get_file_format test2.db] [get_file_format]
|
||||||
|
} {2 3}
|
||||||
|
do_test alter3-5.6 {
|
||||||
|
execsql {
|
||||||
|
ALTER TABLE aux.t1 ADD COLUMN d DEFAULT 1000;
|
||||||
|
SELECT sql FROM aux.sqlite_master;
|
||||||
|
}
|
||||||
|
} {{CREATE TABLE t1(a,b, c VARCHAR(128), d DEFAULT 1000)}}
|
||||||
|
do_test alter3-5.7 {
|
||||||
|
execsql {
|
||||||
|
SELECT * FROM aux.t1;
|
||||||
|
}
|
||||||
|
} {1 one {} 1000 2 two {} 1000}
|
||||||
|
do_test alter3-5.8 {
|
||||||
|
execsql {
|
||||||
|
PRAGMA aux.schema_version;
|
||||||
|
}
|
||||||
|
} {32}
|
||||||
|
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.
|
||||||
|
#
|
||||||
|
ifcapable trigger {
|
||||||
|
do_test alter3-6.1 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE t1(a, b);
|
||||||
|
CREATE TABLE log(trig, a, b);
|
||||||
|
|
||||||
|
CREATE TRIGGER t1_a AFTER INSERT ON t1 BEGIN
|
||||||
|
INSERT INTO log VALUES('a', new.a, new.b);
|
||||||
|
END;
|
||||||
|
CREATE TEMP TRIGGER t1_b AFTER INSERT ON t1 BEGIN
|
||||||
|
INSERT INTO log VALUES('b', new.a, new.b);
|
||||||
|
END;
|
||||||
|
|
||||||
|
INSERT INTO t1 VALUES(1, 2);
|
||||||
|
SELECT * FROM log;
|
||||||
|
}
|
||||||
|
} {b 1 2 a 1 2}
|
||||||
|
do_test alter3-6.2 {
|
||||||
|
execsql {
|
||||||
|
ALTER TABLE t1 ADD COLUMN c DEFAULT 'c';
|
||||||
|
INSERT INTO t1(a, b) VALUES(3, 4);
|
||||||
|
SELECT * FROM log;
|
||||||
|
}
|
||||||
|
} {b 1 2 a 1 2 b 3 4 a 3 4}
|
||||||
|
}
|
||||||
|
|
||||||
|
ifcapable vacuum {
|
||||||
|
do_test alter3-7.1 {
|
||||||
|
execsql {
|
||||||
|
VACUUM;
|
||||||
|
}
|
||||||
|
get_file_format
|
||||||
|
} {1}
|
||||||
|
do_test alter3-7.2 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE abc(a, b, c);
|
||||||
|
ALTER TABLE abc ADD d DEFAULT NULL;
|
||||||
|
}
|
||||||
|
get_file_format
|
||||||
|
} {2}
|
||||||
|
do_test alter3-7.3 {
|
||||||
|
execsql {
|
||||||
|
ALTER TABLE abc ADD e DEFAULT 10;
|
||||||
|
}
|
||||||
|
get_file_format
|
||||||
|
} {3}
|
||||||
|
do_test alter3-7.4 {
|
||||||
|
execsql {
|
||||||
|
ALTER TABLE abc ADD f DEFAULT NULL;
|
||||||
|
}
|
||||||
|
get_file_format
|
||||||
|
} {3}
|
||||||
|
do_test alter3-7.5 {
|
||||||
|
execsql {
|
||||||
|
VACUUM;
|
||||||
|
}
|
||||||
|
get_file_format
|
||||||
|
} {1}
|
||||||
|
}
|
||||||
|
|
||||||
|
finish_test
|
||||||
|
|
33
www/lang.tcl
33
www/lang.tcl
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# Run this Tcl script to generate the lang-*.html files.
|
# Run this Tcl script to generate the lang-*.html files.
|
||||||
#
|
#
|
||||||
set rcsid {$Id: lang.tcl,v 1.84 2005/02/19 12:44:16 drh Exp $}
|
set rcsid {$Id: lang.tcl,v 1.85 2005/03/17 05:03:40 danielk1977 Exp $}
|
||||||
source common.tcl
|
source common.tcl
|
||||||
|
|
||||||
if {[llength $argv]>0} {
|
if {[llength $argv]>0} {
|
||||||
@@ -139,16 +139,23 @@ proc Section {name label} {
|
|||||||
Section {ALTER TABLE} altertable
|
Section {ALTER TABLE} altertable
|
||||||
|
|
||||||
Syntax {sql-statement} {
|
Syntax {sql-statement} {
|
||||||
ALTER TABLE [<database-name> .] <table-name> RENAME TO <new-table-name>
|
ALTER TABLE [<database-name> .] <table-name> <alteration>
|
||||||
|
} {alteration} {
|
||||||
|
RENAME TO <new-table-name>
|
||||||
|
} {alteration} {
|
||||||
|
ADD [COLUMN] <column-def>
|
||||||
}
|
}
|
||||||
|
|
||||||
puts {
|
puts {
|
||||||
<p>SQLite's version of the ALTER TABLE command allows the user to
|
<p>SQLite's version of the ALTER TABLE command allows the user to
|
||||||
rename an existing table. The table identified by
|
rename or add a new column to an existing table. It is not possible
|
||||||
<i>[database-name.]table-name</i> is renamed to
|
to remove a column from a table.
|
||||||
<i>new-table-name</i>. This command cannot be used to move a
|
</p>
|
||||||
table between attached databases, only to rename a table within
|
|
||||||
the same database.</p>
|
<p>The RENAME TO syntax is used to rename the table identified by
|
||||||
|
<i>[database-name.]table-name</i> to <i>new-table-name</i>. This command
|
||||||
|
cannot be used to move a table between attached databases, only to rename
|
||||||
|
a table within the same database.</p>
|
||||||
|
|
||||||
<p>If the table being renamed has triggers or indices, then these remain
|
<p>If the table being renamed has triggers or indices, then these remain
|
||||||
attached to the table after it has been renamed. However, if there are
|
attached to the table after it has been renamed. However, if there are
|
||||||
@@ -157,6 +164,18 @@ the table being renamed, these are not automatically modified to use the new
|
|||||||
table name. If this is required, the triggers or view definitions must be
|
table name. If this is required, the triggers or view definitions must be
|
||||||
dropped and recreated to use the new table name by hand.
|
dropped and recreated to use the new table name by hand.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>The ADD [COLUMN] syntax is used to add a new column to an existing table.
|
||||||
|
The new column is always appended to the end of the list of existing columns.
|
||||||
|
<i>Column-def</i> may take any of the forms permissable in a CREATE TABLE
|
||||||
|
statement, with the following restrictions:
|
||||||
|
<ul>
|
||||||
|
<li>The column may not have a PRIMARY KEY or UNIQUE constraint.</li>
|
||||||
|
<li>The column may not have a default value of CURRENT_TIME, CURRENT_DATE
|
||||||
|
or CURRENT_TIMESTAMP.</li>
|
||||||
|
<li>If a NOT NULL constraint is specified, then the column must have a
|
||||||
|
default value other than NULL.
|
||||||
|
</ul>
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {ATTACH DATABASE} attach
|
Section {ATTACH DATABASE} attach
|
||||||
|
Reference in New Issue
Block a user