Pg: Add escape_literal() and escape_identifier()

Didn't expect I'd ever need these, but they're useful for generating SQL
scripts.
This commit is contained in:
Yorhel 2025-04-07 13:45:24 +02:00
parent 3bf98e4d8f
commit b3281924d1
4 changed files with 41 additions and 0 deletions

18
FU.xs
View file

@ -216,6 +216,24 @@ void status(fupg_conn *c)
CODE:
ST(0) = sv_2mortal(newSVpv(fupg_conn_status(c), 0));
void escape_literal(fupg_conn *c, SV *v)
CODE:
STRLEN len;
const char *str = SvPVutf8(v, len);
char *r = PQescapeLiteral(c->conn, str, len);
if (!r) fupg_conn_croak(c, "escapeLiteral");
ST(0) = newSVpvn_flags(r, strlen(r), SVf_UTF8|SVs_TEMP);
PQfreemem(r);
void escape_identifier(fupg_conn *c, SV *v)
CODE:
STRLEN len;
const char *str = SvPVutf8(v, len);
char *r = PQescapeIdentifier(c->conn, str, len);
if (!r) fupg_conn_croak(c, "escapeIdentifier");
ST(0) = newSVpvn_flags(r, strlen(r), SVf_UTF8|SVs_TEMP);
PQfreemem(r);
void cache(fupg_conn *x, ...)
ALIAS:
FU::Pg::conn::text_params = FUPG_TEXT_PARAMS

View file

@ -123,6 +123,17 @@ Connection is dead or otherwise unusable.
=back
=item $conn->escape_literal($str)
Return an escaped version of C<$str> suitable for use as a string literal in an
SQL statement. You'll rarely need this, it's often better to pass data as bind
parameters instead.
=item $conn->escape_identifier($str)
Return an escaped version of C<$str> suitable for use as an identifier (name of
a table, column, function, etc) in an SQL statement.
=item $conn->cache($enable)
=item $conn->text_params($enable)

View file

@ -43,6 +43,8 @@ typedef enum { PQTRANS_IDLE, PQTRANS_ACTIVE, PQTRANS_INTRANS, PQTRANS_INERROR, P
X(PQconnectdb, PGconn *, const char *) \
X(PQenterPipelineMode, int, PGconn *) \
X(PQerrorMessage, char *, const PGconn *) \
X(PQescapeIdentifier, char *, PGconn *, const char *, size_t) \
X(PQescapeLiteral, char *, PGconn *, const char *, size_t) \
X(PQexec, PGresult *, PGconn *, const char *) \
X(PQexecParams, PGresult *, PGconn *, const char *, int, const Oid *, const char * const *, const int *, const int *, int) \
X(PQexecPrepared, PGresult *, PGconn *, const char *, int, const char * const *, const int *, const int *, int) \

View file

@ -353,6 +353,16 @@ subtest 'txn', sub {
is_deeply $st->val, [1,3], 'not deep copy';
}
{
# Exact format returned by escape_literal() can differ between Postgres versions and configurations.
my $x = q{"' \" \\};
is $conn->q('SELECT '.$conn->escape_literal($x))->val, $x;
# Format can also change, but unsure how to test this otherwise.
is $conn->escape_identifier('hel\l"o'), '"hel\l""o"';
}
subtest 'Prepared statement cache', sub {
my $txn = $conn->cache_size(2)->txn->cache;
my sub numexec($sql) {