hiding variable-length fields from Form_pg_* structs

Started by Peter Eisentrautabout 14 years ago11 messages
#1Peter Eisentraut
peter_e@gmx.net

It would be helpful if variable length catalog fields (except the first
one) would not be visible on the C level in the Form_pg_* structs. We
keep them listed in the include/catalog/pg_*.h files so that the BKI
generating code can see them and for general documentation, but the
fields are meaningless in C, and some catalog files contain free-form
comments advising the reader of that. If we could hide them somehow, we
would avoid random programming bugs, deconfuse compilers trying to
generate useful warnings, and save occasional stack space. There are
several known cases of the first and second issue, at least.

I haven't found the ideal way to implement that yet, but the general
idea would be something like:

CATALOG(pg_attribute,1249) BKI_BOOTSTRAP ...
{
...
int4 attinhcount;
Oid attcollation;
aclitem attacl[1];
CATVARLEN(
text attoptions[1];
text attfdwoptions[1];
)
} FormData_pg_attribute;

where CATVARLEN is defined empty in C, and ignored in the BKI generation
code.

The real trick is to find something that handles well with pgindent and
indenting text editors.

Ideas?

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#1)
Re: hiding variable-length fields from Form_pg_* structs

Peter Eisentraut <peter_e@gmx.net> writes:

CATALOG(pg_attribute,1249) BKI_BOOTSTRAP ...
{
...
int4 attinhcount;
Oid attcollation;
aclitem attacl[1];
CATVARLEN(
text attoptions[1];
text attfdwoptions[1];
)
} FormData_pg_attribute;

where CATVARLEN is defined empty in C, and ignored in the BKI generation
code.

The real trick is to find something that handles well with pgindent and
indenting text editors.

The low-tech way would be

CATALOG(pg_attribute,1249) BKI_BOOTSTRAP ...
{
...
int4 attinhcount;
Oid attcollation;
aclitem attacl[1];
#ifdef CATALOG_VARLEN_FIELDS
text attoptions[1];
text attfdwoptions[1];
#endif
} FormData_pg_attribute;

regards, tom lane

#3Peter Eisentraut
peter_e@gmx.net
In reply to: Tom Lane (#2)
Re: hiding variable-length fields from Form_pg_* structs

On sön, 2011-11-27 at 18:20 -0500, Tom Lane wrote:

The low-tech way would be

CATALOG(pg_attribute,1249) BKI_BOOTSTRAP ...
{
...
int4 attinhcount;
Oid attcollation;
aclitem attacl[1];
#ifdef CATALOG_VARLEN_FIELDS
text attoptions[1];
text attfdwoptions[1];
#endif
} FormData_pg_attribute;

Good enough.

To clarify, I believe the rule is that the first variable-length field
can be accessed as a struct field. Are there any exceptions to this?
This kind of comment is pretty confusing:

CATALOG(pg_rewrite,2618)
{
NameData rulename;
Oid ev_class;
int2 ev_attr;
char ev_type;
char ev_enabled;
bool is_instead;

/* NB: remaining fields must be accessed via heap_getattr */
pg_node_tree ev_qual;
pg_node_tree ev_action;
} FormData_pg_rewrite;

Also, this code in relcache.c accesses indclass, which is after an
int2vector and an oidvector field:

/* Check to see if it is a unique, non-partial btree index on OID */
if (index->indnatts == 1 &&
index->indisunique && index->indimmediate &&
index->indkey.values[0] == ObjectIdAttributeNumber &&
index->indclass.values[0] == OID_BTREE_OPS_OID &&
heap_attisnull(htup, Anum_pg_index_indpred))
oidIndex = index->indexrelid;

#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#3)
Re: hiding variable-length fields from Form_pg_* structs

Peter Eisentraut <peter_e@gmx.net> writes:

To clarify, I believe the rule is that the first variable-length field
can be accessed as a struct field. Are there any exceptions to this?

If it is known not null, yes, but I wonder just how many places actually
depend on that. It might be better to remove all varlena fields from C
visibility and require use of the accessor functions. We should at
least look into what that would cost us.

Also, this code in relcache.c accesses indclass, which is after an
int2vector and an oidvector field:

/* Check to see if it is a unique, non-partial btree index on OID */
if (index->indnatts == 1 &&
index->indisunique && index->indimmediate &&
index->indkey.values[0] == ObjectIdAttributeNumber &&
index->indclass.values[0] == OID_BTREE_OPS_OID &&
heap_attisnull(htup, Anum_pg_index_indpred))
oidIndex = index->indexrelid;

Hmm, that does look mighty broken, doesn't it ... but somehow it works,
else GetNewOid would be bleating all the time. (Thinks about that for
a bit) Oh, it accidentally fails to fail because the C declarations
for int2vector and oidvector are actually correct if there is a single
element in the arrays, which we already verified with the indnatts test.
But yeah, this seems horribly fragile, and it should not be performance
critical because we only go through here when loading up a cache entry.
So let's change it.

regards, tom lane

#5Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#4)
Re: hiding variable-length fields from Form_pg_* structs

On Mon, Dec 5, 2011 at 2:47 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Peter Eisentraut <peter_e@gmx.net> writes:

To clarify, I believe the rule is that the first variable-length field
can be accessed as a struct field.  Are there any exceptions to this?

If it is known not null, yes, but I wonder just how many places actually
depend on that.  It might be better to remove all varlena fields from C
visibility and require use of the accessor functions.  We should at
least look into what that would cost us.

My impression is that all the varlena fields also allow nulls. So I
think there's no point in trying to keep the first one C-accessible.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#5)
Re: hiding variable-length fields from Form_pg_* structs

Robert Haas <robertmhaas@gmail.com> writes:

On Mon, Dec 5, 2011 at 2:47 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Peter Eisentraut <peter_e@gmx.net> writes:

To clarify, I believe the rule is that the first variable-length field
can be accessed as a struct field. Are there any exceptions to this?

If it is known not null, yes, but I wonder just how many places actually
depend on that.

My impression is that all the varlena fields also allow nulls.

See MARKNOTNULL in bootstrap.c. I think the exceptions were designed to
protect direct accesses to pg_index.

regards, tom lane

#7Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#6)
Re: hiding variable-length fields from Form_pg_* structs

On Mon, Dec 5, 2011 at 3:12 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

On Mon, Dec 5, 2011 at 2:47 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Peter Eisentraut <peter_e@gmx.net> writes:

To clarify, I believe the rule is that the first variable-length field
can be accessed as a struct field.  Are there any exceptions to this?

If it is known not null, yes, but I wonder just how many places actually
depend on that.

My impression is that all the varlena fields also allow nulls.

See MARKNOTNULL in bootstrap.c.  I think the exceptions were designed to
protect direct accesses to pg_index.

Hmm, OK.

rhaas=# select r.relname, a.attname, a.atttypid::regtype from pg_class
r, pg_attribute a where relnamespace=11 and relkind='r' and attrelid =
r.oid and a.attnotnull and a.attlen<0;
relname | attname | atttypid
------------+--------------+------------
pg_proc | proargtypes | oidvector
pg_index | indkey | int2vector
pg_index | indcollation | oidvector
pg_index | indclass | oidvector
pg_index | indoption | int2vector
pg_trigger | tgattr | int2vector
(6 rows)

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#8Peter Eisentraut
peter_e@gmx.net
In reply to: Robert Haas (#7)
1 attachment(s)
Re: hiding variable-length fields from Form_pg_* structs

So here is a patch for that.

There are a few cases that break when hiding all variable length fields:

Access to indclass in relcache.c, as discussed upthread, which should be
fixed.

Access to pg_largeobject.data. This is apparently OK, per comment in
inv_api.c.

Access to pg_proc.proargtypes in various places. This is clearly
useful, so we'll keep it visible.

So I think the relcache.c thing should be fixed and then this might be
good to go.

Attachments:

catalog-varlen.patchtext/x-patch; charset=UTF-8; name=catalog-varlen.patchDownload
diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm
index 1ba935c..1b8db1b 100644
--- a/src/backend/catalog/Catalog.pm
+++ b/src/backend/catalog/Catalog.pm
@@ -143,6 +143,7 @@ sub Catalogs
             elsif ($declaring_attributes)
             {
                 next if (/^{|^$/);
+                next if (/^#/);
                 if (/^}/)
                 {
                     undef $declaring_attributes;
@@ -150,6 +151,7 @@ sub Catalogs
                 else
                 {
                     my ($atttype, $attname) = split /\s+/, $_;
+                    die unless $attname;
                     if (exists $RENAME_ATTTYPE{$atttype})
                     {
                         $atttype = $RENAME_ATTTYPE{$atttype};
diff --git a/src/include/catalog/genbki.h b/src/include/catalog/genbki.h
index 5fe3a5b..bcf31e6 100644
--- a/src/include/catalog/genbki.h
+++ b/src/include/catalog/genbki.h
@@ -22,6 +22,16 @@
 /* Introduces a catalog's structure definition */
 #define CATALOG(name,oid)	typedef struct CppConcat(FormData_,name)
 
+/*
+ * This is never defined; it's here only for documentation.
+ *
+ * Variable-length catalog fields (except possibly the first not nullable one)
+ * should not be visible in C structures, so they are made invisible by #ifdefs
+ * of an undefined symbol.  See also MARKNOTNULL in bootstrap.c for how this is
+ * handled.
+ */
+#undef CATALOG_VARLEN
+
 /* Options that may appear after CATALOG (on the same line) */
 #define BKI_BOOTSTRAP
 #define BKI_SHARED_RELATION
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 4e10021..c420bdb 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -44,7 +44,9 @@ CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS
 	regproc		aggfinalfn;
 	Oid			aggsortop;
 	Oid			aggtranstype;
+#ifdef CATALOG_VARLEN
 	text		agginitval;		/* VARIABLE LENGTH FIELD */
+#endif
 } FormData_pg_aggregate;
 
 /* ----------------
diff --git a/src/include/catalog/pg_attrdef.h b/src/include/catalog/pg_attrdef.h
index 01f663c..ee205ec 100644
--- a/src/include/catalog/pg_attrdef.h
+++ b/src/include/catalog/pg_attrdef.h
@@ -32,8 +32,10 @@ CATALOG(pg_attrdef,2604)
 {
 	Oid			adrelid;		/* OID of table containing attribute */
 	int2		adnum;			/* attnum of attribute */
+#ifdef CATALOG_VARLEN
 	pg_node_tree adbin;			/* nodeToString representation of default */
 	text		adsrc;			/* human-readable representation of default */
+#endif
 } FormData_pg_attrdef;
 
 /* ----------------
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 5cb16f6..7d6d0ac 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -151,6 +151,7 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
 	 * NOTE: the following fields are not present in tuple descriptors!
 	 */
 
+#ifdef CATALOG_VARLEN
 	/* Column-level access permissions */
 	aclitem		attacl[1];
 
@@ -159,6 +160,7 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
 
 	/* Column-level FDW options */
 	text		attfdwoptions[1];
+#endif
 } FormData_pg_attribute;
 
 /*
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 829f9b9..97a51d3 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -74,8 +74,10 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
 	 * NOTE: these fields are not present in a relcache entry's rd_rel field.
 	 */
 
+#ifdef CATALOG_VARLEN
 	aclitem		relacl[1];		/* access permissions */
 	text		reloptions[1];	/* access-method-specific options */
+#endif
 } FormData_pg_class;
 
 /* Size of fixed part of pg_class tuples, not counting var-length fields */
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index c962834..91bcc54 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -130,6 +130,7 @@ CATALOG(pg_constraint,2606)
 	 */
 	Oid			conexclop[1];
 
+#ifdef CATALOG_VARLEN
 	/*
 	 * If a check constraint, nodeToString representation of expression
 	 */
@@ -139,6 +140,7 @@ CATALOG(pg_constraint,2606)
 	 * If a check constraint, source-text representation of expression
 	 */
 	text		consrc;
+#endif
 } FormData_pg_constraint;
 
 /* ----------------
diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h
index 57537c8..3875d3f 100644
--- a/src/include/catalog/pg_database.h
+++ b/src/include/catalog/pg_database.h
@@ -42,7 +42,9 @@ CATALOG(pg_database,1262) BKI_SHARED_RELATION BKI_ROWTYPE_OID(1248) BKI_SCHEMA_M
 	Oid			datlastsysoid;	/* highest OID to consider a system OID */
 	TransactionId datfrozenxid; /* all Xids < this are frozen in this DB */
 	Oid			dattablespace;	/* default table space for this DB */
+#ifdef CATALOG_VARLEN
 	aclitem		datacl[1];		/* access permissions (VAR LENGTH) */
+#endif
 } FormData_pg_database;
 
 /* ----------------
diff --git a/src/include/catalog/pg_db_role_setting.h b/src/include/catalog/pg_db_role_setting.h
index 1924043..573f83a 100644
--- a/src/include/catalog/pg_db_role_setting.h
+++ b/src/include/catalog/pg_db_role_setting.h
@@ -35,7 +35,9 @@ CATALOG(pg_db_role_setting,2964) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
 {
 	Oid			setdatabase;	/* database */
 	Oid			setrole;		/* role */
+#ifdef CATALOG_VARLEN
 	text		setconfig[1];	/* GUC settings to apply at login */
+#endif
 } FormData_pg_db_role_setting;
 
 typedef FormData_pg_db_role_setting *Form_pg_db_role_setting;
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index 9fd207d..63d9ed8 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -37,7 +37,9 @@ CATALOG(pg_default_acl,826)
 	 * VARIABLE LENGTH FIELDS start here.
 	 */
 
+#ifdef CATALOG_VARLEN
 	aclitem		defaclacl[1];	/* permissions to add at CREATE time */
+#endif
 } FormData_pg_default_acl;
 
 /* ----------------
diff --git a/src/include/catalog/pg_description.h b/src/include/catalog/pg_description.h
index 0e06500..6947608 100644
--- a/src/include/catalog/pg_description.h
+++ b/src/include/catalog/pg_description.h
@@ -50,7 +50,9 @@ CATALOG(pg_description,2609) BKI_WITHOUT_OIDS
 	Oid			objoid;			/* OID of object itself */
 	Oid			classoid;		/* OID of table containing object */
 	int4		objsubid;		/* column number, or 0 if not used */
+#ifdef CATALOG_VARLEN
 	text		description;	/* description of object */
+#endif
 } FormData_pg_description;
 
 /* ----------------
diff --git a/src/include/catalog/pg_extension.h b/src/include/catalog/pg_extension.h
index 606ed9a..9f25f9c 100644
--- a/src/include/catalog/pg_extension.h
+++ b/src/include/catalog/pg_extension.h
@@ -40,9 +40,11 @@ CATALOG(pg_extension,3079)
 	 *
 	 * extversion should never be null, but the others can be.
 	 */
+#ifdef CATALOG_VARLEN
 	text		extversion;		/* extension version name */
 	Oid			extconfig[1];	/* dumpable configuration tables */
 	text		extcondition[1];	/* WHERE clauses for config tables */
+#endif
 } FormData_pg_extension;
 
 /* ----------------
diff --git a/src/include/catalog/pg_foreign_data_wrapper.h b/src/include/catalog/pg_foreign_data_wrapper.h
index 90a51a2..1dfc1b5 100644
--- a/src/include/catalog/pg_foreign_data_wrapper.h
+++ b/src/include/catalog/pg_foreign_data_wrapper.h
@@ -37,8 +37,10 @@ CATALOG(pg_foreign_data_wrapper,2328)
 
 	/* VARIABLE LENGTH FIELDS start here. */
 
+#ifdef CATALOG_VARLEN
 	aclitem		fdwacl[1];		/* access permissions */
 	text		fdwoptions[1];	/* FDW options */
+#endif
 } FormData_pg_foreign_data_wrapper;
 
 /* ----------------
diff --git a/src/include/catalog/pg_foreign_server.h b/src/include/catalog/pg_foreign_server.h
index a96328c..7a51eee 100644
--- a/src/include/catalog/pg_foreign_server.h
+++ b/src/include/catalog/pg_foreign_server.h
@@ -35,10 +35,12 @@ CATALOG(pg_foreign_server,1417)
 	/*
 	 * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
 	 */
+#ifdef CATALOG_VARLEN
 	text		srvtype;
 	text		srvversion;
 	aclitem		srvacl[1];		/* access permissions */
 	text		srvoptions[1];	/* FDW-specific options */
+#endif
 } FormData_pg_foreign_server;
 
 /* ----------------
diff --git a/src/include/catalog/pg_foreign_table.h b/src/include/catalog/pg_foreign_table.h
index 71992d3..7c85b93 100644
--- a/src/include/catalog/pg_foreign_table.h
+++ b/src/include/catalog/pg_foreign_table.h
@@ -30,7 +30,9 @@ CATALOG(pg_foreign_table,3118) BKI_WITHOUT_OIDS
 {
 	Oid			ftrelid;		/* OID of foreign table */
 	Oid			ftserver;		/* OID of foreign server */
+#ifdef CATALOG_VARLEN
 	text		ftoptions[1];	/* FDW-specific options */
+#endif
 } FormData_pg_foreign_table;
 
 /* ----------------
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index 6c301ff..0546f59 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -47,11 +47,14 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO
 	oidvector	indcollation;	/* collation identifiers */
 	oidvector	indclass;		/* opclass identifiers */
 	int2vector	indoption;		/* per-column flags (AM-specific meanings) */
+/*FIXME*/
+#ifdef CATALOG_VARLEN
 	pg_node_tree indexprs;		/* expression trees for index attributes that
 								 * are not simple column references; one for
 								 * each zero entry in indkey[] */
 	pg_node_tree indpred;		/* expression tree for predicate, if a partial
 								 * index; else NULL */
+#endif
 } FormData_pg_index;
 
 /* ----------------
diff --git a/src/include/catalog/pg_language.h b/src/include/catalog/pg_language.h
index fd8ea0b..8bd819d 100644
--- a/src/include/catalog/pg_language.h
+++ b/src/include/catalog/pg_language.h
@@ -37,7 +37,9 @@ CATALOG(pg_language,2612)
 	Oid			lanplcallfoid;	/* Call handler for PL */
 	Oid			laninline;		/* Optional anonymous-block handler function */
 	Oid			lanvalidator;	/* Optional validation function */
+#ifdef CATALOG_VARLEN
 	aclitem		lanacl[1];		/* Access privileges */
+#endif
 } FormData_pg_language;
 
 /* ----------------
diff --git a/src/include/catalog/pg_largeobject.h b/src/include/catalog/pg_largeobject.h
index 7c53e1e..0f4965c 100644
--- a/src/include/catalog/pg_largeobject.h
+++ b/src/include/catalog/pg_largeobject.h
@@ -32,7 +32,9 @@ CATALOG(pg_largeobject,2613) BKI_WITHOUT_OIDS
 {
 	Oid			loid;			/* Identifier of large object */
 	int4		pageno;			/* Page number (starting from 0) */
+/*FIXME: #ifdef CATALOG_VARLEN*/
 	bytea		data;			/* Data for page (may be zero-length) */
+/*#endif*/
 } FormData_pg_largeobject;
 
 /* ----------------
diff --git a/src/include/catalog/pg_largeobject_metadata.h b/src/include/catalog/pg_largeobject_metadata.h
index dea4d12..7f235ee 100644
--- a/src/include/catalog/pg_largeobject_metadata.h
+++ b/src/include/catalog/pg_largeobject_metadata.h
@@ -31,7 +31,9 @@
 CATALOG(pg_largeobject_metadata,2995)
 {
 	Oid			lomowner;		/* OID of the largeobject owner */
+#ifdef CATALOG_VARLEN
 	aclitem		lomacl[1];		/* access permissions */
+#endif
 } FormData_pg_largeobject_metadata;
 
 /* ----------------
diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h
index 52a97f1..07e1b5e 100644
--- a/src/include/catalog/pg_namespace.h
+++ b/src/include/catalog/pg_namespace.h
@@ -37,7 +37,9 @@ CATALOG(pg_namespace,2615)
 {
 	NameData	nspname;
 	Oid			nspowner;
+#ifdef CATALOG_VARLEN
 	aclitem		nspacl[1];		/* VARIABLE LENGTH FIELD */
+#endif
 } FormData_pg_namespace;
 
 /* ----------------
diff --git a/src/include/catalog/pg_pltemplate.h b/src/include/catalog/pg_pltemplate.h
index ce249c0..29f356f 100644
--- a/src/include/catalog/pg_pltemplate.h
+++ b/src/include/catalog/pg_pltemplate.h
@@ -33,11 +33,13 @@ CATALOG(pg_pltemplate,1136) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
 	NameData	tmplname;		/* name of PL */
 	bool		tmpltrusted;	/* PL is trusted? */
 	bool		tmpldbacreate;	/* PL is installable by db owner? */
+#ifdef CATALOG_VARLEN
 	text		tmplhandler;	/* name of call handler function */
 	text		tmplinline;		/* name of anonymous-block handler, or NULL */
 	text		tmplvalidator;	/* name of validator function, or NULL */
 	text		tmpllibrary;	/* path of shared library */
 	aclitem		tmplacl[1];		/* access privileges for template */
+#endif
 } FormData_pg_pltemplate;
 
 /* ----------------
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 355c61a..0a18685 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -55,6 +55,7 @@ CATALOG(pg_proc,1255) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81) BKI_SCHEMA_MACRO
 
 	/* VARIABLE LENGTH FIELDS: */
 	oidvector	proargtypes;	/* parameter types (excludes OUT params) */
+#ifdef CATALOG_VARLEN
 	Oid			proallargtypes[1];		/* all param types (NULL if IN only) */
 	char		proargmodes[1]; /* parameter modes (NULL if IN only) */
 	text		proargnames[1]; /* parameter names (NULL if no names) */
@@ -64,6 +65,7 @@ CATALOG(pg_proc,1255) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81) BKI_SCHEMA_MACRO
 	text		probin;			/* secondary procedure info (can be NULL) */
 	text		proconfig[1];	/* procedure-local GUC settings */
 	aclitem		proacl[1];		/* access permissions */
+#endif
 } FormData_pg_proc;
 
 /* ----------------
diff --git a/src/include/catalog/pg_rewrite.h b/src/include/catalog/pg_rewrite.h
index eb16b2d..997e0c3 100644
--- a/src/include/catalog/pg_rewrite.h
+++ b/src/include/catalog/pg_rewrite.h
@@ -41,8 +41,10 @@ CATALOG(pg_rewrite,2618)
 	bool		is_instead;
 
 	/* NB: remaining fields must be accessed via heap_getattr */
+#ifdef CATALOG_VARLEN
 	pg_node_tree ev_qual;
 	pg_node_tree ev_action;
+#endif
 } FormData_pg_rewrite;
 
 /* ----------------
diff --git a/src/include/catalog/pg_seclabel.h b/src/include/catalog/pg_seclabel.h
index b468d69..fa9b1e1 100644
--- a/src/include/catalog/pg_seclabel.h
+++ b/src/include/catalog/pg_seclabel.h
@@ -25,8 +25,10 @@ CATALOG(pg_seclabel,3596) BKI_WITHOUT_OIDS
 	Oid			objoid;			/* OID of the object itself */
 	Oid			classoid;		/* OID of table containing the object */
 	int4		objsubid;		/* column number, or 0 if not used */
+#ifdef CATALOG_VARLEN
 	text		provider;		/* name of label provider */
 	text		label;			/* security label of the object */
+#endif
 } FormData_pg_seclabel;
 
 /* ----------------
diff --git a/src/include/catalog/pg_shdescription.h b/src/include/catalog/pg_shdescription.h
index 6d0ee3b..c0f12e6 100644
--- a/src/include/catalog/pg_shdescription.h
+++ b/src/include/catalog/pg_shdescription.h
@@ -42,7 +42,9 @@ CATALOG(pg_shdescription,2396) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
 {
 	Oid			objoid;			/* OID of object itself */
 	Oid			classoid;		/* OID of table containing object */
+#ifdef CATALOG_VARLEN
 	text		description;	/* description of object */
+#endif
 } FormData_pg_shdescription;
 
 /* ----------------
diff --git a/src/include/catalog/pg_shseclabel.h b/src/include/catalog/pg_shseclabel.h
index 235564f..0f10faf 100644
--- a/src/include/catalog/pg_shseclabel.h
+++ b/src/include/catalog/pg_shseclabel.h
@@ -24,8 +24,10 @@ CATALOG(pg_shseclabel,3592) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
 {
 	Oid			objoid;		/* OID of the shared object itself */
 	Oid			classoid;	/* OID of table containing the shared object */
+#ifdef CATALOG_VARLEN
 	text		provider;	/* name of label provider */
 	text		label;		/* security label of the object */
+#endif
 } FormData_pg_shseclabel;
 
 /* ----------------
diff --git a/src/include/catalog/pg_statistic.h b/src/include/catalog/pg_statistic.h
index 7d1d127..4625eab 100644
--- a/src/include/catalog/pg_statistic.h
+++ b/src/include/catalog/pg_statistic.h
@@ -121,10 +121,12 @@ CATALOG(pg_statistic,2619) BKI_WITHOUT_OIDS
 	 * presently have to cheat quite a bit to allow polymorphic arrays of this
 	 * kind, but perhaps someday it'll be a less bogus facility.
 	 */
+#ifdef CATALOG_VARLEN
 	anyarray	stavalues1;
 	anyarray	stavalues2;
 	anyarray	stavalues3;
 	anyarray	stavalues4;
+#endif
 } FormData_pg_statistic;
 
 #define STATISTIC_NUM_SLOTS  4
diff --git a/src/include/catalog/pg_tablespace.h b/src/include/catalog/pg_tablespace.h
index a6a0686..399bb64 100644
--- a/src/include/catalog/pg_tablespace.h
+++ b/src/include/catalog/pg_tablespace.h
@@ -32,8 +32,10 @@ CATALOG(pg_tablespace,1213) BKI_SHARED_RELATION
 {
 	NameData	spcname;		/* tablespace name */
 	Oid			spcowner;		/* owner of tablespace */
+#ifdef CATALOG_VARLEN
 	aclitem		spcacl[1];		/* access permissions (VAR LENGTH) */
 	text		spcoptions[1];	/* per-tablespace options */
+#endif
 } FormData_pg_tablespace;
 
 /* ----------------
diff --git a/src/include/catalog/pg_trigger.h b/src/include/catalog/pg_trigger.h
index 1f29d21..7e5d9d0 100644
--- a/src/include/catalog/pg_trigger.h
+++ b/src/include/catalog/pg_trigger.h
@@ -52,8 +52,10 @@ CATALOG(pg_trigger,2620)
 
 	/* VARIABLE LENGTH FIELDS (note: tgattr and tgargs must not be null) */
 	int2vector	tgattr;			/* column numbers, if trigger is on columns */
+#ifdef CATALOG_VARLEN
 	bytea		tgargs;			/* first\000second\000tgnargs\000 */
 	pg_node_tree tgqual;		/* WHEN expression, or NULL if none */
+#endif
 } FormData_pg_trigger;
 
 /* ----------------
diff --git a/src/include/catalog/pg_ts_dict.h b/src/include/catalog/pg_ts_dict.h
index 5036b8c..86e33e8 100644
--- a/src/include/catalog/pg_ts_dict.h
+++ b/src/include/catalog/pg_ts_dict.h
@@ -36,7 +36,9 @@ CATALOG(pg_ts_dict,3600)
 	Oid			dictnamespace;	/* name space */
 	Oid			dictowner;		/* owner */
 	Oid			dicttemplate;	/* dictionary's template */
+#ifdef CATALOG_VARLEN
 	text		dictinitoption; /* options passed to dict_init() */
+#endif
 } FormData_pg_ts_dict;
 
 typedef FormData_pg_ts_dict *Form_pg_ts_dict;
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index e12efe4..b7fea03 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -200,6 +200,7 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
 	 */
 	Oid			typcollation;
 
+#ifdef CATALOG_VARLEN
 	/*
 	 * If typdefaultbin is not NULL, it is the nodeToString representation of
 	 * a default expression for the type.  Currently this is only used for
@@ -221,6 +222,7 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
 	 * Access permissions
 	 */
 	aclitem		typacl[1];		/* VARIABLE LENGTH FIELD */
+#endif
 } FormData_pg_type;
 
 /* ----------------
diff --git a/src/include/catalog/pg_user_mapping.h b/src/include/catalog/pg_user_mapping.h
index 551000f..76d7422 100644
--- a/src/include/catalog/pg_user_mapping.h
+++ b/src/include/catalog/pg_user_mapping.h
@@ -36,7 +36,9 @@ CATALOG(pg_user_mapping,1418)
 	 * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
 	 */
 
+#ifdef CATALOG_VARLEN
 	text		umoptions[1];	/* user mapping options */
+#endif
 } FormData_pg_user_mapping;
 
 /* ----------------
#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#8)
Re: hiding variable-length fields from Form_pg_* structs

Peter Eisentraut <peter_e@gmx.net> writes:

So I think the relcache.c thing should be fixed and then this might be
good to go.

Cosmetic gripes: I think we could get rid of the various comments that
say things like "variable length fields start here", since the #ifdef
CATALOG_VARLEN lines now represent that in a standardized fashion.
Possibly those lines should be

#ifdef CATALOG_VARLEN /* variable-length fields start here */

to be even clearer.

What would be appropriate to add instead of those inconsistently-used
comments is explicit comments about the exception cases such as
proargtypes, to make it clear that the placement of the #ifdef
CATALOG_VARLEN is intentional and not a bug in those cases.

regards, tom lane

#10Peter Eisentraut
peter_e@gmx.net
In reply to: Tom Lane (#9)
1 attachment(s)
Re: hiding variable-length fields from Form_pg_* structs

On mån, 2012-01-09 at 14:04 -0500, Tom Lane wrote:

#ifdef CATALOG_VARLEN /* variable-length fields start here
*/

to be even clearer.

What would be appropriate to add instead of those inconsistently-used
comments is explicit comments about the exception cases such as
proargtypes, to make it clear that the placement of the #ifdef
CATALOG_VARLEN is intentional and not a bug in those cases.

I implemented your suggestions in the attached patch.

Attachments:

catalog-varlen.patchtext/x-patch; charset=UTF-8; name=catalog-varlen.patchDownload
diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm
index 1ba935c..cdb0bee 100644
--- a/src/backend/catalog/Catalog.pm
+++ b/src/backend/catalog/Catalog.pm
@@ -143,6 +143,7 @@ sub Catalogs
             elsif ($declaring_attributes)
             {
                 next if (/^{|^$/);
+                next if (/^#/);
                 if (/^}/)
                 {
                     undef $declaring_attributes;
@@ -150,6 +151,7 @@ sub Catalogs
                 else
                 {
                     my ($atttype, $attname) = split /\s+/, $_;
+                    die "parse error ($input_file)" unless $attname;
                     if (exists $RENAME_ATTTYPE{$atttype})
                     {
                         $atttype = $RENAME_ATTTYPE{$atttype};
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index d0138fc..a59950e 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -3351,15 +3351,30 @@ RelationGetIndexList(Relation relation)
 	while (HeapTupleIsValid(htup = systable_getnext(indscan)))
 	{
 		Form_pg_index index = (Form_pg_index) GETSTRUCT(htup);
+		Datum		indclassDatum;
+		oidvector  *indclass;
+		bool		isnull;
 
 		/* Add index's OID to result list in the proper order */
 		result = insert_ordered_oid(result, index->indexrelid);
 
+		/*
+		 * indclass cannot be referenced directly through the C struct, because
+		 * it comes after the variable-width indkey field.  Must extract the
+		 * datum the hard way...
+		 */
+		indclassDatum = heap_getattr(htup,
+									 Anum_pg_index_indclass,
+									 GetPgIndexDescriptor(),
+									 &isnull);
+		Assert(!isnull);
+		indclass = (oidvector *) DatumGetPointer(indclassDatum);
+
 		/* Check to see if it is a unique, non-partial btree index on OID */
 		if (index->indnatts == 1 &&
 			index->indisunique && index->indimmediate &&
 			index->indkey.values[0] == ObjectIdAttributeNumber &&
-			index->indclass.values[0] == OID_BTREE_OPS_OID &&
+			indclass->values[0] == OID_BTREE_OPS_OID &&
 			heap_attisnull(htup, Anum_pg_index_indpred))
 			oidIndex = index->indexrelid;
 	}
diff --git a/src/include/catalog/genbki.h b/src/include/catalog/genbki.h
index 5fe3a5b..bcf31e6 100644
--- a/src/include/catalog/genbki.h
+++ b/src/include/catalog/genbki.h
@@ -22,6 +22,16 @@
 /* Introduces a catalog's structure definition */
 #define CATALOG(name,oid)	typedef struct CppConcat(FormData_,name)
 
+/*
+ * This is never defined; it's here only for documentation.
+ *
+ * Variable-length catalog fields (except possibly the first not nullable one)
+ * should not be visible in C structures, so they are made invisible by #ifdefs
+ * of an undefined symbol.  See also MARKNOTNULL in bootstrap.c for how this is
+ * handled.
+ */
+#undef CATALOG_VARLEN
+
 /* Options that may appear after CATALOG (on the same line) */
 #define BKI_BOOTSTRAP
 #define BKI_SHARED_RELATION
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 4e10021..0c8a20c 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -44,7 +44,9 @@ CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS
 	regproc		aggfinalfn;
 	Oid			aggsortop;
 	Oid			aggtranstype;
-	text		agginitval;		/* VARIABLE LENGTH FIELD */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
+	text		agginitval;
+#endif
 } FormData_pg_aggregate;
 
 /* ----------------
diff --git a/src/include/catalog/pg_attrdef.h b/src/include/catalog/pg_attrdef.h
index 01f663c..ad770e4 100644
--- a/src/include/catalog/pg_attrdef.h
+++ b/src/include/catalog/pg_attrdef.h
@@ -32,8 +32,10 @@ CATALOG(pg_attrdef,2604)
 {
 	Oid			adrelid;		/* OID of table containing attribute */
 	int2		adnum;			/* attnum of attribute */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	pg_node_tree adbin;			/* nodeToString representation of default */
 	text		adsrc;			/* human-readable representation of default */
+#endif
 } FormData_pg_attrdef;
 
 /* ----------------
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 5cb16f6..45e38e4 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -145,11 +145,8 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
 	/* attribute's collation */
 	Oid			attcollation;
 
-	/*
-	 * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
-	 *
-	 * NOTE: the following fields are not present in tuple descriptors!
-	 */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
+	/* NOTE: The following fields are not present in tuple descriptors. */
 
 	/* Column-level access permissions */
 	aclitem		attacl[1];
@@ -159,6 +156,7 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
 
 	/* Column-level FDW options */
 	text		attfdwoptions[1];
+#endif
 } FormData_pg_attribute;
 
 /*
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 829f9b9..3b01bb4 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -68,14 +68,11 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
 	bool		relhassubclass; /* has (or has had) derived classes */
 	TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */
 
-	/*
-	 * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
-	 *
-	 * NOTE: these fields are not present in a relcache entry's rd_rel field.
-	 */
-
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
+	/* NOTE: These fields are not present in a relcache entry's rd_rel field. */
 	aclitem		relacl[1];		/* access permissions */
 	text		reloptions[1];	/* access-method-specific options */
+#endif
 } FormData_pg_class;
 
 /* Size of fixed part of pg_class tuples, not counting var-length fields */
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index c962834..77015ae 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -91,10 +91,7 @@ CATALOG(pg_constraint,2606)
 	/* Has a local definition and cannot be inherited */
 	bool		conisonly;
 
-	/*
-	 * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
-	 */
-
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	/*
 	 * Columns of conrelid that the constraint applies to, if known (this is
 	 * NULL for trigger constraints)
@@ -139,6 +136,7 @@ CATALOG(pg_constraint,2606)
 	 * If a check constraint, source-text representation of expression
 	 */
 	text		consrc;
+#endif
 } FormData_pg_constraint;
 
 /* ----------------
diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h
index 57537c8..e8509f5 100644
--- a/src/include/catalog/pg_database.h
+++ b/src/include/catalog/pg_database.h
@@ -42,7 +42,9 @@ CATALOG(pg_database,1262) BKI_SHARED_RELATION BKI_ROWTYPE_OID(1248) BKI_SCHEMA_M
 	Oid			datlastsysoid;	/* highest OID to consider a system OID */
 	TransactionId datfrozenxid; /* all Xids < this are frozen in this DB */
 	Oid			dattablespace;	/* default table space for this DB */
-	aclitem		datacl[1];		/* access permissions (VAR LENGTH) */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
+	aclitem		datacl[1];		/* access permissions */
+#endif
 } FormData_pg_database;
 
 /* ----------------
diff --git a/src/include/catalog/pg_db_role_setting.h b/src/include/catalog/pg_db_role_setting.h
index 1924043..c6e2f3b 100644
--- a/src/include/catalog/pg_db_role_setting.h
+++ b/src/include/catalog/pg_db_role_setting.h
@@ -35,7 +35,9 @@ CATALOG(pg_db_role_setting,2964) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
 {
 	Oid			setdatabase;	/* database */
 	Oid			setrole;		/* role */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	text		setconfig[1];	/* GUC settings to apply at login */
+#endif
 } FormData_pg_db_role_setting;
 
 typedef FormData_pg_db_role_setting *Form_pg_db_role_setting;
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index 9fd207d..f36ce52 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -32,12 +32,9 @@ CATALOG(pg_default_acl,826)
 	Oid			defaclrole;		/* OID of role owning this ACL */
 	Oid			defaclnamespace;	/* OID of namespace, or 0 for all */
 	char		defaclobjtype;	/* see DEFACLOBJ_xxx constants below */
-
-	/*
-	 * VARIABLE LENGTH FIELDS start here.
-	 */
-
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	aclitem		defaclacl[1];	/* permissions to add at CREATE time */
+#endif
 } FormData_pg_default_acl;
 
 /* ----------------
diff --git a/src/include/catalog/pg_description.h b/src/include/catalog/pg_description.h
index 0e06500..352c517 100644
--- a/src/include/catalog/pg_description.h
+++ b/src/include/catalog/pg_description.h
@@ -50,7 +50,9 @@ CATALOG(pg_description,2609) BKI_WITHOUT_OIDS
 	Oid			objoid;			/* OID of object itself */
 	Oid			classoid;		/* OID of table containing object */
 	int4		objsubid;		/* column number, or 0 if not used */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	text		description;	/* description of object */
+#endif
 } FormData_pg_description;
 
 /* ----------------
diff --git a/src/include/catalog/pg_extension.h b/src/include/catalog/pg_extension.h
index 606ed9a..812ccad 100644
--- a/src/include/catalog/pg_extension.h
+++ b/src/include/catalog/pg_extension.h
@@ -34,15 +34,11 @@ CATALOG(pg_extension,3079)
 	Oid			extowner;		/* extension owner */
 	Oid			extnamespace;	/* namespace of contained objects */
 	bool		extrelocatable; /* if true, allow ALTER EXTENSION SET SCHEMA */
-
-	/*
-	 * VARIABLE LENGTH FIELDS start here.
-	 *
-	 * extversion should never be null, but the others can be.
-	 */
+#ifdef CATALOG_VARLEN			 /* variable-length fields start here */
 	text		extversion;		/* extension version name */
 	Oid			extconfig[1];	/* dumpable configuration tables */
 	text		extcondition[1];	/* WHERE clauses for config tables */
+#endif
 } FormData_pg_extension;
 
 /* ----------------
diff --git a/src/include/catalog/pg_foreign_data_wrapper.h b/src/include/catalog/pg_foreign_data_wrapper.h
index 90a51a2..b6dd8eb 100644
--- a/src/include/catalog/pg_foreign_data_wrapper.h
+++ b/src/include/catalog/pg_foreign_data_wrapper.h
@@ -34,11 +34,10 @@ CATALOG(pg_foreign_data_wrapper,2328)
 	Oid			fdwowner;		/* FDW owner */
 	Oid			fdwhandler;		/* handler function, or 0 if none */
 	Oid			fdwvalidator;	/* option validation function, or 0 if none */
-
-	/* VARIABLE LENGTH FIELDS start here. */
-
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	aclitem		fdwacl[1];		/* access permissions */
 	text		fdwoptions[1];	/* FDW options */
+#endif
 } FormData_pg_foreign_data_wrapper;
 
 /* ----------------
diff --git a/src/include/catalog/pg_foreign_server.h b/src/include/catalog/pg_foreign_server.h
index a96328c..dd1e65e 100644
--- a/src/include/catalog/pg_foreign_server.h
+++ b/src/include/catalog/pg_foreign_server.h
@@ -31,14 +31,12 @@ CATALOG(pg_foreign_server,1417)
 	NameData	srvname;		/* foreign server name */
 	Oid			srvowner;		/* server owner */
 	Oid			srvfdw;			/* server FDW */
-
-	/*
-	 * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
-	 */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	text		srvtype;
 	text		srvversion;
 	aclitem		srvacl[1];		/* access permissions */
 	text		srvoptions[1];	/* FDW-specific options */
+#endif
 } FormData_pg_foreign_server;
 
 /* ----------------
diff --git a/src/include/catalog/pg_foreign_table.h b/src/include/catalog/pg_foreign_table.h
index 71992d3..9af983e 100644
--- a/src/include/catalog/pg_foreign_table.h
+++ b/src/include/catalog/pg_foreign_table.h
@@ -30,7 +30,9 @@ CATALOG(pg_foreign_table,3118) BKI_WITHOUT_OIDS
 {
 	Oid			ftrelid;		/* OID of foreign table */
 	Oid			ftserver;		/* OID of foreign server */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	text		ftoptions[1];	/* FDW-specific options */
+#endif
 } FormData_pg_foreign_table;
 
 /* ----------------
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index 6c301ff..92ca22c 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -42,8 +42,9 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO
 	bool		indcheckxmin;	/* must we wait for xmin to be old? */
 	bool		indisready;		/* is this index ready for inserts? */
 
-	/* VARIABLE LENGTH FIELDS: */
+	/* variable-length fields start here, but we allow direct access to indkey */
 	int2vector	indkey;			/* column numbers of indexed cols, or 0 */
+#ifdef CATALOG_VARLEN
 	oidvector	indcollation;	/* collation identifiers */
 	oidvector	indclass;		/* opclass identifiers */
 	int2vector	indoption;		/* per-column flags (AM-specific meanings) */
@@ -52,6 +53,7 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO
 								 * each zero entry in indkey[] */
 	pg_node_tree indpred;		/* expression tree for predicate, if a partial
 								 * index; else NULL */
+#endif
 } FormData_pg_index;
 
 /* ----------------
diff --git a/src/include/catalog/pg_language.h b/src/include/catalog/pg_language.h
index fd8ea0b..eb4ae5a 100644
--- a/src/include/catalog/pg_language.h
+++ b/src/include/catalog/pg_language.h
@@ -37,7 +37,9 @@ CATALOG(pg_language,2612)
 	Oid			lanplcallfoid;	/* Call handler for PL */
 	Oid			laninline;		/* Optional anonymous-block handler function */
 	Oid			lanvalidator;	/* Optional validation function */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	aclitem		lanacl[1];		/* Access privileges */
+#endif
 } FormData_pg_language;
 
 /* ----------------
diff --git a/src/include/catalog/pg_largeobject.h b/src/include/catalog/pg_largeobject.h
index 7c53e1e..b89d4ec 100644
--- a/src/include/catalog/pg_largeobject.h
+++ b/src/include/catalog/pg_largeobject.h
@@ -32,6 +32,7 @@ CATALOG(pg_largeobject,2613) BKI_WITHOUT_OIDS
 {
 	Oid			loid;			/* Identifier of large object */
 	int4		pageno;			/* Page number (starting from 0) */
+	/* data has variable length, but we allow direct access; see inv_api.c */
 	bytea		data;			/* Data for page (may be zero-length) */
 } FormData_pg_largeobject;
 
diff --git a/src/include/catalog/pg_largeobject_metadata.h b/src/include/catalog/pg_largeobject_metadata.h
index dea4d12..c280176 100644
--- a/src/include/catalog/pg_largeobject_metadata.h
+++ b/src/include/catalog/pg_largeobject_metadata.h
@@ -31,7 +31,9 @@
 CATALOG(pg_largeobject_metadata,2995)
 {
 	Oid			lomowner;		/* OID of the largeobject owner */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	aclitem		lomacl[1];		/* access permissions */
+#endif
 } FormData_pg_largeobject_metadata;
 
 /* ----------------
diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h
index 52a97f1..aad76a1 100644
--- a/src/include/catalog/pg_namespace.h
+++ b/src/include/catalog/pg_namespace.h
@@ -37,7 +37,9 @@ CATALOG(pg_namespace,2615)
 {
 	NameData	nspname;
 	Oid			nspowner;
-	aclitem		nspacl[1];		/* VARIABLE LENGTH FIELD */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
+	aclitem		nspacl[1];
+#endif
 } FormData_pg_namespace;
 
 /* ----------------
diff --git a/src/include/catalog/pg_pltemplate.h b/src/include/catalog/pg_pltemplate.h
index ce249c0..00abd53 100644
--- a/src/include/catalog/pg_pltemplate.h
+++ b/src/include/catalog/pg_pltemplate.h
@@ -33,11 +33,13 @@ CATALOG(pg_pltemplate,1136) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
 	NameData	tmplname;		/* name of PL */
 	bool		tmpltrusted;	/* PL is trusted? */
 	bool		tmpldbacreate;	/* PL is installable by db owner? */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	text		tmplhandler;	/* name of call handler function */
 	text		tmplinline;		/* name of anonymous-block handler, or NULL */
 	text		tmplvalidator;	/* name of validator function, or NULL */
 	text		tmpllibrary;	/* path of shared library */
 	aclitem		tmplacl[1];		/* access privileges for template */
+#endif
 } FormData_pg_pltemplate;
 
 /* ----------------
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ba4f5b6..5e880d2 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -53,8 +53,9 @@ CATALOG(pg_proc,1255) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81) BKI_SCHEMA_MACRO
 	int2		pronargdefaults;	/* number of arguments with defaults */
 	Oid			prorettype;		/* OID of result type */
 
-	/* VARIABLE LENGTH FIELDS: */
+	/* variable-length fields start here, but we allow direct access to proargtypes */
 	oidvector	proargtypes;	/* parameter types (excludes OUT params) */
+#ifdef CATALOG_VARLEN
 	Oid			proallargtypes[1];		/* all param types (NULL if IN only) */
 	char		proargmodes[1]; /* parameter modes (NULL if IN only) */
 	text		proargnames[1]; /* parameter names (NULL if no names) */
@@ -64,6 +65,7 @@ CATALOG(pg_proc,1255) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81) BKI_SCHEMA_MACRO
 	text		probin;			/* secondary procedure info (can be NULL) */
 	text		proconfig[1];	/* procedure-local GUC settings */
 	aclitem		proacl[1];		/* access permissions */
+#endif
 } FormData_pg_proc;
 
 /* ----------------
diff --git a/src/include/catalog/pg_rewrite.h b/src/include/catalog/pg_rewrite.h
index eb16b2d..e04ba81 100644
--- a/src/include/catalog/pg_rewrite.h
+++ b/src/include/catalog/pg_rewrite.h
@@ -39,10 +39,10 @@ CATALOG(pg_rewrite,2618)
 	char		ev_type;
 	char		ev_enabled;
 	bool		is_instead;
-
-	/* NB: remaining fields must be accessed via heap_getattr */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	pg_node_tree ev_qual;
 	pg_node_tree ev_action;
+#endif
 } FormData_pg_rewrite;
 
 /* ----------------
diff --git a/src/include/catalog/pg_seclabel.h b/src/include/catalog/pg_seclabel.h
index b468d69..101ec3c 100644
--- a/src/include/catalog/pg_seclabel.h
+++ b/src/include/catalog/pg_seclabel.h
@@ -25,8 +25,10 @@ CATALOG(pg_seclabel,3596) BKI_WITHOUT_OIDS
 	Oid			objoid;			/* OID of the object itself */
 	Oid			classoid;		/* OID of table containing the object */
 	int4		objsubid;		/* column number, or 0 if not used */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	text		provider;		/* name of label provider */
 	text		label;			/* security label of the object */
+#endif
 } FormData_pg_seclabel;
 
 /* ----------------
diff --git a/src/include/catalog/pg_shdescription.h b/src/include/catalog/pg_shdescription.h
index 6d0ee3b..377f433 100644
--- a/src/include/catalog/pg_shdescription.h
+++ b/src/include/catalog/pg_shdescription.h
@@ -42,7 +42,9 @@ CATALOG(pg_shdescription,2396) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
 {
 	Oid			objoid;			/* OID of object itself */
 	Oid			classoid;		/* OID of table containing object */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	text		description;	/* description of object */
+#endif
 } FormData_pg_shdescription;
 
 /* ----------------
diff --git a/src/include/catalog/pg_shseclabel.h b/src/include/catalog/pg_shseclabel.h
index 235564f..d7c49e7 100644
--- a/src/include/catalog/pg_shseclabel.h
+++ b/src/include/catalog/pg_shseclabel.h
@@ -24,8 +24,10 @@ CATALOG(pg_shseclabel,3592) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
 {
 	Oid			objoid;		/* OID of the shared object itself */
 	Oid			classoid;	/* OID of table containing the shared object */
+#ifdef CATALOG_VARLEN		/* variable-length fields start here */
 	text		provider;	/* name of label provider */
 	text		label;		/* security label of the object */
+#endif
 } FormData_pg_shseclabel;
 
 /* ----------------
diff --git a/src/include/catalog/pg_statistic.h b/src/include/catalog/pg_statistic.h
index 7d1d127..0b15b00 100644
--- a/src/include/catalog/pg_statistic.h
+++ b/src/include/catalog/pg_statistic.h
@@ -116,6 +116,7 @@ CATALOG(pg_statistic,2619) BKI_WITHOUT_OIDS
 	float4		stanumbers3[1];
 	float4		stanumbers4[1];
 
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	/*
 	 * Values in these arrays are values of the column's data type.  We
 	 * presently have to cheat quite a bit to allow polymorphic arrays of this
@@ -125,6 +126,7 @@ CATALOG(pg_statistic,2619) BKI_WITHOUT_OIDS
 	anyarray	stavalues2;
 	anyarray	stavalues3;
 	anyarray	stavalues4;
+#endif
 } FormData_pg_statistic;
 
 #define STATISTIC_NUM_SLOTS  4
diff --git a/src/include/catalog/pg_tablespace.h b/src/include/catalog/pg_tablespace.h
index a6a0686..0650a5f 100644
--- a/src/include/catalog/pg_tablespace.h
+++ b/src/include/catalog/pg_tablespace.h
@@ -32,8 +32,10 @@ CATALOG(pg_tablespace,1213) BKI_SHARED_RELATION
 {
 	NameData	spcname;		/* tablespace name */
 	Oid			spcowner;		/* owner of tablespace */
-	aclitem		spcacl[1];		/* access permissions (VAR LENGTH) */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
+	aclitem		spcacl[1];		/* access permissions */
 	text		spcoptions[1];	/* per-tablespace options */
+#endif
 } FormData_pg_tablespace;
 
 /* ----------------
diff --git a/src/include/catalog/pg_trigger.h b/src/include/catalog/pg_trigger.h
index 1f29d21..0ee5b8a 100644
--- a/src/include/catalog/pg_trigger.h
+++ b/src/include/catalog/pg_trigger.h
@@ -50,10 +50,13 @@ CATALOG(pg_trigger,2620)
 	bool		tginitdeferred; /* constraint trigger is deferred initially */
 	int2		tgnargs;		/* # of extra arguments in tgargs */
 
-	/* VARIABLE LENGTH FIELDS (note: tgattr and tgargs must not be null) */
+	/* Variable-length fields start here, but we allow direct access to tgattr.
+	 * Note: tgattr and tgargs must not be null. */
 	int2vector	tgattr;			/* column numbers, if trigger is on columns */
+#ifdef CATALOG_VARLEN
 	bytea		tgargs;			/* first\000second\000tgnargs\000 */
 	pg_node_tree tgqual;		/* WHEN expression, or NULL if none */
+#endif
 } FormData_pg_trigger;
 
 /* ----------------
diff --git a/src/include/catalog/pg_ts_dict.h b/src/include/catalog/pg_ts_dict.h
index 5036b8c..31fcdd8 100644
--- a/src/include/catalog/pg_ts_dict.h
+++ b/src/include/catalog/pg_ts_dict.h
@@ -36,7 +36,9 @@ CATALOG(pg_ts_dict,3600)
 	Oid			dictnamespace;	/* name space */
 	Oid			dictowner;		/* owner */
 	Oid			dicttemplate;	/* dictionary's template */
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	text		dictinitoption; /* options passed to dict_init() */
+#endif
 } FormData_pg_ts_dict;
 
 typedef FormData_pg_ts_dict *Form_pg_ts_dict;
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index e12efe4..d9855a7 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -200,12 +200,13 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
 	 */
 	Oid			typcollation;
 
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	/*
 	 * If typdefaultbin is not NULL, it is the nodeToString representation of
 	 * a default expression for the type.  Currently this is only used for
 	 * domains.
 	 */
-	pg_node_tree typdefaultbin; /* VARIABLE LENGTH FIELD */
+	pg_node_tree typdefaultbin;
 
 	/*
 	 * typdefault is NULL if the type has no associated default value. If
@@ -215,12 +216,13 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
 	 * external representation of the type's default value, which may be fed
 	 * to the type's input converter to produce a constant.
 	 */
-	text		typdefault;		/* VARIABLE LENGTH FIELD */
+	text		typdefault;
 
 	/*
 	 * Access permissions
 	 */
-	aclitem		typacl[1];		/* VARIABLE LENGTH FIELD */
+	aclitem		typacl[1];
+#endif
 } FormData_pg_type;
 
 /* ----------------
diff --git a/src/include/catalog/pg_user_mapping.h b/src/include/catalog/pg_user_mapping.h
index 551000f..1d2f9b0 100644
--- a/src/include/catalog/pg_user_mapping.h
+++ b/src/include/catalog/pg_user_mapping.h
@@ -32,11 +32,9 @@ CATALOG(pg_user_mapping,1418)
 								 * wanted */
 	Oid			umserver;		/* server of this mapping */
 
-	/*
-	 * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
-	 */
-
+#ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	text		umoptions[1];	/* user mapping options */
+#endif
 } FormData_pg_user_mapping;
 
 /* ----------------
#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#10)
Re: hiding variable-length fields from Form_pg_* structs

Peter Eisentraut <peter_e@gmx.net> writes:

On mån, 2012-01-09 at 14:04 -0500, Tom Lane wrote:

#ifdef CATALOG_VARLEN /* variable-length fields start here
*/

to be even clearer.

What would be appropriate to add instead of those inconsistently-used
comments is explicit comments about the exception cases such as
proargtypes, to make it clear that the placement of the #ifdef
CATALOG_VARLEN is intentional and not a bug in those cases.

I implemented your suggestions in the attached patch.

This looks ready to go to me, except for one trivial nit: in
pg_extension.h, please keep the comment pointing out that extversion
should never be null.

regards, tom lane