Skip to content

Commit

Permalink
feat: sections for arrays
Browse files Browse the repository at this point in the history
Quick and dirty implementation.
  • Loading branch information
steve-chavez committed Nov 19, 2023
1 parent 3e357e4 commit 76e827d
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 16 deletions.
80 changes: 64 additions & 16 deletions src/plmustache.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <utils/syscache.h>
#include <utils/lsyscache.h>
#include <utils/fmgroids.h>
#include <funcapi.h>
#include <catalog/pg_proc.h>
#include <catalog/pg_type.h>
Expand All @@ -19,14 +20,20 @@ PG_MODULE_MAGIC;
#define IMPLICIT_ITERATOR "."

typedef struct {
char *prm_name;
char *prm_value;
bool enters_section;
char *prm_name;
char *prm_value;
char **prm_arr;
size_t prm_arr_length;
bool enters_section;
bool is_array;
} plmustache_param;

typedef struct {
size_t num_params;
plmustache_param *params;
char *section_key;
size_t section_idx;
size_t section_arr_length;
} plmustache_ctx;

typedef struct {
Expand All @@ -38,38 +45,55 @@ typedef struct {
} plmustache_call_info;

static int
plmustache_enter_section(void *userdata, const char *name){
plmustache_section_enter(void *userdata, const char *name){
plmustache_ctx *ctx = (plmustache_ctx *)userdata;

for(size_t i = 0; i < ctx->num_params; i++){
plmustache_param* prm = &ctx->params[i];

if(strcmp(prm->prm_name, name) == 0){
ctx->section_key = prm->prm_name;
ctx->section_arr_length = prm->is_array? prm->prm_arr_length : 0;
ctx->section_idx = 0;
return prm->enters_section;
}
}

return 0;
}

// TODO these are used to handle sections, they're mandatory otherwise mustach segfaults
static int plmustache_next(void *closure){
return MUSTACH_OK;
static int plmustache_section_next(void *userdata){
plmustache_ctx *ctx = (plmustache_ctx *)userdata;
return ctx->section_idx < ctx->section_arr_length;
}

static int
plmustache_leave(void *closure){
plmustache_section_leave(void *userdata){
plmustache_ctx *ctx = (plmustache_ctx *)userdata;
ctx->section_key = NULL;
ctx->section_idx = 0;
ctx->section_arr_length = 0;
return MUSTACH_OK;
}

static int
plmustache_get_variable(void *userdata, const char *name, struct mustach_sbuf *sbuf){
plmustache_ctx *ctx = (plmustache_ctx *)userdata;

for(size_t i = 0; i < ctx->num_params; i++){
if (strcmp(name, IMPLICIT_ITERATOR) == 0){
for(size_t i = 0; i < ctx->num_params; i++){
plmustache_param* prm = &ctx->params[i];

if(strcmp(prm->prm_name,ctx->section_key) == 0){
sbuf->value = prm->prm_arr[ctx->section_idx];
}
}
ctx->section_idx = ctx->section_idx + 1;
}
else for(size_t i = 0; i < ctx->num_params; i++){
plmustache_param* prm = &ctx->params[i];

if(strcmp(prm->prm_name, name) == 0){
if(strcmp(name, prm->prm_name) == 0){
sbuf->value = prm->prm_value;
}
}
Expand Down Expand Up @@ -147,9 +171,9 @@ Datum plmustache_handler(PG_FUNCTION_ARGS)
int mustach_code;
size_t mustache_result_size;
struct mustach_itf itf = {
.enter = plmustache_enter_section,
.next = plmustache_next,
.leave = plmustache_leave,
.enter = plmustache_section_enter,
.next = plmustache_section_next,
.leave = plmustache_section_leave,
.get = plmustache_get_variable,
};

Expand All @@ -159,20 +183,44 @@ Datum plmustache_handler(PG_FUNCTION_ARGS)

if(call_info.numargs > 0){
plmustache_param *params = palloc0(sizeof(plmustache_param) * call_info.numargs);
plmustache_ctx ctx = { call_info.numargs, params };
plmustache_ctx ctx = { call_info.numargs, params, NULL, 0 };

for(size_t i = 0; i < call_info.numargs; i++){
params[i].prm_name = call_info.argnames[i];
NullableDatum arg = fcinfo->args[i];
Oid arg_type = call_info.argtypes[i];
Oid array_elem_type = get_element_type(arg_type);

if(arg.isnull){
params[i].prm_value = NULL;
params[i].enters_section = false;
params[i].is_array = false;
}else{
params[i].prm_value = datum_to_cstring(arg.value, call_info.argtypes[i]);
if(call_info.argtypes[i] == BOOLOID)
params[i].prm_value = datum_to_cstring(arg.value, arg_type);

if(arg_type == BOOLOID)
params[i].enters_section = DatumGetBool(arg.value);
else
params[i].enters_section = true;

params[i].is_array = array_elem_type != InvalidOid;

if(params[i].is_array){
int j = 0;
Datum value;
bool isnull;
ArrayType *array = DatumGetArrayTypeP(arg.value);
ArrayIterator array_iterator = array_create_iterator(array, 0, NULL);
int num = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
params[i].prm_arr_length = num;
params[i].prm_arr = palloc0(sizeof(char*) * num);

while (array_iterate(array_iterator, &value, &isnull)) {
params[i].prm_arr[j] = isnull? NULL : datum_to_cstring(value, array_elem_type);
j++;
}
}

}
}

Expand Down
13 changes: 13 additions & 0 deletions test/expected/sections.out
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ select foo_test(false);
create or replace function foo_null_test(foo bool) returns text as $$
foo is {{#foo}}full{{/foo}}{{^foo}}null{{/foo}}
$$ language plmustache;
\echo

select foo_null_test(null);
foo_null_test
---------------
Expand All @@ -79,3 +81,14 @@ select foo_null_test(true);
foo is full
(1 row)

create or replace function foo_array(arr text[]) returns text as $$
foo is {{#arr}}{{.}}, {{/arr}}
$$ language plmustache;
\echo

select foo_array(ARRAY['one', 'two', 'three']);
foo_array
--------------------------
foo is one, two, three,
(1 row)

8 changes: 8 additions & 0 deletions test/sql/sections.sql
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,15 @@ select foo_test(false);
create or replace function foo_null_test(foo bool) returns text as $$
foo is {{#foo}}full{{/foo}}{{^foo}}null{{/foo}}
$$ language plmustache;
\echo

select foo_null_test(null);

select foo_null_test(true);

create or replace function foo_array(arr text[]) returns text as $$
foo is {{#arr}}{{.}}, {{/arr}}
$$ language plmustache;
\echo

select foo_array(ARRAY['one', 'two', 'three']);

0 comments on commit 76e827d

Please sign in to comment.