Pg: Skip query preparation when we can

This commit is contained in:
Yorhel 2025-02-28 12:33:30 +01:00
parent 4686097d00
commit 0734bc4991
4 changed files with 33 additions and 28 deletions

14
FU.xs
View file

@ -380,16 +380,12 @@ void prepare_time(fupg_st *st)
ST(0) = !st->prepared ? &PL_sv_undef : sv_2mortal(newSVnv(st->preptime));
void get_cache(fupg_st *st)
ALIAS:
FU::Pg::st::get_text_params = FUPG_TEXT_PARAMS
FU::Pg::st::get_text_results = FUPG_TEXT_RESULTS
CODE:
ST(0) = st->stflags & FUPG_CACHE ? &PL_sv_yes : &PL_sv_no;
void get_text_params(fupg_st *st)
CODE:
ST(0) = st->stflags & FUPG_TEXT_PARAMS ? &PL_sv_yes : &PL_sv_no;
void get_text_results(fupg_st *st)
CODE:
ST(0) = st->stflags & FUPG_TEXT_RESULTS ? &PL_sv_yes : &PL_sv_no;
if (!ix) ix = FUPG_CACHE;
ST(0) = st->stflags & ix ? &PL_sv_yes : &PL_sv_no;
void DESTROY(fupg_st *st)
CODE:

View file

@ -371,7 +371,9 @@ parameter in the given C<$sql> string. Example:
# $oids = []
This method can be called before the query has been executed, but will then
trigger a prepare operation.
trigger a prepare operation. An empty array is also returned if the query has
already been executed without a separate preparation step; this happens if
prepared statement caching is disabled and C<text_params> is enabled.
=item $st->columns

View file

@ -271,10 +271,13 @@ static void fupg_st_execute(pTHX_ fupg_st *st) {
* results in a streaming fashion without breaking API compat. */
if (st->result) fu_confess("Invalid attempt to execute statement multiple times");
/* TODO: prepare can be skipped when prepared statement caching is disabled and (text-format queries or no bind params) */
/* Whether we can do a direct call or need to prepare first */
int direct = !st->describe && (st->nbind == 0 || st->stflags & FUPG_TEXT_PARAMS) && !(st->stflags & FUPG_CACHE);
if (!direct) {
fupg_st_prepare(aTHX_ st);
if (PQnparams(st->describe) != st->nbind)
fu_confess("Statement expects %d bind parameters but %d were given", PQnparams(st->describe), st->nbind);
}
int refresh_done = 0;
fupg_params_setup(aTHX_ st, &refresh_done);
@ -290,13 +293,17 @@ static void fupg_st_execute(pTHX_ fupg_st *st) {
* improvement */
struct timespec t_start;
clock_gettime(CLOCK_MONOTONIC, &t_start);
PGresult *r = PQexecPrepared(st->conn->conn,
st->name,
st->nbind,
PGresult *r = direct ? PQexecParams(st->conn->conn,
st->query, st->nbind, NULL,
(const char * const *)st->param_values,
st->param_lengths,
st->param_formats,
st->stflags & FUPG_TEXT_RESULTS ? 0 : 1);
st->param_lengths, st->param_formats,
st->stflags & FUPG_TEXT_RESULTS ? 0 : 1
) : PQexecPrepared(st->conn->conn,
st->name, st->nbind,
(const char * const *)st->param_values,
st->param_lengths, st->param_formats,
st->stflags & FUPG_TEXT_RESULTS ? 0 : 1
);
struct timespec t_end;
clock_gettime(CLOCK_MONOTONIC, &t_end);
st->exectime = fu_timediff(&t_end, &t_start);

View file

@ -73,10 +73,10 @@ subtest '$st prepare & exec', sub {
is $conn->exec('SELECT 1 FROM pg_prepared_statements'), 0;
ok !eval { $conn->q('SELECT 1', 1)->exec; 1 };
like $@, qr/Statement expects 0 bind parameters but 1 were given/;
like $@, qr/bind message supplies 1 parameters, but prepared statement/;
ok !eval { $conn->q('SELECT $1')->exec; 1 };
like $@, qr/Statement expects 1 bind parameters but 0 were given/;
like $@, qr/bind message supplies 0 parameters, but prepared statement/;
# prepare + describe won't let us detect empty queries, hmm...
is_deeply $conn->q('')->param_types, [];
@ -88,7 +88,7 @@ subtest '$st prepare & exec', sub {
is $conn->q('SET client_encoding=utf8')->exec, undef;
ok !eval { $conn->q('select 1; select 2')->exec; 1 };
okerr ERROR => prepare => qr/cannot insert multiple commands into a prepared statement/;
okerr ERROR => exec => qr/cannot insert multiple commands into a prepared statement/;
# Interleaved
{
@ -400,7 +400,7 @@ subtest 'Tracing', sub {
my @log;
$conn->query_trace(sub($st) { push @log, $st });
is_deeply $conn->q('SELECT 1 AS a, $1 AS b', 123)->rowa, [ 1, 123 ];
is_deeply $conn->q('SELECT 1 AS a, $1 AS b', 123)->text_params(0)->rowa, [ 1, 123 ];
is scalar @log, 1;
my $st = shift @log;
is ref $st, 'FU::Pg::st';
@ -412,8 +412,8 @@ subtest 'Tracing', sub {
ok $st->exec_time > 0 && $st->exec_time < 1;
ok $st->prepare_time > 0 && $st->prepare_time < 1;
ok !$st->get_cache;
ok $st->text_params;
ok $st->text_results;
ok !$st->get_text_params;
ok $st->get_text_results;
$conn->exec('SET client_encoding=UTF8');
is scalar @log, 1;
@ -427,8 +427,8 @@ subtest 'Tracing', sub {
ok $st->exec_time > 0 && $st->exec_time < 1;
ok !defined $st->prepare_time;
ok !$st->get_cache;
ok $st->text_params;
ok $st->text_results;
ok $st->get_text_params;
ok $st->get_text_results;
$conn->query_trace(undef);
$conn->exec('SELECT 1');