Рецепты PostgreSQL: шаблонизатор mustach

от автора

Для приготовления шаблонизатора mustach нам понадобится postgres и mustach. Можно также воспользоваться готовым образом.

Зачем нужен шаблонизатор в базе? Ну, во-первых, если шаблонизатор в базе, то и сами шаблоны тоже должны быть в базе. А зачем нужно хранить шаблоны в базе? Да потому, что шаблоны, как и данные, тоже могут зависеть от времени. Например, пусть в базе есть счета (это данные). Очевидно, что они зависят от времени: в этом месяце сумма одна, в следующем — другая, потом — третья и т.д. Но и шаблон счёта тоже может зависеть от времени: в этом году один, а в следующем уже другой (как это было с введением 20% ). Поэтому удобнее сами шаблоны тоже хранить в базе. Ну а шаблонизатор в базе удобен тем, что можно тут же в базе шаблонизировать, потом (тут же в базе) преобразовать в pdf и (тут же в базе) отправить на email. И всё это можно сделать асинхронно с помощью планировщика.

Код получился не слишком большой, поэтому выкладываю его весь (с добавлением комментариев)

#include <postgres.h> // подключаем необходимые заголовки.  #include <catalog/pg_type.h> // и ещё extern text *cstring_to_text(const char *s); // добавляем объявления extern text *cstring_to_text_with_len(const char *s, int len); // используемых функций extern char *text_to_cstring(const text *t); // потому что,  extern void text_to_cstring_buffer(const text *src, char *dst, size_t dst_len); // если подключить заголовки #define CStringGetTextDatum(s) PointerGetDatum(cstring_to_text(s)) // то происходит конфликт #define TextDatumGetCString(d) text_to_cstring((text *) DatumGetPointer(d)) // с именем json_object #include <mustach/mustach-json-c.h> // вот из этого заголовка  #define EXTENSION(function) Datum (function)(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(function); Datum (function)(PG_FUNCTION_ARGS) // макрос для объявления функции расширения  PG_MODULE_MAGIC; // необходимо для расширения  EXTENSION(json2mustach) { // функция шаблонизации      char *file; // имя файла результата     char *json; // данные для шаблонизации     char *output_data; // результат в памяти     char *template; // шаблон     enum json_tokener_error error; // переменная для хранения ошибки парсера     FILE *out; // файл результата     size_t output_len; // размер результата в памяти     struct json_object *object; // объект парсера     text *output; // результат     if (PG_ARGISNULL(0)) ereport(ERROR, (errmsg("json is null!"))); // первый аргумент не может быть NULL     if (PG_ARGISNULL(1)) ereport(ERROR, (errmsg("template is null!"))); // и второй - тоже     json = TextDatumGetCString(PG_GETARG_DATUM(0)); // получаем C-строку из первого аргумента     template = TextDatumGetCString(PG_GETARG_DATUM(1)); // и из второго     if (!(object = json_tokener_parse_verbose(json, &error))) ereport(ERROR, (errmsg("!json_tokener_parse and %s", json_tokener_error_desc(error)))); // парсим данные для шаблонизации и при неудаче сообщаем об этом     switch (PG_NARGS()) { // в зависимости от количества аргументов         case 2: if (!(out = open_memstream(&output_data, &output_len))) ereport(ERROR, (errmsg("!open_memstream"))); break; // при двух аргументах открываем файл для результата в памяти         case 3: // при трёх аргументах             if (PG_ARGISNULL(2)) ereport(ERROR, (errmsg("file is null!"))); // третий аргумент не может быть NULL             file = TextDatumGetCString(PG_GETARG_DATUM(2)); // получаем C-строку из третьего аргумента             if (!(out = fopen(file, "wb"))) ereport(ERROR, (errmsg("!fopen"))); // открываем файл для результата и при неудаче сообщаем об этом             pfree(file); // освобождаем память             break; // продолжаем         default: ereport(ERROR, (errmsg("expect be 2 or 3 args"))); // может быть только два или три аргумента      }     if (fmustach_json_c(template, object, out)) ereport(ERROR, (errmsg("fmustach_json_c"))); // шаблонизируем и в случае неудачи сообщаем об этом     pfree(json); // освобождаем память     pfree(template); // и ещё     if (!json_object_put(object)) ereport(ERROR, (errmsg("!json_object_put"))); // освобождаем память парсера     switch (PG_NARGS()) { // в зависимости от количества аргументов         case 2: // при двух аргументах             fclose(out); // заканчиваем файл в памяти             output = cstring_to_text_with_len(output_data, output_len); // получаем данные результата             free(output_data); // освобождаем память             PG_RETURN_TEXT_P(output); // возвращаем результат             break; // продолжаем         case 3: PG_RETURN_BOOL(true); break; // при трёх аргументах просто возвращаем успех         default: ereport(ERROR, (errmsg("expect be 2 or 3 args"))); // может быть только два или три аргумента      } } 

Используем это так: сначала создаём расширение

create extension pg_mustach

а потом шаблонизируем в память

select json2mustach('{"name":"Мир"}', 'Здравствуй, {{name}}!')

или в файл

select json2mustach('{"name":"Мир"}', 'Здравствуй, {{name}}!', 'file_name')

ссылка на оригинал статьи https://habr.com/ru/post/503832/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *