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>
- <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>
<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
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>
Installation via Package Manager
@@ -149,7 +149,7 @@ sudo apt install postgresql
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);