fix: more parsing errors handling, section body fix

This commit is contained in:
2025-05-14 14:09:48 +00:00
parent e9ba38f4eb
commit 56fa799d53
3 changed files with 274 additions and 167 deletions

View File

@@ -319,12 +319,16 @@
]; ];
initialScript = pkgs.writeText "init-sql-script" '' initialScript = pkgs.writeText "init-sql-script" ''
SET log_min_messages TO DEBUG1; SET log_min_messages TO DEBUG1;
SET client_min_messages TO DEBUG1;
ALTER DATABASE postgres SET log_min_messages TO DEBUG1; ALTER DATABASE postgres SET log_min_messages TO DEBUG1;
ALTER DATABASE postgres SET client_min_messages TO DEBUG1;
CREATE EXTENSION "hemar"; CREATE EXTENSION "hemar";
-- SELECT hemar.parse('{% zalupa %}'); -- SELECT hemar.parse('{% zalupa %}');
SELECT hemar.render('{"a": "b"}'::JSONB, 'a {% a %}'); SELECT hemar.render('{"a": "b"}'::JSONB, 'a {% a %}');
SELECT hemar.render('{"a": ["b", "c"]}'::JSONB, 'a {% for i in a do text %}'); SELECT hemar.render('{"a": ["b", "c"]}'::JSONB, 'a {% for i in a do text %}');
SELECT hemar.render('{"a": {"g": ["b", "c"]}}'::JSONB, 'a {% for i in a.g do {% i %} %}');
SELECT hemar.render('{"a": {"g": ["b", "c"], "b": [{"c": "a"}, {"c": "b"}]}}'::JSONB, 'a {% for i in a.b do {% i.c %} %}');
''; '';
}; };

View File

@@ -33,6 +33,7 @@ static void get_include_data(Datum include_data, char **template_out, Datum *con
static void template_node_to_string(TemplateNode *node, StringInfo result, int indent); static void template_node_to_string(TemplateNode *node, StringInfo result, int indent);
static void debug_jsonb_value(Datum jsonb_value, const char *label); static void debug_jsonb_value(Datum jsonb_value, const char *label);
static bool is_jsonb_container_valid(JsonbContainer *container); static bool is_jsonb_container_valid(JsonbContainer *container);
static JsonbValue *get_jsonb_value_by_path(Jsonb *jb, const char *path, bool *found);
/* Implementation of a simplified validity check for JsonbContainer */ /* Implementation of a simplified validity check for JsonbContainer */
static bool static bool
@@ -121,8 +122,9 @@ init_template_node(MemoryContext context, TemplateNodeType type)
/* Error code to string conversion */ /* Error code to string conversion */
const char * const char *
template_error_to_string(TemplateErrorCode code) template_error_to_string(TemplateErrorCode code, TemplateConfig *config)
{ {
char *message = "";
switch (code) switch (code)
{ {
case TEMPLATE_ERROR_NONE: case TEMPLATE_ERROR_NONE:
@@ -131,8 +133,32 @@ template_error_to_string(TemplateErrorCode code)
return "Unknown tag"; return "Unknown tag";
case TEMPLATE_ERROR_NESTED_INTERPOLATION: case TEMPLATE_ERROR_NESTED_INTERPOLATION:
return "Nested interpolation"; return "Nested interpolation";
case TEMPLATE_ERROR_NESTED_SECTION_ITERATOR: case TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_CONTROLE:
return "Nested section iterator"; message = "Found `";
strcat(message, config->Syntax.Braces.open);
strcat(message, "` in `");
strcat(message, config->Syntax.Section.control);
strcat(message, "` in section block");
return message;
case TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_SOURCE:
message = "Found `";
strcat(message, config->Syntax.Braces.open);
strcat(message, "` in `");
strcat(message, config->Syntax.Section.source);
strcat(message, "` in section block");
return message;
case TEMPLATE_ERROR_UNEXPECTED_INTERPOLATION_END:
return "Unexpected interpolation end";
case TEMPLATE_ERROR_NO_SOURSE_IN_SECTION:
message = "Not found `";
strcat(message, config->Syntax.Section.source);
strcat(message, "` keyword in section block");
return message;
case TEMPLATE_ERROR_NO_BEGIN_IN_SECTION:
message = "Not found `";
strcat(message, config->Syntax.Section.begin);
strcat(message, "` keyword in section block");
return message;
case TEMPLATE_ERROR_UNEXPECTED_SECTION_END: case TEMPLATE_ERROR_UNEXPECTED_SECTION_END:
return "Unexpected section end"; return "Unexpected section end";
case TEMPLATE_ERROR_NESTED_INCLUDE: case TEMPLATE_ERROR_NESTED_INCLUDE:
@@ -298,6 +324,7 @@ template_parse_interpolation(MemoryContext context, const char **s_ptr,
key_len = *s - key_start; key_len = *s - key_start;
node->value->interpolate.key = MemoryContextStrdup(context, pnstrdup(key_start, key_len)); node->value->interpolate.key = MemoryContextStrdup(context, pnstrdup(key_start, key_len));
elog(DEBUG1, "Parsing: %s", node->value->interpolate.key);
*s = skip_whitespace(*s); *s = skip_whitespace(*s);
@@ -305,7 +332,7 @@ template_parse_interpolation(MemoryContext context, const char **s_ptr,
if (strncmp(*s, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close)) != 0) if (strncmp(*s, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close)) != 0)
{ {
if (error_code) if (error_code)
*error_code = TEMPLATE_ERROR_UNEXPECTED_SECTION_END; *error_code = TEMPLATE_ERROR_UNEXPECTED_INTERPOLATION_END;
template_free_node(node); template_free_node(node);
return NULL; return NULL;
} }
@@ -345,7 +372,7 @@ template_parse_section(MemoryContext context, const char **s_ptr,
if (strncmp(*s, config->Syntax.Braces.open, strlen(config->Syntax.Braces.open)) == 0) if (strncmp(*s, config->Syntax.Braces.open, strlen(config->Syntax.Braces.open)) == 0)
{ {
if (error_code) if (error_code)
*error_code = TEMPLATE_ERROR_NESTED_SECTION_ITERATOR; *error_code = TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_CONTROLE;
template_free_node(node); template_free_node(node);
return NULL; return NULL;
} }
@@ -353,7 +380,7 @@ template_parse_section(MemoryContext context, const char **s_ptr,
if (strncmp(*s, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close)) == 0) if (strncmp(*s, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close)) == 0)
{ {
if (error_code) if (error_code)
*error_code = TEMPLATE_ERROR_UNEXPECTED_SECTION_END; *error_code = TEMPLATE_ERROR_NO_SOURSE_IN_SECTION;
template_free_node(node); template_free_node(node);
return NULL; return NULL;
} }
@@ -371,7 +398,7 @@ template_parse_section(MemoryContext context, const char **s_ptr,
if (strncmp(*s, config->Syntax.Section.source, strlen(config->Syntax.Section.source)) != 0) if (strncmp(*s, config->Syntax.Section.source, strlen(config->Syntax.Section.source)) != 0)
{ {
if (error_code) if (error_code)
*error_code = TEMPLATE_ERROR_UNEXPECTED_SECTION_END; *error_code = TEMPLATE_ERROR_NO_SOURSE_IN_SECTION;
template_free_node(node); template_free_node(node);
return NULL; return NULL;
} }
@@ -389,7 +416,7 @@ template_parse_section(MemoryContext context, const char **s_ptr,
if (strncmp(*s, config->Syntax.Braces.open, strlen(config->Syntax.Braces.open)) == 0) if (strncmp(*s, config->Syntax.Braces.open, strlen(config->Syntax.Braces.open)) == 0)
{ {
if (error_code) if (error_code)
*error_code = TEMPLATE_ERROR_NESTED_SECTION_ITERATOR; *error_code = TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_SOURCE;
template_free_node(node); template_free_node(node);
return NULL; return NULL;
} }
@@ -397,7 +424,7 @@ template_parse_section(MemoryContext context, const char **s_ptr,
if (strncmp(*s, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close)) == 0) if (strncmp(*s, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close)) == 0)
{ {
if (error_code) if (error_code)
*error_code = TEMPLATE_ERROR_UNEXPECTED_SECTION_END; *error_code = TEMPLATE_ERROR_NO_BEGIN_IN_SECTION;
template_free_node(node); template_free_node(node);
return NULL; return NULL;
} }
@@ -411,10 +438,11 @@ template_parse_section(MemoryContext context, const char **s_ptr,
/* Check for 'do' keyword */ /* Check for 'do' keyword */
*s = skip_whitespace(*s); *s = skip_whitespace(*s);
// TODO: why check begin second time, first in while
if (strncmp(*s, config->Syntax.Section.begin, strlen(config->Syntax.Section.begin)) != 0) if (strncmp(*s, config->Syntax.Section.begin, strlen(config->Syntax.Section.begin)) != 0)
{ {
if (error_code) if (error_code)
*error_code = TEMPLATE_ERROR_UNEXPECTED_SECTION_END; *error_code = TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_SOURCE;
template_free_node(node); template_free_node(node);
return NULL; return NULL;
} }
@@ -436,9 +464,28 @@ template_parse_section(MemoryContext context, const char **s_ptr,
const char *body_start = *s; const char *body_start = *s;
const char *original_s = *s; const char *original_s = *s;
int inner_braces_opened_count = 0;
/* Find the end of the section */ /* Find the end of the section */
while (**s && strncmp(*s, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close)) != 0) while (**s) {
// s = {% a %} %}
elog(DEBUG1, "Step, braces opened: %d, s: %s", inner_braces_opened_count, *s);
if (strncmp(*s, config->Syntax.Braces.open, strlen(config->Syntax.Braces.open)) == 0) {
elog(DEBUG1, "inner_braces_opened_count++");
inner_braces_opened_count++;
}
if (strncmp(*s, config->Syntax.Braces.close, strlen(config->Syntax.Braces.close)) == 0) {
if (inner_braces_opened_count > 0) {
elog(DEBUG1, "inner_braces_opened_count--");
inner_braces_opened_count--;
}
else {
elog(DEBUG1, "exit");
break;
}
}
(*s)++; (*s)++;
}
if (!**s) if (!**s)
{ {
@@ -1243,7 +1290,6 @@ get_jsonb_path_value(Datum jsonb_context, const char *path, bool *found)
{ {
Jsonb *jb = (Jsonb *) DatumGetPointer(jsonb_context); Jsonb *jb = (Jsonb *) DatumGetPointer(jsonb_context);
JsonbValue *jbv_result; JsonbValue *jbv_result;
JsonbValue key;
JsonbIterator *it; JsonbIterator *it;
JsonbValue v; JsonbValue v;
JsonbIteratorToken token; JsonbIteratorToken token;
@@ -1266,89 +1312,73 @@ get_jsonb_path_value(Datum jsonb_context, const char *path, bool *found)
PG_TRY(); PG_TRY();
{ {
/* Handle simple top-level key */ /* Use the new path traversal function */
if (strchr(path, '.') == NULL && strchr(path, '[') == NULL) jbv_result = get_jsonb_value_by_path(jb, path, found);
if (*found && jbv_result)
{ {
key.type = jbvString; if (jbv_result->type == jbvString)
key.val.string.val = (char *) path;
key.val.string.len = strlen(path);
jbv_result = findJsonbValueFromContainer(&jb->root, JB_FOBJECT, &key);
if (jbv_result)
{ {
*found = true; result = pnstrdup(jbv_result->val.string.val, jbv_result->val.string.len);
elog(DEBUG1, "Found string value for key %s: %s", path, result);
}
else if (jbv_result->type == jbvNumeric)
{
Numeric num = jbv_result->val.numeric;
result = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(num)));
elog(DEBUG1, "Found numeric value for key %s: %s", path, result);
}
else if (jbv_result->type == jbvBool)
{
result = pstrdup(jbv_result->val.boolean ? "true" : "false");
elog(DEBUG1, "Found boolean value for key %s: %s", path, result);
}
else if (jbv_result->type == jbvNull)
{
result = pstrdup("");
elog(DEBUG1, "Found null value for key %s", path);
}
else if (jbv_result->type == jbvBinary)
{
/* Check if it's an array first */
if (is_jsonb_container_valid((JsonbContainer *)jbv_result->val.binary.data))
{
it = JsonbIteratorInit((JsonbContainer *)jbv_result->val.binary.data);
token = JsonbIteratorNext(&it, &v, false);
if (jbv_result->type == jbvString) if (token == WJB_BEGIN_ARRAY)
{
result = pnstrdup(jbv_result->val.string.val, jbv_result->val.string.len);
elog(DEBUG1, "Found string value for key %s: %s", path, result);
}
else if (jbv_result->type == jbvNumeric)
{
Numeric num = jbv_result->val.numeric;
result = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(num)));
elog(DEBUG1, "Found numeric value for key %s: %s", path, result);
}
else if (jbv_result->type == jbvBool)
{
result = pstrdup(jbv_result->val.boolean ? "true" : "false");
elog(DEBUG1, "Found boolean value for key %s: %s", path, result);
}
else if (jbv_result->type == jbvNull)
{
result = pstrdup("");
elog(DEBUG1, "Found null value for key %s", path);
}
else if (jbv_result->type == jbvBinary)
{
/* Check if it's an array first */
if (is_jsonb_container_valid((JsonbContainer *)&jbv_result->val.binary))
{ {
it = JsonbIteratorInit((JsonbContainer *)&jbv_result->val.binary); /* For arrays, convert to "[Array]" placeholder */
token = JsonbIteratorNext(&it, &v, false); result = pstrdup("[Array]");
elog(DEBUG1, "Found array value for key %s", path);
if (token == WJB_BEGIN_ARRAY) }
{ else if (token == WJB_BEGIN_OBJECT)
/* For arrays, convert to "[Array]" placeholder */ {
result = pstrdup("[Array]"); /* For objects, convert to "{Object}" placeholder */
elog(DEBUG1, "Found array value for key %s", path); result = pstrdup("{Object}");
} elog(DEBUG1, "Found object value for key %s", path);
else if (token == WJB_BEGIN_OBJECT)
{
/* For objects, convert to "{Object}" placeholder */
result = pstrdup("{Object}");
elog(DEBUG1, "Found object value for key %s", path);
}
else
{
/* Convert binary type to string representation */
StringInfoData buf;
initStringInfo(&buf);
appendStringInfoString(&buf, "[Complex Value]");
result = buf.data;
elog(DEBUG1, "Found complex value for key %s", path);
}
} }
else else
{ {
result = pstrdup("[Invalid Binary]"); /* Convert binary type to string representation */
elog(DEBUG1, "Found invalid binary value for key %s", path); StringInfoData buf;
initStringInfo(&buf);
appendStringInfoString(&buf, "[Complex Value]");
result = buf.data;
elog(DEBUG1, "Found complex value for key %s", path);
} }
} }
} else
else {
{ result = pstrdup("[Invalid Binary]");
elog(DEBUG1, "Key %s not found in object", path); elog(DEBUG1, "Found invalid binary value for key %s", path);
}
} }
} }
else else
{ {
/* Handle nested paths using a JSON path expression */ elog(DEBUG1, "Path %s not found in object", path);
elog(DEBUG1, "Complex path not fully supported: %s", path);
result = pstrdup("[Complex Path]");
*found = true;
} }
} }
PG_CATCH(); PG_CATCH();
@@ -1372,7 +1402,6 @@ get_jsonb_array(Datum jsonb_context, const char *path, bool *found)
{ {
Jsonb *jb = (Jsonb *) DatumGetPointer(jsonb_context); Jsonb *jb = (Jsonb *) DatumGetPointer(jsonb_context);
JsonbValue *jbv_result; JsonbValue *jbv_result;
JsonbValue key;
JsonbIterator *it; JsonbIterator *it;
JsonbValue v; JsonbValue v;
JsonbIteratorToken token; JsonbIteratorToken token;
@@ -1393,109 +1422,98 @@ get_jsonb_array(Datum jsonb_context, const char *path, bool *found)
return result; return result;
} }
/* Handle simple top-level key */ elog(DEBUG1, "Looking for array at path: %s", path);
if (strchr(path, '.') == NULL)
/* Use PG_TRY/PG_CATCH to handle any errors during iteration */
PG_TRY();
{ {
elog(DEBUG1, "Looking for array at path: %s", path); /* Use the new path traversal function */
bool path_found = false;
jbv_result = get_jsonb_value_by_path(jb, path, &path_found);
/* Use PG_TRY/PG_CATCH to handle any errors during iteration */ if (path_found && jbv_result)
PG_TRY();
{ {
key.type = jbvString; elog(DEBUG1, "Found value for key %s (type=%d)", path, jbv_result->type);
key.val.string.val = (char *) path;
key.val.string.len = strlen(path);
jbv_result = findJsonbValueFromContainer(&jb->root, JB_FOBJECT, &key); if (jbv_result->type == jbvBinary)
if (jbv_result)
{ {
elog(DEBUG1, "Found value for key %s (type=%d) value=%s", path, jbv_result->type, jbv_result->val.string.val); /* Get more details about the binary data */
elog(DEBUG1, "Binary value found, trying to examine structure");
if (jbv_result->type == jbvBinary) /* Validate the binary container before iterating */
if (!is_jsonb_container_valid(jbv_result->val.binary.data))
{ {
/* Get more details about the binary data */ elog(WARNING, "Invalid binary JSONB container for key: %s", path);
elog(DEBUG1, "Binary value found, trying to examine structure"); return result;
}
/* Validate the binary container before iterating */ elog(DEBUG1, "Trying to initialize the iterator...");
if (!is_jsonb_container_valid(jbv_result->val.binary.data)) /* Try to initialize the iterator */
PG_TRY();
{
/* Log raw pointer for debugging */
elog(DEBUG1, "Binary container address: %p", jbv_result->val.binary.data);
/* Try to get the first 4 bytes of the binary data */
uint32 header = *(uint32 *)jbv_result->val.binary.data;
elog(DEBUG1, "Binary container header: %u", header);
/* Initialize the iterator with careful error handling */
elog(DEBUG1, "Initializing iterator for binary container");
it = JsonbIteratorInit(jbv_result->val.binary.data);
elog(DEBUG1, "Iterator initialized successfully");
elog(DEBUG1, "Getting first token");
token = JsonbIteratorNext(&it, &v, false);
elog(DEBUG1, "First token retrieved: %d", token);
if (token == WJB_BEGIN_ARRAY)
{ {
elog(WARNING, "Invalid binary JSONB container for key: %s", path); /* It's a valid array */
*found = true;
result = PointerGetDatum(JsonbValueToJsonb(jbv_result));
elog(DEBUG1, "Found array at path %s (result=%p)", path, DatumGetPointer(result));
debug_jsonb_value(result, "Array");
return result; return result;
} }
else
elog(DEBUG1, "Trying to initialize the iterator...");
/* Try to initialize the iterator */
PG_TRY();
{ {
/* Log raw pointer for debugging */ elog(DEBUG1, "Path %s exists but is not an array (token type: %d)", path, token);
elog(DEBUG1, "Binary container address: %p", &jbv_result->val.binary);
/* Try to get the first 4 bytes of the binary data */
uint32 header = *(uint32 *)&jbv_result->val.binary;
elog(DEBUG1, "Binary container header: %u", header);
/* Initialize the iterator with careful error handling */
elog(DEBUG1, "Initializing iterator for binary container");
it = JsonbIteratorInit(jbv_result->val.binary.data);
elog(DEBUG1, "Iterator initialized successfully");
elog(DEBUG1, "Getting first token");
token = JsonbIteratorNext(&it, &v, false);
elog(DEBUG1, "First token retrieved: %d", token);
if (token == WJB_BEGIN_ARRAY)
{
/* It's a valid array */
*found = true;
result = PointerGetDatum(JsonbValueToJsonb(jbv_result));
elog(DEBUG1, "Found array at path %s (result=%p)", path, DatumGetPointer(result));
debug_jsonb_value(result, "Array");
return result;
}
else
{
elog(DEBUG1, "Path %s exists but is not an array (token type: %d)", path, token);
}
} }
PG_CATCH();
{
elog(WARNING, "Error initializing JSON iterator for path %s", path);
/* Get more details about the error */
ErrorData *edata = CopyErrorData();
elog(WARNING, "Error message: %s", edata->message);
elog(WARNING, "Error detail: %s", edata->detail ? edata->detail : "none");
elog(WARNING, "Error hint: %s", edata->hint ? edata->hint : "none");
elog(WARNING, "Error context: %s", edata->context ? edata->context : "none");
FreeErrorData(edata);
FlushErrorState();
}
PG_END_TRY();
} }
else PG_CATCH();
{ {
elog(DEBUG1, "Path %s exists but is not binary JSONB (type: %d)", path, jbv_result->type); elog(WARNING, "Error initializing JSON iterator for path %s", path);
/* Get more details about the error */
ErrorData *edata = CopyErrorData();
elog(WARNING, "Error message: %s", edata->message);
elog(WARNING, "Error detail: %s", edata->detail ? edata->detail : "none");
elog(WARNING, "Error hint: %s", edata->hint ? edata->hint : "none");
elog(WARNING, "Error context: %s", edata->context ? edata->context : "none");
FreeErrorData(edata);
FlushErrorState();
} }
PG_END_TRY();
} }
else else
{ {
elog(DEBUG1, "Path %s not found in JSONB", path); elog(DEBUG1, "Path %s exists but is not binary JSONB (type: %d)", path, jbv_result->type);
} }
} }
PG_CATCH(); else
{ {
elog(WARNING, "Exception while processing array at path %s", path); elog(DEBUG1, "Path %s not found in JSONB", path);
ErrorData *edata = CopyErrorData();
elog(WARNING, "Error message: %s", edata->message);
FreeErrorData(edata);
FlushErrorState();
} }
PG_END_TRY();
} }
else { PG_CATCH();
elog(DEBUG1, "Complex path not fully supported: %s", path); {
result = pstrdup("[Complex Path]"); elog(WARNING, "Exception while processing array at path %s", path);
*found = true; ErrorData *edata = CopyErrorData();
elog(WARNING, "Error message: %s", edata->message);
FreeErrorData(edata);
FlushErrorState();
} }
PG_END_TRY();
return result; return result;
} }
@@ -2327,7 +2345,7 @@ pg_template_parse(PG_FUNCTION_ARGS)
{ {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("Template parsing error: %s", template_error_to_string(error_code)))); errmsg("Template parsing error: %s", template_error_to_string(error_code, &config))));
} }
/* Convert the parsed template to a string representation for debugging */ /* Convert the parsed template to a string representation for debugging */
@@ -2408,7 +2426,7 @@ pg_render(PG_FUNCTION_ARGS)
{ {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("Template parsing error: %s", template_error_to_string(error_code)))); errmsg("Template parsing error: %s", template_error_to_string(error_code, &config))));
} }
elog(DEBUG1, "Template parsed successfully, starting render"); elog(DEBUG1, "Template parsed successfully, starting render");
@@ -2587,3 +2605,84 @@ direct_array_check(Datum jsonb_context, const char *path, bool *found)
return result; return result;
} }
/* Function to get JsonbValue by dot-separated path */
static JsonbValue *
get_jsonb_value_by_path(Jsonb *jb, const char *path, bool *found)
{
JsonbValue *result = NULL;
char *path_copy, *token, *saveptr;
JsonbValue key;
JsonbContainer *container;
*found = false;
if (!jb || !path || !is_jsonb_container_valid(&jb->root))
{
elog(DEBUG1, "Invalid JSONB or path in get_jsonb_value_by_path");
return NULL;
}
/* Make a copy of the path to tokenize */
path_copy = pstrdup(path);
container = &jb->root;
/* Use strtok_r to split the path by dots */
token = strtok_r(path_copy, ".", &saveptr);
while (token != NULL)
{
/* 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);
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, "Key '%s' not found in object", token);
pfree(path_copy);
return NULL;
}
/* If there are more path segments, we need to continue with the next container */
token = strtok_r(NULL, ".", &saveptr);
if (token != NULL)
{
/* We need to go deeper, so the current result must be a container */
if (result->type != jbvBinary)
{
elog(DEBUG1, "Path segment '%s' points to a non-container value", token);
pfree(path_copy);
return NULL;
}
/* Move to the next container */
container = (JsonbContainer *)result->val.binary.data;
/* Validate the container */
if (!is_jsonb_container_valid(container))
{
elog(WARNING, "Invalid JSONB container during path traversal");
pfree(path_copy);
return NULL;
}
}
}
/* If we got here, we found the value */
*found = true;
pfree(path_copy);
return result;
}

View File

@@ -15,9 +15,13 @@
typedef enum { typedef enum {
TEMPLATE_ERROR_NONE = 0, TEMPLATE_ERROR_NONE = 0,
TEMPLATE_ERROR_UNKNOWN_TAG, TEMPLATE_ERROR_UNKNOWN_TAG,
TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_CONTROLE,
TEMPLATE_UNEXPECTED_OPEN_BRACES_AFFTER_SECTION_SOURCE,
TEMPLATE_ERROR_UNEXPECTED_INTERPOLATION_END,
TEMPLATE_ERROR_NO_SOURSE_IN_SECTION,
TEMPLATE_ERROR_NESTED_INTERPOLATION, TEMPLATE_ERROR_NESTED_INTERPOLATION,
TEMPLATE_ERROR_NESTED_SECTION_ITERATOR,
TEMPLATE_ERROR_UNEXPECTED_SECTION_END, TEMPLATE_ERROR_UNEXPECTED_SECTION_END,
TEMPLATE_ERROR_NO_BEGIN_IN_SECTION,
TEMPLATE_ERROR_NESTED_INCLUDE, TEMPLATE_ERROR_NESTED_INCLUDE,
TEMPLATE_ERROR_NESTED_EXECUTE, TEMPLATE_ERROR_NESTED_EXECUTE,
TEMPLATE_ERROR_INVALID_CONFIG, TEMPLATE_ERROR_INVALID_CONFIG,
@@ -106,6 +110,6 @@ TemplateConfig template_default_config(MemoryContext context);
bool template_validate_config(const TemplateConfig *config, TemplateErrorCode *error_code); bool template_validate_config(const TemplateConfig *config, TemplateErrorCode *error_code);
TemplateNode *template_parse(MemoryContext context, const char **s, const TemplateConfig *config, bool inner_parse, TemplateErrorCode *error_code); TemplateNode *template_parse(MemoryContext context, const char **s, const TemplateConfig *config, bool inner_parse, TemplateErrorCode *error_code);
void template_free_node(TemplateNode *node); void template_free_node(TemplateNode *node);
const char *template_error_to_string(TemplateErrorCode code); const char *template_error_to_string(TemplateErrorCode code, TemplateConfig *config);
#endif /* HEMAR_TEMPLATE_H */ #endif /* HEMAR_TEMPLATE_H */