Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: sections for arrays #12

Merged
merged 1 commit into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]]]);