Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support nested containers as attribute type #658

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/datatypes/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

#include "array.h"
#include "map.h"
#include "../util/arr.h"
#include "../util/qsort.h"
#include <limits.h>
Expand Down Expand Up @@ -48,12 +49,16 @@ bool SIArray_ContainsType(SIValue siarray, SIType t) {
SIValue elem = siarray.array[i];
if(SI_TYPE(elem) & t) return true;

// recursively check nested arrays
// recursively check nested containers
if(SI_TYPE(elem) == T_ARRAY) {
bool type_is_nested = SIArray_ContainsType(elem, t);
if(type_is_nested) return true;
} else if(SI_TYPE(elem) == T_MAP) {
bool type_is_nested = Map_ContainsType(elem, t);
if(type_is_nested) return true;
}
}

return false;
}

Expand Down
85 changes: 85 additions & 0 deletions src/datatypes/map.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,36 @@ SIValue Map_New
return map;
}

// create a map from keys and values arrays
// keys and values are both of length n
// map takes ownership over keys and values elements
// the function will nullify both arrays
SIValue Map_FromArrays
(
SIValue *keys, // keys
SIValue *values, // values
uint n // arrays length
) {
ASSERT(keys != NULL);
ASSERT(values != NULL);

SIValue map = Map_New(n);

for(uint i = 0; i < n; i++) {
Pair p = {
.key = keys[i],
.val = values[i]
};

array_append(map.map, p);

keys[i] = SI_NullVal();
values[i] = SI_NullVal();
}

return map;
}

// clone map
SIValue Map_Clone
(
Expand All @@ -92,6 +122,35 @@ SIValue Map_Clone
return clone;
}

// create map from binary stream
SIValue Map_FromBinary
(
FILE *stream // binary stream
) {
// format:
// key count
// key:value

ASSERT(stream != NULL);

// read number of keys in map
uint32_t n;
fread_assert(&n, sizeof(uint32_t), stream);

SIValue map = Map_New(n);

for(uint32_t i = 0; i < n; i++) {
Pair p = {
.key = SIValue_FromBinary(stream),
.val = SIValue_FromBinary(stream)
};

array_append(map.map, p);
}

return map;
}

// adds key/value to map
void Map_Add
(
Expand Down Expand Up @@ -205,6 +264,32 @@ bool Map_Contains
return (Map_KeyIdx(map, key) != -1);
}

// check if map contains a key with type 't'
bool Map_ContainsType
(
SIValue map, // map to scan
SIType t // type to match
) {
ASSERT(SI_TYPE(map) == T_MAP);

uint n = Map_KeyCount(map);
for(uint i = 0; i < n; i++) {
Pair p = map.map[i];
SIValue v = p.val;

if(SI_TYPE(v) & t) return true;

// recursively check nested containers
if(SI_TYPE(v) == T_ARRAY) {
if(SIArray_ContainsType(v, t) == true) return true;
} else if(SI_TYPE(v) == T_MAP) {
if(Map_ContainsType(v, t) == true) return true;
}
}

return false;
}

uint Map_KeyCount
(
SIValue map
Expand Down
24 changes: 24 additions & 0 deletions src/datatypes/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,29 @@ SIValue Map_New
uint capacity // map initial capacity
);

// create a map from keys and values arrays
// keys and values are both of length n
// map takes ownership over keys and values elements
// the function will nullify each element within the arrays
SIValue Map_FromArrays
(
SIValue *keys, // keys
SIValue *values, // values
uint n // arrays length
);

// clones map
SIValue Map_Clone
(
SIValue map // map to clone
);

// create map from binary stream
SIValue Map_FromBinary
(
FILE *stream // binary stream
);

// adds key/value to map
void Map_Add
(
Expand Down Expand Up @@ -81,6 +98,13 @@ bool Map_Contains
SIValue key // key to look-up
);

// check if map contains a key with type 't'
bool Map_ContainsType
(
SIValue map, // map to scan
SIType t // type to match
);

// return number of keys in map
uint Map_KeyCount
(
Expand Down
35 changes: 35 additions & 0 deletions src/effects/effects.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "RG.h"
#include "effects.h"
#include "../query_ctx.h"
#include "../datatypes/map.h"
#include "../datatypes/vector.h"

// determine block available space
Expand Down Expand Up @@ -47,6 +48,12 @@ static void EffectsBuffer_WriteSIVector
EffectsBuffer *buff // effect buffer
);

static void EffectsBuffer_WriteMap
(
const SIValue *map, // map
EffectsBuffer *buff // effects buffer
);

// create a new effects-buffer block
static struct EffectsBufferBlock *EffectsBufferBlock_New
(
Expand Down Expand Up @@ -192,6 +199,9 @@ static void EffectsBuffer_WriteSIValue
case T_VECTOR_F32:
EffectsBuffer_WriteSIVector(v, buff);
break;
case T_MAP:
EffectsBuffer_WriteMap(v, buff);
break;
default:
assert(false && "unknown SIValue type");
}
Expand Down Expand Up @@ -243,6 +253,31 @@ static void EffectsBuffer_WriteSIVector
}
}

static void EffectsBuffer_WriteMap
(
const SIValue *map, // map
EffectsBuffer *buff // effects buffer
) {
// format:
// key count
// key:value

// write number of keys in map
uint32_t n = Map_KeyCount(*map);
EffectsBuffer_WriteBytes(&n, sizeof(uint32_t), buff);

// write each key:value pair to buffer
for(uint32_t i = 0; i < n; i++) {
SIValue key;
SIValue value;
Map_GetIdx(*map, i, &key, &value);

ASSERT(SI_TYPE(key) == T_STRING);
EffectsBuffer_WriteSIValue(&key, buff);
EffectsBuffer_WriteSIValue(&value, buff);
}
}

// dump attributes to stream
static void EffectsBuffer_WriteAttributeSet
(
Expand Down
14 changes: 7 additions & 7 deletions src/errors/error_msgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
#define EMSG_SHORTESTPATH_SINGLE_RELATIONSHIP "shortestPath requires a path containing a single relationship"
#define EMSG_SHORTESTPATH_MINIMAL_LENGTH "shortestPath does not support a minimal length different from 0 or 1"
#define EMSG_SHORTESTPATH_MAX_HOPS "Maximum number of hops must be greater than or equal to minimum number of hops"
#define EMSG_SHORTESTPATH_UNDIRECTED "RedisGraph does not currently support undirected shortestPath traversals"
#define EMSG_SHORTESTPATH_RELATIONSHIP_FILTERS "RedisGraph does not currently support filters on relationships in shortestPath"
#define EMSG_SHORTESTPATH_UNDIRECTED "FalkorDB does not currently support undirected shortestPath traversals"
#define EMSG_SHORTESTPATH_RELATIONSHIP_FILTERS "FalkorDB does not currently support filters on relationships in shortestPath"
#define EMSG_SHORTESTPATH_NODE_FILTERS "Node filters may not be introduced in shortestPath"
#define EMSG_FUNCTION_REQUIER_PREDICATE "'%s' function requires a WHERE predicate"
#define EMSG_NESTED_AGGREGATION "Can't use aggregate functions inside of aggregate functions."
Expand Down Expand Up @@ -65,7 +65,7 @@
#define EMSG_CALLSUBQUERY_INVALID_REFERENCES "WITH imports in CALL {} must consist of only simple references to outside variables"
#define EMSG_VAIABLE_ALREADY_DECLARED_IN_OUTER_SCOPE "Variable `%s` already declared in outer scope"
#define EMSG_DELETE_INVALID_ARGUMENTS "DELETE can only be called on nodes, paths and relationships"
#define EMSG_SET_LHS_NON_ALIAS "RedisGraph does not currently support non-alias references on the left-hand side of SET expressions"
#define EMSG_SET_LHS_NON_ALIAS "FalkorDB does not currently support non-alias references on the left-hand side of SET expressions"
#define EMSG_UNION_COMBINATION "Invalid combination of UNION and UNION ALL."
#define EMSG_FOREACH_INVALID_BODY "Error: Only updating clauses may reside in FOREACH"
#define EMSG_QUERY_INVALID_LAST_CLAUSE "Query cannot conclude with %s (must be a RETURN clause, an update clause, a procedure call or a non-returning subquery)"
Expand All @@ -75,8 +75,8 @@
#define EMSG_MISSING_WITH_AFTER_MATCH "A WITH clause is required to introduce a MATCH clause after an OPTIONAL MATCH."
#define EMSG_UNSUPPORTED_QUERY_TYPE "Encountered unsupported query type '%s'"
#define EMSG_EMPTY_QUERY "Error: empty query."
#define EMSG_ALLSHORTESTPATH_SUPPORT "RedisGraph support allShortestPaths only in match clauses"
#define EMSG_SHORTESTPATH_SUPPORT "RedisGraph currently only supports shortestPaths in WITH or RETURN clauses"
#define EMSG_ALLSHORTESTPATH_SUPPORT "FalkorDB support allShortestPaths only in match clauses"
#define EMSG_SHORTESTPATH_SUPPORT "FalkorDB currently only supports shortestPaths in WITH or RETURN clauses"
#define EMSG_EXPLAIN_PROFILE_USAGE "Please use GRAPH.%s 'key' 'query' command instead of GRAPH.QUERY 'key' '%s query'"
#define EMSG_DUPLICATE_PARAMETERS "Duplicated parameter: %s"
#define EMSG_PARSER_ERROR "errMsg: %s line: %u, column: %u, offset: %zu errCtx: %s errCtxOffset: %zu"
Expand All @@ -87,8 +87,8 @@
#define EMSG_COULD_NOT_PARSE_QUERY "Error: could not parse query"
#define EMSG_UNABLE_TO_RESOLVE_FILTER_ALIAS "Unable to resolve filtered alias '%s'"
#define EMSG_TYPE_MISMATCH "Type mismatch: expected %s but was %s"
#define EMSG_REDISGRAPH_SUPPORT "RedisGraph does not currently support %s"
#define EMSG_INVALID_PROPERTY_VALUE "Property values can only be of primitive types or arrays of primitive types"
#define EMSG_REDISGRAPH_SUPPORT "FalkorDB does not currently support %s"
#define EMSG_INVALID_PROPERTY_VALUE "Property values can only be of primitive types arrays or maps"
#define EMSG_DIVISION_BY_ZERO "Division by zero"
#define EMSG_ALLSHORTESTPATH_SRC_DST_RESLOVED "Source and destination must already be resolved to call allShortestPaths"
#define EMSG_DELETE_OPERATE_ON_CHILD "Delete was constructed without a child operation"
Expand Down
25 changes: 22 additions & 3 deletions src/execution_plan/ops/shared/create_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ void ConvertPropertyMap
SIValue vals[property_count];
AttributeID ids[property_count];
uint attrs_count = 0;

for(int i = 0; i < property_count; i++) {
// note that AR_EXP_Evaluate may raise a run-time exception
// in which case the allocations in this function will leak
Expand All @@ -291,6 +292,7 @@ void ConvertPropertyMap
Error_InvalidPropertyValue();
ErrorCtx_RaiseRuntimeException(NULL);
}

// the value was NULL
// if this was prohibited in this context, raise an exception,
// otherwise skip this value
Expand All @@ -299,7 +301,8 @@ void ConvertPropertyMap
for(int j = 0; j < i; j++) {
SIValue_Free(vals[j]);
}
ErrorCtx_RaiseRuntimeException("Cannot merge node using null property value");
ErrorCtx_RaiseRuntimeException(
"Cannot merge node using null property value");
}

// don't add null to attrribute set
Expand All @@ -309,8 +312,24 @@ void ConvertPropertyMap
// emit an error and exit if we're trying to add
// an array containing an invalid type
if(SI_TYPE(val) == T_ARRAY) {
SIType invalid_properties = ~SI_VALID_PROPERTY_VALUE;
bool res = SIArray_ContainsType(val, invalid_properties);
SIType invalid_types = ~SI_VALID_PROPERTY_VALUE & ~T_NULL;
bool res = SIArray_ContainsType(val, invalid_types);
if(res) {
// validation failed
SIValue_Free(val);
for(int j = 0; j < i; j++) {
SIValue_Free(vals[j]);
}
Error_InvalidPropertyValue();
ErrorCtx_RaiseRuntimeException(NULL);
}
}

// emit an error and exit if we're trying to add
// a map containing an invalid type
if(SI_TYPE(val) == T_MAP) {
SIType invalid_types = ~SI_VALID_PROPERTY_VALUE & ~T_NULL;
bool res = SIArray_ContainsType(val, invalid_types);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong function call, should be SIMap_ContainsType!

if(res) {
// validation failed
SIValue_Free(val);
Expand Down
7 changes: 5 additions & 2 deletions src/execution_plan/ops/shared/update_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ static bool _ValidateAttrType
// in case of an array, make sure each element is of an
// acceptable type
if(t == T_ARRAY) {
SIType invalid_properties = ~SI_VALID_PROPERTY_VALUE;
return !SIArray_ContainsType(v, invalid_properties);
SIType invalid_types = ~SI_VALID_PROPERTY_VALUE & ~T_NULL;
return !SIArray_ContainsType(v, invalid_types);
} else if(t == T_MAP) {
SIType invalid_types = ~SI_VALID_PROPERTY_VALUE & ~T_NULL;
return !Map_ContainsType(v, invalid_types);
}

return true;
Expand Down
3 changes: 2 additions & 1 deletion src/graph/entities/graph_entity.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,10 @@ inline AttributeSet GraphEntity_GetAttributes
return *e->attributes;
}

// frees entity attribute set
inline int GraphEntity_ClearAttributes
(
GraphEntity *e
GraphEntity *e // entity to modify
) {
ASSERT(e != NULL);

Expand Down
Loading