Skip to content

Commit

Permalink
fix: Supports VERTICES, EDGES on cypher list predicate functions. (#603)
Browse files Browse the repository at this point in the history
Previously, predicate function must requires JSONB(Array) type. so, Array
type(e.g., Vertices or Edges) cannot be used.

so, Makes CypherListComp supports Vertex and Edge Array, and uses their
properties.

additionally, implement `isEmpty(Jsonb)` function.

(cherry picked from commit c9c8a8d)
  • Loading branch information
emotionbug committed Dec 22, 2022
1 parent f1bdb55 commit 019e39b
Show file tree
Hide file tree
Showing 10 changed files with 451 additions and 67 deletions.
43 changes: 38 additions & 5 deletions src/backend/executor/execExpr.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
#include "parser/parse_type.h"
#include "utils/fmgroids.h"
#include "utils/syscache.h"


typedef struct LastAttnumInfo
Expand Down Expand Up @@ -3745,8 +3748,17 @@ ExecInitCypherListComp(ExprEvalStep *scratch, CypherListCompExpr *listcompexpr,
ExprEvalStep elem_step;
ExprEvalStep loop_step;
int end_stepno;
bool is_array_type;

Assert(exprType((Node *) listcompexpr->list) == JSONBOID);
Type targetType;
Form_pg_type typeForm;

targetType = typeidType(exprType((const Node *) listcompexpr->list));
typeForm = (Form_pg_type) GETSTRUCT(targetType);

is_array_type = typeForm->typelem != InvalidOid &&
typeForm->typstorage != TYPSTORAGE_PLAIN;
ReleaseSysCache(targetType);

ExecInitExprRec(listcompexpr->list, state, scratch->resvalue,
scratch->resnull);
Expand All @@ -3768,8 +3780,24 @@ ExecInitCypherListComp(ExprEvalStep *scratch, CypherListCompExpr *listcompexpr,
init_step.opcode = EEOP_CYPHERLISTCOMP_ITER_INIT;
init_step.d.cypherlistcomp_iter.listvalue = scratch->resvalue;
init_step.d.cypherlistcomp_iter.listnull = scratch->resnull;
init_step.d.cypherlistcomp_iter.listiter =
(JsonbIterator **) palloc(sizeof(JsonbIterator *));

init_step.d.cypherlistcomp_iter.is_null_list_or_array =
(bool *) palloc(sizeof(bool));
*init_step.d.cypherlistcomp_iter.is_null_list_or_array = false;

if (is_array_type)
{
init_step.d.cypherlistcomp_iter.jsonb_list_iterator = NULL;
init_step.d.cypherlistcomp_iter.array_iterator =
(CypherListCompArrayIterator *) palloc(sizeof(CypherListCompArrayIterator));
}
else
{
init_step.d.cypherlistcomp_iter.jsonb_list_iterator =
(JsonbIterator **) palloc(sizeof(JsonbIterator *));
init_step.d.cypherlistcomp_iter.array_iterator = NULL;
}

ExprEvalPushStep(state, &init_step);

elem_resvalue = (Datum *) palloc(sizeof(Datum));
Expand All @@ -3778,8 +3806,13 @@ ExecInitCypherListComp(ExprEvalStep *scratch, CypherListCompExpr *listcompexpr,
next_step.opcode = EEOP_CYPHERLISTCOMP_ITER_NEXT;
next_step.resvalue = elem_resvalue;
next_step.resnull = elem_resnull;
next_step.d.cypherlistcomp_iter.listiter =
init_step.d.cypherlistcomp_iter.listiter;
next_step.d.cypherlistcomp_iter.is_null_list_or_array =
init_step.d.cypherlistcomp_iter.is_null_list_or_array;
next_step.d.cypherlistcomp_iter.jsonb_list_iterator =
init_step.d.cypherlistcomp_iter.jsonb_list_iterator;
next_step.d.cypherlistcomp_iter.array_iterator =
init_step.d.cypherlistcomp_iter.array_iterator;

ExprEvalPushStep(state, &next_step);
next_stepno = state->steps_len - 1;

Expand Down
110 changes: 84 additions & 26 deletions src/backend/executor/execExprInterp.c
Original file line number Diff line number Diff line change
Expand Up @@ -5254,42 +5254,100 @@ void ExecEvalCypherListCompEnd(ExprState *state, ExprEvalStep *op)

void ExecEvalCypherListCompIterInit(ExprState *state, ExprEvalStep *op)
{
Jsonb *listjb;
JsonbIterator **ji;
JsonbValue jv;
if (op->d.cypherlistcomp_iter.jsonb_list_iterator != NULL)
{
Jsonb *listjb;
JsonbIterator **ji;
JsonbValue jv;

Assert(!*op->d.cypherlistcomp_iter.listnull);
Assert(!*op->d.cypherlistcomp_iter.listnull);

listjb = DatumGetJsonbP(*op->d.cypherlistcomp_iter.listvalue);
if (!JB_ROOT_IS_ARRAY(listjb) || JB_ROOT_IS_SCALAR(listjb))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("list is expected but %s",
JsonbToCString(NULL, &listjb->root,
VARSIZE(listjb)))));

ji = op->d.cypherlistcomp_iter.listiter;
*ji = JsonbIteratorInit(&listjb->root);
JsonbIteratorNext(ji, &jv, false);
listjb = DatumGetJsonbP(*op->d.cypherlistcomp_iter.listvalue);
*op->d.cypherlistcomp_iter.is_null_list_or_array = !JB_ROOT_IS_ARRAY(listjb) || JB_ROOT_IS_SCALAR(listjb);

ji = op->d.cypherlistcomp_iter.jsonb_list_iterator;
*ji = JsonbIteratorInit(&listjb->root);
JsonbIteratorNext(ji, &jv, false);
}
else
{
CypherListCompArrayIterator *cypher_array_iter = op->d.cypherlistcomp_iter.array_iterator;
array_iter *it = &cypher_array_iter->array_iter;
AnyArrayType *array = DatumGetAnyArrayP(*op->d.cypherlistcomp_iter.listvalue);

cypher_array_iter->array_size = ArrayGetNItems(AARR_NDIM(array),
AARR_DIMS(array));
cypher_array_iter->array_position = 0;
cypher_array_iter->array_typid = AARR_ELEMTYPE(array);

get_typlenbyvalalign(cypher_array_iter->array_typid,
&cypher_array_iter->typlen,
&cypher_array_iter->typbyval,
&cypher_array_iter->typalign);
array_iter_setup(it, array);
}
}

void ExecEvalCypherListCompIterInitNext(ExprState *state, ExprEvalStep *op)
{
JsonbIterator **ji;
JsonbValue jv;
JsonbIteratorToken jt;

ji = op->d.cypherlistcomp_iter.listiter;
jt = JsonbIteratorNext(ji, &jv, true);
if (jt == WJB_ELEM)
if (op->d.cypherlistcomp_iter.jsonb_list_iterator != NULL)
{
*op->resvalue = JsonbPGetDatum(JsonbValueToJsonb(&jv));
*op->resnull = false;
JsonbIterator **ji;
JsonbValue jv;
JsonbIteratorToken jt;

if (*op->d.cypherlistcomp_iter.is_null_list_or_array)
{
*op->resvalue = (Datum) 0;
*op->resnull = true;
return;
}

ji = op->d.cypherlistcomp_iter.jsonb_list_iterator;
jt = JsonbIteratorNext(ji, &jv, true);
if (jt == WJB_ELEM)
{
*op->resvalue = JsonbPGetDatum(JsonbValueToJsonb(&jv));
*op->resnull = false;
}
else
{
*op->resvalue = (Datum) 0;
*op->resnull = true;
}
}
else
{
*op->resvalue = (Datum) 0;
*op->resnull = true;
CypherListCompArrayIterator *array_iter = op->d.cypherlistcomp_iter.array_iterator;
Datum vertex_or_edge_datum;

if (array_iter->array_position >= array_iter->array_size)
{
*op->resvalue = (Datum) 0;
*op->resnull = true;
return;
}

vertex_or_edge_datum = array_iter_next(&array_iter->array_iter,
op->resnull,
array_iter->array_position++,
array_iter->typlen,
array_iter->typbyval,
array_iter->typalign);

if (*op->resnull)
{
return;
}

if (array_iter->array_typid == VERTEXOID)
{
*op->resvalue = getVertexPropDatum(vertex_or_edge_datum);
}
else
{
*op->resvalue = getEdgePropDatum(vertex_or_edge_datum);
}
}
}

Expand Down
15 changes: 12 additions & 3 deletions src/backend/parser/parse_cypher_expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -721,9 +721,15 @@ transformCypherListComp(ParseState *pstate, CypherListComp *clc)

list = transformCypherExprRecurse(pstate, (Node *) clc->list);
type = exprType(list);
if (type != JSONBOID)
{
list = coerce_all_to_jsonb(pstate, list);

switch (type) {
case JSONBOID:
case VERTEXARRAYOID:
case EDGEARRAYOID:
break;
default:
list = coerce_all_to_jsonb(pstate, list);
break;
}

save_varname = pstate->p_lc_varname;
Expand Down Expand Up @@ -882,6 +888,9 @@ transformFuncCall(ParseState *pstate, FuncCall *fn)
/* translate log() into ln() for cypher queries */
else if (strcmp(funcname, "log") == 0)
fn->funcname = list_make1(makeString("ln"));
/* Resolve isEmpty(..) function duplicates. */
else if (strcmp(funcname, "isempty") == 0)
fn->funcname = list_make1(makeString("cypher_jsonb_isempty"));
}

args = preprocess_func_args(pstate, fn);
Expand Down
29 changes: 28 additions & 1 deletion src/backend/utils/adt/cypher_empty_funcs.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,36 @@

#include "utils/cypher_empty_funcs.h"
#include "utils/fmgrprotos.h"
#include "utils/jsonb.h"

Datum
cypher_to_jsonb(PG_FUNCTION_ARGS)
{
return to_jsonb(fcinfo);
}
}

Datum
cypher_isempty_jsonb(PG_FUNCTION_ARGS)
{
Jsonb *jb = PG_GETARG_JSONB_P(0);

if (JB_ROOT_IS_SCALAR(jb))
{
JsonbValue *sjv;

sjv = getIthJsonbValueFromContainer(&jb->root, 0);
if (sjv->type == jbvString)
{
PG_RETURN_BOOL(sjv->val.string.len <= 0);
}
}
else if (JB_ROOT_IS_ARRAY(jb) || JB_ROOT_IS_OBJECT(jb))
{
PG_RETURN_BOOL(JB_ROOT_COUNT(jb) <= 0);
}

ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("isEmpty(): list or object or string is expected but %s",
JsonbToCString(NULL, &jb->root, VARSIZE(jb)))));
}
3 changes: 3 additions & 0 deletions src/include/catalog/pg_proc.dat
Original file line number Diff line number Diff line change
Expand Up @@ -11333,6 +11333,9 @@
{ oid => '7247', descr => 'map input to jsonb (cypher)',
proname => 'cypher_to_jsonb', prorettype => 'jsonb',
proargtypes => 'anyelement', prosrc => 'cypher_to_jsonb' },
{ oid => '7248', descr => 'is jsonb empty?',
proname => 'cypher_jsonb_isempty', prorettype => 'bool',
proargtypes => 'jsonb', prosrc => 'cypher_isempty_jsonb' },

# agensgraph array functions
{ oid => '7300', descr => 'the first element in a array',
Expand Down
17 changes: 16 additions & 1 deletion src/include/executor/execExpr.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
#include "executor/nodeAgg.h"
#include "nodes/execnodes.h"
#include "utils/jsonb.h"
#include "utils/arrayaccess.h"

/* forward references to avoid circularity */
struct ExprEvalStep;
struct SubscriptingRefState;
struct CypherAccessPathElem;
struct CypherListCompArrayIterator;

/* Bits in ExprState->flags (see also execnodes.h for public flag bits): */
/* expression's interpreter has been initialized */
Expand Down Expand Up @@ -699,7 +701,9 @@ typedef struct ExprEvalStep
{
Datum *listvalue;
bool *listnull;
JsonbIterator **listiter;
JsonbIterator **jsonb_list_iterator;
struct CypherListCompArrayIterator *array_iterator;
bool *is_null_list_or_array;
} cypherlistcomp_iter;

struct
Expand Down Expand Up @@ -776,6 +780,17 @@ typedef struct CypherAccessPathElem
CypherIndexResult uidx;
} CypherAccessPathElem;

typedef struct CypherListCompArrayIterator
{
array_iter array_iter;
int array_size;
int array_position;
Oid array_typid;
int16 typlen;
bool typbyval;
char typalign;
} CypherListCompArrayIterator;


/* functions in execExpr.c */
extern void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s);
Expand Down
2 changes: 2 additions & 0 deletions src/include/utils/cypher_empty_funcs.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@
#include "fmgr.h"

extern Datum cypher_to_jsonb(PG_FUNCTION_ARGS);
extern Datum cypher_isempty_jsonb(PG_FUNCTION_ARGS);

#endif
Loading

0 comments on commit 019e39b

Please sign in to comment.