/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "postgres.h" #include "nodes/parsenodes.h" #include "nodes/primnodes.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/cypher_pathnode.h" #include "optimizer/cypher_paths.h" #include "utils/ag_func.h" typedef enum cypher_clause_kind { CYPHER_CLAUSE_NONE, CYPHER_CLAUSE_CREATE, CYPHER_CLAUSE_SET, CYPHER_CLAUSE_DELETE, CYPHER_CLAUSE_MERGE } cypher_clause_kind; static set_rel_pathlist_hook_type prev_set_rel_pathlist_hook; static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); static cypher_clause_kind get_cypher_clause_kind(RangeTblEntry *rte); static void handle_cypher_create_clause(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); static void handle_cypher_set_clause(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); static void handle_cypher_delete_clause(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); static void handle_cypher_merge_clause(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); void set_rel_pathlist_init(void) { prev_set_rel_pathlist_hook = set_rel_pathlist_hook; set_rel_pathlist_hook = set_rel_pathlist; } void set_rel_pathlist_fini(void) { set_rel_pathlist_hook = prev_set_rel_pathlist_hook; } static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { if (prev_set_rel_pathlist_hook) prev_set_rel_pathlist_hook(root, rel, rti, rte); switch (get_cypher_clause_kind(rte)) { case CYPHER_CLAUSE_CREATE: handle_cypher_create_clause(root, rel, rti, rte); break; case CYPHER_CLAUSE_SET: handle_cypher_set_clause(root, rel, rti, rte); break; case CYPHER_CLAUSE_DELETE: handle_cypher_delete_clause(root, rel, rti, rte); break; case CYPHER_CLAUSE_MERGE: handle_cypher_merge_clause(root, rel, rti, rte); break; case CYPHER_CLAUSE_NONE: break; default: ereport(ERROR, (errmsg_internal("invalid cypher_clause_kind"))); } } /* * Check to see if the rte is a Cypher clause. An rte is only a Cypher clause * if it is a subquery, with the last entry in its target list, that is a * FuncExpr. */ static cypher_clause_kind get_cypher_clause_kind(RangeTblEntry *rte) { TargetEntry *te; FuncExpr *fe; // If it's not a subquery, it's not a Cypher clause. if (rte->rtekind != RTE_SUBQUERY) return CYPHER_CLAUSE_NONE; // Make sure the targetList isn't NULL. NULL means potential EXIST subclause if (rte->subquery->targetList == NULL) return CYPHER_CLAUSE_NONE; // A Cypher clause function is always the last entry. te = llast(rte->subquery->targetList); // If the last entry is not a FuncExpr, it's not a Cypher clause. if (!IsA(te->expr, FuncExpr)) return CYPHER_CLAUSE_NONE; fe = (FuncExpr *)te->expr; if (is_oid_ag_func(fe->funcid, CREATE_CLAUSE_FUNCTION_NAME)) return CYPHER_CLAUSE_CREATE; if (is_oid_ag_func(fe->funcid, SET_CLAUSE_FUNCTION_NAME)) return CYPHER_CLAUSE_SET; if (is_oid_ag_func(fe->funcid, DELETE_CLAUSE_FUNCTION_NAME)) return CYPHER_CLAUSE_DELETE; if (is_oid_ag_func(fe->funcid, MERGE_CLAUSE_FUNCTION_NAME)) return CYPHER_CLAUSE_MERGE; else return CYPHER_CLAUSE_NONE; } // replace all possible paths with our CustomPath static void handle_cypher_delete_clause(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { TargetEntry *te; FuncExpr *fe; List *custom_private; CustomPath *cp; // Add the pattern to the CustomPath te = (TargetEntry *)llast(rte->subquery->targetList); fe = (FuncExpr *)te->expr; // pass the const that holds the data structure to the path. custom_private = fe->args; cp = create_cypher_delete_path(root, rel, custom_private); // Discard any preexisting paths rel->pathlist = NIL; rel->partial_pathlist = NIL; add_path(rel, (Path *)cp); } /* * Take the paths possible for the RelOptInfo that represents our * _cypher_delete_clause function replace them with our delete clause * path. The original paths will be children to the new delete path. */ static void handle_cypher_create_clause(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { TargetEntry *te; FuncExpr *fe; List *custom_private; CustomPath *cp; // Add the pattern to the CustomPath te = (TargetEntry *)llast(rte->subquery->targetList); fe = (FuncExpr *)te->expr; // pass the const that holds the data structure to the path. custom_private = fe->args; cp = create_cypher_create_path(root, rel, custom_private); // Discard any preexisting paths, they should be under the cp path rel->pathlist = NIL; rel->partial_pathlist = NIL; // Add the new path to the rel. add_path(rel, (Path *)cp); } // replace all possible paths with our CustomPath static void handle_cypher_set_clause(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { TargetEntry *te; FuncExpr *fe; List *custom_private; CustomPath *cp; // Add the pattern to the CustomPath te = (TargetEntry *)llast(rte->subquery->targetList); fe = (FuncExpr *)te->expr; // pass the const that holds the data structure to the path. custom_private = fe->args; cp = create_cypher_set_path(root, rel, custom_private); // Discard any preexisting paths rel->pathlist = NIL; rel->partial_pathlist = NIL; add_path(rel, (Path *)cp); } // replace all possible paths with our CustomPath static void handle_cypher_merge_clause(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { TargetEntry *te; FuncExpr *fe; List *custom_private; CustomPath *cp; // Add the pattern to the CustomPath te = (TargetEntry *)llast(rte->subquery->targetList); fe = (FuncExpr *)te->expr; // pass the const that holds the data structure to the path. custom_private = fe->args; cp = create_cypher_merge_path(root, rel, custom_private); // Discard any preexisting paths rel->pathlist = NIL; rel->partial_pathlist = NIL; add_path(rel, (Path *)cp); }