* 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.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_VLE
} 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);
static void handle_cypher_vle_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_VLE:
handle_cypher_vle_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 (rte->rtekind != RTE_SUBQUERY)
return CYPHER_CLAUSE_NONE;
if (rte->subquery->targetList == NULL)
return CYPHER_CLAUSE_NONE;
te = (TargetEntry*)llast(rte->subquery->targetList);
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;
if (is_oid_ag_func(fe->funcid, VLE_CLAUSE_FUNCTION_NAME))
return CYPHER_CLAUSE_VLE;
else
return CYPHER_CLAUSE_NONE;
}
static void handle_cypher_delete_clause(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
TargetEntry *te;
FuncExpr *fe;
List *custom_private;
ExtensiblePath *cp;
te = (TargetEntry *)llast(rte->subquery->targetList);
fe = (FuncExpr *)te->expr;
custom_private = fe->args;
cp = create_cypher_delete_path(root, rel, custom_private);
if (root->distinct_pathkeys) {
rel->cheapest_startup_path = (Path *)cp;
rel->cheapest_total_path = list_make1((Path *)cp);
rel->cheapest_parameterized_paths =list_make1((Path *)cp);
}
rel->pathlist = NIL;
add_path(root, 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;
ExtensiblePath *cp;
te = (TargetEntry *)llast(rte->subquery->targetList);
fe = (FuncExpr *)te->expr;
custom_private = fe->args;
cp = create_cypher_create_path(root, rel, custom_private);
rel->pathlist = NIL;
add_path(root, rel, &(cp->path));
}
static void handle_cypher_set_clause(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
TargetEntry *te;
FuncExpr *fe;
List *custom_private;
ExtensiblePath *cp;
te = (TargetEntry *)llast(rte->subquery->targetList);
fe = (FuncExpr *)te->expr;
custom_private = fe->args;
cp = create_cypher_set_path(root, rel, custom_private);
rel->pathlist = NIL;
add_path(root, rel, (Path *)cp);
}
static void handle_cypher_merge_clause(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
TargetEntry *te;
FuncExpr *fe;
List *custom_private;
ExtensiblePath *cp;
te = (TargetEntry *)llast(rte->subquery->targetList);
fe = (FuncExpr *)te->expr;
custom_private = fe->args;
cp = create_cypher_merge_path(root, rel, custom_private);
rel->pathlist = NIL;
add_path(root, rel, (Path *)cp);
}
* Check that the specified List is valid (so far as we can tell).
*/
static void check_list_invariants(const List *list)
{
if (list == NIL) {
return;
}
Assert(list->length > 0);
Assert(list->head != NULL);
Assert(list->tail != NULL);
Assert(list->type == T_List || list->type == T_IntList || list->type == T_OidList);
if (list->length == 1) {
Assert(list->head == list->tail);
}
if (list->length == 2) {
Assert(list->head->next == list->tail);
}
Assert(list->tail->next == NULL);
}
static List *
list_delete_last(List *list)
{
check_list_invariants(list);
if (list == NIL)
return NIL;
if (list_length(list) <= 1)
{
list_free(list);
return NIL;
}
return list_truncate(list, list_length(list) - 1);
}
static void handle_cypher_vle_clause(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
TargetEntry *te;
FuncExpr *fe;
List *custom_private;
ExtensiblePath *cp;
te = (TargetEntry *)llast(rte->subquery->targetList);
fe = (FuncExpr *)te->expr;
list_delete_last(rte->subquery->targetList);
list_delete_last( rel-> subplan->targetlist);
custom_private = fe->args;
cp = create_cypher_vle_path(root, rel, custom_private);
rel->pathlist = NIL;
rel->cheapest_parameterized_paths =list_make1((Path *)cp);
rel->cheapest_startup_path = (Path *)cp;
rel->cheapest_total_path= list_make1((Path *)cp);
add_path(root, rel, (Path *)cp);
}