diff --git a/FU.xs b/FU.xs index f5cf9a3..9f9b779 100644 --- a/FU.xs +++ b/FU.xs @@ -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: diff --git a/FU/Pg.pm b/FU/Pg.pm index d676dd2..c058a93 100644 --- a/FU/Pg.pm +++ b/FU/Pg.pm @@ -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 is enabled. =item $st->columns diff --git a/c/pgst.c b/c/pgst.c index a9e3448..94c8072 100644 --- a/c/pgst.c +++ b/c/pgst.c @@ -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) */ - 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); + /* 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); diff --git a/t/pgconnect.t b/t/pgconnect.t index 27f339a..b7d1996 100644 --- a/t/pgconnect.t +++ b/t/pgconnect.t @@ -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');