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
doc/src/sgml/ref
src
backend
include/nodes
test/regress
@ -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>
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user