Skip to content

Commit

Permalink
feat: sections for arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
steve-chavez committed Aug 5, 2024
1 parent ce7c12f commit 6da689f
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 16 deletions.
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ Hello {{you}}!
You just won {{qt}} at {{at}}.
$$ language plmustache;

select win_money('Slonik', '12000', now());
select win_money('Sir Meowalot', '12000', now());
win_money
-----------------------------------------------------------
Hello Slonik! +
Hello Sir Meowalot! +
You just won $12,000.00 at 2023-12-04 07:44:26.915735-05.
(1 row)
```
Expand Down Expand Up @@ -60,6 +60,8 @@ select do_not_escape_me('<script>evil()</script>');

### Sections

Boolean sections:

```sql
create or replace function show_cat(cat text, show bool default true) returns text as $$
{{#show}}
Expand All @@ -85,6 +87,21 @@ select show_cat('Mr. Sleepy', false);
(1 row)
```

Array iterators:

```sql
create or replace function hello_cats(cats text[]) returns text as $$
Say hello to: {{#cats}}{{.}}, {{/cats}}
$$ language plmustache;


postgres=# select hello_cats('{Sir Meowalot, Mr. Sleepy, Paquito}');
hello_cats
---------------------------------------------------
Say hello to: Sir Meowalot, Mr. Sleepy, Paquito,
(1 row)
```

## Installation

Clone the repo and submodules:
Expand Down
66 changes: 55 additions & 11 deletions src/build.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,55 @@
const char IMPLICIT_ITERATOR = '.';

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 (name[0] == IMPLICIT_ITERATOR){
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 All @@ -60,9 +77,9 @@ plmustache_get_variable(void *userdata, const char *name, struct mustach_sbuf *s

struct mustach_itf build_mustach_itf(){
return (struct mustach_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 Down Expand Up @@ -143,15 +160,42 @@ build_mustache_ctx(plmustache_call_info call_info, NullableDatum args[]){
for(size_t i = 0; i < call_info.numargs; i++){
params[i].prm_name = call_info.argnames[i];
NullableDatum arg = args[i];
Oid arg_type = call_info.argtypes[i];
Oid array_elem_type = get_element_type(arg_type);
bool arg_is_array = array_elem_type != InvalidOid;

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], ereporter);
if(call_info.argtypes[i] == BOOLOID)
if(arg_type == BOOLOID)
params[i].enters_section = DatumGetBool(arg.value);
else
params[i].enters_section = true;

if(arg_is_array){
params[i].is_array = true;
ArrayType *array = DatumGetArrayTypeP(arg.value);
ArrayIterator array_iterator = array_create_iterator(array, 0, NULL);
int arr_ndim = ARR_NDIM(array);
int arr_length = ArrayGetNItems(arr_ndim, ARR_DIMS(array));
if(arr_ndim > 1)
ereport(ERROR, errmsg("support for multidimensional arrays is not implemented"));

if(arr_length > 0){
Datum value; bool isnull; int j = 0;
params[i].prm_arr_length = arr_length;
params[i].prm_arr = palloc0(sizeof(char*) * arr_length);

while (array_iterate(array_iterator, &value, &isnull)) {
params[i].prm_arr[j] = isnull? NULL : datum_to_cstring(value, array_elem_type, ereporter);
j++;
}
} else
params[i].enters_section = false;
}
}
}
}
Expand Down
12 changes: 9 additions & 3 deletions src/build.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@
#include "observation.h"

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;
char *template;
} plmustache_ctx;

Expand Down
75 changes: 75 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,76 @@ select foo_null_test(true);
foo is full
(1 row)

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

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

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

select foo_array(ARRAY[1, 2, 3]::int[]);
foo_array
------------------
arr is 1, 2, 3,
(1 row)

-- empty array is handled properly
select foo_array(ARRAY[]::int[]);
foo_array
-----------
arr is
(1 row)

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

select mixed_var_array(4, ARRAY[1, 2, 3]::int[]);
mixed_var_array
----------------------------
var is 4, arr is 1, 2, 3,
(1 row)

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

select mixed_array_var(ARRAY[1, 2, 3]::int[], 4);
mixed_array_var
---------------------------
arr is 1, 2, 3, var is 4
(1 row)

create or replace function mixed_array_var_array(arr1 int[], var text, arr2 int[]) returns text as $$
arr1 is {{#arr1}}{{.}}, {{/arr1}} var is {{var}}
arr2 is {{#arr2}}{{.}}, {{/arr2}} var is {{var}}
$$ language plmustache;
\echo

select mixed_array_var_array(ARRAY[1, 2, 3], 'something', ARRAY[4, 5, 6]);
mixed_array_var_array
------------------------------------
arr1 is 1, 2, 3, var is something+
arr2 is 4, 5, 6, var is something
(1 row)

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

select nested_array(ARRAY[[1,2,3], [4,5,6]]);
ERROR: support for multidimensional arrays is not implemented
select nested_array(ARRAY[[[1,2], [3,4]], [[5,6], [7,8]]]);
ERROR: support for multidimensional arrays is not implemented
49 changes: 49 additions & 0 deletions test/sql/sections.sql
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,56 @@ 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 $$
arr is {{#arr}}{{.}}, {{/arr}}
$$ language plmustache;
\echo

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

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

select foo_array(ARRAY[1, 2, 3]::int[]);

-- empty array is handled properly
select foo_array(ARRAY[]::int[]);

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

select mixed_var_array(4, ARRAY[1, 2, 3]::int[]);

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

select mixed_array_var(ARRAY[1, 2, 3]::int[], 4);

create or replace function mixed_array_var_array(arr1 int[], var text, arr2 int[]) returns text as $$
arr1 is {{#arr1}}{{.}}, {{/arr1}} var is {{var}}
arr2 is {{#arr2}}{{.}}, {{/arr2}} var is {{var}}
$$ language plmustache;
\echo

select mixed_array_var_array(ARRAY[1, 2, 3], 'something', ARRAY[4, 5, 6]);

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

select nested_array(ARRAY[[1,2,3], [4,5,6]]);

select nested_array(ARRAY[[[1,2], [3,4]], [[5,6], [7,8]]]);

0 comments on commit 6da689f

Please sign in to comment.