blob: b1ac6938aa675d4420db2b048d9b7354ebeeb8e4 [file] [log] [blame]
/*
* 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 "access/sysattr.h"
#include "catalog/pg_type_d.h"
#include "nodes/parsenodes.h"
#include "nodes/primnodes.h"
#include "nodes/relation.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 pre-existing 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 pre-existing 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 pre-existing 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 pre-existing paths
rel->pathlist = NIL;
rel->partial_pathlist = NIL;
add_path(rel, (Path *)cp);
}