Skip to content

Commit fc3438f

Browse files
committed
feat: sections for arrays
1 parent ce7c12f commit fc3438f

File tree

5 files changed

+207
-16
lines changed

5 files changed

+207
-16
lines changed

README.md

+19-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ Hello {{you}}!
2424
You just won {{qt}} at {{at}}.
2525
$$ language plmustache;
2626

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

6161
### Sections
6262

63+
Boolean sections:
64+
6365
```sql
6466
create or replace function show_cat(cat text, show bool default true) returns text as $$
6567
{{#show}}
@@ -85,6 +87,21 @@ select show_cat('Mr. Sleepy', false);
8587
(1 row)
8688
```
8789

90+
Array iterators:
91+
92+
```sql
93+
create or replace function hello_cats(cats text[]) returns text as $$
94+
Say hello to: {{#cats}}{{.}}, {{/cats}}
95+
$$ language plmustache;
96+
97+
98+
postgres=# select hello_cats('{Sir Meowalot, Mr. Sleepy, Paquito}');
99+
hello_cats
100+
---------------------------------------------------
101+
Say hello to: Sir Meowalot, Mr. Sleepy, Paquito,
102+
(1 row)
103+
```
104+
88105
## Installation
89106

90107
Clone the repo and submodules:

src/build.c

+55-11
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,55 @@
1919
const char IMPLICIT_ITERATOR = '.';
2020

2121
static int
22-
plmustache_enter_section(void *userdata, const char *name){
22+
plmustache_section_enter(void *userdata, const char *name){
2323
plmustache_ctx *ctx = (plmustache_ctx *)userdata;
2424

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

2828
if(strcmp(prm->prm_name, name) == 0){
29+
ctx->section_key = prm->prm_name;
30+
ctx->section_arr_length = prm->is_array? prm->prm_arr_length : 0;
31+
ctx->section_idx = 0;
2932
return prm->enters_section;
3033
}
3134
}
3235

3336
return 0;
3437
}
3538

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

4144
static int
42-
plmustache_leave(void *closure){
45+
plmustache_section_leave(void *userdata){
46+
plmustache_ctx *ctx = (plmustache_ctx *)userdata;
47+
ctx->section_key = NULL;
48+
ctx->section_idx = 0;
49+
ctx->section_arr_length = 0;
4350
return MUSTACH_OK;
4451
}
4552

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

50-
for(size_t i = 0; i < ctx->num_params; i++){
57+
if (name[0] == IMPLICIT_ITERATOR){
58+
for(size_t i = 0; i < ctx->num_params; i++){
59+
plmustache_param* prm = &ctx->params[i];
60+
61+
if(strcmp(prm->prm_name,ctx->section_key) == 0){
62+
sbuf->value = prm->prm_arr[ctx->section_idx];
63+
}
64+
}
65+
ctx->section_idx = ctx->section_idx + 1;
66+
}
67+
else for(size_t i = 0; i < ctx->num_params; i++){
5168
plmustache_param* prm = &ctx->params[i];
5269

53-
if(strcmp(prm->prm_name, name) == 0){
70+
if(strcmp(name, prm->prm_name) == 0){
5471
sbuf->value = prm->prm_value;
5572
}
5673
}
@@ -60,9 +77,9 @@ plmustache_get_variable(void *userdata, const char *name, struct mustach_sbuf *s
6077

6178
struct mustach_itf build_mustach_itf(){
6279
return (struct mustach_itf)
63-
{ .enter = plmustache_enter_section
64-
, .next = plmustache_next
65-
, .leave = plmustache_leave
80+
{ .enter = plmustache_section_enter
81+
, .next = plmustache_section_next
82+
, .leave = plmustache_section_leave
6683
, .get = plmustache_get_variable
6784
};
6885
}
@@ -143,15 +160,42 @@ build_mustache_ctx(plmustache_call_info call_info, NullableDatum args[]){
143160
for(size_t i = 0; i < call_info.numargs; i++){
144161
params[i].prm_name = call_info.argnames[i];
145162
NullableDatum arg = args[i];
163+
Oid arg_type = call_info.argtypes[i];
164+
Oid array_elem_type = get_element_type(arg_type);
165+
bool arg_is_array = array_elem_type != InvalidOid;
166+
146167
if(arg.isnull){
147168
params[i].prm_value = NULL;
148169
params[i].enters_section = false;
170+
params[i].is_array = false;
149171
}else{
150172
params[i].prm_value = datum_to_cstring(arg.value, call_info.argtypes[i], ereporter);
151-
if(call_info.argtypes[i] == BOOLOID)
173+
if(arg_type == BOOLOID)
152174
params[i].enters_section = DatumGetBool(arg.value);
153175
else
154176
params[i].enters_section = true;
177+
178+
if(arg_is_array){
179+
params[i].is_array = true;
180+
ArrayType *array = DatumGetArrayTypeP(arg.value);
181+
ArrayIterator array_iterator = array_create_iterator(array, 0, NULL);
182+
int arr_ndim = ARR_NDIM(array);
183+
int arr_length = ArrayGetNItems(arr_ndim, ARR_DIMS(array));
184+
if(arr_ndim > 1)
185+
ereport(ERROR, errmsg("support for multidimensional arrays is not implemented"));
186+
187+
if(arr_length > 0){
188+
Datum value; bool isnull; int j = 0;
189+
params[i].prm_arr_length = arr_length;
190+
params[i].prm_arr = palloc0(sizeof(char*) * arr_length);
191+
192+
while (array_iterate(array_iterator, &value, &isnull)) {
193+
params[i].prm_arr[j] = isnull? NULL : datum_to_cstring(value, array_elem_type, ereporter);
194+
j++;
195+
}
196+
} else
197+
params[i].enters_section = false;
198+
}
155199
}
156200
}
157201
}

src/build.h

+9-3
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@
66
#include "observation.h"
77

88
typedef struct {
9-
char *prm_name;
10-
char *prm_value;
11-
bool enters_section;
9+
char *prm_name;
10+
char *prm_value;
11+
char **prm_arr;
12+
size_t prm_arr_length;
13+
bool enters_section;
14+
bool is_array;
1215
} plmustache_param;
1316

1417
typedef struct {
1518
size_t num_params;
1619
plmustache_param *params;
20+
char *section_key;
21+
size_t section_idx;
22+
size_t section_arr_length;
1723
char *template;
1824
} plmustache_ctx;
1925

test/expected/sections.out

+75
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ select foo_test(false);
6767
create or replace function foo_null_test(foo bool) returns text as $$
6868
foo is {{#foo}}full{{/foo}}{{^foo}}null{{/foo}}
6969
$$ language plmustache;
70+
\echo
71+
7072
select foo_null_test(null);
7173
foo_null_test
7274
---------------
@@ -79,3 +81,76 @@ select foo_null_test(true);
7981
foo is full
8082
(1 row)
8183

84+
create or replace function foo_array(arr text[]) returns text as $$
85+
arr is {{#arr}}{{.}}, {{/arr}}
86+
$$ language plmustache;
87+
\echo
88+
89+
select foo_array(ARRAY['one', 'two', 'three']);
90+
foo_array
91+
--------------------------
92+
arr is one, two, three,
93+
(1 row)
94+
95+
create or replace function foo_array(arr int[]) returns text as $$
96+
arr is {{#arr}}{{.}}, {{/arr}}
97+
$$ language plmustache;
98+
\echo
99+
100+
select foo_array(ARRAY[1, 2, 3]::int[]);
101+
foo_array
102+
------------------
103+
arr is 1, 2, 3,
104+
(1 row)
105+
106+
-- empty array is handled properly
107+
select foo_array(ARRAY[]::int[]);
108+
foo_array
109+
-----------
110+
arr is
111+
(1 row)
112+
113+
create or replace function mixed_var_array(var int, arr int[]) returns text as $$
114+
var is {{var}}, arr is {{#arr}}{{.}}, {{/arr}}
115+
$$ language plmustache;
116+
\echo
117+
118+
select mixed_var_array(4, ARRAY[1, 2, 3]::int[]);
119+
mixed_var_array
120+
----------------------------
121+
var is 4, arr is 1, 2, 3,
122+
(1 row)
123+
124+
create or replace function mixed_array_var(arr int[], var int) returns text as $$
125+
arr is {{#arr}}{{.}}, {{/arr}} var is {{var}}
126+
$$ language plmustache;
127+
\echo
128+
129+
select mixed_array_var(ARRAY[1, 2, 3]::int[], 4);
130+
mixed_array_var
131+
---------------------------
132+
arr is 1, 2, 3, var is 4
133+
(1 row)
134+
135+
create or replace function mixed_array_var_array(arr1 int[], var text, arr2 int[]) returns text as $$
136+
arr1 is {{#arr1}}{{.}}, {{/arr1}} var is {{var}}
137+
arr2 is {{#arr2}}{{.}}, {{/arr2}} var is {{var}}
138+
$$ language plmustache;
139+
\echo
140+
141+
select mixed_array_var_array(ARRAY[1, 2, 3], 'something', ARRAY[4, 5, 6]);
142+
mixed_array_var_array
143+
------------------------------------
144+
arr1 is 1, 2, 3, var is something+
145+
arr2 is 4, 5, 6, var is something
146+
(1 row)
147+
148+
create or replace function nested_array(arr int[]) returns text as $$
149+
arr is {{#arr}}{{.}}, {{/arr}}
150+
$$ language plmustache;
151+
\echo
152+
153+
select nested_array(ARRAY[[1,2,3], [4,5,6]]);
154+
ERROR: support for multidimensional arrays is not implemented
155+
select nested_array(ARRAY[[[1,2], [3,4]], [[5,6], [7,8]]]);
156+
ERROR: support for multidimensional arrays is not implemented

test/sql/sections.sql

+49
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,56 @@ select foo_test(false);
3838
create or replace function foo_null_test(foo bool) returns text as $$
3939
foo is {{#foo}}full{{/foo}}{{^foo}}null{{/foo}}
4040
$$ language plmustache;
41+
\echo
4142

4243
select foo_null_test(null);
4344

4445
select foo_null_test(true);
46+
47+
create or replace function foo_array(arr text[]) returns text as $$
48+
arr is {{#arr}}{{.}}, {{/arr}}
49+
$$ language plmustache;
50+
\echo
51+
52+
select foo_array(ARRAY['one', 'two', 'three']);
53+
54+
create or replace function foo_array(arr int[]) returns text as $$
55+
arr is {{#arr}}{{.}}, {{/arr}}
56+
$$ language plmustache;
57+
\echo
58+
59+
select foo_array(ARRAY[1, 2, 3]::int[]);
60+
61+
-- empty array is handled properly
62+
select foo_array(ARRAY[]::int[]);
63+
64+
create or replace function mixed_var_array(var int, arr int[]) returns text as $$
65+
var is {{var}}, arr is {{#arr}}{{.}}, {{/arr}}
66+
$$ language plmustache;
67+
\echo
68+
69+
select mixed_var_array(4, ARRAY[1, 2, 3]::int[]);
70+
71+
create or replace function mixed_array_var(arr int[], var int) returns text as $$
72+
arr is {{#arr}}{{.}}, {{/arr}} var is {{var}}
73+
$$ language plmustache;
74+
\echo
75+
76+
select mixed_array_var(ARRAY[1, 2, 3]::int[], 4);
77+
78+
create or replace function mixed_array_var_array(arr1 int[], var text, arr2 int[]) returns text as $$
79+
arr1 is {{#arr1}}{{.}}, {{/arr1}} var is {{var}}
80+
arr2 is {{#arr2}}{{.}}, {{/arr2}} var is {{var}}
81+
$$ language plmustache;
82+
\echo
83+
84+
select mixed_array_var_array(ARRAY[1, 2, 3], 'something', ARRAY[4, 5, 6]);
85+
86+
create or replace function nested_array(arr int[]) returns text as $$
87+
arr is {{#arr}}{{.}}, {{/arr}}
88+
$$ language plmustache;
89+
\echo
90+
91+
select nested_array(ARRAY[[1,2,3], [4,5,6]]);
92+
93+
select nested_array(ARRAY[[[1,2], [3,4]], [[5,6], [7,8]]]);

0 commit comments

Comments
 (0)