/*
|
* 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);
|
}
|