Code review for CREATE OR REPLACE VIEW patch. Do things in a saner order to
result in hopefully-less-confusing error messages when the new definition isn't compatible with the old; minor other cleanup.
This commit is contained in:
parent
78b25fd2e9
commit
4da65a23e7
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$PostgreSQL: pgsql/doc/src/sgml/ref/create_view.sgml,v 1.38 2008/12/06 23:22:46 momjian Exp $
|
$PostgreSQL: pgsql/doc/src/sgml/ref/create_view.sgml,v 1.39 2008/12/15 21:35:31 tgl Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -38,9 +38,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. The new query must
|
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
|
generate the same columns that were generated by the existing view query
|
||||||
in the same order and with the same data types, but may add additional
|
(that is, the same column names in the same order and with the same data
|
||||||
columns to the end of the list.
|
types), but it may add additional columns to the end of the list. The
|
||||||
|
calculations giving rise to the output columns may be completely different.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -77,7 +78,7 @@ CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">n
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><replaceable class="parameter">name</replaceable></term>
|
<term><replaceable class="parameter">name</replaceable></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
@ -164,7 +165,7 @@ CREATE VIEW comedies AS
|
|||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>Compatibility</title>
|
<title>Compatibility</title>
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.273 2008/12/13 19:13:44 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.274 2008/12/15 21:35:31 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -3459,9 +3459,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
|||||||
attrdesc;
|
attrdesc;
|
||||||
HeapTuple reltup;
|
HeapTuple reltup;
|
||||||
FormData_pg_attribute attribute;
|
FormData_pg_attribute attribute;
|
||||||
int i;
|
int newattnum;
|
||||||
int minattnum,
|
|
||||||
maxatts;
|
|
||||||
char relkind;
|
char relkind;
|
||||||
HeapTuple typeTuple;
|
HeapTuple typeTuple;
|
||||||
Oid typeOid;
|
Oid typeOid;
|
||||||
@ -3520,6 +3518,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
|||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(reltup))
|
if (!HeapTupleIsValid(reltup))
|
||||||
elog(ERROR, "cache lookup failed for relation %u", myrelid);
|
elog(ERROR, "cache lookup failed for relation %u", myrelid);
|
||||||
|
relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* this test is deliberately not attisdropped-aware, since if one tries to
|
* this test is deliberately not attisdropped-aware, since if one tries to
|
||||||
@ -3534,15 +3533,12 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
|||||||
errmsg("column \"%s\" of relation \"%s\" already exists",
|
errmsg("column \"%s\" of relation \"%s\" already exists",
|
||||||
colDef->colname, RelationGetRelationName(rel))));
|
colDef->colname, RelationGetRelationName(rel))));
|
||||||
|
|
||||||
minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
|
newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
|
||||||
relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
|
if (newattnum > MaxHeapAttributeNumber)
|
||||||
maxatts = minattnum + 1;
|
|
||||||
if (maxatts > MaxHeapAttributeNumber)
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_TOO_MANY_COLUMNS),
|
(errcode(ERRCODE_TOO_MANY_COLUMNS),
|
||||||
errmsg("tables can have at most %d columns",
|
errmsg("tables can have at most %d columns",
|
||||||
MaxHeapAttributeNumber)));
|
MaxHeapAttributeNumber)));
|
||||||
i = minattnum + 1;
|
|
||||||
|
|
||||||
typeTuple = typenameType(NULL, colDef->typename, &typmod);
|
typeTuple = typenameType(NULL, colDef->typename, &typmod);
|
||||||
tform = (Form_pg_type) GETSTRUCT(typeTuple);
|
tform = (Form_pg_type) GETSTRUCT(typeTuple);
|
||||||
@ -3551,6 +3547,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
|||||||
/* make sure datatype is legal for a column */
|
/* make sure datatype is legal for a column */
|
||||||
CheckAttributeType(colDef->colname, typeOid);
|
CheckAttributeType(colDef->colname, typeOid);
|
||||||
|
|
||||||
|
/* construct new attribute's pg_attribute entry */
|
||||||
attribute.attrelid = myrelid;
|
attribute.attrelid = myrelid;
|
||||||
namestrcpy(&(attribute.attname), colDef->colname);
|
namestrcpy(&(attribute.attname), colDef->colname);
|
||||||
attribute.atttypid = typeOid;
|
attribute.atttypid = typeOid;
|
||||||
@ -3558,7 +3555,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
|||||||
attribute.attlen = tform->typlen;
|
attribute.attlen = tform->typlen;
|
||||||
attribute.attcacheoff = -1;
|
attribute.attcacheoff = -1;
|
||||||
attribute.atttypmod = typmod;
|
attribute.atttypmod = typmod;
|
||||||
attribute.attnum = i;
|
attribute.attnum = newattnum;
|
||||||
attribute.attbyval = tform->typbyval;
|
attribute.attbyval = tform->typbyval;
|
||||||
attribute.attndims = list_length(colDef->typename->arrayBounds);
|
attribute.attndims = list_length(colDef->typename->arrayBounds);
|
||||||
attribute.attstorage = tform->typstorage;
|
attribute.attstorage = tform->typstorage;
|
||||||
@ -3578,7 +3575,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
|||||||
/*
|
/*
|
||||||
* Update number of attributes in pg_class tuple
|
* Update number of attributes in pg_class tuple
|
||||||
*/
|
*/
|
||||||
((Form_pg_class) GETSTRUCT(reltup))->relnatts = maxatts;
|
((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
|
||||||
|
|
||||||
simple_heap_update(pgclass, &reltup->t_self, reltup);
|
simple_heap_update(pgclass, &reltup->t_self, reltup);
|
||||||
|
|
||||||
@ -3635,9 +3632,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
|||||||
* 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.
|
* We skip this step completely for views. For a view, we can only get
|
||||||
|
* here from CREATE OR REPLACE VIEW, which historically doesn't set up
|
||||||
|
* defaults, not even for domain-typed columns. And in any case we mustn't
|
||||||
|
* invoke Phase 3 on a view, since it has no storage.
|
||||||
*/
|
*/
|
||||||
if (relkind != RELKIND_VIEW) {
|
if (relkind != RELKIND_VIEW)
|
||||||
|
{
|
||||||
defval = (Expr *) build_column_default(rel, attribute.attnum);
|
defval = (Expr *) build_column_default(rel, attribute.attnum);
|
||||||
|
|
||||||
if (!defval && GetDomainConstraints(typeOid) != NIL)
|
if (!defval && GetDomainConstraints(typeOid) != NIL)
|
||||||
@ -3680,7 +3681,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
|||||||
/*
|
/*
|
||||||
* Add needed dependency entries for the new column.
|
* Add needed dependency entries for the new column.
|
||||||
*/
|
*/
|
||||||
add_column_datatype_dependency(myrelid, i, attribute.atttypid);
|
add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.108 2008/12/06 23:22:46 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.109 2008/12/15 21:35:31 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -165,6 +165,9 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
|
|||||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
||||||
RelationGetRelationName(rel));
|
RelationGetRelationName(rel));
|
||||||
|
|
||||||
|
/* Also check it's not in use already */
|
||||||
|
CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Due to the namespace visibility rules for temporary objects, we
|
* Due to the namespace visibility rules for temporary objects, we
|
||||||
* should only end up replacing a temporary view with another
|
* should only end up replacing a temporary view with another
|
||||||
@ -172,30 +175,6 @@ 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 that the old column list is an initial prefix of the new
|
* verify that the old column list is an initial prefix of the new
|
||||||
@ -204,6 +183,34 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
|
|||||||
descriptor = BuildDescForRelation(attrList);
|
descriptor = BuildDescForRelation(attrList);
|
||||||
checkViewTupleDesc(descriptor, rel->rd_att);
|
checkViewTupleDesc(descriptor, rel->rd_att);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If new attributes have been added, we must add pg_attribute entries
|
||||||
|
* for them. It is convenient (although overkill) to use the ALTER
|
||||||
|
* TABLE ADD COLUMN infrastructure for this.
|
||||||
|
*/
|
||||||
|
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 = (Node *) lfirst(c);
|
||||||
|
atcmds = lappend(atcmds, atcmd);
|
||||||
|
}
|
||||||
|
AlterTableInternal(viewOid, atcmds, true);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Seems okay, so return the OID of the pre-existing view.
|
* Seems okay, so return the OID of the pre-existing view.
|
||||||
*/
|
*/
|
||||||
@ -238,6 +245,7 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
|
|||||||
* Verify that tupledesc associated with proposed new view definition
|
* Verify that tupledesc associated with proposed new view definition
|
||||||
* matches tupledesc of old view. This is basically a cut-down version
|
* matches tupledesc of old view. This is basically a cut-down version
|
||||||
* of equalTupleDescs(), with code added to generate specific complaints.
|
* of equalTupleDescs(), with code added to generate specific complaints.
|
||||||
|
* Also, we allow the new tupledesc to have more columns than the old.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
|
checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
|
||||||
|
@ -53,7 +53,7 @@ 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: column "b" of relation "viewtest" already exists
|
ERROR: cannot change name of view column "a"
|
||||||
-- 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;
|
||||||
|
Loading…
Reference in New Issue
Block a user