\set command in EDB Postgresql - postgresql

In EDB Postgres, \set command is used to set variables, like for example \set detpno 80 and while inserting we can use :deptno instaed of actual value like
insert into dept values (:deptno, 'SALES', 'HYD');
it's working fine, but when i use it in Procedure or anonymous block its throwing error.
simple anonymous block
begin
insert into dept values (:deptno, 'SALES', 'LONDON');
end;
when i execute this block, I am getting below error
ERROR: syntax error at or near ":"
LINE 2: insert into dept1 values (:deptno, 'SALES', 'BAN');
Please help me out to use \set variable in procedures or functions.

The substitution of :varname with something that was previously \set is a feature of a particular database client, namely "psql" (although maybe other clients have emulated this behavior). It is not a feature of the database itself. The database doesn't see the :varname, it only sees what it was substituted with.
When you use an anonymous code block, you are by-passing the client's substitution behavior.
Perhaps you should create a function, which declares what parameters it takes in a proper way, rather than using shell-like variable interpolation.

The psql variables cannot be used inside server side code - anonymous blocks. It is just not allowed - the substitution is blocked inside string. You can use a workaround.
set a custom guc variable (custom configuration value). The prefix 'xx' is requested.
postgres=# \set myval AHOJ
postgres=# select set_config('xx.myval', :'myval', false);
┌────────────┐
│ set_config │
╞════════════╡
│ AHOJ │
└────────────┘
(1 row)
Now you can read the value by function current_setting on server side
postgres=# do $$
begin
raise notice '%', current_setting('xx.myval');
end;
$$;
NOTICE: AHOJ
DO
Currently there are not any other way how to do it. This is Postgres - but EDB is very similar in this case.

Related

How to use DO in postgres

I am attempting to get a better understanding of the DO command in postgreSQL 9.1
I have following code block,
DO
$do$
BEGIN
IF 1=1 THEN
SELECT 'foo';
ELSE
SELECT 'bar';
END IF;
END
$do$
However it returns the following error:
[42601] ERROR: query has no destination for result data Hint: If you want to discard the results of a SELECT, use PERFORM instead. Where: PL/pgSQL function "inline_code_block" line 4 at SQL statement
PostgreSQL DO command creates and executes some specific short life function. This function has not any interface, and then it cannot to return any output other then changes data in tables and some debug output.
Your example has more than one issues:
Only PostgreSQL table functions can returns some tabular data. But the mechanism is significantly different than MSSQL. PostgreSQL user's should to use RETURN NEXT or RETURN QUERY commands.
CREATE OR REPLACE FUNCTION foo(a int)
RETURNS TABLE(b int, c int) AS $$
BEGIN
RETURN QUERY SELECT i, i + 1 FROM generate_series(1,a) g(i);
END;
$$ LANGUAGE plpgsql;
SELECT * FROM foo(10);
DO anonymous functions are not table functions - so no output is allowed.
PostgreSQL 9.1 is not supported version, please upgrade
If you have some experience only from MSSQL, then forget it. A concept of stored procedures of PostgreSQL is very similar to Oracle or DB2, and it is significantly different to MSSQL does. T-SQL integrates procedural and SQL constructs to one set. Oracle, PostgreSQL, ... procedural functionality can embedded SQL, but procedural functionality is not integrated to SQL.
Please, read PostgreSQL PLpgSQL documentation for better imagine about this technology.

Alias for a complete SQL statement? (in PostgreSQL psql)

In PostgreSQL 9.5 psql (for use within an interactive session), I wold like to create an alias to a complete SQL statement, analogous to a shell alias. The objective is to just get the output printed on the screen.
If I could enable formatted server output (in Oracle terms) from within a stored procedure, it would look like this:
CREATE or replace FUNCTION print_my_table()
RETURNS void
AS $$
-- somehow enable output here
SELECT * from my_table;
$$ LANGUAGE SQL;
This would be invoked as print_my_table(); (as opposed to SELECT x FROM ...)
I know I can use 'RAISE NOTICE' to print from within a stored procedure, but to do that I would need to reimplement pretty-printing of a table.
Perhaps there is a completely different mechanism to do this?
(my_table stands for a complex SQL statement that collects server data accounting information, or a my_table() stored procedure returning a table)
EDIT
The solution provided by #Abelisto (using psql variables) enables the creation of aliases to arbitrary statements, beyond merely printing the result to the screen.
There is so called internal variables in the psql utility which will be replaced by its content (except inside the string constants):
postgres=# \set foo 'select 1;'
postgres=# :foo
?column?
----------
1
(1 row)
It can be also set by the command line option -v:
psql -v foo='select 1;' -v bar='select 2;'
Create the text file like
\set foo 'select 1;'
\set bar 'select 2;'
\set stringinside 'select $$abc$$;'
and load it using \i command.
Finally you can create the file ~/.psqlrc (its purpose is like ~/.bashrc file) and its content will be automatically executed each time when psql starts.

How to use a subquery as a database name in a DDL command?

I am wondering if it's possible to use the result of a subquery as database name in a PostgreSQL (9.5.1) DDL statement.
For example, I wanted to alter the current database with something like:
ALTER DATABASE (SELECT current_database()) SET a_var TO 'a_value';
If I run this, an error occurs:
ERROR: syntax error at or near "("
LINE 1: ALTER DATABASE (SELECT current_database()) SET ...
What's the correct way to use the sub-query (if possible)?
You need dynamic SQL for that:
DO
$do$
BEGIN
EXECUTE format($f$ALTER DATABASE %I SET x.a_var TO 'a_value'$f$, current_database());
END
$do$;
Using format() to escape the db name safely while being at it.
BTW, to unset:
ALTER DATABASE your_db RESET x.a_var;
To see the current setting:
SELECT current_setting('x.a_var');
(The DB default is not active before you start a new session.)
Related:
Table name as a PostgreSQL function parameter
Error when setting n_distinct using a plpgsql variable

Accessing variables inside trigger function

I'm trying to make a trigger function to create a time stamp based on a base date stored in a variable plus an interval in seconds.
This base date is given to the psql script with the -v option, e.g. "-v start_time='2013-10-10 13:48:00'".
I want to access this variable from within a trigger function a do something like:
NEW.mytimestamp = timestamp :start_time + interval NEW.elapsed_seconds ' s';
Unfortunately I cannot figure out the right syntax for that. Any ideas?
It is impossible. psql variables (accessed via :varname) are client side variables. Trigger functions are executed on the server and cannot access these variables.
There is a way around this, but a little difficult (one cannot simple initialize values via command line). You can use custom configuration setting variables:
postgres=# select set_config('public.xxx', '10', false);
set_config
------------
10
(1 row)
create or replace function foo_trg()
returns trigger as $$
begin
raise notice '%', current_setting('public.xxx');
return new;
end;
$$ language plpgsql;
create table foo(a int);
create trigger hh before insert on foo for each row execute procedure foo_trg();
postgres=# insert into foo values(200);
NOTICE: 10
INSERT 0 1
Another (more established) technique would be to use an auxiliary table.
On second thought, trigger parametrization (based on some global value) is usually a terrible idea. It indicates you are doing some wrong. Use a function instead.

PostgreSQL execute statement conditionally by server version

I'm currently writing some installer script that fires SQL files against different database types depending on the system's configuration (the webapplication supports multiple database server like MySQL, MSSQL and PostgreSQL).
One of those types is PostgreSQL. I'm not fluent with it and I would like to know if it's possible to make a statement into a define/populate SQL file that makes an SQL query conditional to a specific PostgreSQL server version.
How to make an SQL statement conditionally in plain PGSQL so that it is only executed in version 9? The command is:
ALTER DATABASE dbname SET bytea_output='escape';
The version check is to compare the version with 9.
Postgres does have version() function, however there is no major_vesion(). Assuming that output string always includes version number as number(s).number(s).number(s) you could write your own wrapper as:
CREATE OR REPLACE FUNCTION major_version() RETURNS smallint
AS $BODY$
SELECT substring(version() from $$(\d+)\.\d+\.\d+$$)::smallint;
$BODY$ LANGUAGE SQL;
Example:
=> Select major_version();
major_version
---------------
9
(1 row)
However real issue here is that AFAIK you can't execute your commands conditionally in "pure" SQL and best what you can do is to write some stored function like this:
CREATE OR REPLACE FUNCTION conditionalInvoke() RETURNS void
AS $BODY$
BEGIN
IF major_version() = 9 THEN
ALTER DATABASE postgres SET bytea_output='escape';
END IF;
RETURN;
END;
$BODY$ LANGUAGE plpgsql;
I think that you should rather use some scripting language and generate appropriate SQL with it.
Or you could just use
select setting from pg_settings where name = 'server_version'
Or
select setting from pg_settings where name = 'server_version_num'
If you need major version only
select Substr(setting, 1, 1) from pg_settings where name = 'server_version_num'
or
select Substr(setting, 1, strpos(setting, '.')-1) from pg_settings where name = 'server_version'
if you want it to be compatible with two digit versions.
Maybe you could make things dependent on the output of
select version();
(probably you'll have to trim and substring that a bit)
BTW (some) DDL statements may not be issued from within functions; maybe you'll have to escape to shell-programming and here-documents.