diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 78c7f27cda..f6625a763e 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -122,6 +122,7 @@ foreach my $infile (@ARGV)
 	my $supertype;
 	my $supertype_field;
 
+	my $node_attrs = '';
 	my @my_fields;
 	my %my_field_types;
 	my %my_field_attrs;
@@ -151,9 +152,18 @@ foreach my $infile (@ARGV)
 				$is_node_struct = 0;
 				$supertype = undef;
 				next if $line eq '{';
-				die;
+				die "$infile:$.: expected opening brace\n";
 			}
-			# second line should have node tag or supertype
+			# second line could be node attributes
+			elsif ($subline == 2 &&
+			       $line =~ /^\s*pg_node_attr\(([\w(), ]*)\)$/)
+			{
+				$node_attrs = $1;
+				# hack: don't count the line
+				$subline--;
+				next;
+			}
+			# next line should have node tag or supertype
 			elsif ($subline == 2)
 			{
 				if ($line =~ /^\s*NodeTag\s+type;/)
@@ -171,10 +181,8 @@ foreach my $infile (@ARGV)
 			}
 
 			# end of struct
-			if ($line =~ /^\}\s*(?:\Q$in_struct\E\s*)?(?:pg_node_attr\(([\w(), ]*)\))?;$/)
+			if ($line =~ /^\}\s*(?:\Q$in_struct\E\s*)?;$/)
 			{
-				my $node_attrs = $1 || '';
-
 				if ($is_node_struct)
 				{
 					# This is the end of a node struct definition.
@@ -287,6 +295,7 @@ foreach my $infile (@ARGV)
 
 				# start new cycle
 				$in_struct = undef;
+				$node_attrs = '';
 				@my_fields = ();
 				%my_field_types = ();
 				%my_field_attrs = ();
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 9ead14b651..a001e448ba 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -237,13 +237,17 @@ extern PGDLLIMPORT const TupleTableSlotOps TTSOpsBufferHeapTuple;
 
 typedef struct VirtualTupleTableSlot
 {
+	pg_node_attr(abstract)
+
 	TupleTableSlot base;
 
 	char	   *data;			/* data for materialized slots */
-} VirtualTupleTableSlot pg_node_attr(abstract);
+} VirtualTupleTableSlot;
 
 typedef struct HeapTupleTableSlot
 {
+	pg_node_attr(abstract)
+
 	TupleTableSlot base;
 
 #define FIELDNO_HEAPTUPLETABLESLOT_TUPLE 1
@@ -251,11 +255,13 @@ typedef struct HeapTupleTableSlot
 #define FIELDNO_HEAPTUPLETABLESLOT_OFF 2
 	uint32		off;			/* saved state for slot_deform_heap_tuple */
 	HeapTupleData tupdata;		/* optional workspace for storing tuple */
-} HeapTupleTableSlot pg_node_attr(abstract);
+} HeapTupleTableSlot;
 
 /* heap tuple residing in a buffer */
 typedef struct BufferHeapTupleTableSlot
 {
+	pg_node_attr(abstract)
+
 	HeapTupleTableSlot base;
 
 	/*
@@ -265,10 +271,12 @@ typedef struct BufferHeapTupleTableSlot
 	 * such a case, since presumably tts_tuple is pointing into the buffer.)
 	 */
 	Buffer		buffer;			/* tuple's buffer, or InvalidBuffer */
-} BufferHeapTupleTableSlot pg_node_attr(abstract);
+} BufferHeapTupleTableSlot;
 
 typedef struct MinimalTupleTableSlot
 {
+	pg_node_attr(abstract)
+
 	TupleTableSlot base;
 
 	/*
@@ -284,7 +292,7 @@ typedef struct MinimalTupleTableSlot
 	HeapTupleData minhdr;		/* workspace for minimal-tuple-only case */
 #define FIELDNO_MINIMALTUPLETABLESLOT_OFF 4
 	uint32		off;			/* saved state for slot_deform_heap_tuple */
-} MinimalTupleTableSlot pg_node_attr(abstract);
+} MinimalTupleTableSlot;
 
 /*
  * TupIsNull -- is a TupleTableSlot empty?
diff --git a/src/include/nodes/extensible.h b/src/include/nodes/extensible.h
index 9706828ef4..34936db894 100644
--- a/src/include/nodes/extensible.h
+++ b/src/include/nodes/extensible.h
@@ -31,9 +31,11 @@
  */
 typedef struct ExtensibleNode
 {
+	pg_node_attr(custom_copy_equal, custom_read_write)
+
 	NodeTag		type;
 	const char *extnodename;	/* identifier of ExtensibleNodeMethods */
-} ExtensibleNode pg_node_attr(custom_copy_equal, custom_read_write);
+} ExtensibleNode;
 
 /*
  * node_size is the size of an extensible node of this type in bytes.
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8451a51749..0b6a7bb365 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -117,6 +117,8 @@ typedef uint32 AclMode;			/* a bitmask of privilege bits */
  */
 typedef struct Query
 {
+	pg_node_attr(custom_read_write)
+
 	NodeTag		type;
 
 	CmdType		commandType;	/* select|insert|update|delete|merge|utility */
@@ -201,7 +203,7 @@ typedef struct Query
 	 */
 	int			stmt_location;	/* start location, or -1 if unknown */
 	int			stmt_len;		/* length in bytes; 0 means "rest of string" */
-} Query		pg_node_attr(custom_read_write);
+} Query;
 
 
 /****************************************************************************
@@ -291,19 +293,23 @@ typedef enum A_Expr_Kind
 
 typedef struct A_Expr
 {
+	pg_node_attr(custom_read_write, no_read)
+
 	NodeTag		type;
 	A_Expr_Kind kind;			/* see above */
 	List	   *name;			/* possibly-qualified name of operator */
 	Node	   *lexpr;			/* left argument, or NULL if none */
 	Node	   *rexpr;			/* right argument, or NULL if none */
 	int			location;		/* token location, or -1 if unknown */
-} A_Expr	pg_node_attr(custom_read_write, no_read);
+} A_Expr;
 
 /*
  * A_Const - a literal constant
  */
 typedef struct A_Const
 {
+	pg_node_attr(custom_copy_equal, custom_read_write, no_read)
+
 	NodeTag		type;
 
 	/*
@@ -321,7 +327,7 @@ typedef struct A_Const
 	}			val;
 	bool		isnull;			/* SQL NULL constant */
 	int			location;		/* token location, or -1 if unknown */
-} A_Const	pg_node_attr(custom_copy_equal, custom_read_write, no_read);
+} A_Const;
 
 /*
  * TypeCast - a CAST expression
@@ -403,8 +409,10 @@ typedef struct FuncCall
  */
 typedef struct A_Star
 {
+	pg_node_attr(no_read)
+
 	NodeTag		type;
-} A_Star	pg_node_attr(no_read);
+} A_Star;
 
 /*
  * A_Indices - array subscript or slice bounds ([idx] or [lidx:uidx])
@@ -1015,6 +1023,8 @@ typedef enum RTEKind
 
 typedef struct RangeTblEntry
 {
+	pg_node_attr(custom_read_write)
+
 	NodeTag		type;
 
 	RTEKind		rtekind;		/* see above */
@@ -1174,7 +1184,7 @@ typedef struct RangeTblEntry
 	Bitmapset  *updatedCols;	/* columns needing UPDATE permission */
 	Bitmapset  *extraUpdatedCols;	/* generated columns being updated */
 	List	   *securityQuals;	/* security barrier quals to apply, if any */
-} RangeTblEntry pg_node_attr(custom_read_write);
+} RangeTblEntry;
 
 /*
  * RangeTblFunction -
@@ -2611,6 +2621,8 @@ typedef enum ConstrType			/* types of constraints */
 
 typedef struct Constraint
 {
+	pg_node_attr(custom_read_write, no_read)
+
 	NodeTag		type;
 	ConstrType	contype;		/* see above */
 
@@ -2661,7 +2673,7 @@ typedef struct Constraint
 	/* Fields used for constraints that allow a NOT VALID specification */
 	bool		skip_validation;	/* skip validation of existing rows? */
 	bool		initially_valid;	/* mark the new constraint as valid? */
-} Constraint pg_node_attr(custom_read_write, no_read);
+} Constraint;
 
 /* ----------------------
  *		Create/Drop Table Space Statements
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 6193126d20..44ffc73f15 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -94,6 +94,8 @@ typedef enum UpperRelationKind
  */
 typedef struct PlannerGlobal
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	/* Param values provided to planner() */
@@ -155,7 +157,7 @@ typedef struct PlannerGlobal
 
 	/* partition descriptors */
 	PartitionDirectory partition_directory pg_node_attr(read_write_ignore);
-} PlannerGlobal pg_node_attr(no_copy_equal, no_read);
+} PlannerGlobal;
 
 /* macro for fetching the Plan associated with a SubPlan node */
 #define planner_subplan_get_plan(root, subplan) \
@@ -185,6 +187,8 @@ typedef struct PlannerInfo PlannerInfo;
 
 struct PlannerInfo
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	/* the Query being planned */
@@ -476,7 +480,7 @@ struct PlannerInfo
 
 	/* Does this query modify any partition key columns? */
 	bool		partColsUpdated;
-}			pg_node_attr(no_copy_equal, no_read);
+};
 
 
 /*
@@ -775,6 +779,8 @@ typedef enum RelOptKind
 
 typedef struct RelOptInfo
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	RelOptKind	reloptkind;
@@ -946,7 +952,7 @@ typedef struct RelOptInfo
 	List	  **partexprs pg_node_attr(read_write_ignore);
 	/* Nullable partition key expressions */
 	List	  **nullable_partexprs pg_node_attr(read_write_ignore);
-} RelOptInfo pg_node_attr(no_copy_equal, no_read);
+} RelOptInfo;
 
 /*
  * Is given relation partitioned?
@@ -1006,6 +1012,8 @@ typedef struct IndexOptInfo IndexOptInfo;
 
 struct IndexOptInfo
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	/* OID of the index relation */
@@ -1105,7 +1113,7 @@ struct IndexOptInfo
 	bool		amcanmarkpos pg_node_attr(read_write_ignore);
 	/* Rather than include amapi.h here, we declare amcostestimate like this */
 	void		(*amcostestimate) ();	/* AM's cost estimator */
-}			pg_node_attr(no_copy_equal, no_read);
+};
 
 /*
  * ForeignKeyOptInfo
@@ -1117,6 +1125,8 @@ struct IndexOptInfo
  */
 typedef struct ForeignKeyOptInfo
 {
+	pg_node_attr(custom_read_write, no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	/*
@@ -1154,7 +1164,7 @@ typedef struct ForeignKeyOptInfo
 	struct EquivalenceMember *fk_eclass_member[INDEX_MAX_KEYS];
 	/* List of non-EC RestrictInfos matching each column's condition */
 	List	   *rinfos[INDEX_MAX_KEYS];
-} ForeignKeyOptInfo pg_node_attr(custom_read_write, no_copy_equal, no_read);
+} ForeignKeyOptInfo;
 
 /*
  * StatisticExtInfo
@@ -1165,6 +1175,8 @@ typedef struct ForeignKeyOptInfo
  */
 typedef struct StatisticExtInfo
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	/* OID of the statistics row */
@@ -1187,7 +1199,7 @@ typedef struct StatisticExtInfo
 
 	/* expressions */
 	List	   *exprs;
-} StatisticExtInfo pg_node_attr(no_copy_equal, no_read);
+} StatisticExtInfo;
 
 /*
  * EquivalenceClasses
@@ -1235,6 +1247,8 @@ typedef struct StatisticExtInfo
  */
 typedef struct EquivalenceClass
 {
+	pg_node_attr(custom_read_write, no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	List	   *ec_opfamilies;	/* btree operator family OIDs */
@@ -1252,7 +1266,7 @@ typedef struct EquivalenceClass
 	Index		ec_min_security;	/* minimum security_level in ec_sources */
 	Index		ec_max_security;	/* maximum security_level in ec_sources */
 	struct EquivalenceClass *ec_merged; /* set if merged into another EC */
-} EquivalenceClass pg_node_attr(custom_read_write, no_copy_equal, no_read);
+} EquivalenceClass;
 
 /*
  * If an EC contains a const and isn't below-outer-join, any PathKey depending
@@ -1285,6 +1299,8 @@ typedef struct EquivalenceClass
  */
 typedef struct EquivalenceMember
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	Expr	   *em_expr;		/* the expression represented */
@@ -1293,7 +1309,7 @@ typedef struct EquivalenceMember
 	bool		em_is_const;	/* expression is pseudoconstant? */
 	bool		em_is_child;	/* derived version for a child relation? */
 	Oid			em_datatype;	/* the "nominal type" used by the opfamily */
-} EquivalenceMember pg_node_attr(no_copy_equal, no_read);
+} EquivalenceMember;
 
 /*
  * PathKeys
@@ -1314,23 +1330,27 @@ typedef struct EquivalenceMember
  */
 typedef struct PathKey
 {
+	pg_node_attr(no_read)
+
 	NodeTag		type;
 
 	EquivalenceClass *pk_eclass;	/* the value that is ordered */
 	Oid			pk_opfamily;	/* btree opfamily defining the ordering */
 	int			pk_strategy;	/* sort direction (ASC or DESC) */
 	bool		pk_nulls_first; /* do NULLs come before normal values? */
-} PathKey	pg_node_attr(no_read);
+} PathKey;
 
 /*
  * Combines information about pathkeys and the associated clauses.
  */
 typedef struct PathKeyInfo
 {
+	pg_node_attr(no_read)
+
 	NodeTag		type;
 	List	   *pathkeys;
 	List	   *clauses;
-} PathKeyInfo pg_node_attr(no_read);
+} PathKeyInfo;
 
 /*
  * VolatileFunctionStatus -- allows nodes to cache their
@@ -1369,6 +1389,8 @@ typedef enum VolatileFunctionStatus
  */
 typedef struct PathTarget
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	/* list of expressions to be computed */
@@ -1385,7 +1407,7 @@ typedef struct PathTarget
 
 	/* indicates if exprs contain any volatile functions */
 	VolatileFunctionStatus has_volatile_expr;
-} PathTarget pg_node_attr(no_copy_equal, no_read);
+} PathTarget;
 
 /* Convenience macro to get a sort/group refno from a PathTarget */
 #define get_pathtarget_sortgroupref(target, colno) \
@@ -1408,12 +1430,14 @@ typedef struct PathTarget
  */
 typedef struct ParamPathInfo
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	Relids		ppi_req_outer;	/* rels supplying parameters used by path */
 	Cardinality ppi_rows;		/* estimated number of result tuples */
 	List	   *ppi_clauses;	/* join clauses available from outer rels */
-} ParamPathInfo pg_node_attr(no_copy_equal, no_read);
+} ParamPathInfo;
 
 
 /*
@@ -1451,6 +1475,8 @@ typedef struct ParamPathInfo
  */
 typedef struct Path
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	/* tag identifying scan/join method */
@@ -1494,7 +1520,7 @@ typedef struct Path
 
 	/* sort ordering of path's output; a List of PathKey nodes; see above */
 	List	   *pathkeys;
-} Path		pg_node_attr(no_copy_equal, no_read);
+} Path;
 
 /* Macro for extracting a path's parameterization relids; beware double eval */
 #define PATH_REQ_OUTER(path)  \
@@ -1586,13 +1612,15 @@ typedef struct IndexPath
  */
 typedef struct IndexClause
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 	struct RestrictInfo *rinfo; /* original restriction or join clause */
 	List	   *indexquals;		/* indexqual(s) derived from it */
 	bool		lossy;			/* are indexquals a lossy version of clause? */
 	AttrNumber	indexcol;		/* index column the clause uses (zero-based) */
 	List	   *indexcols;		/* multiple index columns, if RowCompare */
-} IndexClause pg_node_attr(no_copy_equal, no_read);
+} IndexClause;
 
 /*
  * BitmapHeapPath represents one or more indexscans that generate TID bitmaps
@@ -1881,6 +1909,8 @@ typedef struct GatherMergePath
 
 typedef struct JoinPath
 {
+	pg_node_attr(abstract)
+
 	Path		path;
 
 	JoinType	jointype;
@@ -1898,7 +1928,7 @@ typedef struct JoinPath
 	 * joinrestrictinfo is needed in JoinPath, and can't be merged into the
 	 * parent RelOptInfo.
 	 */
-} JoinPath	pg_node_attr(abstract);
+} JoinPath;
 
 /*
  * A nested-loop path needs no special fields.
@@ -2083,13 +2113,17 @@ typedef struct AggPath
 
 typedef struct GroupingSetData
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 	List	   *set;			/* grouping set as list of sortgrouprefs */
 	Cardinality numGroups;		/* est. number of result groups */
-} GroupingSetData pg_node_attr(no_copy_equal, no_read);
+} GroupingSetData;
 
 typedef struct RollupData
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 	List	   *groupClause;	/* applicable subset of parse->groupClause */
 	List	   *gsets;			/* lists of integer indexes into groupClause */
@@ -2097,7 +2131,7 @@ typedef struct RollupData
 	Cardinality numGroups;		/* est. number of result groups */
 	bool		hashable;		/* can be hashed */
 	bool		is_hashed;		/* to be implemented as a hashagg */
-} RollupData pg_node_attr(no_copy_equal, no_read);
+} RollupData;
 
 /*
  * GroupingSetsPath represents a GROUPING SETS aggregation
@@ -2363,6 +2397,8 @@ typedef struct LimitPath
 
 typedef struct RestrictInfo
 {
+	pg_node_attr(no_read)
+
 	NodeTag		type;
 
 	/* the represented clause of WHERE or JOIN */
@@ -2488,7 +2524,7 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
-} RestrictInfo pg_node_attr(no_read);
+} RestrictInfo;
 
 /*
  * This macro embodies the correct way to test whether a RestrictInfo is
@@ -2631,6 +2667,8 @@ typedef struct SpecialJoinInfo SpecialJoinInfo;
 
 struct SpecialJoinInfo
 {
+	pg_node_attr(no_read)
+
 	NodeTag		type;
 	Relids		min_lefthand;	/* base relids in minimum LHS for join */
 	Relids		min_righthand;	/* base relids in minimum RHS for join */
@@ -2644,7 +2682,7 @@ struct SpecialJoinInfo
 	bool		semi_can_hash;	/* true if semi_operators are all hash */
 	List	   *semi_operators; /* OIDs of equality join operators */
 	List	   *semi_rhs_exprs; /* righthand-side expressions of these ops */
-}			pg_node_attr(no_read);
+};
 
 /*
  * Append-relation info.
@@ -2753,13 +2791,15 @@ typedef struct AppendRelInfo
  */
 typedef struct RowIdentityVarInfo
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	Var		   *rowidvar;		/* Var to be evaluated (but varno=ROWID_VAR) */
 	int32		rowidwidth;		/* estimated average width */
 	char	   *rowidname;		/* name of the resjunk column */
 	Relids		rowidrels;		/* RTE indexes of target rels using this */
-} RowIdentityVarInfo pg_node_attr(no_copy_equal, no_read);
+} RowIdentityVarInfo;
 
 /*
  * For each distinct placeholder expression generated during planning, we
@@ -2789,6 +2829,8 @@ typedef struct RowIdentityVarInfo
 
 typedef struct PlaceHolderInfo
 {
+	pg_node_attr(no_read)
+
 	NodeTag		type;
 
 	/* ID for PH (unique within planner run) */
@@ -2811,7 +2853,7 @@ typedef struct PlaceHolderInfo
 
 	/* estimated attribute width */
 	int32		ph_width;
-} PlaceHolderInfo pg_node_attr(no_read);
+} PlaceHolderInfo;
 
 /*
  * This struct describes one potentially index-optimizable MIN/MAX aggregate
@@ -2820,6 +2862,8 @@ typedef struct PlaceHolderInfo
  */
 typedef struct MinMaxAggInfo
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	/* pg_proc Oid of the aggregate */
@@ -2845,7 +2889,7 @@ typedef struct MinMaxAggInfo
 
 	/* param for subplan's output */
 	Param	   *param;
-} MinMaxAggInfo pg_node_attr(no_copy_equal, no_read);
+} MinMaxAggInfo;
 
 /*
  * At runtime, PARAM_EXEC slots are used to pass values around from one plan
@@ -2896,11 +2940,13 @@ typedef struct MinMaxAggInfo
  */
 typedef struct PlannerParamItem
 {
+	pg_node_attr(no_copy_equal, no_read)
+
 	NodeTag		type;
 
 	Node	   *item;			/* the Var, PlaceHolderVar, or Aggref */
 	int			paramId;		/* its assigned PARAM_EXEC slot number */
-} PlannerParamItem pg_node_attr(no_copy_equal, no_read);
+} PlannerParamItem;
 
 /*
  * When making cost estimates for a SEMI/ANTI/inner_unique join, there are
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 846977f443..6ed765cbe4 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -45,6 +45,8 @@
  */
 typedef struct PlannedStmt
 {
+	pg_node_attr(no_equal)
+
 	NodeTag		type;
 
 	CmdType		commandType;	/* select|insert|update|delete|merge|utility */
@@ -92,7 +94,7 @@ typedef struct PlannedStmt
 	/* statement location in source string (copied from Query) */
 	int			stmt_location;	/* start location, or -1 if unknown */
 	int			stmt_len;		/* length in bytes; 0 means "rest of string" */
-} PlannedStmt pg_node_attr(no_equal);
+} PlannedStmt;
 
 /* macro for fetching the Plan associated with a SubPlan node */
 #define exec_subplan_get_plan(plannedstmt, subplan) \
@@ -113,6 +115,8 @@ typedef struct PlannedStmt
  */
 typedef struct Plan
 {
+	pg_node_attr(abstract, no_equal)
+
 	NodeTag		type;
 
 	/*
@@ -162,7 +166,7 @@ typedef struct Plan
 	 */
 	Bitmapset  *extParam;
 	Bitmapset  *allParam;
-} Plan		pg_node_attr(abstract, no_equal);
+} Plan;
 
 /* ----------------
  *	these are defined to avoid confusion problems with "left"
@@ -767,11 +771,13 @@ typedef struct CustomScan
  */
 typedef struct Join
 {
+	pg_node_attr(abstract)
+
 	Plan		plan;
 	JoinType	jointype;
 	bool		inner_unique;
 	List	   *joinqual;		/* JOIN quals (in addition to plan.qual) */
-} Join		pg_node_attr(abstract);
+} Join;
 
 /* ----------------
  *		nest loop join node
@@ -792,10 +798,12 @@ typedef struct NestLoop
 
 typedef struct NestLoopParam
 {
+	pg_node_attr(no_equal)
+
 	NodeTag		type;
 	int			paramno;		/* number of the PARAM_EXEC Param to set */
 	Var		   *paramval;		/* outer-relation Var to assign to Param */
-} NestLoopParam pg_node_attr(no_equal);
+} NestLoopParam;
 
 /* ----------------
  *		merge join node
@@ -1354,6 +1362,8 @@ typedef enum RowMarkType
  */
 typedef struct PlanRowMark
 {
+	pg_node_attr(no_equal)
+
 	NodeTag		type;
 	Index		rti;			/* range table index of markable relation */
 	Index		prti;			/* range table index of parent relation */
@@ -1363,7 +1373,7 @@ typedef struct PlanRowMark
 	LockClauseStrength strength;	/* LockingClause's strength, or LCS_NONE */
 	LockWaitPolicy waitPolicy;	/* NOWAIT and SKIP LOCKED options */
 	bool		isParent;		/* true if this is a "dummy" parent entry */
-} PlanRowMark pg_node_attr(no_equal);
+} PlanRowMark;
 
 
 /*
@@ -1398,10 +1408,12 @@ typedef struct PlanRowMark
  */
 typedef struct PartitionPruneInfo
 {
+	pg_node_attr(no_equal)
+
 	NodeTag		type;
 	List	   *prune_infos;
 	Bitmapset  *other_subplans;
-} PartitionPruneInfo pg_node_attr(no_equal);
+} PartitionPruneInfo;
 
 /*
  * PartitionedRelPruneInfo - Details required to allow the executor to prune
@@ -1422,6 +1434,8 @@ typedef struct PartitionPruneInfo
  */
 typedef struct PartitionedRelPruneInfo
 {
+	pg_node_attr(no_equal)
+
 	NodeTag		type;
 
 	/* RT index of partition rel for this level */
@@ -1453,7 +1467,7 @@ typedef struct PartitionedRelPruneInfo
 
 	/* All PARAM_EXEC Param IDs in exec_pruning_steps */
 	Bitmapset  *execparamids;
-} PartitionedRelPruneInfo pg_node_attr(no_equal);
+} PartitionedRelPruneInfo;
 
 /*
  * Abstract Node type for partition pruning steps (there are no concrete
@@ -1463,9 +1477,11 @@ typedef struct PartitionedRelPruneInfo
  */
 typedef struct PartitionPruneStep
 {
+	pg_node_attr(abstract, no_equal)
+
 	NodeTag		type;
 	int			step_id;
-} PartitionPruneStep pg_node_attr(abstract);
+} PartitionPruneStep;
 
 /*
  * PartitionPruneStepOp - Information to prune using a set of mutually ANDed
@@ -1502,7 +1518,7 @@ typedef struct PartitionPruneStepOp
 	List	   *exprs;
 	List	   *cmpfns;
 	Bitmapset  *nullkeys;
-} PartitionPruneStepOp pg_node_attr(no_equal);
+} PartitionPruneStepOp;
 
 /*
  * PartitionPruneStepCombine - Information to prune using a BoolExpr clause
@@ -1522,7 +1538,7 @@ typedef struct PartitionPruneStepCombine
 
 	PartitionPruneCombineOp combineOp;
 	List	   *source_stepids;
-} PartitionPruneStepCombine pg_node_attr(no_equal);
+} PartitionPruneStepCombine;
 
 
 /*
@@ -1536,10 +1552,12 @@ typedef struct PartitionPruneStepCombine
  */
 typedef struct PlanInvalItem
 {
+	pg_node_attr(no_equal)
+
 	NodeTag		type;
 	int			cacheId;		/* a syscache ID, see utils/syscache.h */
 	uint32		hashValue;		/* hash value of object's cache lookup key */
-} PlanInvalItem pg_node_attr(no_equal);
+} PlanInvalItem;
 
 /*
  * MonotonicFunction
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index fd22fe19b2..1fc2fbffa3 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -160,8 +160,10 @@ typedef struct IntoClause
  */
 typedef struct Expr
 {
+	pg_node_attr(abstract)
+
 	NodeTag		type;
-} Expr		pg_node_attr(abstract);
+} Expr;
 
 /*
  * Var - expression node representing a variable (ie, a table column)
@@ -260,6 +262,8 @@ typedef struct Var
  */
 typedef struct Const
 {
+	pg_node_attr(custom_copy_equal, custom_read_write)
+
 	Expr		xpr;
 	Oid			consttype;		/* pg_type OID of the constant's datatype */
 	int32		consttypmod;	/* typmod value, if any */
@@ -273,7 +277,7 @@ typedef struct Const
 								 * in the Datum. If false, then the Datum
 								 * contains a pointer to the information. */
 	int			location;		/* token location, or -1 if unknown */
-} Const		pg_node_attr(custom_copy_equal, custom_read_write);
+} Const;
 
 /*
  * Param
@@ -758,11 +762,13 @@ typedef enum BoolExprType
 
 typedef struct BoolExpr
 {
+	pg_node_attr(custom_read_write)
+
 	Expr		xpr;
 	BoolExprType boolop;
 	List	   *args;			/* arguments to this expression */
 	int			location;		/* token location, or -1 if unknown */
-} BoolExpr	pg_node_attr(custom_read_write);
+} BoolExpr;
 
 /*
  * SubLink
diff --git a/src/include/nodes/value.h b/src/include/nodes/value.h
index 20347d39dd..5e83b843dc 100644
--- a/src/include/nodes/value.h
+++ b/src/include/nodes/value.h
@@ -27,9 +27,11 @@
 
 typedef struct Integer
 {
+	pg_node_attr(special_read_write)
+
 	NodeTag		type;
 	int			ival;
-} Integer	pg_node_attr(special_read_write);
+} Integer;
 
 /*
  * Float is internally represented as string.  Using T_Float as the node type
@@ -44,27 +46,35 @@ typedef struct Integer
  */
 typedef struct Float
 {
+	pg_node_attr(special_read_write)
+
 	NodeTag		type;
 	char	   *fval;
-} Float		pg_node_attr(special_read_write);
+} Float;
 
 typedef struct Boolean
 {
+	pg_node_attr(special_read_write)
+
 	NodeTag		type;
 	bool		boolval;
-} Boolean	pg_node_attr(special_read_write);
+} Boolean;
 
 typedef struct String
 {
+	pg_node_attr(special_read_write)
+
 	NodeTag		type;
 	char	   *sval;
-} String	pg_node_attr(special_read_write);
+} String;
 
 typedef struct BitString
 {
+	pg_node_attr(special_read_write)
+
 	NodeTag		type;
 	char	   *bsval;
-} BitString pg_node_attr(special_read_write);
+} BitString;
 
 #define intVal(v)		(castNode(Integer, v)->ival)
 #define floatVal(v)		atof(castNode(Float, v)->fval)
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 075a2669fd..2854839ec2 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -268,6 +268,8 @@ typedef struct RelationData
  */
 typedef struct ForeignKeyCacheInfo
 {
+	pg_node_attr(no_equal, no_read)
+
 	NodeTag		type;
 	/* oid of the constraint itself */
 	Oid			conoid;
@@ -287,7 +289,7 @@ typedef struct ForeignKeyCacheInfo
 	AttrNumber	confkey[INDEX_MAX_KEYS] pg_node_attr(array_size(nkeys));
 	/* PK = FK operator OIDs */
 	Oid			conpfeqop[INDEX_MAX_KEYS] pg_node_attr(array_size(nkeys));
-} ForeignKeyCacheInfo pg_node_attr(no_equal, no_read);
+} ForeignKeyCacheInfo;
 
 
 /*
