purescript-postgresql-client/purspgpp

106 lines
3.9 KiB
Raku
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env perl6
use v6;
use NativeCall;
module libpq {
constant LIBPQ = "libpq";
sub PQconnectdb(Str --> Pointer[void]) is native(LIBPQ) {*}
sub PQstatus(Pointer[void] --> int32) is native(LIBPQ) {*}
sub PQerrorMessage(Pointer[void] --> Str) is native(LIBPQ) {*}
sub PQresultStatus(Pointer[void] --> int32) is native(LIBPQ) {*}
sub PQresultErrorMessage(Pointer[void] --> Str) is native(LIBPQ) {*}
sub PQprepare(Pointer[void], Str, Str, int32, Pointer[void] --> Pointer[void]) is native(LIBPQ) {*}
sub PQdescribePrepared(Pointer[void], Str --> Pointer[void]) is native(LIBPQ) {*}
sub PQnparams(Pointer[void] --> int32) is native(LIBPQ) {*}
sub PQparamtype(Pointer[void], int32 --> uint32) is native(LIBPQ) {*}
sub PQnfields(Pointer[void] --> int32) is native(LIBPQ) {*}
sub PQftype(Pointer[void], int32 --> uint32) is native(LIBPQ) {*}
our class Description {
has Int:D @.parameters;
has Int:D @.fields;
}
our class Connection {
has Pointer[void] $!handle;
method new(Str:D $connection-string) {
self.bless(:$connection-string);
}
submethod BUILD(Str:D :$connection-string) {
my $handle = PQconnectdb($connection-string);
die PQerrorMessage($handle) unless PQstatus($handle) == 0;
$!handle = $handle;
}
method prepare(Str:D $name, Str:D $source) {
my $result = PQprepare($!handle, $name, $source, 0, Nil);
die PQerrorMessage($!handle) without $result;
die PQresultErrorMessage($result) unless PQresultStatus($result) == 1;
}
method describe-prepared(Str:D $name) {
my $result = PQdescribePrepared($!handle, $name);
die PQerrorMessage($!handle) without $result;
die PQresultErrorMessage($result) unless PQresultStatus($result) == 1;
my @parameters = (^PQnparams($result)).map({PQparamtype($result, $_)});
my @fields = (^PQnfields($result)).map({PQftype($result, $_)});
Description.new(:@parameters, :@fields);
}
}
}
constant DEFAULT-MAPPING = q:to/EOF/;
16 Boolean
17 ByteString
18 Char
23 Int
25 String
701 Number
2950 UUID
2951 List UUID
EOF
sub process-module(libpq::Connection $conn, %mapping, Str:D $segment --> Str:D) {
S:g/'[query|' (.*?) '|]'/&process-query($conn, %mapping, ~$0)/ given $segment;
}
multi sub process-query(libpq::Connection:D $conn, %mapping, Str:D $source --> Str:D) {
$conn.prepare: '', $source;
my $description = $conn.describe-prepared('');
sub convert-types(@oids) {
die "unknown oid: $_" unless %mapping{$_}:exists for @oids;
(|@oids.map({%mapping{$_}}), "Unit").join(" × ");
};
my $parameters = convert-types($description.parameters);
my $fields = convert-types($description.fields);
"(Query \"\"\"$source\"\"\" :: Query ($parameters) ($fields))";
}
multi sub process-query(libpq::Connection:U $conn, %mapping, Str:D $source --> Str:D) {
"(Query \"\"\"$source\"\"\")";
}
sub main(libpq::Connection $conn, IO::Path:D $in-file, IO::Path:D $out-file, Bool:D $syntax-only, IO::Path $mapping-file) {
my %mapping{Int:D} =
(do with $mapping-file { .slurp } else { DEFAULT-MAPPING })
.lines
.map(*.trim.split(/\s+/, 2))
.grep(*.elems == 2)
.map({+$_[0] => $_[1]});
$out-file.spurt: process-module($conn, %mapping, $in-file.slurp);
}
multi sub MAIN(Str $connection-string, IO(Cool) $in-file, IO(Cool) $out-file = '-', Bool :$syntax-only where !*, IO(Cool) :$mapping-file) {
my $conn = libpq::Connection.new($connection-string);
main($conn, $in-file, $out-file, $syntax-only // False, $mapping-file);
}
multi sub MAIN(IO(Cool) $in-file, IO(Cool) $out-file = '-', Bool :$syntax-only where *, IO(Cool) :$mapping-file) {
main(libpq::Connection, $in-file, $out-file, $syntax-only // False, $mapping-file);
}