How to log query for single table - postgresql

Have simple insert with returning id
INSERT INTO "public"."mytbl" ("col1", "col2" , "col3" , "col4", "col5")
values ('test', 'test' , null , 'test', null) RETURNING id;
want log execution time for only this query. How to do it ? I dont want log all querys ,only this one

If you use the command line interface psql, you can simply enter
\timing
and you'll get the duration of each statement after it has been executed.
If you want to log the duration to the PostgreSQL log file, you can do that by setting the parameter log_min_duration_statement = 0.
The problem is that this parameter can only be set by the superuser, and you probably want to run the statement as a different user.
One way to enable a normal user to log statement duration is to use a SECURITY DEFINER function that is owned by a superuser and sets the parameter for the duration of the current transaction:
CREATE OR REPLACE FUNCTION logduration() RETURNS void AS
'SET LOCAL log_min_duration_statement=0'
LANGUAGE sql SECURITY DEFINER;
REVOKE EXECUTE ON FUNCTION logduration() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION logduration() TO laurenz;
Then user laurenz can do the following:
START TRANSACTION;
SELECT logduration();
INSERT INTO "public"."mytbl" ...
COMMIT;
Your log will contain only the duration of the INSERT command (well, also the duration of the funxtion call, but that shouldn't be a problem).

Related

PostgreSQL : relation [temp table] does not exist error for temporary table

I have a function in PostgreSQL that calls multiple function depends on certain conditions. I create a temporary table in main function dynamically using "Execute" statement and using that temporary table for insertion and selection
in other functions (same dynamic process using "Execute" statement) those I am calling from main function.
However, its working fine as per my requirement. But sometimes it is throwing an error 'relation does not exists' on the temporary table when it is performing selection or insertion on the subroutine(internal function).
SAMPLE
Main Function
CREATE OR REPLACE FUNCTION public.sample_function(
param bigint)
RETURNS TABLE(isfinished boolean)
LANGUAGE 'plpgsql'
COST 100.0
VOLATILE ROWS 1000.0
AS $function$
DECLARE
st_dt DATE;
end_dt DATE;
var4 CHARACTER VARYING := CURRENT_TIME;
var1 character varying;
BEGIN
SELECT SUBSTRING(REPLACE(REPLACE(REPLACE(var4,':',''),'.',''),'+','') FROM 5 FOR 7) INTO var4;
EXECUTE 'CREATE TABLE sampletable'||var4||' (
"emp_id" UUid,
"emp_name" Character Varying( 2044 ),
"start_date" Date,
"end_date" Date)';
select public.innerfunction (st_dt,end_dt,var4)
into var1;
EXECUTE 'DROP TABLE sampletable'||var4;
return query select true ;
END;
- Inner Function
CREATE OR REPLACE FUNCTION public.innerfunction(
st_dt timestamp without time zone,
end_dt timestamp without time zone,
var4 bigint)
RETURNS integer
LANGUAGE 'plpgsql'
COST 100.0
VOLATILE
AS $function$
DECLARE
date1 timestamp without time zone:=st_dt
BEGIN
EXECUTE 'INSERT INTO sampletable'||var4||'
SELECT *
from "abc"'
;
return return_val;
END;
$function$;
- Error Message
ERROR:
relation "sampletable1954209" does not exist
LINE 1: INSERT INTO sampletable1954209
QUERY: INSERT INTO sampletable1954209
SELECT *
from "abc"
;
CONTEXT: PL/pgSQL function innerfunction(timestamp without time zone,
timestamp without time zone) line 51 at EXECUTE
SQL statement "SELECT public.innerfunction(st_dt ,end_dt)"
PL/pgSQL function sample_function(bigint) line 105 at SQL statement
********** Error **********
In above example I created a main function 'sample_function', and I am creating a temporary dynamic table 'sampletable with a random number attached to it. I am using that table on 'innerfunction' for insertion purpose.
When I am calling the main function it working as required but some times it gives the mentioned error 'relation "sampletable1954209" does not exist'.
I am not able to catch the issue.
I just ran into this problem. It turned out to be because the database was behind pgBouncer in transaction pooling mode, which meant each query could conceivably run in a different connection.
There were two symptoms of this behaviour:
I could run CREATE TEMPORARY TABLE test (LIKE sometable); in one connection, and then run SELECT * FROM test in another connection and see the temporary table. This isn't supposed to happen as temporary tables are meant to be session-specific, but because pgBouncer is pooling connections it means multiple sessions can share temporary tables unpredictably, if they are created outside of transactions.
Sometimes the connection would randomly change and my temporary table would disappear. I was creating and accessing the temporary table outside of a transaction which is why pgBouncer thought it was ok to switch connections.
The solution is to wrap everything up inside a transaction, however this was problematic for me because I was calling other code that used transactions. Postgres doesn't support nested transactions, so I was unable to wrap my code in one. (It does approximate nested transactions with savepoints, but this requires your code to use different SQL if it's running inside a transaction or not, which is not practical.)
Another solution is to change the pgBouncer configuration to session pooling instead, which causes it to run the DISCARD ALL statement when a client disconnects, before giving the session to another client. This drops all temporary tables and makes the connections behave more like direct connections.
There's a good write up of the issues at the VMWare support hub.
I see a problem with this code, but it does not quite match your exact error message:
You pass var4 to innerfunction as bigint.
Now if var4 starts with a zero like 0649940, then sample_function will use sampletable0649940, while innerfunction will try to access sampletable649940 because the leading zero is lost in the conversion.
Your error message has a seven-digit number though, so it might be a different problem.
Why don't you use a temporary table and use a fixed name for it? A temporary table is only visible in one session, and there cannot be any name collisions. That's what temporary tables are for.

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

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

Why does pgsql claim the currval of sequence undefined even after calling nextval?

I'm working with a PostgresQL database, in which a trigger function logs changes to a history table. I'm trying to add a column which keeps a logical "commit ID" to group master and detail records together. I've created a (non-temporary) sequence, and before I start the batch of updates, I bump this. All my SQL is logged to a log file, so you can clearly see this happening:
2015-04-16 10:43:37 SQLSelect: SELECT nextval('commit_id_seq')
2015-04-16 10:43:37 commit_id_seq: 8
...but then I attempt the UPDATE, my trigger function attempts to use currval, and it fails:
2015-04-16 10:43:37 ERROR: ERROR: currval of sequence "commit_id_seq" is not yet defined in this session
CONTEXT: SQL statement "INSERT INTO history (table_name, record_id, sec_user_id, created, action, notes, status, before, after, commit_id)
SELECT TG_TABLE_NAME, rec.id, (SELECT oid FROM pg_roles WHERE rolname = CURRENT_USER), now(), SUBSTR(TG_OP,1,1), note, stat, hstore(old), hstore(new), currval('commit_id_seq')"
PL/pgSQL function log_to_history() line 18 at SQL statement
[3]
So my question is basically: WTF?
One of two reasons:
Search_path differences, so you're actually talking about two different sequences.
Different sessions. The "current value" is only defined for the session you call nextval() in.
You can add process-id to the logfile format to check if they are different sessions.

Trying to convert simple execution stored procedure to postgres function - can't get CURRVAL('table_seq') to function

The MySQL Stored Procedure was:
BEGIN
set #sql=_sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
set _ires=LAST_INSERT_ID();
END$$
I tried to convert it to:
BEGIN
EXECUTE _sql;
SELECT INTO _ires CURRVAL('table_seq');
RETURN;
END;
I get the error:
SQL error:
ERROR: relation "table_seq" does not exist
LINE 1: SELECT CURRVAL('table_seq')
^
QUERY: SELECT CURRVAL('table_seq')
CONTEXT: PL/pgSQL function "myexecins" line 4 at SQL statement
In statement:
SELECT myexecins('SELECT * FROM tblbilldate WHERE billid = 2')
The query used is for testing purposes only. I believe this function is used to get the row id of the inserted or created row from the query. Any Suggestions?
When you create tables with serial columns, sequences are, by default, named as tablename_columnname_seq, but it seems that you're trying to access tablename_seq. So with a table called foobar and primary key column foobar_id it ends up being foobar_foobar_id_seq.
By the way, a cleaner way to get the primary key after an insert is using the RETURNING clause in INSERT. E.g.:
_sql = 'INSERT INTO sometable (foobar) VALUES (123) RETURNING sometable_id';
EXECUTE _sql INTO _ires;
PostgreSQL is saying that there is no sequence called "table_seq". Are you sure that that is the right name? The name you would use would depend on what is in _sql as each SERIAL or BIGSERIAL gets its own sequence, you can also define sequences and wire them up by hand.
In any case, lastval() is a closer match to MySQL's LAST_INSERT_ID(), lastval() returns the most recently returned value from any sequence in the current session:
lastval
Return the value most recently returned by nextval in the current session. This
function is identical to currval, except that instead of taking the sequence name
as an argument it fetches the value of the last sequence used by nextval in the
current session. It is an error to call lastval if nextval has not yet been called
in the current session.
Using lastval() also means that you don't have to worry about what's in _sql, unless of course it doesn't use a sequence at all.