diff --git a/src/backend/catalog/ag_label.c b/src/backend/catalog/ag_label.c index dea7e4be4..bbaad5292 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/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c index c4201be23..550da672e 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/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c index 6dd02680d..ce0d1bb0e 100644 --- a/src/backend/utils/adt/agtype.c +++ b/src/backend/utils/adt/agtype.c @@ -2193,12 +2193,9 @@ Datum _agtype_build_vertex(PG_FUNCTION_ARGS) /* handles null */ if (fcinfo->args[0].isnull) { - /* ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("_agtype_build_vertex() graphid cannot be NULL"))); - */ - PG_RETURN_NULL(); } if (fcinfo->args[1].isnull) @@ -2267,12 +2264,9 @@ Datum _agtype_build_edge(PG_FUNCTION_ARGS) /* process graph id */ if (fcinfo->args[0].isnull) { - PG_RETURN_NULL(); - /* ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("_agtype_build_edge() graphid cannot be NULL"))); - */ } id = AG_GETARG_GRAPHID(0);