Skip to content

Commit

Permalink
4.0.7 (#614)
Browse files Browse the repository at this point in the history
* 600 add browser usage for the docker image (#601)

* add instructions to run browser UI

* set hostname to 0.0.0.0

* fix crash in rewrite call subquery (#604)

* fix crash in rewrite call subquery

* simplified test query

---------

Co-authored-by: Roi Lipman <swilly22@users.noreply.github.com>

* use debug assert in rust (#606)

* fix merge init (#609)

* fix merge init

* remove unused function, simplified branch assignment logic

* clean

---------

Co-authored-by: Roi Lipman <roilipman@gmail.com>

* Unwind persist (#613)

* persist unwind record

* deep clone base record

* Disable jit (#612)

* persist unwind record

* disable GraphBLAS JIT

* version bump

---------

Co-authored-by: Dudi <16744955+dudizimber@users.noreply.github.com>
Co-authored-by: Avi Avni <avi.avni@gmail.com>
  • Loading branch information
3 people committed Apr 4, 2024
1 parent ec0be56 commit bec19b4
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 105 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,16 @@ To quickly try out FalkorDB, launch an instance using docker:
docker run -p 6379:6379 -it --rm falkordb/falkordb:edge
```

Once loaded you can interact with FalkorDB using any of the supported [client libraries](#Client-libraries)
Or, to use the built-in browser-based interface, run:

```
docker run -p 6379:6379 -p 3000:3000 -it --rm falkordb/falkordb:edge
```

Then, open your browser and navigate to `http://localhost:3000`.


You can also interact with FalkorDB using any of the supported [client libraries](#Client-libraries)

Here we'll use [FalkorDB Python client](https://pypi.org/project/FalkorDB/) to create a small graph representing a subset of motorcycle riders and teams taking part in the MotoGP league, once created we'll start querying our data.

Expand Down
2 changes: 1 addition & 1 deletion build/GraphBLAS/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ define CMAKE_DEFS +=
BUILD_SHARED_LIBS=OFF
BUILD_TESTING=off
CMAKE_POSITION_INDEPENDENT_CODE=on
NJIT=1
GRAPHBLAS_USE_JIT=off
endef

#----------------------------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion build/docker/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ if [ ${BROWSER:-1} -eq 1 ]
then
if [ -d /FalkorDBBrowser ]
then
cd /FalkorDBBrowser && node server.js &
cd /FalkorDBBrowser && HOSTNAME="0.0.0.0" node server.js &
fi
fi

Expand Down
2 changes: 1 addition & 1 deletion deps/FalkorDB-rs
17 changes: 8 additions & 9 deletions src/ast/ast_rewrite_call_subquery.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,6 @@ static uint _add_names_projections

// add projections to projections array, corresponding to the bound vars (names)
// and their internal representation (inter_names)
// if `direction` is 0 (false), projections from `names` to `inter_names` are added.
// if `directions` is 1, projections from `inter_names` to `names` are added.
// if `directions` is 2, projections from `inter_names` to `inter_names` are
// added.
// returns the new value of `proj_idx`
static uint _add_projections
(
Expand All @@ -95,9 +91,10 @@ static uint _add_projections
inter_names + n_outer_names, names, hide);
}

// -------------------------------------------------------------------------
//--------------------------------------------------------------------------
// create projections for bound vars from outer context
// -------------------------------------------------------------------------
//--------------------------------------------------------------------------

for(uint i = 0; i < n_outer_names; i++) {
// create a projection for the bound var
struct cypher_input_range range = {0};
Expand Down Expand Up @@ -213,13 +210,13 @@ static void _add_first_clause
uint proj_idx = 0;
cypher_astnode_t *projections[n_projections];

// -------------------------------------------------------------------------
//--------------------------------------------------------------------------
// create projections for bound vars
// -------------------------------------------------------------------------
//--------------------------------------------------------------------------
proj_idx = _add_projections(projections, proj_idx, names, inter_names,
true);

// -------------------------------------------------------------------------
//--------------------------------------------------------------------------
// prepare additional arguments
//--------------------------------------------------------------------------

Expand Down Expand Up @@ -425,6 +422,8 @@ static void _rewrite_projections
_add_first_clause(clause, clause_ind, first_ind, names,
*inter_names);

subquery = cypher_ast_call_subquery_get_query(clause);

// update union indeces
for(uint j = i; j < n_union_branches; j++) {
union_indices[j]++;
Expand Down
93 changes: 15 additions & 78 deletions src/execution_plan/ops/op_merge.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,32 +142,6 @@ OpBase *NewMergeOp
return (OpBase *)op;
}

// modification of ExecutionPlan_LocateOp that only follows LHS child
// Otherwise, the assumptions of Merge_SetStreams fail in MERGE..MERGE queries
// Match and Create streams are always guaranteed to not branch
// (have any ops with multiple children)
static OpBase *_LocateOp
(
OpBase *root,
OPType type
) {
OpBase *ret;

if(!root) {
ret = NULL;
}
else if(root->type == type) {
ret = root;
}
else if(root->childCount > 0) {
ret = _LocateOp(root->children[0], type);
} else {
ret = NULL;
}

return ret;
}

static OpResult MergeInit
(
OpBase *opBase
Expand All @@ -181,75 +155,38 @@ static OpResult MergeInit
ASSERT(opBase->childCount == 2 || opBase->childCount == 3);
OpMerge *op = (OpMerge *)opBase;
if(opBase->childCount == 2) {
// if we only have 2 streams
// we simply need to determine which has a MergeCreate op
if(_LocateOp(opBase->children[0], OPType_MERGE_CREATE)) {
// if the Create op is in the first stream, swap the children
// otherwise, the order is already correct
OpBase *tmp = opBase->children[0];
opBase->children[0] = opBase->children[1];
opBase->children[1] = tmp;
}

op->match_stream = opBase->children[0];
// if we only have 2 streams, the first one is the bound variable stream
// and the second is the match stream
op->match_stream = opBase->children[0];
op->create_stream = opBase->children[1];

ASSERT(OpBase_Type(op->create_stream) == OPType_MERGE_CREATE);
return OP_OK;
}

// handling the three-stream case
for(int i = 0; i < opBase->childCount; i ++) {
OpBase *child = opBase->children[i];

bool child_has_merge = _LocateOp(child, OPType_MERGE);
// neither Match stream and Create stream have a Merge op
// the bound variable stream will have a Merge op in-case of a merge merge query
// MERGE (a:A) MERGE (b:B)
// In which case the first Merge has yet to order its streams!
if(!op->bound_variable_stream && child_has_merge) {
op->bound_variable_stream = child;
continue;
}

bool child_has_argument = _LocateOp(child, OPType_ARGUMENT);
// The bound variable stream is the only stream not populated by an Argument op.
if(!op->bound_variable_stream && !child_has_argument) {
op->bound_variable_stream = child;
continue;
}

// The Create stream is the only stream with a MergeCreate op and Argument op.
if(!op->create_stream && _LocateOp(child, OPType_MERGE_CREATE) && child_has_argument) {
op->create_stream = child;
continue;
}

// The Match stream has an unknown set of operations, but is the only other stream
// populated by an Argument op.
if(!op->match_stream && child_has_argument) {
op->match_stream = child;
continue;
}
}
// if we have 3 streams, the first is the bound variable stream
// the second is the match stream, and the third is the create stream
op->bound_variable_stream = opBase->children[0];
op->match_stream = opBase->children[1];
op->create_stream = opBase->children[2];

ASSERT(OpBase_Type(op->create_stream) == OPType_MERGE_CREATE);
ASSERT(op->bound_variable_stream != NULL &&
op->match_stream != NULL &&
op->create_stream != NULL);

// migrate the children so that EXPLAIN calls print properly
opBase->children[0] = op->bound_variable_stream;
opBase->children[1] = op->match_stream;
opBase->children[2] = op->create_stream;
op->match_stream != NULL &&
op->create_stream != NULL);

// find and store references to the:
// Argument taps for the Match and Create streams
// the Match stream is populated by an Argument tap
// store a reference to it
op->match_argument_tap =
(Argument *)ExecutionPlan_LocateOp(op->match_stream, OPType_ARGUMENT);
ASSERT(op->match_argument_tap != NULL);

// if the create stream is populated by an Argument tap, store a reference to it.
op->create_argument_tap =
(Argument *)ExecutionPlan_LocateOp(op->create_stream, OPType_ARGUMENT);
ASSERT(op->create_argument_tap != NULL);

// set up an array to store records produced by the bound variable stream
op->input_records = array_new(Record, 1);
Expand Down
8 changes: 3 additions & 5 deletions src/execution_plan/ops/op_unwind.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,15 @@ static Record _handoff
) {
// if there is a new value ready, return it
if(op->listIdx < op->listLen) {
Record r = OpBase_CloneRecord(op->currentRecord);
Record r = OpBase_DeepCloneRecord(op->currentRecord);
SIValue v = SIArray_Get(op->list, op->listIdx);

// persist 'v', as we have no control over it once it is added to 'r'
// eventually 'list' will be freed as a result of either pulling from
// child or resetting
if(unlikely(!(v.type & SI_GRAPHENTITY))) {
if(!(SI_TYPE(v) & SI_GRAPHENTITY)) {
SIValue_Persist(&v);
}

Record_Add(r, op->unwindRecIdx, v);

op->listIdx++;
return r;
}
Expand Down
16 changes: 8 additions & 8 deletions src/execution_plan/record.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,17 @@ void Record_DeepClone
const Record r,
Record clone
) {
int entry_count = Record_length(r);
size_t required_record_size = sizeof(Entry) * entry_count;
int entry_count = Record_length(r);
size_t required_record_size = sizeof(Entry) * entry_count;

memcpy(clone->entries, r->entries, required_record_size);
memcpy(clone->entries, r->entries, required_record_size);

// Deep copy scalars
for(uint i = 0; i < entry_count; i++) {
if(r->entries[i].type == REC_TYPE_SCALAR) {
clone->entries[i].value.s = SI_CloneValue(r->entries[i].value.s);
}
// deep copy scalars
for(uint i = 0; i < entry_count; i++) {
if(r->entries[i].type == REC_TYPE_SCALAR) {
clone->entries[i].value.s = SI_CloneValue(r->entries[i].value.s);
}
}
}

void Record_Merge
Expand Down
2 changes: 1 addition & 1 deletion src/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#define REDISGRAPH_VERSION_MAJOR 4
#define REDISGRAPH_VERSION_MINOR 0
#define REDISGRAPH_VERSION_PATCH 6
#define REDISGRAPH_VERSION_PATCH 7

#define REDISGRAPH_SEMANTIC_VERSION(major, minor, patch) \
(major * 10000 + minor * 100 + patch)
Expand Down
31 changes: 31 additions & 0 deletions tests/flow/test_call_subquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -2103,3 +2103,34 @@ def test31_following_scans(self):
plan = self.graph.explain(query)
scan = locate_operation(plan.structured_plan, "Conditional Traverse")
self.env.assertEquals(str(scan), "Conditional Traverse | (n:N)->(n:N)")

def test32_rewrite_call_subquery(self):
self.graph.delete()

# create the node (:N {v: 1})
self.graph.query("CREATE (:N {v: 1})")

res = self.graph.query("""
MATCH (n)
CALL {
FOREACH ( n2 IN [] | CREATE () )
RETURN 0 AS n3
UNION
RETURN 0 AS n3
}
RETURN 0""")
self.env.assertEquals(res.result_set, [[0]])

def test33_merge_after_call_subquery(self):
self.graph.delete()

# create the node (:N {v: 1})
self.graph.query("CREATE (:N {v: 1})")
res = self.graph.query("""
CALL {
RETURN 1 AS x
}
MERGE (n:N {v: 1})
RETURN n.v
""")
self.env.assertEquals(res.result_set, [[1]])
13 changes: 13 additions & 0 deletions tests/flow/test_unwind_clause.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,16 @@ def test06_access_undefined_var(self):
except Exception as e:
self.env.assertTrue("'i' not defined" in str(e))

def test07_nested_unwind(self):
# n0 is a heap allocated array
# which gets free on the third call to consume of the nested UNWIND
query = """WITH [0] AS n0
UNWIND [0, 0] AS n1
WITH *
UNWIND [0, 0] AS n2
MERGE ({n3:0})"""

result = self.graph.query(query)
self.env.assertEqual(result.nodes_created, 1)
self.env.assertEqual(result.properties_set, 1)

0 comments on commit bec19b4

Please sign in to comment.