aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/ref/create_view.sgml9
-rw-r--r--src/backend/commands/tablecmds.c79
-rw-r--r--src/backend/commands/view.c37
-rw-r--r--src/backend/parser/parse_utilcmd.c3
-rw-r--r--src/include/nodes/parsenodes.h3
-rw-r--r--src/test/regress/expected/create_view.out7
-rw-r--r--src/test/regress/sql/create_view.sql4
7 files changed, 95 insertions, 47 deletions
diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml
index 07c8f805b7a..7cbbdbe1ced 100644
--- a/doc/src/sgml/ref/create_view.sgml
+++ b/doc/src/sgml/ref/create_view.sgml
@@ -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>
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d252632ee7e..8e12a77d11c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -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,44 +3634,48 @@ 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;
-
- 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 && 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");
+ }
- if (defval)
- {
- NewColumnValue *newval;
+ if (defval)
+ {
+ NewColumnValue *newval;
- newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
- newval->attnum = attribute.attnum;
- newval->expr = defval;
+ newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
+ newval->attnum = attribute.attnum;
+ newval->expr = defval;
- tab->newvals = lappend(tab->newvals, newval);
- }
+ 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 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.
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 5d207861dca..23a03d28a9c 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -173,8 +173,33 @@ 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,
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 4e0efe84bba..bb3a9142d6f 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -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;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 828c959ef22..47e3f37206c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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 */
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index cbee9dceed8..e9b6c83e25d 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -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
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 30a9bb1152d..f8942c93f5c 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -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;