4

Background: I'm writing my first pgTAP test cases for a PL/pgSQL function and starting small with psql test scripts. No problems on that but I run into a small annoyance on psql variables.

In my test scripts I first dump quite a bit of test data to a relevant tables and later refer to the data with a primary keys that were generated by sequences. I'd find it handy to be able to create a variable that would contain the primary key. This is what I'm looking for:

scalasb=> \set the_id (select currval('id_data_id_seq'))
scalasb=> \echo :the_id
54754
scalasb=> 

But this is what I get:

scalasb=> \set the_id (select currval('id_data_id_seq'))
scalasb=> \echo :the_id
(selectcurrval('id_data_id_seq'))
scalasb=> 

I have a workaround (please see the example below), but it looks like psql variables are not the right tool for this job. Or they are just a different of what I've used with Oracle sqlplus bind variables ...

So my question: how I can bind the return value of a SQL query into a variable in psql script ?

I'm working on a linux with 9.1.

-- this is a simplified example to illustrate the problem
begin;

create table id_data(id serial primary key, data text not null);

create or replace function get_text(p_id bigint)
returns text
as $$
declare
  v_data constant text := data from id_data where id = p_id;
begin
  return v_data;
end;
$$ language plpgsql;

insert into id_data(data) values('lorem ipsum');

-- this works correctly but is a rather verbose (especially when one have
-- more of these, in the same query/function)
select get_text((select currval('id_data_id_seq')));

-- instead I'd like to set the id to a variable and use that but this
-- seems to be impossible with psql, right ?
\set the_id (select currval('id_data_id_seq'))
\echo First try: :the_id
--select get_text(:the_id); -- this will fail

-- this works and reveals psql variables' true nature - they are just a
-- textual replacements
\set the_id '(select currval(\'id_data_id_seq\'))'
\echo Second try: :the_id
select get_text(:the_id);

rollback;

1 Answer 1

6

I afraid so you cannot do it - I wrote some patches for psql, but it is not in core

you can use workaround:

postgres=# \set myvar `psql -A -t -c "select version()" postgres `
postgres=# \echo :myvar
PostgreSQL 9.1.4 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.7.0 20120507 (Red Hat 4.7.0-5), 64-bit

Note: PostgreSQL 9.3 (2013) and newer supports \gset command. With this command, the mentioned workaround is baseless:

(2023-04-02 17:03:00) postgres=# select version() as myvar \gset
(2023-04-02 17:03:06) postgres=# \echo :myvar
PostgreSQL 16devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 13.0.1 20230318 (Red Hat 13.0.1-0), 64-bit
Sign up to request clarification or add additional context in comments.

3 Comments

Argh ... so it looks like there is no good options at all - thanks for conforming that anyway. For now on I'll use my own work-around as executing psql several times looks even weirder. Another idea how to work around this issue is to set all sequences to start in 1000 in installation and then reset the sequences to 1 in unit test scripts. This way I know exactly all the time the ids of the test data and I can rely on magic numbers.
you can use custom session variables on server side
Custom session variables looks like a bit overkill, but a usage of temporary tables (I learned from this question) might fit to my case. I'll explore these options next time I've to re-factor this code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.