Skip to content
Permalink
Browse files
Refactor function get_agtype_value_object_value
Refactored the function get_agtype_value_object_value to use binary
search to find the specified key. Previously it just did a linear
seach through the keys.

Removed the function get_agtype_key as it was redundant.

Added the macro GET_AGTYPE_VALUE_OBJECT_VALUE for usage with string
constants. Thank you Alex!

Replaced all occurrences of get_agtype_key to use either the
get_agtype_value_object_value or GET_AGTYPE_VALUE_OBJECT_VALUE
calls, depending on whether string constants were used or not.

Adjusted all calls to get_agtype_value_object_value due to the
addition of a length parameter.

All regression tests succeeded.
  • Loading branch information
jrgemignani committed Mar 3, 2022
1 parent 3aacd45 commit d8777c0a40c5101eb92eb187a6fc74b63d69b62c
Showing 8 changed files with 126 additions and 86 deletions.
@@ -551,7 +551,7 @@ static Datum create_vertex(cypher_create_custom_scan_state *css,
errmsg("agtype must resolve to a vertex")));

// extract the id agtype field
id_value = get_agtype_value_object_value(v, "id");
id_value = GET_AGTYPE_VALUE_OBJECT_VALUE(v, "id");

// extract the graphid and cast to a Datum
id = GRAPHID_GET_DATUM(id_value->val.int_value);
@@ -393,8 +393,8 @@ static void process_delete_list(CustomScanState *node)
original_entity_value = extract_entity(node, scanTupleSlot,
entity_position);

id = get_agtype_value_object_value(original_entity_value, "id");
label = get_agtype_value_object_value(original_entity_value, "label");
id = GET_AGTYPE_VALUE_OBJECT_VALUE(original_entity_value, "id");
label = GET_AGTYPE_VALUE_OBJECT_VALUE(original_entity_value, "label");
label_name = pnstrdup(label->val.string.val, label->val.string.len);

resultRelInfo = create_entity_result_rel_info(estate, css->delete_data->graph_name, label_name);
@@ -736,7 +736,7 @@ static Datum merge_vertex(cypher_merge_custom_scan_state *css,
errmsg("agtype must resolve to a vertex")));

// extract the id agtype field
id_value = get_agtype_value_object_value(v, "id");
id_value = GET_AGTYPE_VALUE_OBJECT_VALUE(v, "id");

// extract the graphid and cast to a Datum
id = GRAPHID_GET_DATUM(id_value->val.int_value);
@@ -187,7 +187,7 @@ static bool check_path(agtype_value *path, graphid updated_id)
{
agtype_value *elem = &path->val.array.elems[i];

agtype_value *id = get_agtype_value_object_value(elem, "id");
agtype_value *id = GET_AGTYPE_VALUE_OBJECT_VALUE(elem, "id");

if (updated_id == id->val.int_value)
return true;
@@ -227,7 +227,7 @@ static agtype_value *replace_entity_in_path(agtype_value *path,
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unsupported agtype found in a path")));

id = get_agtype_value_object_value(elem, "id");
id = GET_AGTYPE_VALUE_OBJECT_VALUE(elem, "id");

if (updated_id == id->val.int_value)
parsed_agtype_value = push_agtype_value(&parse_state, WAGT_ELEM,
@@ -359,12 +359,12 @@ static void process_update_list(CustomScanState *node)
clause_name)));

/* get the id and label for later */
id = get_agtype_value_object_value(original_entity_value, "id");
label = get_agtype_value_object_value(original_entity_value, "label");
id = GET_AGTYPE_VALUE_OBJECT_VALUE(original_entity_value, "id");
label = GET_AGTYPE_VALUE_OBJECT_VALUE(original_entity_value, "label");
label_name = pnstrdup(label->val.string.val, label->val.string.len);

/* get the properties we need to update */
original_properties = get_agtype_value_object_value(original_entity_value,
original_properties = GET_AGTYPE_VALUE_OBJECT_VALUE(original_entity_value,
"properties");

/*
@@ -417,8 +417,8 @@ static void process_update_list(CustomScanState *node)
}
else if (original_entity_value->type == AGTV_EDGE)
{
agtype_value *startid = get_agtype_value_object_value(original_entity_value, "start_id");
agtype_value *endid = get_agtype_value_object_value(original_entity_value, "end_id");
agtype_value *startid = GET_AGTYPE_VALUE_OBJECT_VALUE(original_entity_value, "start_id");
agtype_value *endid = GET_AGTYPE_VALUE_OBJECT_VALUE(original_entity_value, "end_id");

new_entity = make_edge(GRAPHID_GET_DATUM(id->val.int_value),
GRAPHID_GET_DATUM(startid->val.int_value),
@@ -323,9 +323,9 @@ static bool is_an_edge_match(VLE_local_context *vlelctx, edge_entry *ee)
agtype_value *condition_value = NULL;

/* get the condition_value for the specified edge_property_key */
condition_value = get_agtype_key(agtv_edge_conditions,
edge_property_key.val.string.val,
edge_property_key.val.string.len);
condition_value = get_agtype_value_object_value(agtv_edge_conditions,
edge_property_key.val.string.val,
edge_property_key.val.string.len);

/* if one exists, we have a key match */
if (condition_value != NULL)
@@ -526,7 +526,7 @@ static VLE_local_context *build_local_vle_context(FunctionCallInfo fcinfo)
AGTV_VERTEX, false);
if (agtv_temp != NULL && agtv_temp->type == AGTV_VERTEX)
{
agtv_temp = get_agtype_value_object_value(agtv_temp, "id");
agtv_temp = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_temp, "id");
}
else if (agtv_temp == NULL || agtv_temp->type != AGTV_INTEGER)
{
@@ -559,7 +559,7 @@ static VLE_local_context *build_local_vle_context(FunctionCallInfo fcinfo)
AGTV_VERTEX, false);
if (agtv_temp != NULL && agtv_temp->type == AGTV_VERTEX)
{
agtv_temp = get_agtype_value_object_value(agtv_temp, "id");
agtv_temp = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_temp, "id");
}
else if (agtv_temp == NULL || agtv_temp->type != AGTV_INTEGER)
{
@@ -576,10 +576,10 @@ static VLE_local_context *build_local_vle_context(FunctionCallInfo fcinfo)
AGTV_EDGE, true);

/* get the edge prototype's property conditions */
vlelctx->edge_conditions = get_agtype_value_object_value(agtv_temp,
vlelctx->edge_conditions = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_temp,
"properties");
/* get the edge prototype's label name */
agtv_temp = get_agtype_value_object_value(agtv_temp, "label");
agtv_temp = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_temp, "label");
if (agtv_temp->type == AGTV_STRING &&
agtv_temp->val.string.len != 0)
{
@@ -2776,8 +2776,9 @@ static agtype_value *execute_map_access_operator(agtype *map,
/* if we were passed an agtype_value OBJECT */
else if (map_value != NULL && map_value->type == AGTV_OBJECT)
{
map_value = get_agtype_key(map_value, key_value->val.string.val,
key_value->val.string.len);
map_value = get_agtype_value_object_value(map_value,
key_value->val.string.val,
key_value->val.string.len);
}
/* otherwise, we don't know how to process it */
else
@@ -2861,38 +2862,89 @@ static agtype_value *execute_array_access_operator(agtype *array,
}

/*
* Helper function to iterate through all object pairs, looking for a specific
* key. It will return the key or NULL if not found.
* Helper function to do a binary search through an object's key/value pairs,
* looking for a specific key. It will return the key or NULL if not found.
*/
agtype_value *get_agtype_key(agtype_value *agtv, char *search_key,
int search_key_len)
agtype_value *get_agtype_value_object_value(const agtype_value *agtv_object,
char *search_key,
int search_key_length)
{
int i = 0;
agtype_value *agtv_key = NULL;
int current_key_length = 0;
int middle = 0;
int num_pairs = 0;
int left = 0;
int right = 0;
int result = 0;

if (agtv == NULL || search_key == NULL || search_key_len <= 0)
if (agtv_object == NULL || search_key == NULL || search_key_length <= 0)
{
return NULL;
}

/* iterate through all pairs */
for (i = 0; i < agtv->val.object.num_pairs; i++)
/* get the number of object pairs */
num_pairs = agtv_object->val.object.num_pairs;

/* do a binary search through the pairs */
right = num_pairs - 1;
middle = num_pairs / 2;

/* while middle is within the constraints */
while (middle >= left && middle <= right)
{
agtype_value *agtv_key = &agtv->val.object.pairs[i].key;
agtype_value *agtv_value = &agtv->val.object.pairs[i].value;
/* get the current key length */
agtv_key = &agtv_object->val.object.pairs[middle].key;
current_key_length = agtv_key->val.string.len;

char *current_key = agtv_key->val.string.val;
int current_key_len = agtv_key->val.string.len;
/* if not the same length, divide the search space and continue */
if (current_key_length != search_key_length)
{
/* if we need to search in the lower half */
if (search_key_length < current_key_length)
{
middle -= 1;
right = middle;
middle = ((middle - left) / 2) + left;
}
/* else we need to search in the upper half */
else
{
middle += 1;
left = middle;
middle = ((right - middle) / 2) + left;
}
continue;
}

Assert(agtv_key->type == AGTV_STRING);
/* they are the same length so compare the keys */
result = strncmp(search_key, agtv_key->val.string.val,
search_key_length);

/* check for an id of type integer */
if (current_key_len == search_key_len &&
pg_strcasecmp(current_key, search_key) == 0)
/* if they don't match */
if (result != 0)
{
return agtv_value;
/* if smaller */
if (result < 0)
{
middle -= 1;
right = middle;
middle = ((middle - left) / 2) + left;
}
/* if larger */
else
{
middle += 1;
left = middle;
middle = ((right - middle) / 2) + left;
}
continue;
}

/* they match */
return (&agtv_object->val.object.pairs[middle].value);
}

/* they don't match */
return NULL;
}

@@ -3982,35 +4034,6 @@ Datum agtype_typecast_path(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(agtype_value_to_agtype(path.res));
}

/* helper function to retrieve a value, given a key, from an agtype_value */
agtype_value *get_agtype_value_object_value(const agtype_value *agtv_object,
char *key)
{
int i;
int length;

Assert(agtv_object != NULL);
Assert(agtv_object->type != AGTV_OBJECT);
Assert(key != NULL);

length = strlen(key);
for (i = 0; i < agtv_object->val.object.num_pairs; i++)
{
agtype_value *agtv_key = &agtv_object->val.object.pairs[i].key;
agtype_value *agtv_value = &agtv_object->val.object.pairs[i].value;

Assert(agtv_key != NULL);
Assert(agtv_key->type == AGTV_STRING);

if (agtv_key->val.string.len == length &&
strncmp(agtv_key->val.string.val, key,
agtv_key->val.string.len)== 0)
return agtv_value;
}

return NULL;
}

PG_FUNCTION_INFO_V1(_property_constraint_check);

Datum _property_constraint_check(PG_FUNCTION_ARGS)
@@ -4060,7 +4083,7 @@ Datum age_id(PG_FUNCTION_ARGS)
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("id() argument must be a vertex, an edge or null")));

agtv_result = get_agtype_value_object_value(agtv_object, "id");
agtv_result = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_object, "id");

Assert(agtv_result != NULL);
Assert(agtv_result->type = AGTV_INTEGER);
@@ -4098,7 +4121,7 @@ Datum age_start_id(PG_FUNCTION_ARGS)
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("start_id() argument must be an edge or null")));

agtv_result = get_agtype_value_object_value(agtv_object, "start_id");
agtv_result = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_object, "start_id");

Assert(agtv_result != NULL);
Assert(agtv_result->type = AGTV_INTEGER);
@@ -4136,7 +4159,7 @@ Datum age_end_id(PG_FUNCTION_ARGS)
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("end_id() argument must be an edge or null")));

agtv_result = get_agtype_value_object_value(agtv_object, "end_id");
agtv_result = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_object, "end_id");

Assert(agtv_result != NULL);
Assert(agtv_result->type = AGTV_INTEGER);
@@ -4356,7 +4379,7 @@ Datum age_startnode(PG_FUNCTION_ARGS)
errmsg("startNode() argument must be an edge or null")));

/* get the graphid for start_id */
agtv_value = get_agtype_value_object_value(agtv_object, "start_id");
agtv_value = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_object, "start_id");
/* it must not be null and must be an integer */
Assert(agtv_value != NULL);
Assert(agtv_value->type = AGTV_INTEGER);
@@ -4421,7 +4444,7 @@ Datum age_endnode(PG_FUNCTION_ARGS)
errmsg("endNode() argument must be an edge or null")));

/* get the graphid for the end_id */
agtv_value = get_agtype_value_object_value(agtv_object, "end_id");
agtv_value = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_object, "end_id");
/* it must not be null and must be an integer */
Assert(agtv_value != NULL);
Assert(agtv_value->type = AGTV_INTEGER);
@@ -4537,7 +4560,7 @@ Datum age_properties(PG_FUNCTION_ARGS)
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("properties() argument must be a vertex, an edge or null")));

agtv_result = get_agtype_value_object_value(agtv_object, "properties");
agtv_result = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_object, "properties");

Assert(agtv_result != NULL);
Assert(agtv_result->type = AGTV_OBJECT);
@@ -5138,7 +5161,7 @@ Datum age_type(PG_FUNCTION_ARGS)
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("type() argument must be an edge or null")));

agtv_result = get_agtype_value_object_value(agtv_object, "label");
agtv_result = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_object, "label");

Assert(agtv_result != NULL);
Assert(agtv_result->type = AGTV_STRING);
@@ -5218,7 +5241,7 @@ Datum age_label(PG_FUNCTION_ARGS)
}

// extract the label agtype value from the vertex or edge
label = get_agtype_value_object_value(agtv_value, "label");
label = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_value, "label");

PG_RETURN_POINTER(agtype_value_to_agtype(label));
}
@@ -9013,7 +9036,8 @@ Datum age_keys(PG_FUNCTION_ARGS)
if (agtv_result->type == AGTV_EDGE ||
agtv_result->type == AGTV_VERTEX)
{
agtv_result = get_agtype_value_object_value(agtv_result, "properties");
agtv_result = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_result,
"properties");

Assert(agtv_result != NULL);
Assert(agtv_result->type = AGTV_OBJECT);
@@ -9158,7 +9182,7 @@ Datum age_labels(PG_FUNCTION_ARGS)
}

/* get the label from the vertex */
agtv_label = get_agtype_value_object_value(agtv_temp, "label");
agtv_label = GET_AGTYPE_VALUE_OBJECT_VALUE(agtv_temp, "label");
/* it cannot be NULL */
Assert(agtv_label != NULL);

@@ -1436,7 +1436,7 @@ void agtype_hash_scalar_value_extended(const agtype_value *scalar_val,
case AGTV_VERTEX:
{
graphid id;
agtype_value *id_agt = get_agtype_value_object_value(scalar_val, "id");
agtype_value *id_agt = GET_AGTYPE_VALUE_OBJECT_VALUE(scalar_val, "id");
id = id_agt->val.int_value;
tmp = DatumGetUInt64(DirectFunctionCall2(
hashint8extended, Float8GetDatum(id), UInt64GetDatum(seed)));
@@ -1445,7 +1445,7 @@ void agtype_hash_scalar_value_extended(const agtype_value *scalar_val,
case AGTV_EDGE:
{
graphid id;
agtype_value *id_agt = get_agtype_value_object_value(scalar_val, "id");
agtype_value *id_agt = GET_AGTYPE_VALUE_OBJECT_VALUE(scalar_val, "id");
id = id_agt->val.int_value;
tmp = DatumGetUInt64(DirectFunctionCall2(
hashint8extended, Float8GetDatum(id), UInt64GetDatum(seed)));
@@ -1607,8 +1607,8 @@ int compare_agtype_scalar_values(agtype_value *a, agtype_value *b)
agtype_value *a_id, *b_id;
graphid a_graphid, b_graphid;

a_id = get_agtype_value_object_value(a, "id");
b_id = get_agtype_value_object_value(b, "id");
a_id = GET_AGTYPE_VALUE_OBJECT_VALUE(a, "id");
b_id = GET_AGTYPE_VALUE_OBJECT_VALUE(b, "id");

a_graphid = a_id->val.int_value;
b_graphid = b_id->val.int_value;

0 comments on commit d8777c0

Please sign in to comment.