When converting a table to a view, remove its system columns.
Views should not have any pg_attribute entries for system columns. However, we forgot to remove such entries when converting a table to a view. This could lead to crashes later on, if someone attempted to reference such a column, as reported by Kohei KaiGai. Patch in HEAD only. This bug has been there forever, but in the back branches we will have to defend against existing mis-converted views, so it doesn't seem worthwhile to change the conversion code too.
This commit is contained in:
parent
f4c4335a4a
commit
a4e8680a6c
@ -1434,6 +1434,47 @@ DeleteAttributeTuples(Oid relid)
|
||||
heap_close(attrel, RowExclusiveLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* DeleteSystemAttributeTuples
|
||||
*
|
||||
* Remove pg_attribute rows for system columns of the given relid.
|
||||
*
|
||||
* Note: this is only used when converting a table to a view. Views don't
|
||||
* have system columns, so we should remove them from pg_attribute.
|
||||
*/
|
||||
void
|
||||
DeleteSystemAttributeTuples(Oid relid)
|
||||
{
|
||||
Relation attrel;
|
||||
SysScanDesc scan;
|
||||
ScanKeyData key[2];
|
||||
HeapTuple atttup;
|
||||
|
||||
/* Grab an appropriate lock on the pg_attribute relation */
|
||||
attrel = heap_open(AttributeRelationId, RowExclusiveLock);
|
||||
|
||||
/* Use the index to scan only system attributes of the target relation */
|
||||
ScanKeyInit(&key[0],
|
||||
Anum_pg_attribute_attrelid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(relid));
|
||||
ScanKeyInit(&key[1],
|
||||
Anum_pg_attribute_attnum,
|
||||
BTLessEqualStrategyNumber, F_INT2LE,
|
||||
Int16GetDatum(0));
|
||||
|
||||
scan = systable_beginscan(attrel, AttributeRelidNumIndexId, true,
|
||||
SnapshotNow, 2, key);
|
||||
|
||||
/* Delete all the matching tuples */
|
||||
while ((atttup = systable_getnext(scan)) != NULL)
|
||||
simple_heap_delete(attrel, &atttup->t_self);
|
||||
|
||||
/* Clean up after the scan */
|
||||
systable_endscan(scan);
|
||||
heap_close(attrel, RowExclusiveLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* RemoveAttributeById
|
||||
*
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "access/htup_details.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/indexing.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/objectaccess.h"
|
||||
@ -510,13 +511,19 @@ DefineQueryRewrite(char *rulename,
|
||||
}
|
||||
|
||||
/*
|
||||
* IF the relation is becoming a view, delete the storage files associated
|
||||
* with it. NB: we had better have AccessExclusiveLock to do this ...
|
||||
* If the relation is becoming a view, delete the storage files associated
|
||||
* with it. Also, get rid of any system attribute entries in pg_attribute,
|
||||
* because a view shouldn't have any of those.
|
||||
*
|
||||
* NB: we had better have AccessExclusiveLock to do this ...
|
||||
*
|
||||
* XXX what about getting rid of its TOAST table? For now, we don't.
|
||||
*/
|
||||
if (RelisBecomingView)
|
||||
{
|
||||
RelationDropStorage(event_relation);
|
||||
DeleteSystemAttributeTuples(event_relid);
|
||||
}
|
||||
|
||||
/* Close rel, but keep lock till commit... */
|
||||
heap_close(event_relation, NoLock);
|
||||
|
@ -107,6 +107,7 @@ extern Node *cookDefault(ParseState *pstate,
|
||||
|
||||
extern void DeleteRelationTuple(Oid relid);
|
||||
extern void DeleteAttributeTuples(Oid relid);
|
||||
extern void DeleteSystemAttributeTuples(Oid relid);
|
||||
extern void RemoveAttributeById(Oid relid, AttrNumber attnum);
|
||||
extern void RemoveAttrDefault(Oid relid, AttrNumber attnum,
|
||||
DropBehavior behavior, bool complain, bool internal);
|
||||
|
@ -1457,6 +1457,28 @@ ERROR: cannot drop rule _RETURN on view fooview because view fooview requires i
|
||||
HINT: You can drop view fooview instead.
|
||||
drop view fooview;
|
||||
--
|
||||
-- test conversion of table to view (needed to load some pg_dump files)
|
||||
--
|
||||
create table fooview (x int, y text);
|
||||
select xmin, * from fooview;
|
||||
xmin | x | y
|
||||
------+---+---
|
||||
(0 rows)
|
||||
|
||||
create rule "_RETURN" as on select to fooview do instead
|
||||
select 1 as x, 'aaa'::text as y;
|
||||
select * from fooview;
|
||||
x | y
|
||||
---+-----
|
||||
1 | aaa
|
||||
(1 row)
|
||||
|
||||
select xmin, * from fooview; -- fail, views don't have such a column
|
||||
ERROR: column "xmin" does not exist
|
||||
LINE 1: select xmin, * from fooview;
|
||||
^
|
||||
drop view fooview;
|
||||
--
|
||||
-- check for planner problems with complex inherited UPDATES
|
||||
--
|
||||
create table id (id serial primary key, name text);
|
||||
|
@ -859,6 +859,21 @@ create view fooview as select 'foo'::text;
|
||||
drop rule "_RETURN" on fooview;
|
||||
drop view fooview;
|
||||
|
||||
--
|
||||
-- test conversion of table to view (needed to load some pg_dump files)
|
||||
--
|
||||
|
||||
create table fooview (x int, y text);
|
||||
select xmin, * from fooview;
|
||||
|
||||
create rule "_RETURN" as on select to fooview do instead
|
||||
select 1 as x, 'aaa'::text as y;
|
||||
|
||||
select * from fooview;
|
||||
select xmin, * from fooview; -- fail, views don't have such a column
|
||||
|
||||
drop view fooview;
|
||||
|
||||
--
|
||||
-- check for planner problems with complex inherited UPDATES
|
||||
--
|
||||
|
Loading…
x
Reference in New Issue
Block a user