From 609bae352fc3cc738590a9036473c582ee18c39a Mon Sep 17 00:00:00 2001 From: yukkop Date: Tue, 13 May 2025 01:15:43 +0000 Subject: [PATCH] fix: `hemar`: unbeliveble, without memmory error? not true --- flake.nix | 1 + package/c/hectic/hectic.c | 33 ++- package/c/hectic/hectic.h | 19 ++ package/c/hemar/hemar--0.1.sql | 27 +-- package/c/hemar/hemar.c | 421 ++++----------------------------- 5 files changed, 96 insertions(+), 405 deletions(-) diff --git a/flake.nix b/flake.nix index 7daa12c..f93c33e 100644 --- a/flake.nix +++ b/flake.nix @@ -326,6 +326,7 @@ environment.systemPackages = with pkgs; [ ]; programs.zsh.shellAliases = self.lib.sharedShellAliases // { conn = "sudo su postgres -c 'psql -p 64317'"; + check = "journalctl -u postgresql"; }; virtualisation = { diff --git a/package/c/hectic/hectic.c b/package/c/hectic/hectic.c index b545818..7ee893c 100644 --- a/package/c/hectic/hectic.c +++ b/package/c/hectic/hectic.c @@ -6,6 +6,29 @@ #include #include +MemoryAllocator default_allocator = { + .malloc = malloc, + .free = free +}; + +void init_default_allocator(void) { + default_allocator.malloc = malloc; + default_allocator.free = free; +} + +void set_memory_allocator(MemoryAllocator allocator) { + default_allocator = allocator; +} + +// TODO(yukkop): rename without arena_ prefix +void* arena_memory_alloc(size_t size) { + return default_allocator.malloc(size); +} + +void arena_memory_free(void* ptr) { + default_allocator.free(ptr); +} + // On systems without strsep, provide a custom implementation #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 @@ -231,7 +254,7 @@ void logger_level(LogLevel level) { // NOTE(yukkop): This function not uses POSITION_INFO because it's not have a user error. All possible errors are realization errors. void logger_init(void) { - log_rules_arena = malloc(sizeof(Arena)); + log_rules_arena = arena_memory_alloc(sizeof(Arena)); if (!log_rules_arena) { fprintf(stderr, "INIT: Failed to allocate memory for logger arena\n"); exit(1); @@ -682,7 +705,7 @@ Arena arena_init__(POSITION_INFO_DECLARATION, size_t size) { "ARENA INIT: Creating arena (size: %zu bytes)", size); Arena arena; - arena.begin = malloc(size); + arena.begin = arena_memory_alloc(size); // Check for allocation failure if (!arena.begin) { @@ -725,7 +748,7 @@ void* arena_alloc_or_null__(POSITION_INFO_DECLARATION, Arena *arena, size_t size raise_message(LOG_LEVEL_WARN, POSITION_INFO, "ARENA ALLOC: Expanding arena (old: %zu, new: %zu)", arena->capacity, new_capacity); - void *new_mem = malloc(new_capacity); + void *new_mem = arena_memory_alloc(new_capacity); if (!new_mem) { raise_message(LOG_LEVEL_WARN, POSITION_INFO, "ARENA ALLOC: Failed to expand arena (requested: %zu bytes)", new_capacity); @@ -733,7 +756,7 @@ void* arena_alloc_or_null__(POSITION_INFO_DECLARATION, Arena *arena, size_t size } memcpy(new_mem, arena->begin, used); - free(arena->begin); + arena_memory_free(arena->begin); arena->begin = new_mem; arena->current = (char *)new_mem + used; arena->capacity = new_capacity; @@ -840,7 +863,7 @@ void arena_free__(POSITION_INFO_DECLARATION, Arena *arena) { size_t used = (size_t)arena->current - (size_t)arena->begin; // Free the memory - free(arena->begin); + arena_memory_free(arena->begin); // Success logging raise_message(LOG_LEVEL_LOG, POSITION_INFO, diff --git a/package/c/hectic/hectic.h b/package/c/hectic/hectic.h index e06a88d..2d5ca7a 100644 --- a/package/c/hectic/hectic.h +++ b/package/c/hectic/hectic.h @@ -365,6 +365,25 @@ void substr_clone__(const char *file, const char *func, int line, const char * c #define ARENA_DEFAULT_SIZE MEM_MiB +// Memory allocation interface +typedef struct { + void* (*malloc)(size_t size); + void (*free)(void* ptr); +} MemoryAllocator; + +// Global memory allocator +extern MemoryAllocator default_allocator; + +// Initialize default allocator with system malloc +void init_default_allocator(void); + +// Set custom memory allocator +void set_memory_allocator(MemoryAllocator allocator); + +// Memory allocation functions that use current allocator +void* arena_memory_alloc(size_t size); +void arena_memory_free(void* ptr); + typedef struct { void *begin; void *current; diff --git a/package/c/hemar/hemar--0.1.sql b/package/c/hemar/hemar--0.1.sql index ede0943..850473a 100755 --- a/package/c/hemar/hemar--0.1.sql +++ b/package/c/hemar/hemar--0.1.sql @@ -20,27 +20,12 @@ CREATE SCHEMA hemar; -- $hemar$ -- ); -- ``` -CREATE FUNCTION "hemar"."render"("declare" jsonb, "template" text) -RETURNS text -AS 'hemar', 'pg_render' -LANGUAGE C STRICT; - -CREATE FUNCTION "hemar"."test_log"() -RETURNS void -AS 'hemar', 'pg_test_log' -LANGUAGE C STRICT; - -CREATE FUNCTION "hemar"."test_log_2"(text, text) -RETURNS void -AS 'hemar', 'pg_test_log_2' -LANGUAGE C STRICT; - -CREATE FUNCTION "hemar"."test_log_3"(name1 text, name2 text) -RETURNS void -AS 'hemar', 'pg_test_log_2' -LANGUAGE C STRICT; +--CREATE FUNCTION "hemar"."render"("declare" jsonb, "template" text) +--RETURNS text +--LANGUAGE C STRICT +--AS 'hemar', 'pg_render'; CREATE FUNCTION "hemar"."parse"("template" text) RETURNS text -AS 'hemar', 'pg_template_parse' -LANGUAGE C STRICT; +LANGUAGE C STRICT +AS 'hemar', 'pg_template_parse'; diff --git a/package/c/hemar/hemar.c b/package/c/hemar/hemar.c index 8bd2455..9053e7c 100755 --- a/package/c/hemar/hemar.c +++ b/package/c/hemar/hemar.c @@ -1,395 +1,61 @@ -#include -#include -#include -#include -#include -#include "hectic.h" +#include +#include +#include +#include +#include +#include "postgres.h" +#include "catalog/pg_type_d.h" +#include "fmgr.h" +#include "nodes/pg_list.h" +#include "parser/parse_func.h" +#include "utils/builtins.h" +#include "utils/datum.h" +#include "utils/json.h" +#include "utils/memutils.h" +#include "utils/regproc.h" #include +#include "hectic.h" #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif +static MemoryContext HemarContext = NULL; + #define LOG_FILE "/tmp/hemar.log" #define INIT \ + MemoryContext oldctx; \ + oldctx = MemoryContextSwitchTo(HemarContext); \ logger_init(); \ logger_set_file(LOG_FILE); \ logger_set_output_mode(LOG_OUTPUT_BOTH); \ Arena arena = arena_init(MEM_MiB); + #define FREE \ - DISPOSABLE_ARENA_FREE; \ - arena_free(&arena); \ - logger_free(); + /*DISPOSABLE_ARENA_FREE*/; \ + /*arena_free(&arena);*/ \ + /*logger_free();*/ \ + MemoryContextSwitchTo(oldctx); \ + MemoryContextReset(HemarContext); -/* helper function to get a JSON value by key path */ -static Json *json_get_by_path(Arena *arena, const Json *context, const char *key_path) { - - char *path_copy; - char *token; - Json *current; - - if (!context || !key_path || !*key_path) { - return NULL; - } - - path_copy = arena_strdup(arena, key_path); - token = strtok(path_copy, "."); - current = (Json*)context; - - while (token && current) { - current = json_get_object_item(current, token); - token = strtok(NULL, "."); - } - - return current; +void noop_free(void* ptr) { + (void)ptr; // suppress unused warning } -/* Convert JSON value to string */ -static char *json_value_to_string(Arena *arena, const Json *json) { - if (!json) { - return ""; - } - - switch (json->type) { - case JSON_STRING: - return json->value.string; - case JSON_NUMBER: { - char *buf = arena_alloc(arena, 64); - snprintf(buf, 64, "%.6g", json->value.number); - return buf; - } - case JSON_BOOL: - return json->value.boolean ? "true" : "false"; - case JSON_NULL: - return ""; - case JSON_ARRAY: - case JSON_OBJECT: - return JSON_TO_STR(arena, json); - default: - return ""; - } -} - -/* Forward declaration for recursive function */ -static char *render_template_node(Arena *arena, const TemplateNode *node, const Json *context); - -/* Render a text node */ -static char *render_text_node(Arena *arena, const TemplateNode *node) { - if (!node || node->type != TEMPLATE_NODE_TEXT) { - return ""; - } - - return node->value->text.content; -} - -/* Render an interpolation node */ -static char *render_interpolation_node(Arena *arena, const TemplateNode *node, const Json *context) { - const char *key; - Json *value; - - if (!node || node->type != TEMPLATE_NODE_INTERPOLATE || !context) { - return ""; - } - - key = node->value->interpolate.key; - value = json_get_by_path(arena, context, key); - - if (!value) { - return ""; - } - - return json_value_to_string(arena, value); -} - -/* Render a section node (for loop) */ -static char *render_section_node(Arena *arena, const TemplateNode *node, const Json *context) { - const char *collection_key; - const char *iterator_name; - TemplateNode *body; - - Json *collection; - - size_t buffer_size; - char *buffer; - size_t buffer_pos; - - Json *item; - - const char *empty_json; - Json *iter_context; - - Json *item_json; - - char *rendered_body; - size_t rendered_len; - - if (!node || node->type != TEMPLATE_NODE_SECTION || !context) { - return ""; - } - - collection_key = node->value->section.collection; - iterator_name = node->value->section.iterator; - body = node->value->section.body; - - collection = json_get_by_path(arena, context, collection_key); - - if (!collection || collection->type != JSON_ARRAY) { - return ""; - } - - buffer_size = 1024; - buffer = arena_alloc(arena, buffer_size); - buffer_pos = 0; - - item = collection->value.child; - while (item) { - empty_json = "{}"; - iter_context = json_parse(arena, &empty_json); - if (!iter_context) { - return ""; - } - - item_json = arena_alloc(arena, sizeof(Json)); - memcpy(item_json, item, sizeof(Json)); - item_json->key = arena_strdup(arena, iterator_name); - item_json->next = NULL; - - rendered_body = render_template_node(arena, body, iter_context); - - rendered_len = strlen(rendered_body); - if (buffer_pos + rendered_len + 1 > buffer_size) { - buffer_size = (buffer_pos + rendered_len + 1) * 2; - buffer = arena_realloc(arena, buffer, buffer_size / 2, buffer_size); - } - - strcpy(buffer + buffer_pos, rendered_body); - buffer_pos += rendered_len; - - item = item->next; - } - - buffer[buffer_pos] = '\0'; - return buffer; -} - -/* Render an include node */ -static char *render_include_node(Arena *arena, const TemplateNode *node, const Json *context) { - - const char *include_key; - Json *include_value; - - char *buffer; - size_t buffer_pos; - - Json *include_item; - Json *template_json; - Json *content_json; - Json *context_json; - - if (!node || node->type != TEMPLATE_NODE_INCLUDE || !context) { - return ""; - } - include_key = node->value->include.key; - include_value = json_get_by_path(arena, context, include_key); - - if (!include_value || include_value->type != JSON_ARRAY) { - return ""; - } - - buffer = arena_alloc(arena, 1024); - buffer_pos = 0; - - - include_item = include_value->value.child; - while (include_item) { - if (include_item->type == JSON_OBJECT) { - template_json = json_get_object_item(include_item, "template"); - content_json = json_get_object_item(include_item, "content"); - context_json = json_get_object_item(include_item, "context"); - - if (template_json && template_json->type == JSON_STRING) { - const char *template_str = template_json->value.string; - const Json *include_context = context_json ? context_json : context; - - TemplateConfig config = template_default_config(arena); - TemplateResult template_result = template_parse(arena, &template_str, &config); - - if (!IS_RESULT_ERROR(template_result)) { - TemplateNode template_node = RESULT_SOME_VALUE(template_result); - - char *rendered = render_template_node(arena, &template_node, include_context); - - buffer_pos += sprintf(buffer + buffer_pos, "%s", rendered); - } - } else if (content_json && content_json->type == JSON_STRING) { - buffer_pos += sprintf(buffer + buffer_pos, "%s", content_json->value.string); - } - } - - include_item = include_item->next; - } - - buffer[buffer_pos] = '\0'; - return buffer; -} - -/* Render a template node tree recursively */ -static char *render_template_node(Arena *arena, const TemplateNode *node, const Json *context) { - - size_t buffer_size = 4096; - char *output = arena_alloc(arena, buffer_size); - size_t output_pos = 0; - size_t rendered_len; - const TemplateNode *current; - char *rendered; - - if (!node) { - return ""; - } - - current = node; - - while (current) { - rendered = NULL; - - switch (current->type) { - case TEMPLATE_NODE_TEXT: - rendered = render_text_node(arena, current); - break; - - case TEMPLATE_NODE_INTERPOLATE: - rendered = render_interpolation_node(arena, current, context); - break; - - case TEMPLATE_NODE_SECTION: - rendered = render_section_node(arena, current, context); - break; - - case TEMPLATE_NODE_INCLUDE: - rendered = render_include_node(arena, current, context); - break; - - case TEMPLATE_NODE_EXECUTE: - todo; - rendered = ""; - break; - - default: - rendered = ""; - break; - } - - rendered_len = strlen(rendered); - if (output_pos + rendered_len + 1 > buffer_size) { - buffer_size = (output_pos + rendered_len + 1) * 2; - output = arena_realloc(arena, output, buffer_size / 2, buffer_size); - } - - strcpy(output + output_pos, rendered); - output_pos += rendered_len; - - current = current->next; - } - - output[output_pos] = '\0'; - return output; -} - -/* Define the function render */ -PG_FUNCTION_INFO_V1(pg_render); - -/* - * Function to render templates using hectic library with JSON context - * Arguments: - * 1. declare - JSON context for rendering - * 2. template - The template text to render - */ -Datum pg_render(PG_FUNCTION_ARGS) +void _PG_init(void) { - INIT; + HemarContext = AllocSetContextCreate(TopMemoryContext, + "HemarContext", + ALLOCSET_DEFAULT_SIZES); - printf("Rendering template\n"); + MemoryAllocator allocators = { + .malloc = palloc, + .free = noop_free + }; - text *context_text = PG_GETARG_TEXT_PP(0); - text *template_text = PG_GETARG_TEXT_PP(1); - - printf("Context: %s\n", text_to_cstring(context_text)); - - /* Convert input text to C string */ - char *template_str = text_to_cstring(template_text); - char *context_str = text_to_cstring(context_text); - - printf("Template: %s\n", template_str); - - TemplateNode root_node; - TemplateResult template_result; - - Json *context; - - const char *template_ptr; - - char *result_str; - text *result; - - /* Parse the JSON context */ - const char *json_ptr = context_str; - TemplateConfig config = template_default_config(&arena); - context = json_parse(&arena, &json_ptr); - - if (!context) { - FREE; - ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Invalid JSON context"))); - } - - /* Parse the template text */ - template_ptr = template_str; - template_result = template_parse(&arena, &template_ptr, &config); - - if (IS_RESULT_ERROR(template_result)) { - FREE; - ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("Failed to parse template: %s", - RESULT_ERROR_MESSAGE(template_result)))); - } - - /* Render the template */ - root_node = RESULT_SOME_VALUE(template_result); - result_str = render_template_node(&arena, &root_node, context); - - /* Prepare return value */ - result = cstring_to_text(result_str); - - FREE; - PG_RETURN_TEXT_P(result); -} - -PG_FUNCTION_INFO_V1(pg_test_log); - -Datum pg_test_log(PG_FUNCTION_ARGS) { - INIT; - raise_info("Testing log"); - - FREE; - PG_RETURN_VOID(); -} - -PG_FUNCTION_INFO_V1(pg_test_log_2); - -Datum pg_test_log_2(PG_FUNCTION_ARGS) { - INIT; - raise_info("Testing log"); - - text *context_text = PG_GETARG_TEXT_PP(0); - text *template_text = PG_GETARG_TEXT_PP(1); - - raise_info("Context: %s", text_to_cstring(context_text)); - raise_info("Template: %s", text_to_cstring(template_text)); - - FREE; - PG_RETURN_VOID(); + set_memory_allocator(allocators); } PG_FUNCTION_INFO_V1(pg_template_parse); @@ -397,15 +63,15 @@ PG_FUNCTION_INFO_V1(pg_template_parse); Datum pg_template_parse(PG_FUNCTION_ARGS) { INIT; - text *context_text = PG_GETARG_TEXT_PP(0); - char *content = text_to_cstring(context_text); + text *template_text = PG_GETARG_TEXT_PP(0); + const char *template_str = text_to_cstring(template_text); + raise_notice("%s", template_str); - const char *template_ptr; TemplateResult template_result; TemplateConfig config = template_default_config(&arena); raise_info("start parsing...."); - template_result = template_parse(&arena, &template_ptr, &config); + template_result = template_parse(&arena, &template_str, &config); raise_info("parsing finished...."); if (IS_RESULT_ERROR(template_result)) { @@ -421,12 +87,9 @@ Datum pg_template_parse(PG_FUNCTION_ARGS) { char *result_str = JSON_TO_STR(&arena, json); raise_notice("%s", result_str); - - char *result_str_clone = malloc(strlen(result_str) + 1); - if (result_str_clone) strcpy(result_str_clone, result_str); + text *result = cstring_to_text(result_str); FREE; - text *result = cstring_to_text(result_str_clone); PG_RETURN_TEXT_P(result); }