Removing another gen_node_support.pl special case - Mailing list pgsql-hackers

From Tom Lane
Subject Removing another gen_node_support.pl special case
Date
Msg-id 263413.1669513145@sss.pgh.pa.us
Whole thread Raw
Responses Re: Removing another gen_node_support.pl special case
List pgsql-hackers
I got confused about how we were managing EquivalenceClass pointers
in the copy/equal infrastructure, and it took me awhile to remember
that the reason it works is that gen_node_support.pl has hard-wired
knowledge about that.  I think that's something we'd be best off
dropping in favor of explicit annotations on affected fields.
Hence, I propose the attached.  This results in zero change in
the generated copy/equal code.

            regards, tom lane

diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index b6f086e262..9f7b4b833f 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -166,9 +166,6 @@ my @custom_read_write;
 # Track node types with manually assigned NodeTag numbers.
 my %manual_nodetag_number;

-# EquivalenceClasses are never moved, so just shallow-copy the pointer
-push @scalar_types, qw(EquivalenceClass* EquivalenceMember*);
-
 # This is a struct, so we can copy it by assignment.  Equal support is
 # currently not required.
 push @scalar_types, qw(QualCost);
@@ -458,9 +455,14 @@ foreach my $infile (@ARGV)
                                 && $attr !~ /^copy_as\(\w+\)$/
                                 && $attr !~ /^read_as\(\w+\)$/
                                 && !elem $attr,
-                                qw(equal_ignore equal_ignore_if_zero read_write_ignore
-                                write_only_relids write_only_nondefault_pathtarget write_only_req_outer)
-                              )
+                                qw(copy_as_scalar
+                                equal_as_scalar
+                                equal_ignore
+                                equal_ignore_if_zero
+                                read_write_ignore
+                                write_only_relids
+                                write_only_nondefault_pathtarget
+                                write_only_req_outer))
                             {
                                 die
                                   "$infile:$lineno: unrecognized attribute \"$attr\"\n";
@@ -692,6 +694,8 @@ _equal${n}(const $n *a, const $n *b)
         # extract per-field attributes
         my $array_size_field;
         my $copy_as_field;
+        my $copy_as_scalar  = 0;
+        my $equal_as_scalar = 0;
         foreach my $a (@a)
         {
             if ($a =~ /^array_size\(([\w.]+)\)$/)
@@ -702,19 +706,41 @@ _equal${n}(const $n *a, const $n *b)
             {
                 $copy_as_field = $1;
             }
+            elsif ($a eq 'copy_as_scalar')
+            {
+                $copy_as_scalar = 1;
+            }
+            elsif ($a eq 'equal_as_scalar')
+            {
+                $equal_as_scalar = 1;
+            }
             elsif ($a eq 'equal_ignore')
             {
                 $equal_ignore = 1;
             }
         }

-        # override type-specific copy method if copy_as is specified
+        # override type-specific copy method if requested
         if (defined $copy_as_field)
         {
             print $cff "\tnewnode->$f = $copy_as_field;\n"
               unless $copy_ignore;
             $copy_ignore = 1;
         }
+        elsif ($copy_as_scalar)
+        {
+            print $cff "\tCOPY_SCALAR_FIELD($f);\n"
+              unless $copy_ignore;
+            $copy_ignore = 1;
+        }
+
+        # override type-specific equal method if requested
+        if ($equal_as_scalar)
+        {
+            print $eff "\tCOMPARE_SCALAR_FIELD($f);\n"
+              unless $equal_ignore;
+            $equal_ignore = 1;
+        }

         # select instructions by field type
         if ($t eq 'char*')
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index a80f43e540..d98f2c91d9 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -86,6 +86,12 @@ typedef enum NodeTag
  *
  * - copy_as(VALUE): In copyObject(), replace the field's value with VALUE.
  *
+ * - copy_as_scalar: In copyObject(), copy the field's pointer value
+ *   even if it is a node-type pointer.
+ *
+ * - equal_as_scalar: In equal(), compare the field by pointer equality
+ *   even if it is a node-type pointer.
+ *
  * - equal_ignore: Ignore the field for equality.
  *
  * - equal_ignore_if_zero: Ignore the field for equality if it is zero.
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a544b313d3..885ad42327 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1273,7 +1273,9 @@ typedef struct StatisticExtInfo
  *
  * NB: EquivalenceClasses are never copied after creation.  Therefore,
  * copyObject() copies pointers to them as pointers, and equal() compares
- * pointers to EquivalenceClasses via pointer equality.
+ * pointers to EquivalenceClasses via pointer equality.  This is implemented
+ * by putting copy_as_scalar and equal_as_scalar attributes on fields that
+ * are pointers to EquivalenceClasses.  The same goes for EquivalenceMembers.
  */
 typedef struct EquivalenceClass
 {
@@ -1364,7 +1366,8 @@ typedef struct PathKey

     NodeTag        type;

-    EquivalenceClass *pk_eclass;    /* the value that is ordered */
+    /* the value that is ordered */
+    EquivalenceClass *pk_eclass pg_node_attr(copy_as_scalar, equal_as_scalar);
     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? */
@@ -2472,7 +2475,7 @@ typedef struct RestrictInfo
      * Generating EquivalenceClass.  This field is NULL unless clause is
      * potentially redundant.
      */
-    EquivalenceClass *parent_ec pg_node_attr(equal_ignore, read_write_ignore);
+    EquivalenceClass *parent_ec pg_node_attr(copy_as_scalar, equal_ignore, read_write_ignore);

     /*
      * cache space for cost and selectivity
@@ -2500,13 +2503,13 @@ typedef struct RestrictInfo
      */

     /* EquivalenceClass containing lefthand */
-    EquivalenceClass *left_ec pg_node_attr(equal_ignore, read_write_ignore);
+    EquivalenceClass *left_ec pg_node_attr(copy_as_scalar, equal_ignore, read_write_ignore);
     /* EquivalenceClass containing righthand */
-    EquivalenceClass *right_ec pg_node_attr(equal_ignore, read_write_ignore);
+    EquivalenceClass *right_ec pg_node_attr(copy_as_scalar, equal_ignore, read_write_ignore);
     /* EquivalenceMember for lefthand */
-    EquivalenceMember *left_em pg_node_attr(equal_ignore);
+    EquivalenceMember *left_em pg_node_attr(copy_as_scalar, equal_ignore);
     /* EquivalenceMember for righthand */
-    EquivalenceMember *right_em pg_node_attr(equal_ignore);
+    EquivalenceMember *right_em pg_node_attr(copy_as_scalar, equal_ignore);

     /*
      * List of MergeScanSelCache structs.  Those aren't Nodes, so hard to

pgsql-hackers by date:

Previous
From: Tomas Vondra
Date:
Subject: Re: CF 2022-11: entries "Waiting for Committer" but no recent activity
Next
From: Reid Thompson
Date:
Subject: Re: Add tracking of backend memory allocated to pg_stat_activity