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

refactor: build logic #20

Merged
merged 1 commit into from
May 1, 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ results/
.history
*.so
*.bc
tags
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ EXTVERSION = 0.1
DATA = $(wildcard sql/*--*.sql)

MODULE_big = $(EXTENSION)
OBJS = src/plmustache.o src/observation.o
OBJS = src/plmustache.o src/observation.o src/build.o

TESTS = $(wildcard test/sql/*.sql)
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
Expand Down
167 changes: 167 additions & 0 deletions src/build.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#include <postgres.h>

#include <tcop/utility.h>
#include <utils/builtins.h>
#include <utils/varlena.h>

#include <utils/syscache.h>
#include <utils/lsyscache.h>
#include <funcapi.h>
#include <catalog/pg_proc.h>
#include <catalog/pg_type.h>
#include <access/htup_details.h>

#include <mustach/mustach.h>

#include "observation.h"
#include "build.h"

const char IMPLICIT_ITERATOR = '.';

static int
plmustache_enter_section(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){
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_leave(void *closure){
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++){
plmustache_param* prm = &ctx->params[i];

if(strcmp(prm->prm_name, name) == 0){
sbuf->value = prm->prm_value;
}
}

return MUSTACH_OK;
}

struct mustach_itf build_mustach_itf(){
return (struct mustach_itf)
{ .enter = plmustache_enter_section
, .next = plmustache_next
, .leave = plmustache_leave
, .get = plmustache_get_variable
};
}

static char *
datum_to_cstring(Datum datum, Oid typeoid, plmustache_obs_handler observer)
{
HeapTuple typetuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(typeoid), 0, 0, 0);
if (!HeapTupleIsValid(typetuple))
observer((plmustache_observation){ERROR_NO_TYPE_OID, .error_type_oid = typeoid});

Form_pg_type pg_type_entry = (Form_pg_type) GETSTRUCT(typetuple);

Datum ret = OidFunctionCall1(pg_type_entry->typoutput, datum);

ReleaseSysCache(typetuple);

return DatumGetCString(ret);
}


plmustache_call_info
build_call_info(Oid function_oid, FunctionCallInfo fcinfo, plmustache_obs_handler observer){
HeapTuple proc_tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(function_oid), 0, 0, 0);
if (!HeapTupleIsValid(proc_tuple))
observer((plmustache_observation){ERROR_NO_OID, .error_function_oid = function_oid});

bool is_null;
Oid prorettype = SysCacheGetAttr(PROCOID, proc_tuple, Anum_pg_proc_prorettype, &is_null);
if(is_null)
observer((plmustache_observation){ERROR_NO_RETTYPE});

Datum prosrc = SysCacheGetAttr(PROCOID, proc_tuple, Anum_pg_proc_prosrc, &is_null);
if(is_null)
observer((plmustache_observation){ERROR_NO_SRC});

Oid* argtypes; char** argnames; char* argmodes;
int numargs = get_func_arg_info(proc_tuple, &argtypes, &argnames, &argmodes);

if(argmodes) // argmodes is non-NULL when any of the parameters are OUT or INOUT
observer((plmustache_observation){ERROR_NOT_ONLY_IN_PARAMS});

// This already prevents functions from being used as triggers.
// So it's not necessary to use CALLED_AS_TRIGGER and CALLED_AS_EVENT_TRIGGER
if(getBaseType(prorettype) != TEXTOID)
observer((plmustache_observation){ERROR_NO_TEXT_RET});

// when having a single unnamed parameter, the argnames are NULL
if (!argnames && numargs == 1)
observer((plmustache_observation){ERROR_UNNAMED_PARAMS});
for(size_t i = 0; i < numargs; i++){
if (strlen(argnames[i]) == 0)
observer((plmustache_observation){ERROR_UNNAMED_PARAMS});
if (argnames[i][0] == IMPLICIT_ITERATOR)
observer((plmustache_observation){ERROR_PARAM_IMPLICIT_ITERATOR, .error_implicit_iterator = IMPLICIT_ITERATOR});
}

return (plmustache_call_info)
{ proc_tuple
, prosrc
, numargs
, argtypes
, argnames
};
}

plmustache_ctx
build_mustache_ctx(plmustache_call_info call_info, NullableDatum args[]){
plmustache_ctx ctx = {0};

ctx.template = TextDatumGetCString(DirectFunctionCall2(btrim, call_info.prosrc, CStringGetTextDatum("\n")));

if(call_info.numargs > 0){
plmustache_param *params = palloc0(sizeof(plmustache_param) * call_info.numargs);
ctx.num_params = call_info.numargs;
ctx.params = params;

for(size_t i = 0; i < call_info.numargs; i++){
params[i].prm_name = call_info.argnames[i];
NullableDatum arg = args[i];
if(arg.isnull){
params[i].prm_value = NULL;
params[i].enters_section = false;
}else{
params[i].prm_value = datum_to_cstring(arg.value, call_info.argtypes[i], ereporter);
if(call_info.argtypes[i] == BOOLOID)
params[i].enters_section = DatumGetBool(arg.value);
else
params[i].enters_section = true;
}
}
}

return ctx;
}

plmustache_ctx free_plmustache_ctx(plmustache_ctx ctx){
if(ctx.params) pfree(ctx.params);
if(ctx.template) pfree(ctx.template);
ctx = (plmustache_ctx){0};
return ctx;
}
38 changes: 38 additions & 0 deletions src/build.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#ifndef BUILD_H
#define BUILD_H

#include <mustach/mustach.h>
#include <postgres.h>
#include "observation.h"

typedef struct {
char *prm_name;
char *prm_value;
bool enters_section;
} plmustache_param;

typedef struct {
size_t num_params;
plmustache_param *params;
char *template;
} plmustache_ctx;

typedef struct {
HeapTuple proc_tuple;
Datum prosrc;
int numargs;
Oid* argtypes;
char** argnames;
} plmustache_call_info;

struct mustach_itf build_mustach_itf(void);

plmustache_call_info
build_call_info(Oid function_oid, FunctionCallInfo fcinfo, plmustache_obs_handler observer);

plmustache_ctx
build_mustache_ctx(plmustache_call_info call_info, NullableDatum args[]);

plmustache_ctx free_plmustache_ctx(plmustache_ctx ctx);

#endif
Loading