Inheritance operations must treat the OID column, if any, much like
regular user columns. But MergeAttributesIntoExisting() neglected to
do that, leading to weird results after a table with OIDs is associated
to a parent with OIDs via ALTER TABLE ... INHERIT.
Report and patch by Amit Langote, reviewed by Ashutosh Bapat, some
adjustments by me. It's been broken all along, so back-patch to
all supported branches.
Discussion: https://postgr.es/m/
cb13cfe7-a48c-5720-c383-
bb843ab28298@lab.ntt.co.jp
* execute if the user attempts to create a table with hundreds of
* thousands of columns.
*
- * Note that we also need to check that we do not exceed this figure
- * after including columns from inherited relations.
+ * Note that we also need to check that we do not exceed this figure after
+ * including columns from inherited relations.
*/
if (list_length(schema) > MaxHeapAttributeNumber)
ereport(ERROR,
}
}
+ /*
+ * If the parent has an OID column, so must the child, and we'd better
+ * update the child's attinhcount and attislocal the same as for normal
+ * columns. We needn't check data type or not-nullness though.
+ */
+ if (tupleDesc->tdhasoid)
+ {
+ /*
+ * Here we match by column number not name; the match *must* be the
+ * system column, not some random column named "oid".
+ */
+ tuple = SearchSysCacheCopy2(ATTNUM,
+ ObjectIdGetDatum(RelationGetRelid(child_rel)),
+ Int16GetDatum(ObjectIdAttributeNumber));
+ if (HeapTupleIsValid(tuple))
+ {
+ Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
+
+ /* See comments above; these changes should be the same */
+ childatt->attinhcount++;
+
+ if (child_is_partition)
+ {
+ Assert(childatt->attinhcount == 1);
+ childatt->attislocal = false;
+ }
+
+ simple_heap_update(attrrel, &tuple->t_self, tuple);
+ CatalogUpdateIndexes(attrrel, tuple);
+ heap_freetuple(tuple);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("child table is missing column \"%s\"",
+ "oid")));
+ }
+ }
+
heap_close(attrrel, RowExclusiveLock);
}
32 | one | two | three
(1 row)
+-- check that oid column is handled properly during alter table inherit
+create table oid_parent (a int) with oids;
+create table oid_child () inherits (oid_parent);
+select attinhcount, attislocal from pg_attribute
+ where attrelid = 'oid_child'::regclass and attname = 'oid';
+ attinhcount | attislocal
+-------------+------------
+ 1 | f
+(1 row)
+
+drop table oid_child;
+create table oid_child (a int) without oids;
+alter table oid_child inherit oid_parent; -- fail
+ERROR: table "oid_child" without OIDs cannot inherit from table "oid_parent" with OIDs
+alter table oid_child set with oids;
+select attinhcount, attislocal from pg_attribute
+ where attrelid = 'oid_child'::regclass and attname = 'oid';
+ attinhcount | attislocal
+-------------+------------
+ 0 | t
+(1 row)
+
+alter table oid_child inherit oid_parent;
+select attinhcount, attislocal from pg_attribute
+ where attrelid = 'oid_child'::regclass and attname = 'oid';
+ attinhcount | attislocal
+-------------+------------
+ 1 | t
+(1 row)
+
+alter table oid_child set without oids; -- fail
+ERROR: cannot drop inherited column "oid"
+alter table oid_parent set without oids;
+select attinhcount, attislocal from pg_attribute
+ where attrelid = 'oid_child'::regclass and attname = 'oid';
+ attinhcount | attislocal
+-------------+------------
+ 0 | t
+(1 row)
+
+alter table oid_child set without oids;
+select attinhcount, attislocal from pg_attribute
+ where attrelid = 'oid_child'::regclass and attname = 'oid';
+ attinhcount | attislocal
+-------------+------------
+(0 rows)
+
+drop table oid_parent cascade;
+NOTICE: drop cascades to table oid_child
-- Test non-inheritable parent constraints
create table p1(ff1 int);
alter table p1 add constraint p1chk check (ff1 > 0) no inherit;
alter table a alter column aa type integer using bit_length(aa);
select * from d;
+-- check that oid column is handled properly during alter table inherit
+create table oid_parent (a int) with oids;
+
+create table oid_child () inherits (oid_parent);
+select attinhcount, attislocal from pg_attribute
+ where attrelid = 'oid_child'::regclass and attname = 'oid';
+drop table oid_child;
+
+create table oid_child (a int) without oids;
+alter table oid_child inherit oid_parent; -- fail
+alter table oid_child set with oids;
+select attinhcount, attislocal from pg_attribute
+ where attrelid = 'oid_child'::regclass and attname = 'oid';
+alter table oid_child inherit oid_parent;
+select attinhcount, attislocal from pg_attribute
+ where attrelid = 'oid_child'::regclass and attname = 'oid';
+alter table oid_child set without oids; -- fail
+alter table oid_parent set without oids;
+select attinhcount, attislocal from pg_attribute
+ where attrelid = 'oid_child'::regclass and attname = 'oid';
+alter table oid_child set without oids;
+select attinhcount, attislocal from pg_attribute
+ where attrelid = 'oid_child'::regclass and attname = 'oid';
+
+drop table oid_parent cascade;
+
-- Test non-inheritable parent constraints
create table p1(ff1 int);
alter table p1 add constraint p1chk check (ff1 > 0) no inherit;