feat: hemar: handling [] in path
This commit is contained in:
@@ -1429,7 +1429,7 @@ get_jsonb_path_value(Datum jsonb_context, const char *path, bool *found)
|
||||
|
||||
if (*found && jbv_result)
|
||||
{
|
||||
elog(DEBUG1, "GJVB: Found element (type=%d)", jbv_result->type);
|
||||
elog(DEBUG1, "GJVB: Found element (type=%s)", jbv_type_to_string(jbv_result->type));
|
||||
if (jbv_result->type == jbvString)
|
||||
{
|
||||
result = pnstrdup(jbv_result->val.string.val, jbv_result->val.string.len);
|
||||
@@ -2195,8 +2195,10 @@ get_jsonb_value_by_path(Jsonb *jb, const char *path, bool *found)
|
||||
{
|
||||
JsonbValue *result = NULL;
|
||||
char *path_copy, *token, *saveptr;
|
||||
char *array_index_start, *array_index_end;
|
||||
JsonbValue key;
|
||||
JsonbContainer *container;
|
||||
int array_index;
|
||||
|
||||
*found = false;
|
||||
|
||||
@@ -2215,10 +2217,35 @@ get_jsonb_value_by_path(Jsonb *jb, const char *path, bool *found)
|
||||
|
||||
while (token != NULL)
|
||||
{
|
||||
/* Check if we're still working with an object (?) */
|
||||
elog(DEBUG1, "GJVB: Processing token: %s", token);
|
||||
|
||||
/* Check if we're dealing with an array index: something[N] */
|
||||
array_index_start = strchr(token, '[');
|
||||
|
||||
elog(DEBUG1, "GJVB: Token: %s, array_index_start: %s", token, array_index_start);
|
||||
|
||||
if (array_index_start != NULL)
|
||||
{
|
||||
/* We have an array index pattern */
|
||||
array_index_end = strchr(array_index_start, ']');
|
||||
|
||||
if (array_index_end == NULL || array_index_end <= array_index_start + 1)
|
||||
{
|
||||
elog(DEBUG1, "GJVB: Invalid array index syntax in '%s'", token);
|
||||
pfree(path_copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Extract the property name before the [ */
|
||||
*array_index_start = '\0';
|
||||
|
||||
/* If we have a property name before the [, get that object first */
|
||||
if (token[0] != '\0')
|
||||
{
|
||||
/* Check if we're still working with an object */
|
||||
if (!(container->header & JB_FOBJECT))
|
||||
{
|
||||
elog(DEBUG1, "Path segment '%s' cannot be applied to non-object", token);
|
||||
elog(DEBUG1, "GJVB: Path segment '%s' cannot be applied to non-object", token);
|
||||
pfree(path_copy);
|
||||
return NULL;
|
||||
}
|
||||
@@ -2238,10 +2265,127 @@ get_jsonb_value_by_path(Jsonb *jb, const char *path, bool *found)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If there are more path segments, we need to continue with the next container */
|
||||
token = strtok_r(NULL, ".", &saveptr);
|
||||
/* We need to go deeper, so the current result must be a container */
|
||||
if (result->type != jbvBinary)
|
||||
{
|
||||
elog(DEBUG1, "GJVB: Path segment '%s' points to a non-container value", token);
|
||||
pfree(path_copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (token != NULL)
|
||||
/* Move to the next container, which should be an array for the index */
|
||||
container = (JsonbContainer *)result->val.binary.data;
|
||||
|
||||
/* Validate the container */
|
||||
if (!is_jsonb_container_valid(container))
|
||||
{
|
||||
elog(WARNING, "GJVB: Invalid JSONB container during path traversal");
|
||||
pfree(path_copy);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now get the array index */
|
||||
*array_index_end = '\0';
|
||||
array_index = atoi(array_index_start + 1);
|
||||
elog(DEBUG1, "GJVB: Array index: %d", array_index);
|
||||
|
||||
/* Check if container is an array */
|
||||
if (!(container->header & JB_FARRAY))
|
||||
{
|
||||
elog(DEBUG1, "GJVB: Path segment '%s[%d]' cannot be applied to non-array", token, array_index);
|
||||
pfree(path_copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (array_index < 0)
|
||||
{
|
||||
elog(DEBUG1, "GJVB: Invalid negative array index %d", array_index);
|
||||
pfree(path_copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get array element by index using iterator */
|
||||
JsonbIterator *it = JsonbIteratorInit(container);
|
||||
JsonbValue v;
|
||||
JsonbIteratorToken itToken;
|
||||
int current_index = 0;
|
||||
|
||||
/* Skip the WJB_BEGIN_ARRAY token */
|
||||
itToken = JsonbIteratorNext(&it, &v, false);
|
||||
if (itToken != WJB_BEGIN_ARRAY)
|
||||
{
|
||||
elog(DEBUG1, "GJVB: Expected array start token but got %d", itToken);
|
||||
pfree(path_copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find the element at the specified index */
|
||||
result = NULL;
|
||||
while ((itToken = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
|
||||
{
|
||||
if (itToken == WJB_ELEM)
|
||||
{
|
||||
if (current_index == array_index)
|
||||
{
|
||||
/* We found our element */
|
||||
result = palloc(sizeof(JsonbValue));
|
||||
*result = v;
|
||||
break;
|
||||
}
|
||||
current_index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
elog(DEBUG1, "GJVB: Array index %d out of bounds (max: %d)", array_index, current_index - 1);
|
||||
pfree(path_copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If the result is a binary container, update our container for next segment */
|
||||
if (result->type == jbvBinary)
|
||||
{
|
||||
container = (JsonbContainer *)result->val.binary.data;
|
||||
|
||||
/* Validate the container */
|
||||
if (!is_jsonb_container_valid(container))
|
||||
{
|
||||
elog(WARNING, "GJVB: Invalid JSONB container during path traversal");
|
||||
pfree(path_copy);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Standard property access */
|
||||
/* Check if we're still working with an object */
|
||||
if (!(container->header & JB_FOBJECT))
|
||||
{
|
||||
elog(DEBUG1, "GJVB: Path segment '%s' cannot be applied to non-object", token);
|
||||
pfree(path_copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set up the key to search for */
|
||||
key.type = jbvString;
|
||||
key.val.string.val = token;
|
||||
key.val.string.len = strlen(token);
|
||||
|
||||
/* Find the value for this key */
|
||||
result = findJsonbValueFromContainer(container, JB_FOBJECT, &key);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
elog(DEBUG1, "GJVB: Key '%s' not found in object", token);
|
||||
pfree(path_copy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If this isn't the last token, prepare for the next path segment */
|
||||
if (saveptr && *saveptr != '\0')
|
||||
{
|
||||
/* We need to go deeper, so the current result must be a container */
|
||||
if (result->type != jbvBinary)
|
||||
@@ -2264,6 +2408,11 @@ get_jsonb_value_by_path(Jsonb *jb, const char *path, bool *found)
|
||||
}
|
||||
}
|
||||
|
||||
/* Get next token */
|
||||
token = strtok_r(NULL, ".", &saveptr);
|
||||
elog(DEBUG1, "GJVB: Next token: %s", token ? token : "NULL");
|
||||
}
|
||||
|
||||
/* If we got here, we found the value */
|
||||
*found = true;
|
||||
pfree(path_copy);
|
||||
|
||||
Reference in New Issue
Block a user