pg: Adventures in writing a new postgresql client
This commit is contained in:
parent
0d19ccdc1b
commit
b242176071
7 changed files with 132 additions and 4 deletions
54
FU.xs
54
FU.xs
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
#define PERL_NO_GET_CONTEXT
|
#define PERL_NO_GET_CONTEXT
|
||||||
#include "EXTERN.h"
|
#include "EXTERN.h"
|
||||||
#include "perl.h"
|
#include "perl.h"
|
||||||
|
|
@ -6,12 +8,28 @@
|
||||||
#include "c/common.c"
|
#include "c/common.c"
|
||||||
#include "c/jsonfmt.c"
|
#include "c/jsonfmt.c"
|
||||||
#include "c/jsonparse.c"
|
#include "c/jsonparse.c"
|
||||||
|
#include "c/pgconn.c"
|
||||||
|
|
||||||
|
|
||||||
|
MODULE = FU
|
||||||
|
|
||||||
|
PROTOTYPES: DISABLE
|
||||||
|
|
||||||
|
|
||||||
|
TYPEMAP: <<EOT
|
||||||
|
TYPEMAP
|
||||||
|
fupg_conn * FUPG_CONN
|
||||||
|
|
||||||
|
INPUT
|
||||||
|
FUPG_CONN
|
||||||
|
if (sv_derived_from($arg, \"FU::PG::conn\")) $var = (fupg_conn *)SvIVX(SvRV($arg));
|
||||||
|
else croak(\"invalid object\");
|
||||||
|
#"
|
||||||
|
EOT
|
||||||
|
|
||||||
|
|
||||||
MODULE = FU PACKAGE = FU::Util
|
MODULE = FU PACKAGE = FU::Util
|
||||||
|
|
||||||
PROTOTYPES: DISABLE
|
|
||||||
|
|
||||||
void json_format(SV *val, ...)
|
void json_format(SV *val, ...)
|
||||||
CODE:
|
CODE:
|
||||||
ST(0) = fujson_fmt_xs(aTHX_ ax, items, val);
|
ST(0) = fujson_fmt_xs(aTHX_ ax, items, val);
|
||||||
|
|
@ -19,3 +37,35 @@ void json_format(SV *val, ...)
|
||||||
void json_parse(SV *val, ...)
|
void json_parse(SV *val, ...)
|
||||||
CODE:
|
CODE:
|
||||||
ST(0) = fujson_parse_xs(aTHX_ ax, items, val);
|
ST(0) = fujson_parse_xs(aTHX_ ax, items, val);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MODULE = FU PACKAGE = FU::PG
|
||||||
|
|
||||||
|
void _load_libpq()
|
||||||
|
CODE:
|
||||||
|
if (!PQconnectdb) fupg_load();
|
||||||
|
|
||||||
|
int lib_version()
|
||||||
|
CODE:
|
||||||
|
RETVAL = PQlibVersion();
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
|
void connect(const char *pkg, const char *conninfo)
|
||||||
|
CODE:
|
||||||
|
(void)pkg;
|
||||||
|
ST(0) = sv_setref_pv(sv_newmortal(), "FU::PG::conn", fupg_connect(aTHX_ conninfo));
|
||||||
|
|
||||||
|
|
||||||
|
MODULE = FU PACKAGE = FU::PG::conn
|
||||||
|
|
||||||
|
int server_version(fupg_conn *c)
|
||||||
|
CODE:
|
||||||
|
RETVAL = PQserverVersion(c->conn);
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
|
void DESTROY(fupg_conn *c)
|
||||||
|
CODE:
|
||||||
|
fupg_destroy(c);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ be a good measure". I've used these benchmarks to find and optimize hotspots in
|
||||||
FU, which in turn means these numbers may look better than they are in
|
FU, which in turn means these numbers may look better than they are in
|
||||||
real-world use.
|
real-world use.
|
||||||
|
|
||||||
|
B<DISCLAIMER#3:> Many of these benchmarks exists solely to test edge case
|
||||||
|
performance, these numbers are not representative for real-world use.
|
||||||
|
|
||||||
=head1 MODULE VERSIONS
|
=head1 MODULE VERSIONS
|
||||||
|
|
||||||
The following module versions were used:
|
The following module versions were used:
|
||||||
|
|
@ -39,7 +42,7 @@ The following module versions were used:
|
||||||
|
|
||||||
=head1 BENCHMARKS
|
=head1 BENCHMARKS
|
||||||
|
|
||||||
=head2 JSON Formatting
|
=head2 JSON Parsing & Formatting
|
||||||
|
|
||||||
These benchmarks run on large-ish arrays with repeated values. JSON encoding is
|
These benchmarks run on large-ish arrays with repeated values. JSON encoding is
|
||||||
sufficiently fast that Perl function calling overhead tends to dominate for
|
sufficiently fast that Perl function calling overhead tends to dominate for
|
||||||
|
|
|
||||||
11
FU/PG.pm
Normal file
11
FU/PG.pm
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
package FU::PG 0.1;
|
||||||
|
use v5.36;
|
||||||
|
use FU::XS;
|
||||||
|
|
||||||
|
_load_libpq();
|
||||||
|
|
||||||
|
package FU::PG::conn {
|
||||||
|
sub lib_version { FU::PG::lib_version() }
|
||||||
|
};
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use ExtUtils::MakeMaker;
|
use ExtUtils::MakeMaker;
|
||||||
use Config;
|
use Config;
|
||||||
|
|
||||||
|
os_unsupported if $^O eq 'MSWin32'; # I don't know on which OS'es the code will work exactly, but this one I can easily rule out.
|
||||||
os_unsupported if $Config{ivsize} < 8;
|
os_unsupported if $Config{ivsize} < 8;
|
||||||
os_unsupported if $Config{usequadmath};
|
os_unsupported if $Config{usequadmath};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ Things that may or may not happen:
|
||||||
- FU::HTTPServer / FU::FastCGI - Minimal libs to support the web framework.
|
- FU::HTTPServer / FU::FastCGI - Minimal libs to support the web framework.
|
||||||
- FU::JSON - JSON::{XS,PP,etc}-compatible wrapper around FU::Util's JSON functions? I prolly won't need this myself, but could be handy.
|
- FU::JSON - JSON::{XS,PP,etc}-compatible wrapper around FU::Util's JSON functions? I prolly won't need this myself, but could be handy.
|
||||||
- FU::Log - Basic logger.
|
- FU::Log - Basic logger.
|
||||||
- FU::PG - PostgreSQL client with support for custom types and a small query builder.
|
|
||||||
- FU::Util additions: `uri_escape`, `VNDB::Util::query_encode`, `scrypt`, `urandom`.
|
- FU::Util additions: `uri_escape`, `VNDB::Util::query_encode`, `scrypt`, `urandom`.
|
||||||
- FU::Validate - TUWF::Validate & normalization with some improvements.
|
- FU::Validate - TUWF::Validate & normalization with some improvements.
|
||||||
- FU::XML - TUWF::XMLXS with some improvements.
|
- FU::XML - TUWF::XMLXS with some improvements.
|
||||||
|
|
|
||||||
47
c/pgconn.c
Normal file
47
c/pgconn.c
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
typedef struct PGconn PGconn;
|
||||||
|
|
||||||
|
#define PG_FUNCS \
|
||||||
|
X(PQconnectdb, PGconn *, const char *) \
|
||||||
|
X(PQerrorMessage, char *, const PGconn *) \
|
||||||
|
X(PQfinish, void, PGconn *) \
|
||||||
|
X(PQlibVersion, int, void) \
|
||||||
|
X(PQserverVersion, int, const PGconn *) \
|
||||||
|
X(PQstatus, int, const PGconn *)
|
||||||
|
|
||||||
|
#define X(n, r, ...) static r (*n)(__VA_ARGS__);
|
||||||
|
PG_FUNCS
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
static void fupg_load() {
|
||||||
|
void *handle = dlopen("libpq.so", RTLD_LAZY);
|
||||||
|
if (!handle) croak("Unable to load libpq: %s", dlerror());
|
||||||
|
#define X(n, ...) if (!(n = dlsym(handle, #n))) croak("Unable to load libpq: %s", dlerror());
|
||||||
|
PG_FUNCS
|
||||||
|
#undef X
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef PG_FUNCS
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PGconn *conn;
|
||||||
|
} fupg_conn;
|
||||||
|
|
||||||
|
fupg_conn *fupg_connect(pTHX_ const char *str) {
|
||||||
|
SV *sv;
|
||||||
|
PGconn *conn = PQconnectdb(str);
|
||||||
|
if (PQstatus(conn) != 0) {
|
||||||
|
sv = newSVpvf("FU::PG connection error: %s", PQerrorMessage(conn));
|
||||||
|
PQfinish(conn);
|
||||||
|
croak_sv(sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
fupg_conn *c = safecalloc(1, sizeof(fupg_conn));
|
||||||
|
c->conn = conn;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fupg_destroy(fupg_conn *c) {
|
||||||
|
PQfinish(c->conn);
|
||||||
|
safefree(c);
|
||||||
|
}
|
||||||
17
t/pgconnect.t
Normal file
17
t/pgconnect.t
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
use v5.36;
|
||||||
|
use Test::More;
|
||||||
|
|
||||||
|
plan skip_all => $@ if !eval { require FU::PG; } && $@ =~ /Unable to load libpq/;
|
||||||
|
plan skip_all => 'Please set FU_TEST_DB to a PostgreSQL connection string to run these tests' if !$ENV{FU_TEST_DB};
|
||||||
|
|
||||||
|
ok !eval { FU::PG->connect("invalid") };
|
||||||
|
|
||||||
|
ok FU::PG::lib_version() > 100000;
|
||||||
|
|
||||||
|
my $conn = FU::PG->connect($ENV{FU_TEST_DB});
|
||||||
|
|
||||||
|
is ref $conn, 'FU::PG::conn';
|
||||||
|
ok $conn->server_version() > 100000;
|
||||||
|
is $conn->lib_version(), FU::PG::lib_version();
|
||||||
|
|
||||||
|
done_testing;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue