Allow CREATE OR REPLACE VIEW to add columns to the _end_ of the view.

Robert Haas
This commit is contained in:
Bruce Momjian 2008-12-06 23:22:46 +00:00
parent 31076c8beb
commit ff1ea2173a
7 changed files with 97 additions and 49 deletions

View File

@ -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
-->
@ -37,9 +37,10 @@ CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">n
<para>
<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
a view with a new query that generates the identical set of columns
(i.e., same column names and data types).
of the same name already exists, it is replaced. The new query must
generate all of the same columns that were generated by the original query
in the same order and with the same data types, but may add additional
columns to the end of the list.
</para>
<para>

View File

@ -8,7 +8,7 @@
*
*
* 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);
pass = AT_PASS_ADD_COL;
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 */
/*
@ -2555,6 +2561,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
switch (cmd->subtype)
{
case AT_AddColumn: /* ADD COLUMN */
case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def);
break;
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
@ -3455,6 +3462,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
int i;
int minattnum,
maxatts;
char relkind;
HeapTuple typeTuple;
Oid typeOid;
int32 typmod;
@ -3527,6 +3535,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
colDef->colname, RelationGetRelationName(rel))));
minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
maxatts = minattnum + 1;
if (maxatts > MaxHeapAttributeNumber)
ereport(ERROR,
@ -3625,45 +3634,49 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
* Note: we use build_column_default, and not just the cooked default
* returned by AddRelationNewConstraints, so that the right thing happens
* 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)
{
Oid baseTypeId;
int32 baseTypeMod;
if (!defval && GetDomainConstraints(typeOid) != NIL)
{
Oid baseTypeId;
int32 baseTypeMod;
baseTypeMod = typmod;
baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod);
defval = (Expr *) coerce_to_target_type(NULL,
(Node *) defval,
baseTypeId,
typeOid,
typmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST,
-1);
if (defval == NULL) /* should not happen */
elog(ERROR, "failed to coerce base type to domain");
baseTypeMod = typmod;
baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod);
defval = (Expr *) coerce_to_target_type(NULL,
(Node *) defval,
baseTypeId,
typeOid,
typmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST,
-1);
if (defval == NULL) /* should not happen */
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.
*/

View File

@ -8,7 +8,7 @@
*
*
* 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);
/*
* 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
* verify it matches.
* verify that the old column list is an initial prefix of the new
* column list.
*/
descriptor = BuildDescForRelation(attrList);
checkViewTupleDesc(descriptor, rel->rd_att);
@ -219,13 +244,13 @@ checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
{
int i;
if (newdesc->natts != olddesc->natts)
if (newdesc->natts < olddesc->natts)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("cannot change number of columns in view")));
errmsg("cannot drop columns from view")));
/* 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 oldattr = olddesc->attrs[i];
@ -234,7 +259,7 @@ checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
if (newattr->attisdropped != oldattr->attisdropped)
ereport(ERROR,
(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)
ereport(ERROR,

View File

@ -19,7 +19,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* 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)
{
case AT_AddColumn:
case AT_AddColumnToView:
{
ColumnDef *def = (ColumnDef *) cmd->def;

View File

@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* 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
{
AT_AddColumn, /* add column */
AT_AddColumnToView, /* implicitly via CREATE OR REPLACE VIEW */
AT_ColumnDefault, /* alter column default */
AT_DropNotNull, /* alter column drop not null */
AT_SetNotNull, /* alter column set not null */

View File

@ -49,15 +49,18 @@ SELECT * FROM viewtest;
-- should fail
CREATE OR REPLACE VIEW viewtest AS
SELECT a FROM viewtest_tbl WHERE a <> 20;
ERROR: cannot change number of columns in view
ERROR: cannot drop columns from view
-- should fail
CREATE OR REPLACE VIEW viewtest AS
SELECT 1, * FROM viewtest_tbl;
ERROR: cannot change number of columns in view
ERROR: column "b" of relation "viewtest" already exists
-- should fail
CREATE OR REPLACE VIEW viewtest AS
SELECT a, b::numeric FROM viewtest_tbl;
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 TABLE viewtest_tbl;
-- tests for temporary views

View File

@ -61,6 +61,10 @@ CREATE OR REPLACE VIEW viewtest AS
CREATE OR REPLACE VIEW viewtest AS
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 TABLE viewtest_tbl;