fu/FU.xs
Yorhel 1f7e2de9a0 pg: Add prepared statement caching
The tests are not as thourough as I would like. There's many ways to
mess this up.

I was initially planning to drop the ref on the prepared statement
immediately after executing the query, so that the $st object can be
kept around for introspection without consuming excess resources.
Unfortunately, PQcopyResult does not copy over information about bind
parameters, so we need another way to keep that information alive. I
ended up going for the simple solution: keep the ref on the prepared
statement...
2025-02-12 17:19:20 +01:00

270 lines
5.6 KiB
Text

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <dlfcn.h>
#undef PERL_IMPLICIT_SYS
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "c/khashl.h"
#include "c/common.c"
#include "c/jsonfmt.c"
#include "c/jsonparse.c"
#include "c/libpq.h"
#include "c/pgtypes.c"
#include "c/pgconn.c"
#include "c/pgst.c"
#define FUPG_CONN_COOKIE \
if (c->cookie) fu_confess("Invalid operation on the top-level connection while a transaction object exists")
#define FUPG_TXN_COOKIE \
if (!t->cookie) fu_confess("Invalid operation on a transaction that has already been marked as done"); \
if (t->cookie != t->conn->cookie) fu_confess("Invalid operation on transaction while a subtransaction object exists")
#define FUPG_ST_COOKIE \
if (st->cookie != st->conn->cookie) fu_confess("Invalid cross-transaction operation on statement object")
#define FUPG_STFLAGS do {\
if (!ix) ix = FUPG_CACHE;\
if (items == 1 || SvTRUE(ST(1))) x->stflags |= ix; \
else x->stflags &= ~ix; \
XSRETURN(1); \
} while(0)
MODULE = FU
PROTOTYPES: DISABLE
TYPEMAP: <<EOT
TYPEMAP
fupg_conn * FUPG_CONN
fupg_txn * FUPG_TXN
fupg_st * FUPG_ST
INPUT
FUPG_CONN
if (sv_derived_from($arg, \"FU::Pg::conn\")) $var = (fupg_conn *)SvIVX(SvRV($arg));
else fu_confess(\"invalid connection object\");
FUPG_TXN
if (sv_derived_from($arg, \"FU::Pg::txn\")) $var = (fupg_txn *)SvIVX(SvRV($arg));
else fu_confess(\"invalid transaction object\");
FUPG_ST
if (sv_derived_from($arg, \"FU::Pg::st\")) $var = (fupg_st *)SvIVX(SvRV($arg));
else fu_confess(\"invalid statement object\");
#"
EOT
MODULE = FU PACKAGE = FU::Util
void json_format(SV *val, ...)
CODE:
ST(0) = fujson_fmt_xs(aTHX_ ax, items, val);
void json_parse(SV *val, ...)
CODE:
ST(0) = fujson_parse_xs(aTHX_ ax, items, val);
MODULE = FU PACKAGE = FU::Pg
void _load_libpq()
CODE:
if (!PQconnectdb) fupg_load();
void lib_version()
CODE:
XSRETURN_IV(PQlibVersion());
void connect(const char *pkg, const char *conninfo)
CODE:
(void)pkg;
ST(0) = fupg_connect(aTHX_ conninfo);
MODULE = FU PACKAGE = FU::Pg::conn
void server_version(fupg_conn *c)
CODE:
XSRETURN_IV(PQserverVersion(c->conn));
void _debug_trace(fupg_conn *c, bool on)
CODE:
if (on) PQtrace(c->conn, stderr);
else PQuntrace(c->conn);
ST(0) = c->self;
void status(fupg_conn *c)
CODE:
ST(0) = sv_2mortal(newSVpv(fupg_conn_status(c), 0));
void cache(fupg_conn *x, ...)
ALIAS:
FU::Pg::conn::text_params = FUPG_TEXT_PARAMS
FU::Pg::conn::text_results = FUPG_TEXT_RESULTS
FU::Pg::conn::text = FUPG_TEXT
CODE:
FUPG_STFLAGS;
void cache_size(fupg_conn *c, unsigned int n)
CODE:
c->prep_max = n;
fupg_prepared_prune(c);
XSRETURN(1);
void disconnect(fupg_conn *c)
CODE:
fupg_conn_disconnect(c);
void DESTROY(fupg_conn *c)
CODE:
fupg_conn_destroy(c);
void txn(fupg_conn *c)
CODE:
FUPG_CONN_COOKIE;
ST(0) = fupg_conn_txn(c);
void exec(fupg_conn *c, SV *sv)
CODE:
FUPG_CONN_COOKIE;
ST(0) = fupg_exec(aTHX_ c, SvPVutf8_nolen(sv));
void q(fupg_conn *c, SV *sv, ...)
CODE:
FUPG_CONN_COOKIE;
ST(0) = fupg_q(aTHX_ c, c->stflags, SvPVutf8_nolen(sv), ax, items);
MODULE = FU PACKAGE = FU::Pg::txn
void DESTROY(fupg_txn *t)
CODE:
fupg_txn_destroy(t);
void cache(fupg_txn *x, ...)
ALIAS:
FU::Pg::txn::text_params = FUPG_TEXT_PARAMS
FU::Pg::txn::text_results = FUPG_TEXT_RESULTS
FU::Pg::txn::text = FUPG_TEXT
CODE:
FUPG_STFLAGS;
void status(fupg_txn *t)
CODE:
ST(0) = sv_2mortal(newSVpv(fupg_txn_status(t), 0));
void txn(fupg_txn *t)
CODE:
FUPG_TXN_COOKIE;
ST(0) = fupg_txn_txn(t);
void commit(fupg_txn *t)
CODE:
FUPG_TXN_COOKIE;
fupg_txn_commit(t);
void rollback(fupg_txn *t)
CODE:
FUPG_TXN_COOKIE;
fupg_txn_rollback(t);
void exec(fupg_txn *t, SV *sv)
CODE:
FUPG_TXN_COOKIE;
ST(0) = fupg_exec(aTHX_ t->conn, SvPVutf8_nolen(sv));
void q(fupg_txn *t, SV *sv, ...)
CODE:
FUPG_TXN_COOKIE;
ST(0) = fupg_q(aTHX_ t->conn, t->stflags, SvPVutf8_nolen(sv), ax, items);
MODULE = FU PACKAGE = FU::Pg::st
void cache(fupg_st *x, ...)
ALIAS:
FU::Pg::st::text_params = FUPG_TEXT_PARAMS
FU::Pg::st::text_results = FUPG_TEXT_RESULTS
FU::Pg::st::text = FUPG_TEXT
CODE:
FUPG_STFLAGS;
void param_types(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
ST(0) = fupg_st_param_types(aTHX_ st);
void columns(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
ST(0) = fupg_st_columns(aTHX_ st);
void exec(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
ST(0) = fupg_st_exec(aTHX_ st);
void val(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
ST(0) = fupg_st_val(aTHX_ st);
void rowl(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
XSRETURN(fupg_st_rowl(aTHX_ st, ax));
void rowa(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
ST(0) = fupg_st_rowa(aTHX_ st);
void rowh(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
ST(0) = fupg_st_rowh(aTHX_ st);
void alla(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
ST(0) = fupg_st_alla(aTHX_ st);
void allh(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
ST(0) = fupg_st_allh(aTHX_ st);
void flat(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
ST(0) = fupg_st_flat(aTHX_ st);
void kvv(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
ST(0) = fupg_st_kvv(aTHX_ st);
void kva(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
ST(0) = fupg_st_kva(aTHX_ st);
void kvh(fupg_st *st)
CODE:
FUPG_ST_COOKIE;
ST(0) = fupg_st_kvh(aTHX_ st);
void DESTROY(fupg_st *st)
CODE:
fupg_st_destroy(st);