diff --git a/package/c/hemar/hemar.c b/package/c/hemar/hemar.c index cae13f5..dca946a 100755 --- a/package/c/hemar/hemar.c +++ b/package/c/hemar/hemar.c @@ -884,509 +884,498 @@ template_render(MemoryContext context, TemplateNode *node, Datum jsonb_context, elog(DEBUG1, "Starting template rendering"); - /* Use PG_TRY/PG_CATCH to handle errors during the entire rendering process */ PG_TRY(); { while (current) { - PG_TRY(); + switch (current->type) { - switch (current->type) - { - case TEMPLATE_NODE_TEXT: - /* Process text node */ - elog(DEBUG1, ""); - elog(DEBUG1, "> TEXT"); - if (current->value->text.content) - { - elog(DEBUG1, "N*TEXT: Rendering text node: %s", current->value->text.content); - appendStringInfoString(&result, current->value->text.content); - } - break; - - case TEMPLATE_NODE_INTERPOLATE: - /* Process interpolation node */ - elog(DEBUG1, ""); - elog(DEBUG1, "> INTERPOLATE"); + case TEMPLATE_NODE_TEXT: + /* Process text node */ + elog(DEBUG1, ""); + elog(DEBUG1, "> TEXT"); + if (current->value->text.content) + { + elog(DEBUG1, "N*TEXT: Rendering text node: %s", current->value->text.content); + appendStringInfoString(&result, current->value->text.content); + } + break; + + case TEMPLATE_NODE_INTERPOLATE: + /* Process interpolation node */ + elog(DEBUG1, ""); + elog(DEBUG1, "> INTERPOLATE"); - char *value = NULL; - bool found_interpolate = false; + char *value = NULL; + bool found_interpolate = false; + + if (current->value->interpolate.key) + { + elog(DEBUG1, "N*INTR: Processing interpolation for key: %s", current->value->interpolate.key); - if (current->value->interpolate.key) + /* First try to get as a direct path */ + /* Extract value from JSONB context */ + value = get_jsonb_path_value(jsonb_context, current->value->interpolate.key, &found_interpolate); + + if (found_interpolate && value) { - elog(DEBUG1, "N*INTR: Processing interpolation for key: %s", current->value->interpolate.key); + elog(DEBUG1, "N*INTR: Found value for key %s: %s", current->value->interpolate.key, value); + appendStringInfoString(&result, value); + pfree(value); + } + else + { + /* If not found as direct path, check if it's an array */ + Datum array_value; + bool array_found = false; - /* First try to get as a direct path */ - /* Extract value from JSONB context */ - value = get_jsonb_path_value(jsonb_context, current->value->interpolate.key, &found_interpolate); + array_value = get_jsonb_array(jsonb_context, current->value->interpolate.key, &array_found); - if (found_interpolate && value) + if (array_found) { - elog(DEBUG1, "N*INTR: Found value for key %s: %s", current->value->interpolate.key, value); - appendStringInfoString(&result, value); - pfree(value); - } - else - { - /* If not found as direct path, check if it's an array */ - Datum array_value; - bool array_found = false; + /* Convert array to string representation */ + elog(DEBUG1, "N*INTR: Found array for key %s, converting to string", current->value->interpolate.key); - array_value = get_jsonb_array(jsonb_context, current->value->interpolate.key, &array_found); + /* Create a string representation of the array */ + StringInfoData array_str; + initStringInfo(&array_str); + appendStringInfoString(&array_str, "["); - if (array_found) + Jsonb *array_jb = (Jsonb *) DatumGetPointer(array_value); + if (array_jb && is_jsonb_container_valid(&array_jb->root)) { - /* Convert array to string representation */ - elog(DEBUG1, "N*INTR: Found array for key %s, converting to string", current->value->interpolate.key); - - /* Create a string representation of the array */ - StringInfoData array_str; - initStringInfo(&array_str); - appendStringInfoString(&array_str, "["); - - Jsonb *array_jb = (Jsonb *) DatumGetPointer(array_value); - if (array_jb && is_jsonb_container_valid(&array_jb->root)) + /* Check if it's actually an array */ + JsonbContainer *jc = &array_jb->root; + if (!(jc->header & JB_FARRAY)) { - /* Check if it's actually an array */ - JsonbContainer *jc = &array_jb->root; - if (!(jc->header & JB_FARRAY)) + elog(DEBUG1, "N*INTR: JSONB value is not an array"); + appendStringInfoString(&array_str, "[Not an array]"); + } + else + { + /* Iterate through array elements */ + JsonbIterator *it = JsonbIteratorInit(jc); + JsonbValue v; + JsonbIteratorToken token; + bool first_element = true; + + /* Skip the WJB_BEGIN_ARRAY token */ + token = JsonbIteratorNext(&it, &v, false); + + /* Process each array element */ + while ((token = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { - elog(DEBUG1, "N*INTR: JSONB value is not an array"); - appendStringInfoString(&array_str, "[Not an array]"); - } - else - { - /* Iterate through array elements */ - JsonbIterator *it = JsonbIteratorInit(jc); - JsonbValue v; - JsonbIteratorToken token; - bool first_element = true; + if (token != WJB_ELEM) + continue; - /* Skip the WJB_BEGIN_ARRAY token */ - token = JsonbIteratorNext(&it, &v, false); + if (!first_element) + appendStringInfoString(&array_str, ", "); + else + first_element = false; - /* Process each array element */ - while ((token = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) + if (v.type == jbvString) { - if (token != WJB_ELEM) - continue; + appendStringInfoChar(&array_str, '"'); + appendBinaryStringInfo(&array_str, v.val.string.val, v.val.string.len); + appendStringInfoChar(&array_str, '"'); + } + else if (v.type == jbvNumeric) + { + char *num_str = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(v.val.numeric))); + appendStringInfoString(&array_str, num_str); + pfree(num_str); + } + else if (v.type == jbvBool) + { + appendStringInfoString(&array_str, v.val.boolean ? "true" : "false"); + } + else if (v.type == jbvNull) + { + appendStringInfoString(&array_str, "null"); + } + else if (v.type == jbvBinary) + { + /* For complex values, convert to string */ + Datum elem = PointerGetDatum(JsonbValueToJsonb(&v)); + bool elem_found = false; + char *elem_str = get_jsonb_path_value(elem, "value", &elem_found); - if (!first_element) - appendStringInfoString(&array_str, ", "); + if (elem_found && elem_str) + { + appendStringInfoString(&array_str, elem_str); + pfree(elem_str); + } else - first_element = false; - - if (v.type == jbvString) { - appendStringInfoChar(&array_str, '"'); - appendBinaryStringInfo(&array_str, v.val.string.val, v.val.string.len); - appendStringInfoChar(&array_str, '"'); - } - else if (v.type == jbvNumeric) - { - char *num_str = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(v.val.numeric))); - appendStringInfoString(&array_str, num_str); - pfree(num_str); - } - else if (v.type == jbvBool) - { - appendStringInfoString(&array_str, v.val.boolean ? "true" : "false"); - } - else if (v.type == jbvNull) - { - appendStringInfoString(&array_str, "null"); - } - else if (v.type == jbvBinary) - { - /* For complex values, convert to string */ - Datum elem = PointerGetDatum(JsonbValueToJsonb(&v)); - bool elem_found = false; - char *elem_str = get_jsonb_path_value(elem, "value", &elem_found); - - if (elem_found && elem_str) - { - appendStringInfoString(&array_str, elem_str); - pfree(elem_str); - } - else - { - appendStringInfoString(&array_str, "[Complex Value]"); - } + appendStringInfoString(&array_str, "[Complex Value]"); } } } } - - appendStringInfoString(&array_str, "]"); - appendStringInfoString(&result, array_str.data); - pfree(array_str.data); - } - else - { - elog(DEBUG1, "N*INTR: Key %s not found in context", current->value->interpolate.key); - /* Optionally append something to indicate missing key */ - appendStringInfoString(&result, ""); } + + appendStringInfoString(&array_str, "]"); + appendStringInfoString(&result, array_str.data); + pfree(array_str.data); + } + else + { + elog(DEBUG1, "N*INTR: Key %s not found in context", current->value->interpolate.key); + /* Optionally append something to indicate missing key */ + appendStringInfoString(&result, ""); } } - case TEMPLATE_NODE_SECTION: - elog(DEBUG1, ""); - elog(DEBUG1, "> SECTION"); - /* Handle sections (loops) */ - char *collection_path = current->value->section.collection; - Datum array_value; - bool found_section = false; - int array_length; - int i; - JsonbParseState *parse_state = NULL; - JsonbValue *empty_obj; - Datum item_context; - Datum merged_context; - char *item_result; - bool item_error = false; + } + case TEMPLATE_NODE_SECTION: + elog(DEBUG1, ""); + elog(DEBUG1, "> SECTION"); + /* Handle sections (loops) */ + char *collection_path = current->value->section.collection; + Datum array_value; + bool found_section = false; + int array_length; + int i; + JsonbParseState *parse_state = NULL; + JsonbValue *empty_obj; + Datum item_context; + Datum merged_context; + char *item_result; + bool item_error = false; + + if (collection_path) + { + elog(DEBUG1, "N*SECT: Processing section with collection path: %s", collection_path); - if (collection_path) + /* Use the improved get_jsonb_array function that handles nested paths */ + array_value = get_jsonb_array(jsonb_context, collection_path, &found_section); + + if (found_section) { - elog(DEBUG1, "N*SECT: Processing section with collection path: %s", collection_path); + elog(DEBUG1, "N*SECT: Found array for section: %s", collection_path); - /* Use the improved get_jsonb_array function that handles nested paths */ - array_value = get_jsonb_array(jsonb_context, collection_path, &found_section); - - if (found_section) + /* Make sure we have a valid array */ + Jsonb *array_jb = (Jsonb *) DatumGetPointer(array_value); + if (!array_jb || !is_jsonb_container_valid(&array_jb->root)) { - elog(DEBUG1, "N*SECT: Found array for section: %s", collection_path); + elog(WARNING, "N*SECT: Invalid JSONB array container for path: %s", collection_path); + break; + } + + /* Check if it's actually an array */ + JsonbContainer *jc = &array_jb->root; + if (!(jc->header & JB_FARRAY)) + { + elog(DEBUG1, "N*SECT: JSONB value is not an array"); + break; + } + + /* If section body is empty, nothing to do */ + if (current->value->section.body == NULL) + { + elog(DEBUG1, "N*SECT: Section body is empty, skipping"); + break; + } + + elog(DEBUG1, "N*SECT: Rendering section body for each array element"); + + /* Log the section body structure for debugging */ + if (current->value->section.body) + { + StringInfoData section_info; + initStringInfo(§ion_info); + template_node_to_string(current->value->section.body, §ion_info, 0); + elog(DEBUG1, "N*SECT: Section body structure: %s", section_info.data); + pfree(section_info.data); + } + + /* Iterate through array elements */ + JsonbIterator *it = JsonbIteratorInit(jc); + JsonbValue v; + JsonbIteratorToken token; + int i = 0; + int nesting_level = 0; + bool in_element = false; + JsonbParseState *element_state = NULL; + JsonbValue *element_value = NULL; + + /* Skip the WJB_BEGIN_ARRAY token */ + token = JsonbIteratorNext(&it, &v, false); + elog(DEBUG1, "N*SECT: Iterator started, first token: %d", token); + + /* Process each array element */ + while ((token = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) + { + elog(DEBUG1, "N*SECT: Token: %d, Type: %s, Nesting: %d", + token, jbv_type_to_string(v.type), nesting_level); - /* Make sure we have a valid array */ - Jsonb *array_jb = (Jsonb *) DatumGetPointer(array_value); - if (!array_jb || !is_jsonb_container_valid(&array_jb->root)) + /* Handle array elements */ + if (token == WJB_ELEM) { - elog(WARNING, "N*SECT: Invalid JSONB array container for path: %s", collection_path); - break; - } - - /* Check if it's actually an array */ - JsonbContainer *jc = &array_jb->root; - if (!(jc->header & JB_FARRAY)) - { - elog(DEBUG1, "N*SECT: JSONB value is not an array"); - break; - } - - /* If section body is empty, nothing to do */ - if (current->value->section.body == NULL) - { - elog(DEBUG1, "N*SECT: Section body is empty, skipping"); - break; - } - - elog(DEBUG1, "N*SECT: Rendering section body for each array element"); - - /* Log the section body structure for debugging */ - if (current->value->section.body) - { - StringInfoData section_info; - initStringInfo(§ion_info); - template_node_to_string(current->value->section.body, §ion_info, 0); - elog(DEBUG1, "N*SECT: Section body structure: %s", section_info.data); - pfree(section_info.data); - } - - /* Iterate through array elements */ - JsonbIterator *it = JsonbIteratorInit(jc); - JsonbValue v; - JsonbIteratorToken token; - int i = 0; - int nesting_level = 0; - bool in_element = false; - JsonbParseState *element_state = NULL; - JsonbValue *element_value = NULL; - - /* Skip the WJB_BEGIN_ARRAY token */ - token = JsonbIteratorNext(&it, &v, false); - elog(DEBUG1, "N*SECT: Iterator started, first token: %d", token); - - /* Process each array element */ - while ((token = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) - { - elog(DEBUG1, "N*SECT: Token: %d, Type: %s, Nesting: %d", - token, jbv_type_to_string(v.type), nesting_level); + item_context = (Datum) 0; + item_error = false; - /* Handle array elements */ - if (token == WJB_ELEM) + elog(DEBUG1, "N*SECT: Processing array element %d", i); + + /* Convert the JsonbValue to a Datum */ + PG_TRY(); { - item_context = (Datum) 0; - item_error = false; - - elog(DEBUG1, "N*SECT: Processing array element %d", i); - - /* Convert the JsonbValue to a Datum */ - PG_TRY(); + if (v.type == jbvBinary) { - if (v.type == jbvBinary) + /* For binary values, just convert directly */ + item_context = PointerGetDatum(JsonbValueToJsonb(&v)); + } + else if (v.type == jbvNull) + { + /* Handle null values by creating an empty object */ + parse_state = NULL; + pushJsonbValue(&parse_state, WJB_BEGIN_OBJECT, NULL); + empty_obj = pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL); + item_context = PointerGetDatum(JsonbValueToJsonb(empty_obj)); + } + else + { + /* For scalar values, create a proper JSON object */ + parse_state = NULL; + pushJsonbValue(&parse_state, WJB_BEGIN_OBJECT, NULL); + + /* Add a dummy key "value" */ + JsonbValue key; + key.type = jbvString; + key.val.string.val = "value"; + key.val.string.len = 5; + + pushJsonbValue(&parse_state, WJB_KEY, &key); + + /* Add the value */ + pushJsonbValue(&parse_state, WJB_VALUE, &v); + + /* Finish the object */ + empty_obj = pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL); + + /* Convert to Jsonb */ + item_context = PointerGetDatum(JsonbValueToJsonb(empty_obj)); + } + + /* Process this element */ + process_array_element: + + /* Validate we got a valid item back */ + if (item_context != (Datum) 0) + { + Jsonb *item_jb = (Jsonb *) DatumGetPointer(item_context); + if (item_jb && is_jsonb_container_valid(&item_jb->root)) { - /* For binary values, just convert directly */ - item_context = PointerGetDatum(JsonbValueToJsonb(&v)); - } - else if (v.type == jbvNull) - { - /* Handle null values by creating an empty object */ - parse_state = NULL; - pushJsonbValue(&parse_state, WJB_BEGIN_OBJECT, NULL); - empty_obj = pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL); - item_context = PointerGetDatum(JsonbValueToJsonb(empty_obj)); + elog(DEBUG1, "N*SECT: Got valid array element %d", i); + elog(DEBUG1, "N*SECT: Array Element: %s", JsonbToCString(NULL, &item_jb->root, VARSIZE_ANY_EXHDR(item_jb))); + + /* Create context with iterator variable */ + PG_TRY(); + { + merged_context = create_iterator_context(jsonb_context, current->value->section.iterator, item_context); + } + PG_CATCH(); + { + elog(WARNING, "N*SECT: Error creating merged context for array element %d", i); + /* Use parent context as fallback */ + merged_context = jsonb_context; + + /* Reset error state */ + FlushErrorState(); + } + PG_END_TRY(); + + if (merged_context == (Datum) 0) + { + elog(WARNING, "N*SECT: Failed to create merged context for array element %d", i); + i++; + continue; + } + + /* Render section body with new context */ + PG_TRY(); + { + item_result = template_render(context, current->value->section.body, merged_context, &item_error); + + if (!item_error && item_result) + { + appendStringInfoString(&result, item_result); + pfree(item_result); + } + else if (item_error) + { + elog(WARNING, "N*SECT: Error rendering template section for array element %d", i); + *error = true; + return result.data; + } + } + PG_CATCH(); + { + elog(WARNING, "N*SECT: Exception during template rendering for array element %d", i); + /* Continue with next element */ + FlushErrorState(); + } + PG_END_TRY(); } else { - /* For scalar values, create a proper JSON object */ - parse_state = NULL; - pushJsonbValue(&parse_state, WJB_BEGIN_OBJECT, NULL); - - /* Add a dummy key "value" */ - JsonbValue key; - key.type = jbvString; - key.val.string.val = "value"; - key.val.string.len = 5; - - pushJsonbValue(&parse_state, WJB_KEY, &key); - - /* Add the value */ - pushJsonbValue(&parse_state, WJB_VALUE, &v); - - /* Finish the object */ - empty_obj = pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL); - - /* Convert to Jsonb */ - item_context = PointerGetDatum(JsonbValueToJsonb(empty_obj)); - } - - /* Process this element */ - process_array_element: - - /* Validate we got a valid item back */ - if (item_context != (Datum) 0) - { - Jsonb *item_jb = (Jsonb *) DatumGetPointer(item_context); - if (item_jb && is_jsonb_container_valid(&item_jb->root)) - { - elog(DEBUG1, "N*SECT: Got valid array element %d", i); - elog(DEBUG1, "N*SECT: Array Element: %s", JsonbToCString(NULL, &item_jb->root, VARSIZE_ANY_EXHDR(item_jb))); - - /* Create context with iterator variable */ - PG_TRY(); - { - merged_context = create_iterator_context(jsonb_context, current->value->section.iterator, item_context); - } - PG_CATCH(); - { - elog(WARNING, "N*SECT: Error creating merged context for array element %d", i); - /* Use parent context as fallback */ - merged_context = jsonb_context; - - /* Reset error state */ - FlushErrorState(); - } - PG_END_TRY(); - - if (merged_context == (Datum) 0) - { - elog(WARNING, "N*SECT: Failed to create merged context for array element %d", i); - i++; - continue; - } - - /* Render section body with new context */ - PG_TRY(); - { - item_result = template_render(context, current->value->section.body, merged_context, &item_error); - - if (!item_error && item_result) - { - appendStringInfoString(&result, item_result); - pfree(item_result); - } - else if (item_error) - { - elog(WARNING, "N*SECT: Error rendering template section for array element %d", i); - *error = true; - return result.data; - } - } - PG_CATCH(); - { - elog(WARNING, "N*SECT: Exception during template rendering for array element %d", i); - /* Continue with next element */ - FlushErrorState(); - } - PG_END_TRY(); - } - else - { - elog(WARNING, "N*SECT: Got invalid JSONB container for array element %d", i); - } - } - else - { - elog(DEBUG1, "N*SECT: Array element %d is null, creating empty object", i); - /* Create an empty object for null array elements */ - parse_state = NULL; - - pushJsonbValue(&parse_state, WJB_BEGIN_OBJECT, NULL); - empty_obj = pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL); - item_context = PointerGetDatum(JsonbValueToJsonb(empty_obj)); - goto process_array_element; + elog(WARNING, "N*SECT: Got invalid JSONB container for array element %d", i); } } - PG_CATCH(); + else { - elog(WARNING, "N*SECT: Error processing array element %d, creating empty object instead", i); - /* Create an empty object for problematic array elements */ + elog(DEBUG1, "N*SECT: Array element %d is null, creating empty object", i); + /* Create an empty object for null array elements */ parse_state = NULL; pushJsonbValue(&parse_state, WJB_BEGIN_OBJECT, NULL); empty_obj = pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL); item_context = PointerGetDatum(JsonbValueToJsonb(empty_obj)); - - /* Reset error state */ - FlushErrorState(); - goto process_array_element; - } - PG_END_TRY(); - - i++; - } - /* Handle complex objects within the array */ - else if (token == WJB_BEGIN_OBJECT || token == WJB_BEGIN_ARRAY) - { - if (nesting_level == 0) - { - /* Starting a new complex element */ - elog(DEBUG1, "N*SECT: Starting complex element %d", i); - element_state = NULL; - element_value = pushJsonbValue(&element_state, token, NULL); - in_element = true; - } - nesting_level++; - } - else if ((token == WJB_END_OBJECT || token == WJB_END_ARRAY) && in_element) - { - nesting_level--; - - if (nesting_level == 0) - { - /* Finished a complex element */ - elog(DEBUG1, "N*SECT: Finished complex element %d", i); - element_value = pushJsonbValue(&element_state, token, NULL); - in_element = false; - - /* Convert to Datum and process */ - item_context = PointerGetDatum(JsonbValueToJsonb(element_value)); - item_error = false; - - /* Process this complex element */ goto process_array_element; } } - else if (in_element) + PG_CATCH(); { - /* Add to the current element being built */ - pushJsonbValue(&element_state, token, &v); + elog(WARNING, "N*SECT: Error processing array element %d, creating empty object instead", i); + /* Create an empty object for problematic array elements */ + parse_state = NULL; + + pushJsonbValue(&parse_state, WJB_BEGIN_OBJECT, NULL); + empty_obj = pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL); + item_context = PointerGetDatum(JsonbValueToJsonb(empty_obj)); + + /* Reset error state */ + FlushErrorState(); + goto process_array_element; + } + PG_END_TRY(); + + i++; + } + /* Handle complex objects within the array */ + else if (token == WJB_BEGIN_OBJECT || token == WJB_BEGIN_ARRAY) + { + if (nesting_level == 0) + { + /* Starting a new complex element */ + elog(DEBUG1, "N*SECT: Starting complex element %d", i); + element_state = NULL; + element_value = pushJsonbValue(&element_state, token, NULL); + in_element = true; + } + nesting_level++; + } + else if ((token == WJB_END_OBJECT || token == WJB_END_ARRAY) && in_element) + { + nesting_level--; + + if (nesting_level == 0) + { + /* Finished a complex element */ + elog(DEBUG1, "N*SECT: Finished complex element %d", i); + element_value = pushJsonbValue(&element_state, token, NULL); + in_element = false; + + /* Convert to Datum and process */ + item_context = PointerGetDatum(JsonbValueToJsonb(element_value)); + item_error = false; + + /* Process this complex element */ + goto process_array_element; } } - } - else - { - elog(DEBUG1, "N*SECT: Collection not found: %s", collection_path); + else if (in_element) + { + /* Add to the current element being built */ + pushJsonbValue(&element_state, token, &v); + } } } - case TEMPLATE_NODE_EXECUTE: - elog(DEBUG1, ""); - elog(DEBUG1, "> EXECUTE"); - /* Execute is not implemented in this version */ - elog(DEBUG1, "N*EXEC: Execute node type not implemented"); - case TEMPLATE_NODE_INCLUDE: - elog(DEBUG1, ""); - elog(DEBUG1, "> INCLUDE"); - /* Handle includes */ - char *template_key = current->value->include.key; - Datum include_data; - bool found_include = false; - - if (template_key) + else { - elog(DEBUG1, "N*INCL: Processing include with key: %s", template_key); + elog(DEBUG1, "N*SECT: Collection not found: %s", collection_path); + } + } + case TEMPLATE_NODE_EXECUTE: + elog(DEBUG1, ""); + elog(DEBUG1, "> EXECUTE"); + /* Execute is not implemented in this version */ + elog(DEBUG1, "N*EXEC: Execute node type not implemented"); + case TEMPLATE_NODE_INCLUDE: + elog(DEBUG1, ""); + elog(DEBUG1, "> INCLUDE"); + /* Handle includes */ + char *template_key = current->value->include.key; + Datum include_data; + bool found_include = false; + + if (template_key) + { + elog(DEBUG1, "N*INCL: Processing include with key: %s", template_key); + + /* Find include template in context */ + include_data = get_jsonb_include_template(jsonb_context, template_key, &found_include); + + if (found_include) + { + char *include_template = NULL; + Datum include_context = (Datum) 0; + char *include_result; + bool include_error = false; - /* Find include template in context */ - include_data = get_jsonb_include_template(jsonb_context, template_key, &found_include); + /* Extract template and context */ + get_include_data(include_data, &include_template, &include_context); - if (found_include) + /* Parse and render included template */ + if (include_template) { - char *include_template = NULL; - Datum include_context = (Datum) 0; - char *include_result; - bool include_error = false; + TemplateConfig config = template_default_config(context); + TemplateErrorCode error_code; + const char *template_str = include_template; + TemplateNode *include_node = template_parse(context, &template_str, &config, false, &error_code); - /* Extract template and context */ - get_include_data(include_data, &include_template, &include_context); - - /* Parse and render included template */ - if (include_template) + if (include_node && error_code == TEMPLATE_ERROR_NONE) { - TemplateConfig config = template_default_config(context); - TemplateErrorCode error_code; - const char *template_str = include_template; - TemplateNode *include_node = template_parse(context, &template_str, &config, false, &error_code); + include_result = template_render(context, include_node, + include_context != (Datum) 0 ? include_context : jsonb_context, + &include_error); - if (include_node && error_code == TEMPLATE_ERROR_NONE) + if (!include_error && include_result) { - include_result = template_render(context, include_node, - include_context != (Datum) 0 ? include_context : jsonb_context, - &include_error); - - if (!include_error && include_result) - { - appendStringInfoString(&result, include_result); - pfree(include_result); - } - else - { - elog(WARNING, "N*INCL: Error rendering included template: %s", template_key); - *error = true; - } - - template_free_node(include_node); + appendStringInfoString(&result, include_result); + pfree(include_result); } else { - elog(WARNING, "N*INCL: Error parsing included template: %s", template_key); + elog(WARNING, "N*INCL: Error rendering included template: %s", template_key); *error = true; } - pfree(include_template); + template_free_node(include_node); } else { - elog(WARNING, "N*INCL: Included template is null: %s", template_key); + elog(WARNING, "N*INCL: Error parsing included template: %s", template_key); + *error = true; } + + pfree(include_template); } else { - elog(DEBUG1, "N*INCL: Include key not found: %s", template_key); + elog(WARNING, "N*INCL: Included template is null: %s", template_key); } } - break; - default: - /* Unknown node type */ - elog(WARNING, "N*UNKN: Unknown node type: %d", current->type); - break; - } + else + { + elog(DEBUG1, "N*INCL: Include key not found: %s", template_key); + } + } + break; + default: + /* Unknown node type */ + elog(WARNING, "N*UNKN: Unknown node type: %d", current->type); + break; } - PG_CATCH(); - { - elog(WARNING, "Error processing template node of type %d", current->type); - FlushErrorState(); - /* Continue with next node */ - } - PG_END_TRY(); if (*error) break; @@ -2279,5 +2268,4 @@ get_jsonb_value_by_path(Jsonb *jb, const char *path, bool *found) *found = true; pfree(path_copy); return result; -} - +} \ No newline at end of file