This is an automated email from the ASF dual-hosted git repository.

rafsun42 pushed a commit to branch PG16
in repository https://gitbox.apache.org/repos/asf/age.git


The following commit(s) were added to refs/heads/PG16 by this push:
     new 127b367f Pg16 to master (#1454)
127b367f is described below

commit 127b367f591c87e21c831c8e509b5ce8f48c03c4
Author: John Gemignani <[email protected]>
AuthorDate: Wed Dec 27 10:30:47 2023 -0800

    Pg16 to master (#1454)
    
    * Fix Docker file to reflect PostgreSQL version 15 (#1449)
    
    Fixed the following Docker file to reflect running under
    PostgreSQL version 15.
    
    modified:   docker/Dockerfile.dev
    
    This impacts the development DockerHub builds.
    
    * Master to PostgreSQL version 16 (#1451)
    
    * Initial PG16 version (#1237)
    
    Fixed empty string handling.
    
    Previously, the PG 16 outToken emitted NULL for an empty string, but now it 
emits an empty string "". Consequently, our cypher read function required 
modification to properly decode the empty string as a plain token, thereby 
addressing the comparison issue.
    
    Compared the branches and added the necessary changes so that the query's 
rteperminfos variable doesn't stay NULL.
    
    Fix missing include varatt.h (causing undefined symbol VARDATA_ANY) & 
Fixing some test cases of the failings
    
    Added missing include varatt.h to fix the undefined symbol while loading 
age into postgresql because usage of VARDATA_ANY needs to import varatt.h in 
PG16
    
    Modified initialisation of ResultRelInfo and removed unnecessary RTEs
    
    Compared the branches and added the necessary changes so that the query's 
rteperminfos variable doesn't stay NULL.
    
    Modified initialisation of ResultRelInfo and removed unnecessary RTEs
    
    One of the problems that we were facing was related to the ResultRelInfo 
pointing at the wrong RTE via its ri_RangeTableIndex. The 
create_entity_result_rel_info() function does not have the capability of 
setting the ri_RootResultRelInfo to the correct ResultRelInfo node because it 
does not have access to the ModifyTableState node. The solution for this was to 
set the third argument in InitResultRelInfo() to be zero instead of 
list_length(estate->es_range_table).
    
    In the update_entity_tuple() function, when we call table_tuple_update() 
and assign the returned value to the result variable, the buffer variable 
receives the value of 0.
    Made a workaround so that the original value isn't lost.
    
    This is a work in progress for the new field that was added to the struct 
Var called varnullingrels. According to the documentation, this field is 
responsible for marking the Vars as nullable, if they are coming from a JOIN, 
either LEFT JOIN, RIGHT JOIN, or FULL OUTER JOIN. The changes were made 
following an "optional match" clause which is being treated as a LEFT JOIN from 
our extension.
    
    A function markRelsAsNulledBy is added because its internal in Postgres and 
doesn't belong in a header file, therefore it can't be exported. This function 
is added before the creation of the Vars from the make_vertex_expr and 
make_edge_expr, to correctly mark the specific PNSI as nullable, so later in 
the planner stage, the Vars will be correctly nulled.
    
    Fix incorrect typecasting in agtype_to_graphid function.
    
    Fix incorrect returns to the fuction _label_name, _ag_build_vertex and 
_ag_build_edge.
    Contributors
    
    Panagiotis Foliadis <[email protected]>
    Matheus Farias <[email protected]>
    Mohamed Mokhtar <[email protected]>
    Hannan Aamir <[email protected]>
    John Gemignani <[email protected]>
    Muhammad Taha Naveed <[email protected]>
    Wendel de Lana <[email protected]>
    ---------
    
    * Fix Docker files to reflect PostgreSQL version 16 (#1448)
    
    Fixed the following Docker files to reflect running under
    PostgreSQL version 16.
    
        modified:   docker/Dockerfile
        modified:   docker/Dockerfile.dev
    
    This impacts the DockerHub builds.
    
    * Mark null-returning RTEs in outer joins as nullable
    
    RTEs that appear in the right side of a left join are marked as 'nullable'.
    The column references to a nullable RTE within the join's RTE are also
    marked as 'nullable'. This concept is introduced in Postgresql v16.
    
    The change in Postgresql v16's pullup_replace_vars_callback() function 
causes
    different plans to be generated depending on whether appropriate RTEs are
    marked nullable. Without marking nullable, in a left join, any function call
    expression containing right RTE's columns as arguments are not evaluated at
    the scan level, rather it is evaluated after the join is performed. At that
    point, the function call may receive null input, which was unexpected in
    previous Postgresql versions.
    
    See:
    ----
      - Postgresql v16's commit: Make Vars be outer-join-aware
        https://www.postgresql.org/message-id/[email protected]
    
      - The 'Vars and PlaceHolderVars' section in the Postgresql v16's
        optimizer/README.md
    
    * Fix minor code formatting.
    
    ---------
    
    Co-authored-by: Shoaib <[email protected]>
    Co-authored-by: Rafsun Masud <[email protected]>
    
    ---------
    
    Co-authored-by: Shoaib <[email protected]>
    Co-authored-by: Rafsun Masud <[email protected]>
---
 .github/workflows/go-driver.yml       | 18 ++-------
 .github/workflows/installcheck.yaml   |  4 +-
 .github/workflows/jdbc-driver.yaml    | 18 ++-------
 .github/workflows/nodejs-driver.yaml  | 18 ++-------
 .github/workflows/python-driver.yaml  | 18 ++-------
 README.md                             | 10 ++---
 src/backend/catalog/ag_catalog.c      | 21 +++++-----
 src/backend/catalog/ag_label.c        |  7 ++--
 src/backend/executor/cypher_set.c     |  3 +-
 src/backend/executor/cypher_utils.c   | 43 ++++++++++++++++-----
 src/backend/nodes/cypher_readfuncs.c  | 24 +++++++++---
 src/backend/parser/cypher_clause.c    | 72 ++++++++++++++++++++++++++++++++++-
 src/backend/parser/cypher_expr.c      |  3 +-
 src/backend/parser/cypher_item.c      | 14 +++----
 src/backend/parser/cypher_parse_agg.c | 12 +++++-
 src/backend/utils/adt/agtype.c        | 24 ++++++++----
 16 files changed, 197 insertions(+), 112 deletions(-)

diff --git a/.github/workflows/go-driver.yml b/.github/workflows/go-driver.yml
index 10b1abaa..015ab99f 100644
--- a/.github/workflows/go-driver.yml
+++ b/.github/workflows/go-driver.yml
@@ -2,10 +2,10 @@ name: Go Driver Tests
 
 on:
   push:
-    branches: [ "master", "PG16" ]
+    branches: [ "PG16" ]
 
   pull_request:
-    branches: [ "master", "PG16" ]
+    branches: [ "PG16" ]
 
 jobs:
   build:
@@ -23,19 +23,7 @@ jobs:
 
     - name: Set tag based on branch
       run: |
-        if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
-          if [[ "$GITHUB_REF" == "refs/heads/master" ]]; then
-            echo "TAG=latest" >> $GITHUB_ENV
-          elif [[ "$GITHUB_REF" == "refs/heads/PG16" ]]; then
-            echo "TAG=PG16_latest" >> $GITHUB_ENV
-          fi
-        elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
-          if [[ "$GITHUB_BASE_REF" == "master" ]]; then
-            echo "TAG=latest" >> $GITHUB_ENV
-          elif [[ "$GITHUB_BASE_REF" == "PG16" ]]; then
-            echo "TAG=PG16_latest" >> $GITHUB_ENV
-          fi
-        fi
+        echo "TAG=PG16_latest" >> $GITHUB_ENV
 
     - name: Run apache/age docker image
       run: |
diff --git a/.github/workflows/installcheck.yaml 
b/.github/workflows/installcheck.yaml
index a975aeb4..fa8b9a4e 100644
--- a/.github/workflows/installcheck.yaml
+++ b/.github/workflows/installcheck.yaml
@@ -2,9 +2,9 @@ name: Build / Regression
 
 on:
   push:
-    branches: [ 'master', 'PG16' ]
+    branches: [ 'PG16' ]
   pull_request:
-    branches: [ 'master', 'PG16' ]
+    branches: [ 'PG16' ]
 
 jobs:
   build:
diff --git a/.github/workflows/jdbc-driver.yaml 
b/.github/workflows/jdbc-driver.yaml
index 81d2558a..d91c8c97 100644
--- a/.github/workflows/jdbc-driver.yaml
+++ b/.github/workflows/jdbc-driver.yaml
@@ -2,10 +2,10 @@ name: JDBC Driver Tests
 
 on:
   push:
-    branches: [ "master", "PG16" ]
+    branches: [ "PG16" ]
 
   pull_request:
-    branches: [ "master", "PG16" ]
+    branches: [ "PG16" ]
 
 jobs:
   build:
@@ -25,19 +25,7 @@ jobs:
 
     - name: Set tag based on branch
       run: |
-        if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
-          if [[ "$GITHUB_REF" == "refs/heads/master" ]]; then
-            echo "TAG=latest" >> $GITHUB_ENV
-          elif [[ "$GITHUB_REF" == "refs/heads/PG16" ]]; then
-            echo "TAG=PG16_latest" >> $GITHUB_ENV
-          fi
-        elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
-          if [[ "$GITHUB_BASE_REF" == "master" ]]; then
-            echo "TAG=latest" >> $GITHUB_ENV
-          elif [[ "$GITHUB_BASE_REF" == "PG16" ]]; then
-            echo "TAG=PG16_latest" >> $GITHUB_ENV
-          fi
-        fi
+        echo "TAG=PG16_latest" >> $GITHUB_ENV
 
     - name: Build and Test
       run: |
diff --git a/.github/workflows/nodejs-driver.yaml 
b/.github/workflows/nodejs-driver.yaml
index bc926e6f..c6433041 100644
--- a/.github/workflows/nodejs-driver.yaml
+++ b/.github/workflows/nodejs-driver.yaml
@@ -2,10 +2,10 @@ name: Nodejs Driver Tests
 
 on:
   push:
-    branches: [ "master", "PG16" ]
+    branches: [ "PG16" ]
 
   pull_request:
-    branches: [ "master", "PG16" ]
+    branches: [ "PG16" ]
 
 jobs:
   build:
@@ -20,19 +20,7 @@ jobs:
 
     - name: Set tag based on branch
       run: |
-        if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
-          if [[ "$GITHUB_REF" == "refs/heads/master" ]]; then
-            echo "TAG=latest" >> $GITHUB_ENV
-          elif [[ "$GITHUB_REF" == "refs/heads/PG16" ]]; then
-            echo "TAG=PG16_latest" >> $GITHUB_ENV
-          fi
-        elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
-          if [[ "$GITHUB_BASE_REF" == "master" ]]; then
-            echo "TAG=latest" >> $GITHUB_ENV
-          elif [[ "$GITHUB_BASE_REF" == "PG16" ]]; then
-            echo "TAG=PG16_latest" >> $GITHUB_ENV
-          fi
-        fi
+        echo "TAG=PG16_latest" >> $GITHUB_ENV
 
     - name: Run apache/age docker image
       run: |
diff --git a/.github/workflows/python-driver.yaml 
b/.github/workflows/python-driver.yaml
index 3e7f8ee5..ebff5449 100644
--- a/.github/workflows/python-driver.yaml
+++ b/.github/workflows/python-driver.yaml
@@ -2,10 +2,10 @@ name: Python Driver Tests
 
 on:
   push:
-    branches: [ "master", "PG16" ]
+    branches: [ "PG16" ]
 
   pull_request:
-    branches: [ "master", "PG16" ]
+    branches: [ "PG16" ]
 
 jobs:
   build:
@@ -20,19 +20,7 @@ jobs:
 
     - name: Set tag based on branch
       run: |
-        if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
-          if [[ "$GITHUB_REF" == "refs/heads/master" ]]; then
-            echo "TAG=latest" >> $GITHUB_ENV
-          elif [[ "$GITHUB_REF" == "refs/heads/PG16" ]]; then
-            echo "TAG=PG16_latest" >> $GITHUB_ENV
-          fi
-        elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
-          if [[ "$GITHUB_BASE_REF" == "master" ]]; then
-            echo "TAG=latest" >> $GITHUB_ENV
-          elif [[ "$GITHUB_BASE_REF" == "PG16" ]]; then
-            echo "TAG=PG16_latest" >> $GITHUB_ENV
-          fi
-        fi
+        echo "TAG=PG16_latest" >> $GITHUB_ENV
 
     - name: Run apache/age docker image
       run: |
diff --git a/README.md b/README.md
index a6d2728e..48551200 100644
--- a/README.md
+++ b/README.md
@@ -33,8 +33,8 @@
     <img 
src="https://img.shields.io/badge/Release-v1.4.0-FFA500?labelColor=gray&style=flat&link=https://github.com/apache/age/releases"/>
   </a>
   &nbsp;
-  <a href="https://www.postgresql.org/docs/15/index.html";>
-    <img src="https://img.shields.io/badge/Version-Postgresql 
15-00008B?labelColor=gray&style=flat&link=https://www.postgresql.org/docs/15/index.html"/>
+  <a href="https://www.postgresql.org/docs/16/index.html";>
+    <img src="https://img.shields.io/badge/Version-Postgresql 
16-00008B?labelColor=gray&style=flat&link=https://www.postgresql.org/docs/16/index.html"/>
   </a>
   &nbsp;
   <a href="https://github.com/apache/age/issues";>
@@ -131,7 +131,7 @@ Apache AGE is intended to be simple to install and run. It 
can be installed with
 &nbsp;Install PostgreSQL
 </h4>
 
-You will need to install an AGE compatible version of Postgres<a>, for now AGE 
supports Postgres 11, 12, 13, 14 & 15. Supporting the latest versions is on AGE 
roadmap.
+You will need to install an AGE compatible version of Postgres<a>, for now AGE 
supports Postgres 11, 12, 13, 14, 15 & 16. Supporting the latest versions is on 
AGE roadmap.
 
 <h4>
 &nbsp;Installation via Package Manager
@@ -149,7 +149,7 @@ sudo apt install postgresql
 &nbsp;Installation From Source Code
 </h4>
 
-You can <a href="https://www.postgresql.org/ftp/source/";> download the 
Postgres </a> source code and install your own instance of Postgres. You can 
read instructions on how to install from source code for different versions on 
the <a href="https://www.postgresql.org/docs/15/installation.html";>official 
Postgres Website.</a>
+You can <a href="https://www.postgresql.org/ftp/source/";> download the 
Postgres </a> source code and install your own instance of Postgres. You can 
read instructions on how to install from source code for different versions on 
the <a href="https://www.postgresql.org/docs/16/installation.html";>official 
Postgres Website.</a>
 
 
 
@@ -158,7 +158,7 @@ You can <a href="https://www.postgresql.org/ftp/source/";> 
download the Postgres
 
 Clone the <a href="https://github.com/apache/age";>github repository</a> or 
download the <a href="https://github.com/apache/age/releases";>download an 
official release.
 </a>
-Run the pg_config utility and check the version of PostgreSQL. Currently, only 
PostgreSQL versions 11, 12, 13, 14 & 15 are supported. If you have any other 
version of Postgres, you will need to install PostgreSQL version 11, 12, 13, 
14, or 15.
+Run the pg_config utility and check the version of PostgreSQL. Currently, only 
PostgreSQL versions 11, 12, 13, 14, 15 & 16 are supported. If you have any 
other version of Postgres, you will need to install PostgreSQL version 11, 12, 
13, 14, 15 or 16.
 <br>
     
 ```bash
diff --git a/src/backend/catalog/ag_catalog.c b/src/backend/catalog/ag_catalog.c
index ab747ae6..7b8a96c1 100644
--- a/src/backend/catalog/ag_catalog.c
+++ b/src/backend/catalog/ag_catalog.c
@@ -86,26 +86,29 @@ void process_utility_hook_fini(void)
  * from being thrown, we need to disable the object_access_hook before dropping
  * the extension.
  */
-void ag_ProcessUtility_hook(PlannedStmt *pstmt, const char *queryString, bool 
readOnlyTree,
-                             ProcessUtilityContext context, ParamListInfo 
params,
-                             QueryEnvironment *queryEnv, DestReceiver *dest,
-                             QueryCompletion *qc)
+void ag_ProcessUtility_hook(PlannedStmt *pstmt, const char *queryString,
+                            bool readOnlyTree, ProcessUtilityContext context,
+                            ParamListInfo params, QueryEnvironment *queryEnv,
+                            DestReceiver *dest, QueryCompletion *qc)
 {
     if (is_age_drop(pstmt))
+    {
         drop_age_extension((DropStmt *)pstmt->utilityStmt);
+    }
     else if (prev_process_utility_hook)
-        (*prev_process_utility_hook) (pstmt, queryString, readOnlyTree, 
context, params,
-                                      queryEnv, dest, qc);
+    {
+        (*prev_process_utility_hook) (pstmt, queryString, readOnlyTree, 
context,
+                                      params, queryEnv, dest, qc);
+    }
     else
     {
         Assert(IsA(pstmt, PlannedStmt));
         Assert(pstmt->commandType == CMD_UTILITY);
         Assert(queryString != NULL);   /* required as of 8.4 */
         Assert(qc == NULL || qc->commandTag == CMDTAG_UNKNOWN);
-        standard_ProcessUtility(pstmt, queryString, readOnlyTree, context, 
params, queryEnv,
-                                dest, qc);
+        standard_ProcessUtility(pstmt, queryString, readOnlyTree, context,
+                                params, queryEnv, dest, qc);
     }
-        
 }
 
 static void drop_age_extension(DropStmt *stmt)
diff --git a/src/backend/catalog/ag_label.c b/src/backend/catalog/ag_label.c
index 09fba102..563d5348 100644
--- a/src/backend/catalog/ag_label.c
+++ b/src/backend/catalog/ag_label.c
@@ -188,9 +188,10 @@ Datum _label_name(PG_FUNCTION_ARGS)
     uint32 label_id;
 
     if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
-        PG_RETURN_NULL(); 
-        //ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-        //                errmsg("graph_oid and label_id must not be null")));
+    {
+        ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                        errmsg("graph_oid and label_id must not be null")));
+    }
 
     graph = PG_GETARG_OID(0);
 
diff --git a/src/backend/executor/cypher_set.c 
b/src/backend/executor/cypher_set.c
index fdfceda1..44613130 100644
--- a/src/backend/executor/cypher_set.c
+++ b/src/backend/executor/cypher_set.c
@@ -485,7 +485,8 @@ static void process_update_list(CustomScanState *node)
         }
 
         // Alter the properties Agtype value.
-        if (strcmp(update_item->prop_name, ""))
+        if (update_item->prop_name != NULL &&
+            strcmp(update_item->prop_name, "") != 0)
         {
             altered_properties = alter_property_value(original_properties,
                                                       update_item->prop_name,
diff --git a/src/backend/executor/cypher_utils.c 
b/src/backend/executor/cypher_utils.c
index baccc028..e649c113 100644
--- a/src/backend/executor/cypher_utils.c
+++ b/src/backend/executor/cypher_utils.c
@@ -51,16 +51,20 @@
 
 /*
  * Given the graph name and the label name, create a ResultRelInfo for the 
table
- * those to variables represent. Open the Indices too.
+ * those two variables represent. Open the Indices too.
  */
 ResultRelInfo *create_entity_result_rel_info(EState *estate, char *graph_name,
                                              char *label_name)
 {
-    RangeVar *rv;
-    Relation label_relation;
-    ResultRelInfo *resultRelInfo;
+    RangeVar *rv = NULL;
+    Relation label_relation = NULL;
+    ResultRelInfo *resultRelInfo = NULL;
+    ParseState *pstate = NULL;
+    RangeTblEntry *rte = NULL;
+    int pii = 0;
 
-    ParseState *pstate = make_parsestate(NULL);
+    /* create a new parse state for this operation */
+    pstate = make_parsestate(NULL);
 
     resultRelInfo = palloc(sizeof(ResultRelInfo));
 
@@ -75,12 +79,33 @@ ResultRelInfo *create_entity_result_rel_info(EState 
*estate, char *graph_name,
 
     label_relation = parserOpenTable(pstate, rv, RowExclusiveLock);
 
-    // initialize the resultRelInfo
-    InitResultRelInfo(resultRelInfo, label_relation,
-                      0, NULL,
+    /*
+     * Get the rte to determine the correct perminfoindex value. Some rtes
+     * may have it set up, some created here (executor) may not.
+     *
+     * Note: The RTEPermissionInfo structure was added in PostgreSQL version 
16.
+     *
+     * Note: We use the list_length because exec_rt_fetch starts at 1, not 0.
+     *       Doing this gives us the last rte in the es_range_table list, which
+     *       is the rte in question.
+     *
+     *       If the rte is created here and doesn't have a perminfoindex, we
+     *       need to pass on a 0. Otherwise, later on 
GetResultRTEPermissionInfo
+     *       will attempt to get the rte's RTEPermissionInfo data, which 
doesn't
+     *       exist.
+     *
+     * TODO: Ideally, we should consider creating the RTEPermissionInfo data,
+     *       but as this is just a read of the label relation, it is likely
+     *       unnecessary.
+     */
+    rte = exec_rt_fetch(list_length(estate->es_range_table), estate);
+    pii = (rte->perminfoindex == 0) ? 0 : list_length(estate->es_range_table);
+
+    /* initialize the resultRelInfo */
+    InitResultRelInfo(resultRelInfo, label_relation, pii, NULL,
                       estate->es_instrument);
 
-    // open the parse state
+    /* open the indices */
     ExecOpenIndices(resultRelInfo, false);
 
     free_parsestate(pstate);
diff --git a/src/backend/nodes/cypher_readfuncs.c 
b/src/backend/nodes/cypher_readfuncs.c
index 1aa763d5..0587229f 100644
--- a/src/backend/nodes/cypher_readfuncs.c
+++ b/src/backend/nodes/cypher_readfuncs.c
@@ -24,6 +24,7 @@
 #include "nodes/cypher_readfuncs.h"
 #include "nodes/cypher_nodes.h"
 
+static char *nullable_string(const char *token, int length);
 /*
  * Copied From Postgres
  *
@@ -111,7 +112,7 @@
 #define READ_STRING_FIELD(fldname) \
         token = pg_strtok(&length); \
         token = pg_strtok(&length); \
-        local_node->fldname = non_nullable_string(token, length)
+        local_node->fldname = nullable_string(token, length)
 
 // Read a parse location field (and throw away the value, per notes above)
 #define READ_LOCATION_FIELD(fldname) \
@@ -162,11 +163,22 @@
 
 #define strtobool(x)  ((*(x) == 't') ? true : false)
 
-#define nullable_string(token,length)  \
-        ((length) == 0 ? NULL : debackslash(token, length))
-
-#define non_nullable_string(token,length)  \
-        ((length == 2 && token[0] == '"' && token[1] == '"') ? "" : 
debackslash(token, length))
+/* copied from PG16 function of the same name for consistency */
+static char *nullable_string(const char *token, int length)
+{
+    /* outToken emits <> for NULL, and pg_strtok makes that an empty string */
+    if (length == 0)
+    {
+        return NULL;
+    }
+    /* outToken emits "" for empty string */
+    if (length == 2 && token[0] == '"' && token[1] == '"')
+    {
+        return pstrdup("");
+    }
+    /* otherwise, we must remove protective backslashes added by outToken */
+    return debackslash(token, length);
+}
 
 /*
  * Default read function for cypher nodes. For most nodes, we don't expect
diff --git a/src/backend/parser/cypher_clause.c 
b/src/backend/parser/cypher_clause.c
index c4201be2..e89f20b8 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -45,6 +45,7 @@
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
 #include "parser/parsetree.h"
+#include "parser/parse_relation.h"
 #include "rewrite/rewriteHandler.h"
 #include "utils/typcache.h"
 #include "utils/lsyscache.h"
@@ -331,6 +332,8 @@ static List *make_target_list_from_join(ParseState *pstate,
                                         RangeTblEntry *rte);
 static FuncExpr *make_clause_func_expr(char *function_name,
                                        Node *clause_information);
+static void markRelsAsNulledBy(ParseState *pstate, Node *n, int jindex);
+
 /* for VLE support */
 static ParseNamespaceItem *transform_RangeFunction(cypher_parsestate *cpstate,
                                                    RangeFunction *r);
@@ -2472,8 +2475,17 @@ static void get_res_cols(ParseState *pstate, 
ParseNamespaceItem *l_pnsi,
 
         if (var == NULL)
         {
+            Var *v;
+
+            /*
+             * Each join (left) RTE's Var, that references a column of the
+             * right RTE, needs to be marked 'nullable'.
+             */
+            v = lfirst(r_lvar);
+            markNullableIfNeeded(pstate, v);
+
             colnames = lappend(colnames, lfirst(r_lname));
-            colvars = lappend(colvars, lfirst(r_lvar));
+            colvars = lappend(colvars, v);
         }
     }
 
@@ -2522,6 +2534,13 @@ static RangeTblEntry 
*transform_cypher_optional_match_clause(cypher_parsestate *
     j->rarg = transform_clause_for_join(cpstate, clause, &r_rte,
                                         &r_nsitem, r_alias);
 
+    /*
+     * Since this is a left join, we need to mark j->rarg as it may potentially
+     * emit NULL. The jindex argument holds rtindex of the join's RTE, which is
+     * created right after j->arg's RTE in this case.
+     */
+    markRelsAsNulledBy(pstate, j->rarg, r_nsitem->p_rtindex + 1);
+
     // we are done transform the lateral left join
     pstate->p_lateral_active = false;
 
@@ -6377,6 +6396,13 @@ transform_merge_make_lateral_join(cypher_parsestate 
*cpstate, Query *query,
     j->rarg = transform_clause_for_join(cpstate, isolated_merge_clause, &r_rte,
                                             &r_nsitem, r_alias);
 
+    /*
+     * Since this is a left join, we need to mark j->rarg as it may potentially
+     * emit NULL. The jindex argument holds rtindex of the join's RTE, which is
+     * created right after j->arg's RTE in this case.
+     */
+    markRelsAsNulledBy(pstate, j->rarg, r_nsitem->p_rtindex + 1);
+
     // deactivate the lateral flag
     pstate->p_lateral_active = false;
 
@@ -7095,6 +7121,50 @@ static FuncExpr *make_clause_func_expr(char 
*function_name,
     return func_expr;
 }
 
+/*
+ * This function is borrowed from PG version 16.1.
+ *
+ * It is used in transformations involving left join in Optional Match and
+ * Merge in a similar way PG16's transformFromClauseItem() uses it.
+ */
+static void markRelsAsNulledBy(ParseState *pstate, Node *n, int jindex)
+{
+    int varno;
+    ListCell *lc;
+
+    /* Note: we can't see FromExpr here */
+    if (IsA(n, RangeTblRef))
+    {
+        varno = ((RangeTblRef *) n)->rtindex;
+    }
+    else if (IsA(n, JoinExpr))
+    {
+        JoinExpr   *j = (JoinExpr *) n;
+
+        /* recurse to children */
+        markRelsAsNulledBy(pstate, j->larg, jindex);
+        markRelsAsNulledBy(pstate, j->rarg, jindex);
+        varno = j->rtindex;
+    }
+    else
+    {
+        elog(ERROR, "unrecognized node type: %d", (int) nodeTag(n));
+        varno = 0;                             /* keep compiler quiet */
+    }
+
+    /*
+     * Now add jindex to the p_nullingrels set for relation varno.  Since we
+     * maintain the p_nullingrels list lazily, we might need to extend it to
+     * make the varno'th entry exist.
+     */
+    while (list_length(pstate->p_nullingrels) < varno)
+    {
+        pstate->p_nullingrels = lappend(pstate->p_nullingrels, NULL);
+    }
+    lc = list_nth_cell(pstate->p_nullingrels, varno - 1);
+    lfirst(lc) = bms_add_member((Bitmapset *) lfirst(lc), jindex);
+}
+
 /*
  * Utility function that helps a clause add the information needed to
  * the query from the previous clause.
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index d3ced711..cd19a554 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -249,7 +249,8 @@ static Node *transform_A_Const(cypher_parsestate *cpstate, 
A_Const *ac)
             }
             else
             {
-                float8 f = float8in_internal(n, NULL, "double precision", n, 
NULL);
+                float8 f = float8in_internal(n, NULL, "double precision", n,
+                                             NULL);
 
                 d = float_to_agtype(f);
             }
diff --git a/src/backend/parser/cypher_item.c b/src/backend/parser/cypher_item.c
index 6e489fc5..d5d08876 100644
--- a/src/backend/parser/cypher_item.c
+++ b/src/backend/parser/cypher_item.c
@@ -40,7 +40,8 @@
 
 static List *ExpandAllTables(ParseState *pstate, int location);
 static List *expand_pnsi_attrs(ParseState *pstate, ParseNamespaceItem *pnsi,
-                              int sublevels_up, bool require_col_privs, int 
location);
+                              int sublevels_up, bool require_col_privs,
+                               int location);
 
 // see transformTargetEntry()
 TargetEntry *transform_cypher_item(cypher_parsestate *cpstate, Node *node,
@@ -161,10 +162,8 @@ static List *ExpandAllTables(ParseState *pstate, int 
location)
         /* Remember we found a p_cols_visible item */
         found_table = true;
 
-        target = list_concat(target, expand_pnsi_attrs(pstate,
-                                                      nsitem,
-                                                      0,
-                                                      true, location));
+        target = list_concat(target, expand_pnsi_attrs(pstate, nsitem, 0, true,
+                                                       location));
     }
 
     /* Check for "RETURN *;" */
@@ -181,7 +180,8 @@ static List *ExpandAllTables(ParseState *pstate, int 
location)
  * Modified to exclude hidden variables and aliases in RETURN *
  */
 static List *expand_pnsi_attrs(ParseState *pstate, ParseNamespaceItem *pnsi,
-                              int sublevels_up, bool require_col_privs, int 
location)
+                              int sublevels_up, bool require_col_privs,
+                               int location)
 {
     RangeTblEntry *rte = pnsi->p_rte;
     RTEPermissionInfo *perminfo = pnsi->p_perminfo;
@@ -190,7 +190,7 @@ static List *expand_pnsi_attrs(ParseState *pstate, 
ParseNamespaceItem *pnsi,
     List *te_list = NIL;
     int var_prefix_len = strlen(AGE_DEFAULT_VARNAME_PREFIX);
     int alias_prefix_len = strlen(AGE_DEFAULT_ALIAS_PREFIX);
-    
+
     vars = expandNSItemVars(pstate, pnsi, sublevels_up, location, &names);
 
     /*
diff --git a/src/backend/parser/cypher_parse_agg.c 
b/src/backend/parser/cypher_parse_agg.c
index 284a07e6..cd743fcc 100644
--- a/src/backend/parser/cypher_parse_agg.c
+++ b/src/backend/parser/cypher_parse_agg.c
@@ -236,7 +236,9 @@ void parse_check_aggregates(ParseState *pstate, Query *qry)
     finalize_grouping_exprs(clause, pstate, qry, groupClauses, root,
                             have_non_var_grouping);
     if (hasJoinRTEs)
+    {
         clause = flatten_join_alias_vars(root, qry, clause);
+    }
     check_ungrouped_columns(clause, pstate, qry, groupClauses,
                             groupClauseCommonVars, have_non_var_grouping,
                             &func_grouped_rels);
@@ -245,7 +247,9 @@ void parse_check_aggregates(ParseState *pstate, Query *qry)
     finalize_grouping_exprs(clause, pstate, qry, groupClauses, root,
                             have_non_var_grouping);
     if (hasJoinRTEs)
+    {
         clause = flatten_join_alias_vars(root, qry, clause);
+    }
     check_ungrouped_columns(clause, pstate, qry, groupClauses,
                             groupClauseCommonVars, have_non_var_grouping,
                             &func_grouped_rels);
@@ -254,10 +258,12 @@ void parse_check_aggregates(ParseState *pstate, Query 
*qry)
      * Per spec, aggregates can't appear in a recursive term.
      */
     if (pstate->p_hasAggs && hasSelfRefRTEs)
+    {
         ereport(ERROR,
                 (errcode(ERRCODE_INVALID_RECURSION),
                  errmsg("aggregate functions are not allowed in a recursive 
query's recursive term"),
                  parser_errposition(pstate, locate_agg_of_level((Node *) qry, 
0))));
+    }
 }
 
 /*
@@ -562,7 +568,11 @@ static bool finalize_grouping_exprs_walker(Node *node,
                 Index ref = 0;
 
                 if (context->root)
-                    expr = flatten_join_alias_vars(context-> root, 
(Query*)context->root, expr);
+                {
+                    expr = flatten_join_alias_vars(context->root,
+                                                   (Query *)context->root,
+                                                   expr);
+                }
 
                 /*
                  * Each expression must match a grouping entry at the current
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 7751f230..18b9a6ab 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -4277,8 +4277,11 @@ Datum agtype_hash_cmp(PG_FUNCTION_ARGS)
     agtype_value *r;
     uint64 seed = 0xF0F0F0F0;
 
+    /* this function returns INTEGER which is 32 bits */
     if (PG_ARGISNULL(0))
-        PG_RETURN_INT64(0);
+    {
+        PG_RETURN_INT32(0);
+    }
 
     agt = AG_GET_ARG_AGTYPE_P(0);
 
@@ -4301,7 +4304,7 @@ Datum agtype_hash_cmp(PG_FUNCTION_ARGS)
         seed = LEFT_ROTATE(seed, 1);
     }
 
-    PG_RETURN_INT64(hash);
+    PG_RETURN_INT32(hash);
 }
 
 // Comparison function for btree Indexes
@@ -4312,18 +4315,25 @@ Datum agtype_btree_cmp(PG_FUNCTION_ARGS)
     agtype *agtype_lhs;
     agtype *agtype_rhs;
 
+    /* this function returns INTEGER which is 32bits */
     if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
-        PG_RETURN_INT16(0);
+    {
+        PG_RETURN_INT32(0);
+    }
     else if (PG_ARGISNULL(0))
-        PG_RETURN_INT16(1);
+    {
+        PG_RETURN_INT32(1);
+    }
     else if (PG_ARGISNULL(1))
-        PG_RETURN_INT16(-1);
+    {
+        PG_RETURN_INT32(-1);
+    }
 
     agtype_lhs = AG_GET_ARG_AGTYPE_P(0);
     agtype_rhs = AG_GET_ARG_AGTYPE_P(1);
 
-    PG_RETURN_INT64(compare_agtype_containers_orderability(&agtype_lhs->root,
-                                                     &agtype_rhs->root));
+    PG_RETURN_INT32(compare_agtype_containers_orderability(&agtype_lhs->root,
+                                                           &agtype_rhs->root));
 }
 
 PG_FUNCTION_INFO_V1(agtype_typecast_numeric);

Reply via email to