Allow CREATE OR REPLACE VIEW to add columns to the _end_ of the view.
Robert Haas
This commit is contained in:
parent
31076c8beb
commit
ff1ea2173a
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
$PostgreSQL: pgsql/doc/src/sgml/ref/create_view.sgml,v 1.37 2008/11/14 10:22:46 petere Exp $
|
$PostgreSQL: pgsql/doc/src/sgml/ref/create_view.sgml,v 1.38 2008/12/06 23:22:46 momjian Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
@ -37,9 +37,10 @@ CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">n
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<command>CREATE OR REPLACE VIEW</command> is similar, but if a view
|
<command>CREATE OR REPLACE VIEW</command> is similar, but if a view
|
||||||
of the same name already exists, it is replaced. You can only replace
|
of the same name already exists, it is replaced. The new query must
|
||||||
a view with a new query that generates the identical set of columns
|
generate all of the same columns that were generated by the original query
|
||||||
(i.e., same column names and data types).
|
in the same order and with the same data types, but may add additional
|
||||||
|
columns to the end of the list.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.271 2008/11/19 10:34:51 heikki Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.272 2008/12/06 23:22:46 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -2334,6 +2334,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
||||||
ATPrepAddColumn(wqueue, rel, recurse, cmd);
|
ATPrepAddColumn(wqueue, rel, recurse, cmd);
|
||||||
pass = AT_PASS_ADD_COL;
|
pass = AT_PASS_ADD_COL;
|
||||||
break;
|
break;
|
||||||
|
case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
|
||||||
|
ATSimplePermissions(rel, true);
|
||||||
|
/* Performs own recursion */
|
||||||
|
ATPrepAddColumn(wqueue, rel, recurse, cmd);
|
||||||
|
pass = AT_PASS_ADD_COL;
|
||||||
|
break;
|
||||||
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
|
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2555,6 +2561,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
||||||
switch (cmd->subtype)
|
switch (cmd->subtype)
|
||||||
{
|
{
|
||||||
case AT_AddColumn: /* ADD COLUMN */
|
case AT_AddColumn: /* ADD COLUMN */
|
||||||
|
case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
|
||||||
ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def);
|
ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def);
|
||||||
break;
|
break;
|
||||||
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
|
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
|
||||||
|
@ -3455,6 +3462,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
||||||
int i;
|
int i;
|
||||||
int minattnum,
|
int minattnum,
|
||||||
maxatts;
|
maxatts;
|
||||||
|
char relkind;
|
||||||
HeapTuple typeTuple;
|
HeapTuple typeTuple;
|
||||||
Oid typeOid;
|
Oid typeOid;
|
||||||
int32 typmod;
|
int32 typmod;
|
||||||
|
@ -3527,6 +3535,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
||||||
colDef->colname, RelationGetRelationName(rel))));
|
colDef->colname, RelationGetRelationName(rel))));
|
||||||
|
|
||||||
minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
|
minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
|
||||||
|
relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
|
||||||
maxatts = minattnum + 1;
|
maxatts = minattnum + 1;
|
||||||
if (maxatts > MaxHeapAttributeNumber)
|
if (maxatts > MaxHeapAttributeNumber)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -3625,45 +3634,49 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
||||||
* Note: we use build_column_default, and not just the cooked default
|
* Note: we use build_column_default, and not just the cooked default
|
||||||
* returned by AddRelationNewConstraints, so that the right thing happens
|
* returned by AddRelationNewConstraints, so that the right thing happens
|
||||||
* when a datatype's default applies.
|
* when a datatype's default applies.
|
||||||
|
*
|
||||||
|
* We skip this logic completely for views.
|
||||||
*/
|
*/
|
||||||
defval = (Expr *) build_column_default(rel, attribute.attnum);
|
if (relkind != RELKIND_VIEW) {
|
||||||
|
defval = (Expr *) build_column_default(rel, attribute.attnum);
|
||||||
|
|
||||||
if (!defval && GetDomainConstraints(typeOid) != NIL)
|
if (!defval && GetDomainConstraints(typeOid) != NIL)
|
||||||
{
|
{
|
||||||
Oid baseTypeId;
|
Oid baseTypeId;
|
||||||
int32 baseTypeMod;
|
int32 baseTypeMod;
|
||||||
|
|
||||||
baseTypeMod = typmod;
|
baseTypeMod = typmod;
|
||||||
baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
|
baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
|
||||||
defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod);
|
defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod);
|
||||||
defval = (Expr *) coerce_to_target_type(NULL,
|
defval = (Expr *) coerce_to_target_type(NULL,
|
||||||
(Node *) defval,
|
(Node *) defval,
|
||||||
baseTypeId,
|
baseTypeId,
|
||||||
typeOid,
|
typeOid,
|
||||||
typmod,
|
typmod,
|
||||||
COERCION_ASSIGNMENT,
|
COERCION_ASSIGNMENT,
|
||||||
COERCE_IMPLICIT_CAST,
|
COERCE_IMPLICIT_CAST,
|
||||||
-1);
|
-1);
|
||||||
if (defval == NULL) /* should not happen */
|
if (defval == NULL) /* should not happen */
|
||||||
elog(ERROR, "failed to coerce base type to domain");
|
elog(ERROR, "failed to coerce base type to domain");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defval)
|
||||||
|
{
|
||||||
|
NewColumnValue *newval;
|
||||||
|
|
||||||
|
newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
|
||||||
|
newval->attnum = attribute.attnum;
|
||||||
|
newval->expr = defval;
|
||||||
|
|
||||||
|
tab->newvals = lappend(tab->newvals, newval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the new column is NOT NULL, tell Phase 3 it needs to test that.
|
||||||
|
*/
|
||||||
|
tab->new_notnull |= colDef->is_not_null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defval)
|
|
||||||
{
|
|
||||||
NewColumnValue *newval;
|
|
||||||
|
|
||||||
newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
|
|
||||||
newval->attnum = attribute.attnum;
|
|
||||||
newval->expr = defval;
|
|
||||||
|
|
||||||
tab->newvals = lappend(tab->newvals, newval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the new column is NOT NULL, tell Phase 3 it needs to test that.
|
|
||||||
*/
|
|
||||||
tab->new_notnull |= colDef->is_not_null;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add needed dependency entries for the new column.
|
* Add needed dependency entries for the new column.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.107 2008/08/25 22:42:32 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.108 2008/12/06 23:22:46 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -172,9 +172,34 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
|
||||||
*/
|
*/
|
||||||
Assert(relation->istemp == rel->rd_istemp);
|
Assert(relation->istemp == rel->rd_istemp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If new attributes have been added, we must modify the pre-existing
|
||||||
|
* view.
|
||||||
|
*/
|
||||||
|
if (list_length(attrList) > rel->rd_att->natts) {
|
||||||
|
List *atcmds = NIL;
|
||||||
|
ListCell *c;
|
||||||
|
int skip = rel->rd_att->natts;
|
||||||
|
|
||||||
|
foreach(c, attrList) {
|
||||||
|
AlterTableCmd *atcmd;
|
||||||
|
|
||||||
|
if (skip > 0) {
|
||||||
|
--skip;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
atcmd = makeNode(AlterTableCmd);
|
||||||
|
atcmd->subtype = AT_AddColumnToView;
|
||||||
|
atcmd->def = lfirst(c);
|
||||||
|
atcmds = lappend(atcmds, atcmd);
|
||||||
|
}
|
||||||
|
AlterTableInternal(viewOid, atcmds, true);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a tuple descriptor to compare against the existing view, and
|
* Create a tuple descriptor to compare against the existing view, and
|
||||||
* verify it matches.
|
* verify that the old column list is an initial prefix of the new
|
||||||
|
* column list.
|
||||||
*/
|
*/
|
||||||
descriptor = BuildDescForRelation(attrList);
|
descriptor = BuildDescForRelation(attrList);
|
||||||
checkViewTupleDesc(descriptor, rel->rd_att);
|
checkViewTupleDesc(descriptor, rel->rd_att);
|
||||||
|
@ -219,13 +244,13 @@ checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (newdesc->natts != olddesc->natts)
|
if (newdesc->natts < olddesc->natts)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
||||||
errmsg("cannot change number of columns in view")));
|
errmsg("cannot drop columns from view")));
|
||||||
/* we can ignore tdhasoid */
|
/* we can ignore tdhasoid */
|
||||||
|
|
||||||
for (i = 0; i < newdesc->natts; i++)
|
for (i = 0; i < olddesc->natts; i++)
|
||||||
{
|
{
|
||||||
Form_pg_attribute newattr = newdesc->attrs[i];
|
Form_pg_attribute newattr = newdesc->attrs[i];
|
||||||
Form_pg_attribute oldattr = olddesc->attrs[i];
|
Form_pg_attribute oldattr = olddesc->attrs[i];
|
||||||
|
@ -234,7 +259,7 @@ checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
|
||||||
if (newattr->attisdropped != oldattr->attisdropped)
|
if (newattr->attisdropped != oldattr->attisdropped)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
||||||
errmsg("cannot change number of columns in view")));
|
errmsg("cannot drop columns from view")));
|
||||||
|
|
||||||
if (strcmp(NameStr(newattr->attname), NameStr(oldattr->attname)) != 0)
|
if (strcmp(NameStr(newattr->attname), NameStr(oldattr->attname)) != 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.17 2008/09/01 20:42:45 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.18 2008/12/06 23:22:46 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1721,6 +1721,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
|
||||||
switch (cmd->subtype)
|
switch (cmd->subtype)
|
||||||
{
|
{
|
||||||
case AT_AddColumn:
|
case AT_AddColumn:
|
||||||
|
case AT_AddColumnToView:
|
||||||
{
|
{
|
||||||
ColumnDef *def = (ColumnDef *) cmd->def;
|
ColumnDef *def = (ColumnDef *) cmd->def;
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.381 2008/12/04 17:51:27 petere Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.382 2008/12/06 23:22:46 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -989,6 +989,7 @@ typedef struct AlterTableStmt
|
||||||
typedef enum AlterTableType
|
typedef enum AlterTableType
|
||||||
{
|
{
|
||||||
AT_AddColumn, /* add column */
|
AT_AddColumn, /* add column */
|
||||||
|
AT_AddColumnToView, /* implicitly via CREATE OR REPLACE VIEW */
|
||||||
AT_ColumnDefault, /* alter column default */
|
AT_ColumnDefault, /* alter column default */
|
||||||
AT_DropNotNull, /* alter column drop not null */
|
AT_DropNotNull, /* alter column drop not null */
|
||||||
AT_SetNotNull, /* alter column set not null */
|
AT_SetNotNull, /* alter column set not null */
|
||||||
|
|
|
@ -49,15 +49,18 @@ SELECT * FROM viewtest;
|
||||||
-- should fail
|
-- should fail
|
||||||
CREATE OR REPLACE VIEW viewtest AS
|
CREATE OR REPLACE VIEW viewtest AS
|
||||||
SELECT a FROM viewtest_tbl WHERE a <> 20;
|
SELECT a FROM viewtest_tbl WHERE a <> 20;
|
||||||
ERROR: cannot change number of columns in view
|
ERROR: cannot drop columns from view
|
||||||
-- should fail
|
-- should fail
|
||||||
CREATE OR REPLACE VIEW viewtest AS
|
CREATE OR REPLACE VIEW viewtest AS
|
||||||
SELECT 1, * FROM viewtest_tbl;
|
SELECT 1, * FROM viewtest_tbl;
|
||||||
ERROR: cannot change number of columns in view
|
ERROR: column "b" of relation "viewtest" already exists
|
||||||
-- should fail
|
-- should fail
|
||||||
CREATE OR REPLACE VIEW viewtest AS
|
CREATE OR REPLACE VIEW viewtest AS
|
||||||
SELECT a, b::numeric FROM viewtest_tbl;
|
SELECT a, b::numeric FROM viewtest_tbl;
|
||||||
ERROR: cannot change data type of view column "b"
|
ERROR: cannot change data type of view column "b"
|
||||||
|
-- should work
|
||||||
|
CREATE OR REPLACE VIEW viewtest AS
|
||||||
|
SELECT a, b, 0 AS c FROM viewtest_tbl;
|
||||||
DROP VIEW viewtest;
|
DROP VIEW viewtest;
|
||||||
DROP TABLE viewtest_tbl;
|
DROP TABLE viewtest_tbl;
|
||||||
-- tests for temporary views
|
-- tests for temporary views
|
||||||
|
|
|
@ -61,6 +61,10 @@ CREATE OR REPLACE VIEW viewtest AS
|
||||||
CREATE OR REPLACE VIEW viewtest AS
|
CREATE OR REPLACE VIEW viewtest AS
|
||||||
SELECT a, b::numeric FROM viewtest_tbl;
|
SELECT a, b::numeric FROM viewtest_tbl;
|
||||||
|
|
||||||
|
-- should work
|
||||||
|
CREATE OR REPLACE VIEW viewtest AS
|
||||||
|
SELECT a, b, 0 AS c FROM viewtest_tbl;
|
||||||
|
|
||||||
DROP VIEW viewtest;
|
DROP VIEW viewtest;
|
||||||
DROP TABLE viewtest_tbl;
|
DROP TABLE viewtest_tbl;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue