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

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

Related

How do I evaluate a cursor loop variable as a table name?

FOR _r IN curs1 LOOP
ALTER TABLE QUOTE_IDENT(_r.table_name) ALTER COLUMN company_uuid SET NOT NULL;
END LOOP;
I am trying to convert the table name to an identifier so I can use it dynamically.
The error is: ERROR: syntax error at or near "("
Values can be parameterized for the core DML statements SELECT, INSERT, UPDATE, and DELETE.
But identifiers (or syntax elements) cannot be parameterized anywhere in SQL. In PL/pgSQL you can use dynamic SQL with EXECUTE like Anton suggested. I.e., concatenate the whole command as string and then execute. See:
Error when setting n_distinct using a plpgsql variable
Optionally use format() for convenience:
EXECUTE format('ALTER TABLE %I ALTER COLUMN company_uuid SET NOT NULL', _r.table_name);
With the %I specifier for identifiers, unless _r.table_name is already quoted properly. See:
Format specifier for integer variables in format() for EXECUTE?
Seemingly it is invalid to use functions in the command. You may use dynamic sql instead.
EXECUTE 'ALTER TABLE ' || QUOTE_IDENT(_r.table_name) || ' ALTER COLUMN company_uuid SET NOT NULL;';

create table-space in PostgreSQL 12 by passing a string value. how to go about passing values to creating table-space using my custom values?

When I run the command below in PostgreSQL
CREATE TABLESPACE 'forex_eurusd_2020_07'
OWNER 'forex'
LOCATION 'f:\data\forex\eurusd\2020_07';
I get the following error.
ERROR: syntax error at or near "'forex_eurusd_2020_07'"
LINE 3: CREATE TABLESPACE 'forex_eurusd_2020_07'
^
SQL state: 42601
Character: 21
What I am trying to do here is to create table-space and later partition by passing the values in my function as string .
Though this is my test statement
Here is something else I tried below
do $$
declare
tbl_spc varchar(50) := 'forex_eurusd_2020_07';
forex_owner varchar(10) :='forex';
begin
CREATE TABLESPACE tbl_spc
OWNER forex_owner
LOCATION 'f:\data\forex\eurusd\2020_07';
end $$;
And got the following result
ERROR: CREATE TABLESPACE cannot be executed from a function
CONTEXT: SQL statement "CREATE TABLESPACE tbl_spc
OWNER forex_owner
LOCATION 'f:\data\forex\eurusd\2020_07'"
PL/pgSQL function inline_code_block line 6 at SQL statement
SQL state: 25001
In the end I am open to suggestions on how to go about passing values to creating table-space using my custom values?
'forex_eurusd_2020_07' is an identifier so it needs to be double quoted(same with OWNER name). quote_ident(string text)(Docs) is your friend. So:
quote_ident('forex_eurusd_2020_07')
The function issue is explained by the error:
ERROR: CREATE TABLESPACE cannot be executed from a function
That is because Tablespace:
"CREATE TABLESPACE cannot be executed inside a transaction block."
And a function runs inside a transaction block. So you can't use a function for this.
I found the solution by using python. As I was using python to get data which I was trying to post in a partitioned table with its own table-space. I was facing issues dynamically creating the table-space. Which I have achieved by writing the following in my code. I used the code given to create table, instead of table, I modified to create the table-space.
connection = psycopg2.connect(user="forex",
password="MXXXXXX#XXXX",
host="127.0.0.1",
port="5432",
database="MyDB")
connection.autocommit = True
cursor = connection.cursor()
tbl_spc = 'forex_eurusd_2020_07'
forex_owner = 'forex'
location_tbl_spc = "'f:\\data\\forex\\eurusd\\2020_07'"
print(location_tbl_spc)
create_tbl_spc_query = ''' CREATE TABLESPACE '''+tbl_spc+'''
OWNER '''+forex_owner+'''
LOCATION '''+location_tbl_spc+''';'''
print(create_tbl_spc_query)
cursor.execute(create_tbl_spc_query)
connection.commit()
print("Table-space created successfully in PostgreSQL ")
except (Exception, psycopg2.Error) as error:
print("Error while creating PostgreSQL table-space", error)
finally:
closing database connection.
if (connection):
cursor.close()
connection.close()
print("PostgreSQL connection is closed")

PostgreSQL use of CurrentDatabase to set variable

I am trying to use below syntax to set variable under HDB DATABASE.
ALTER DATABASE HDB SET "abc.var1"='aaa';
However I need to provide database name for this. If I want to use CURRENT database instead.
There is a method in PostgreSQL which actually returns current database name: current_database(), but even below example is also not a valid one.
ALTER DATABASE current_database() SET "abc.var1"='aaa';
How can I achieve this ?
You need dynamic SQL for that:
do
$$
begin
execute format('alter database %I SET %I = %L', current_database(), 'abc.var1', 'aaa');
end;
$$
Using parameters and the placeholders %I and %L for the variable name and value avoids nesting quotes in the call to format() and properly deals with quoting.

How to return values from dynamically generated "insert" command?

I have a stored procedure that performs inserts and updates in the tables. The need to create it was to try to centralize all the scan functions before inserting or updating records. Today the need arose to return the value of the field ID of the table so that my application can locate the registry and perform other stored procedures.
Stored procedure
SET TERM ^ ;
CREATE OR ALTER procedure sp_insupd (
iaction varchar(3),
iusuario varchar(20),
iip varchar(15),
imodulo varchar(30),
ifieldsvalues varchar(2000),
iwhere varchar(1000),
idesclogs varchar(200))
returns (
oid integer)
as
declare variable vdesc varchar(10000);
begin
if (iaction = 'ins') then
begin
vdesc = idesclogs;
/*** the error is on the line below ***/
execute statement 'insert into '||:imodulo||' '||:ifieldsvalues||' returning ID into '||:oid||';';
end else
if (iaction = 'upd') then
begin
execute statement 'select '||:idesclogs||' from '||:imodulo||' where '||:iwhere into :vdesc;
execute statement 'execute procedure SP_CREATE_AUDIT('''||:imodulo||''');';
execute statement 'update '||:imodulo||' set '||:ifieldsvalues||' where '||:iwhere||';';
end
insert into LOGS(USUARIO, IP, MODULO, TIPO, DESCRICAO) values (
:iusuario, :iip, :imodulo, (case :iaction when 'ins' then 1 when 'upd' then 2 end), :vdesc);
end^
SET TERM ; ^
The error in the above line is occurring due to syntax error. The procedure is compiled normally, that is, the error does not happen in the compilation, since the line in question is executed through the "execute statement". When there was no need to return the value of the ID field, the procedure worked normally with the line like this:
...
execute statement 'insert into '||:imodulo||' '||:ifieldsvalues||';';
...
What would be the correct way for the value of the ID field to be stored in the OID variable?
What is REAL VALUE in ifieldsvalues ?
you can not have BOTH
'insert into '||:imodulo||' '||:ifieldsvalues
'update '||:imodulo||' set '||:ifieldsvalues
because methods to specify column names and column values in INSERT and UPDATE statements is fundamentally different!!! You either would have broken update-stmt or broken insert-stmt!
The error in the above line is occurring due to syntax error
This is not enough. Show the real error text, all of it.
It includes the actual command you generate and it seems you had generated it really wrong way.
all the scan functions before inserting or updating records
Move those functions out of the SQL server and into your application server.
Then you would not have to make insert/update in that "strings splicing" way, which is VERY fragile and "SQL injection" friendly. You stepped into the road to hell here.
the error does not happen in the compilation
Exactly. And that is only for starters. You are removing all the safety checks that should had helped you in applications development.
http://searchsoftwarequality.techtarget.com/definition/3-tier-application
https://en.wikipedia.org/wiki/Multitier_architecture#Three-tier_architecture
http://bobby-tables.com
On modern Firebird versions EXECUTE STATEMENT command can have the same INTO clause as PSQL SELECT command.
https://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-psql-coding.html#fblangref25-psql-execstmt
Use http://translate.ru to read http://www.firebirdsql.su/doku.php?id=execute_statement
Or just see SQL examples there. Notice, however, those examples all use SELECT dynamic command, not INSERT. So I am not sure it would work that way.
This works in Firebird 2.5 (but not in Firebird 2.1) PSQL blocks.
execute statement 'insert into Z(payload) values(2) returning id' into :i;
To run it from IBExpert/FlameRobin/iSQL interactive shell add that obvious boilerplate:
execute block returns (i integer) as
begin
execute statement 'insert into Z(payload) values(2) returning id' into :i;
suspend;
end

Drop table if exists in PostgreSQL database

I am trying to drop table if it is exists in the present working database of PostgreSQL. For which I am trying the following query.
Example:
var1 := 'IF EXISTS (select * from INFORMATION_SCHEMA.TABLES WHERE name = ''Table_'|| Suffix ||''') then
DROP TABLE Table_'||Suffix||'';
execute var1;
But getting error near IF.
execute executes SQL statements, not PL/pgSQL commands. The IF statement is a PL/pgSQL construct.
In any case you can use
DROP TABLE IF EXISTS ...
(see the manual page for DROP).