Skip to content

Commit f5f7655

Browse files
committed
Support vertex properties in CREATE Clause
1 parent 603f4e2 commit f5f7655

8 files changed

Lines changed: 115 additions & 73 deletions

File tree

regress/sql/cypher_create.sql

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,16 @@ SELECT * FROM cypher_create._ag_label_vertex;
2929

3030
MATCH () RETURN 1;
3131

32-
CREATE VLABEL test;
33-
SELECT * FROM postgraph.ag_label;
32+
SELECT '{}'::postgraph.gtype;
33+
CREATE ({msg: 'Hello World'});
34+
MATCH (a) RETURN a;
35+
SELECT * FROM cypher_create._ag_label_vertex;
3436

35-
CREATE (:test);
36-
SELECT * FROM postgraph.ag_label;
37-
SELECT * FROM cypher_create.test;
3837

38+
CREATE VLABEL test;
39+
CREATE (:test);
3940

4041
CREATE (:test2);
41-
SELECT * FROM postgraph.ag_label;
42-
SELECT * FROM cypher_create.test2;
43-
4442

4543
MATCH () RETURN 1;
4644

@@ -52,11 +50,6 @@ CREATE ()-[]->();
5250

5351
CREATE ()-[:elabel]->();
5452

55-
56-
EXPLAIN MATCH ()-[]->() RETURN 1;
57-
58-
EXPLAIN ANALYZE MATCH ()-[]->() RETURN 1;
59-
6053
MATCH ()-[]->() RETURN 1;
6154

6255
MATCH ()<-[]-() RETURN 1;
@@ -73,12 +66,10 @@ MATCH ()-[]->(a) RETURN a;
7366

7467
MATCH ()<-[]-(a) RETURN a;
7568

76-
SELECT * FROM cypher_create._ag_label_vertex;
77-
78-
SELECT * FROM cypher_create._adj__adj__ag_label_vertex;
79-
SELECT * FROM cypher_create._adj__ag_label_vertex;
80-
MATCH () RETURN 1;
69+
CREATE ()<-[{msg: 'Hello edge'}]-();
70+
CREATE ()-[{msg: 'Hello edge'}]->();
8171

72+
MATCH ()<-[a]-() RETURN a;
8273

8374
CYPHER WITH 1 as a
8475
CREATE ();

src/backend/executor/cypher_create.c

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,12 @@ static TupleTableSlot *exec_cypher_create(CustomScanState *csnode)
267267
EState *estate = css->css.ss.ps.state;
268268
ExprContext *econtext = css->css.ss.ps.ps_ExprContext;
269269

270-
TupleTableSlot *scanTupleSlot = econtext->ecxt_scantuple;
271-
270+
//TupleTableSlot *scanTupleSlot = econtext->ecxt_scantuple;
271+
//econtext->ecxt_scantuple =
272+
// csnode->ss.ps.lefttree->ps_ProjInfo->pi_exprContext->ecxt_scantuple;
273+
TupleTableSlot *slot = ExecProcNode(csnode->ss.ps.lefttree);
274+
slot = csnode->ss.ps.lefttree->ps_ProjInfo->pi_exprContext->ecxt_scantuple;
275+
slot->tts_ops->materialize(slot);
272276
if (list_length(css->pattern) != 1)
273277
ereport(ERROR, (errmsg_internal("executor create found a multi pattern")));
274278

@@ -309,8 +313,16 @@ static TupleTableSlot *exec_cypher_create(CustomScanState *csnode)
309313
elemTupleSlot->tts_isnull[0] = false;
310314

311315
// get the properties for this vertex
312-
elemTupleSlot->tts_values[1] = NULL;
313-
elemTupleSlot->tts_isnull[1] = true;
316+
if (node->prop_attr_num == InvalidAttrNumber) {
317+
elemTupleSlot->tts_values[1] = NULL;
318+
elemTupleSlot->tts_isnull[1] = true;
319+
} else {
320+
//TupleTableSlot *scanTupleSlot = csnode->ss.ss_ScanTupleSlot;
321+
//ereport(WARNING, errmsg("adsf %i", scanTupleSlot->tts_values[node->prop_attr_num]));
322+
elemTupleSlot->tts_values[1] = slot->tts_values[node->prop_attr_num - 1];
323+
elemTupleSlot->tts_isnull[1] = slot->tts_isnull[node->prop_attr_num - 1];
324+
}
325+
314326
// Insert the new vertex
315327
insert_entity_tuple(resultRelInfo, elemTupleSlot, estate);
316328

@@ -331,9 +343,16 @@ static TupleTableSlot *exec_cypher_create(CustomScanState *csnode)
331343

332344
elemTupleSlot->tts_values[2] = css->vertex_ids[0][i+1];
333345
elemTupleSlot->tts_isnull[2] = false;
334-
// get the properties for this vertex
335-
elemTupleSlot->tts_values[3] = NULL;
336-
elemTupleSlot->tts_isnull[3] = true;
346+
347+
348+
if (node->prop_attr_num == InvalidAttrNumber) {
349+
elemTupleSlot->tts_values[3] = NULL;
350+
elemTupleSlot->tts_isnull[3] = true;
351+
} else {
352+
//TupleTableSlot *scanTupleSlot = econtext->ecxt_scantuple;
353+
elemTupleSlot->tts_values[3] = slot->tts_values[node->prop_attr_num];
354+
elemTupleSlot->tts_isnull[3] = slot->tts_isnull[node->prop_attr_num];
355+
}
337356

338357
// Insert the new vertex
339358
insert_entity_tuple(resultRelInfo, elemTupleSlot, estate);

src/backend/nodes/cypher_copyfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ void copy_cypher_target_node(ExtensibleNode *newnode, const ExtensibleNode *from
100100
COPY_SCALAR_FIELD(type);
101101
COPY_SCALAR_FIELD(flags);
102102
COPY_SCALAR_FIELD(dir);
103+
COPY_SCALAR_FIELD(id_attr_num);
103104
COPY_SCALAR_FIELD(prop_attr_num);
104105
COPY_SCALAR_FIELD(relid);
105106
COPY_SCALAR_FIELD(adj_relid);

src/backend/nodes/cypher_outfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ void out_cypher_target_node(StringInfo str, const ExtensibleNode *node)
367367
WRITE_ENUM_FIELD(dir, cypher_rel_dir);
368368
WRITE_NODE_FIELD(id_expr);
369369
WRITE_NODE_FIELD(id_expr_state);
370+
WRITE_INT32_FIELD(id_attr_num);
370371
WRITE_NODE_FIELD(prop_expr);
371372
WRITE_NODE_FIELD(prop_expr_state);
372373
WRITE_INT32_FIELD(prop_attr_num);

src/backend/nodes/cypher_readfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ void read_cypher_target_node(struct ExtensibleNode *node)
216216
READ_ENUM_FIELD(dir, cypher_rel_dir);
217217
READ_NODE_FIELD(id_expr);
218218
READ_NODE_FIELD(id_expr_state);
219+
READ_INT_FIELD(id_attr_num);
219220
READ_NODE_FIELD(prop_expr);
220221
READ_NODE_FIELD(prop_expr_state);
221222
READ_INT_FIELD(prop_attr_num);

src/backend/parser/cypher_clause.c

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,17 @@ static Node *make_edge_expr(cypher_parsestate *cpstate, ParseNamespaceItem *pnsi
301301
return (Node *)func_expr;
302302
}
303303

304+
305+
static Node *
306+
make_graphid_placeholder(cypher_parsestate *cpstate) {
307+
308+
// typtypmod, typcollation, typlen, and typbyval of gtype are hard-coded.
309+
Const *c = makeConst(GRAPHIDOID, -1, InvalidOid, -1, 0, false, false);
310+
c->location = -1;
311+
312+
return (Node *)c;
313+
}
314+
304315
static Node *
305316
make_int_placeholder(cypher_parsestate *cpstate) {
306317

@@ -400,6 +411,7 @@ static void validate_or_create_vlabel(cypher_parsestate *cpstate, cypher_node *n
400411
}
401412
}
402413

414+
403415
static Query *transform_cypher_create(cypher_parsestate *cpstate, cypher_clause *clause) {
404416
ParseState *pstate = (ParseState *)cpstate;
405417
cypher_create *self = (cypher_create *)clause->self;
@@ -439,53 +451,97 @@ static Query *transform_cypher_create(cypher_parsestate *cpstate, cypher_clause
439451
if (i % 2 == 1) {
440452
cypher_node *node = (cypher_node *)lfirst(lc2);
441453

454+
cypher_target_node *target = make_ag_node(cypher_target_node);
455+
442456
if (node->label)
443457
validate_or_create_vlabel(cpstate, node);
444458
else
445459
node->label = AG_DEFAULT_LABEL_VERTEX;
446460

447-
if (node->name)
448-
ereport(ERROR, (errmsg_internal("nodes in CREATE cannot have variable names")));
449-
450-
if (node->props)
451-
ereport(ERROR, (errmsg_internal("nodes in CREATE cannot have properties")));
452-
453-
cypher_target_node *target = make_ag_node(cypher_target_node);
461+
if (node->name) {
462+
ereport(ERROR, (errmsg_internal("nodes in CREATE cannot be a variable")));
463+
/* TODO: After props are supported
464+
target->variable_name = node->name;
465+
466+
query->targetList = lappend(query->targetList,
467+
makeTargetEntry(
468+
make_graphid_placeholder(cpstate),
469+
pstate->p_next_resno++,
470+
make_id_alias(get_next_default_alias(cpstate)),
471+
false));
472+
}
473+
target->id_attr_num = list_length(query->targetList);
474+
475+
query->targetList = lappend(query->targetList,
476+
makeTargetEntry(
477+
make_int_placeholder(cpstate),
478+
pstate->p_next_resno++,
479+
make_id_alias(get_next_default_alias(cpstate)),
480+
false));
481+
}
482+
483+
target->props_attr_num = list_length(query->targetList);
484+
*/
485+
} else
486+
node->name = get_next_default_alias(cpstate);
487+
488+
if (node->props) {
489+
target->prop_attr_num = pstate->p_next_resno;
490+
query->targetList = lappend(query->targetList,
491+
makeTargetEntry(
492+
(Expr *)add_volatile_wrapper(
493+
transform_cypher_expr(cpstate, node->props, EXPR_KIND_INSERT_TARGET)),
494+
pstate->p_next_resno++,
495+
make_property_alias(node->name),
496+
false));
497+
498+
} else {
499+
target->prop_attr_num = InvalidAttrNumber;
500+
}
501+
454502

455503
label_cache_data *lcd = search_label_name_graph_cache(node->label, cpstate->graph_oid);
456504

457505
target->id_expr = (Expr *)build_column_default(RelationIdGetRelation(lcd->relation), 1);
458506
target->relid = lcd->relation;
459507
target->adj_relid = lcd->vertex_adjlist;
460508

461-
TargetEntry *te = makeTargetEntry(make_int_placeholder(cpstate), pstate->p_next_resno++, make_id_alias(get_next_default_alias(cpstate)), false);
462-
463509
ccp->target_nodes = lappend(ccp->target_nodes, target);
464510

465-
query->targetList = lappend(query->targetList, te);
466511
} else {
467512
cypher_relationship *edge = lfirst(lc2);
468-
513+
cypher_target_node *target = make_ag_node(cypher_target_node);
469514
if (edge->label)
470515
validate_or_create_elabel(cpstate, edge);
471516
else
472517
edge->label = AG_DEFAULT_LABEL_EDGE;
473518

474519
if (edge->name)
475520
ereport(ERROR, (errmsg_internal("edges in CREATE cannot have variable names")));
476-
477-
if (edge->props)
478-
ereport(ERROR, (errmsg_internal("edges in CREATE cannot have properties")));
479-
521+
else
522+
edge->name = get_next_default_alias(cpstate);
523+
524+
if (edge->props) {
525+
target->prop_attr_num = pstate->p_next_resno;
526+
query->targetList = lappend(query->targetList,
527+
makeTargetEntry(
528+
(Expr *)add_volatile_wrapper(
529+
transform_cypher_expr(cpstate, edge->props, EXPR_KIND_INSERT_TARGET)),
530+
pstate->p_next_resno++,
531+
make_property_alias(edge->name),
532+
false));
533+
} else {
534+
target->prop_attr_num = InvalidAttrNumber;
535+
}
480536
if (edge->dir != CYPHER_REL_DIR_RIGHT)
481537
ereport(ERROR, (errmsg_internal("edges CREATE are right only right now")));
482538

483-
cypher_target_node *target = make_ag_node(cypher_target_node);
539+
484540
label_cache_data *lcd = search_label_name_graph_cache(edge->label, cpstate->graph_oid);
485541
target->relid = lcd->relation;
486542

487543
target->id_expr = (Expr *)build_column_default(RelationIdGetRelation(lcd->relation), 1);
488-
544+
//target->prop_attr_num = InvalidAttrNumber;
489545
ccp->target_nodes = lappend(ccp->target_nodes, target);
490546

491547
}

src/backend/utils/adt/vertex.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,13 @@ build_vertex(PG_FUNCTION_ARGS) {
102102
graphid id = AG_GETARG_GRAPHID(0);
103103
Oid graph_oid = PG_GETARG_OID(1);
104104
gtype *properties = NULL;
105-
/* if (!PG_ARGISNULL(2)) {
105+
if (!PG_ARGISNULL(2)) {
106106
properties = AG_GET_ARG_GTYPE_P(2);
107107

108108
if (!AGT_ROOT_IS_OBJECT(properties))
109109
PG_RETURN_NULL();
110110
}
111-
*/
111+
112112
AG_RETURN_VERTEX(create_vertex(id, graph_oid, properties));
113113
}
114114

src/include/nodes/cypher_nodes.h

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -348,52 +348,25 @@ typedef struct cypher_create_path
348348
typedef struct cypher_target_node
349349
{
350350
ExtensibleNode extensible;
351-
// 'v' for vertex or 'e' for edge
352351
char type;
353-
// flags defined below, prefaced with CYPHER_TARGET_NODE_FLAG_*
354352
uint32 flags;
355-
// if an edge, denotes direction
356353
cypher_rel_dir dir;
357-
/*
358-
* Used to create the id for the vertex/edge,
359-
* if the CYPHER_TARGET_NODE_FLAG_INSERT flag
360-
* is set. Doing it this way will protect us when
361-
* rescan gets implemented. By calling the function
362-
* that creates the id ourselves, we won't have an
363-
* issue where the id could be created then not used.
364-
* Since there is a limited number of ids available, we
365-
* don't want to waste them.
366-
*/
367354
Expr *id_expr;
368355
ExprState *id_expr_state;
369-
356+
AttrNumber id_attr_num;
370357
Expr *prop_expr;
371358
ExprState *prop_expr_state;
372-
/*
373-
* Attribute Number that this entity's properties
374-
* are stored in the CustomScanState's child TupleTableSlot
375-
*/
376359
AttrNumber prop_attr_num;
377-
// RelInfo for the table this entity will be stored in
378360
ResultRelInfo *resultRelInfo;
379-
// elemTupleSlot used to insert the entity into its table
380361
TupleTableSlot *elemTupleSlot;
381-
// relid that the label stores its entity
382362
Oid relid;
383363

384364
Oid adj_relid;
385365
ResultRelInfo *adj_resultRelInfo;
386-
// elemTupleSlot used to insert the entity into its table
387366
TupleTableSlot *adj_elemTupleSlot;
388-
// label this entity belongs to.
389367
char *label_name;
390-
// variable name for this entity
391368
char *variable_name;
392-
/*
393-
* Attribute number this entity needs to be stored in
394-
* for parent execution nodes to reference it. If the
395-
* entity is a varaible (CYPHER_TARGET_NODE_IS_VAR).
396-
*/
369+
397370
AttrNumber tuple_position;
398371
} cypher_target_node;
399372

0 commit comments

Comments
 (0)