diff --git a/FU.xs b/FU.xs index 3e7a0b2..ec12a7c 100644 --- a/FU.xs +++ b/FU.xs @@ -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 diff --git a/FU/Pg.pm b/FU/Pg.pm index 83b2111..e43776e 100644 --- a/FU/Pg.pm +++ b/FU/Pg.pm @@ -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) diff --git a/c/libpq.h b/c/libpq.h index 94d817e..d47dd16 100644 --- a/c/libpq.h +++ b/c/libpq.h @@ -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) \ diff --git a/t/pgconnect.t b/t/pgconnect.t index b7d1996..d2dfbf5 100644 --- a/t/pgconnect.t +++ b/t/pgconnect.t @@ -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) {