In relation to my other question "What’s the best way to audit log DELETEs?". What's the PostgreSQL equivalent of CONTEXT_INFO?
I want to log deletes using a trigger, but since I'm not using the database user as my app's logical user, I cannot log the CURRENT_USER from the trigger code as the user who deleted the record. But for INSERT and UPDATE it is possible to log the record changes from trigger since you can just add a user field in the record, say inserted_by and last_updated_by, and use these fields to log to audit table.
http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks_II#Any_other_session_variables
PostgreSQL provides more variants, please, look to this site.
The accepted answer is outdated.
In recent versions of postgresql (since 9.3 or 9.4 I think), one can set / retrieve config variables are only alive for the current session [or transaction]. Documentation Reference
example of setting a session variable:
hal=# select set_config('myvar.foo', 'bar', false);
set_config
------------
bar
(1 row)
hal=# select current_setting('myvar.foo');
current_setting
-----------------
bar
(1 row)
It is possible to use the function current_setting as one would use other set returning functions.
example:
create table customers (id int);
insert into customers values (1), (2), (3), (4);
select c.*, s.*
from customers c
left join current_setting('myvar.foo') s
on c.id = length(s)
Related
A trigger seems to be ignoring the 'when condition' in my definition but I'm unsure why. I'm running the following:
create trigger trigger_update_candidate_location
after update on candidates
for each row
when (
OLD.address1 is distinct from NEW.address1
or
OLD.address2 is distinct from NEW.address2
or
OLD.city is distinct from NEW.city
or
OLD.state is distinct from NEW.state
or
OLD.zip is distinct from NEW.zip
or
OLD.country is distinct from NEW.country
)
execute procedure entities.tf_update_candidate_location();
But when I check back in on it, I get the following:
-- auto-generated definition
create trigger trigger_update_candidate_location
after update
on candidates
for each row
execute procedure tf_update_candidate_location();
This is problematic because the procedure I call ends up doing an update on the same table for different columns (lat/lng). Since the 'when' condition is ignored this crates an infinite loop.
My intention is to watch for address change, do a lookup on another table to get lat/lng values.
Postgresql version: 10.6
IDE: DataGrip 2018.1.3
How exactly do you create and "check back"? With datagrip?
The WHEN condition was added with Postgres 9.0. Some old (or poor) clients may be outdated. To be sure, check in pgsql with:
SELECT pg_get_triggerdef(oid, true)
FROM pg_trigger
WHERE tgrelid = 'candidates'::regclass -- schema-qualify name to be sure
AND NOT tgisinternal;
Any actual WHEN qualification is stored in internal format in pg_trigger.tgqual, btw. Details in the manual here.
Also what's your current search_path and what's the schema of table candidates?
It stands out that the table candidates is unqualified, while the trigger function entities.tf_update_candidate_location() has a schema-qualification ... You are not confusing tables of the same name in different DB schemas, are you?
Aside, you can simplify with this shorter, equivalent syntax:
create trigger trigger_update_candidate_location
after update on candidates -- schema-qualify??
for each row
when (
(OLD.address1, OLD.address2, OLD.city, OLD.state, OLD.zip, OLD.country)
IS DISTINCT FROM
(NEW.address1, NEW.address2, NEW.city, NEW.state, NEW.zip, NEW.country)
)
execute procedure entities.tf_update_candidate_location();
Unfortunately, that's the issue of DataGrip. Please follow the ticket to be notified when it's fixed.
https://youtrack.jetbrains.com/issue/DBE-7247
I need to execute statements conditionally in DB2. I searched for DB2 documentation and though if..then..elseif will serves the purpose. But can't i use if without a procedure.?
My DB2 verion is 9.7.6.
My requirement is I have a table say Group(name,gp_id). And I have another table Group_attr(gp_id,value,elem_id). We can ignore ant the elem_id for the requirement now.
-> I need to check the Group if it have a specific name.
-> If it has then nothing to be done.
-> If it doesn't have I need to add it to the Group. Then I need to insert corresponding rows in the Group_attr. Assume the value and elem_id are static.
You can use an anonymous block for PL/SQL or a compound statement for SQL PL code.
BEGIN ATOMIC
FOR ROW AS
SELECT PK, C1, DISCRETIZE(C1) AS D FROM SOURCE
DO
IF ROW.D IS NULL THEN
INSERT INTO EXCEPT VALUES(ROW.PK, ROW.C1);
ELSE
INSERT INTO TARGET VALUES(ROW.PK, ROW.D);
END IF;
END FOR;
END
Compound statements :
http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.sql.ref.doc/doc/r0004240.html
http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.apdv.sqlpl.doc/doc/c0053781.html
http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.sql.ref.doc/doc/r0004239.html
Anonymous block :
http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.apdv.plsql.doc/doc/c0053848.html
http://www.ibm.com/developerworks/data/library/techarticle/dm-0908anonymousblocks/
Many ot this features come since version 9.7
I got a solution for the conditional insert. For the scenario that I mentioned, the solution can be like this.
Insert into Group(name) select 'Name1' from sysibm.sysdummy1 where (select count(*) from Group where name='Name1')=0
Insert into Group_attr(gp_id,value,elem_id) select g.gp_id,'value1','elem1' Group g,group_attr ga where ga.gp_id=g.gp_id and (select count(*) from Group_attr Ga1 where Ga.gp_id=g.gp_id)=0
-- In my case Group_attr will contain some data for sure if the group has exists already
I am trying to implement simple table queue in DB2 database. What I need
is to select and delete the row in the table at once so the multiple clients will not get the same row from the queue twice. I was looking for similiar questions but they describes the solution for another database or describes quite complicated solutions. I only need to select and delete the row at once.
UPDATE: I found on the web db2 clause like this, which look exactly like what i need - the select from delete: example:
SELECT * FROM OLD TABLE (DELETE FROM example WHERE example_id = 1)
but I am wondering if this statement is atomic if the two concurent request don't get the same result or delete the same row.
Something like this:
SELECT COL1, COL2, ... FROM TABLE WHERE TABLE_ID = value
WITH RR USE AND KEEP EXCLUSIVE LOCKS;
DELETE FROM TABLE WHERE TABLE_ID = value;
COMMIT;
If you're on DB2 for z/OS (Mainframe DB2) since version 9.1, or Linux/Unix/Windows (since at least 9.5, search for data-change-table-reference), you can use a SELECT FROM DELETE statement:
SELECT *
FROM OLD TABLE
(DELETE FROM TAB1
WHERE COL1 = 'asdf');
That would give you all the rows that were deleted.
Edit: Ooops, just saw your edit about using this type of statement. And as he said in the comment, two separate application getting the same row depends on your Isolation Level.
How can I find the table creation time in PostgreSQL?
Example: If I created a file I can find the file creation time like that I want to know the table creation time.
I had a look through the pg_* tables, and I couldn't find any creation times in there. It's possible to locate the table files, but then on Linux you can't get file creation time. So I think the answer is that you can only find this information on Windows, using the following steps:
get the database id with select datname, datdba from pg_database;
get the table filenode id with select relname, relfilenode from pg_class;
find the table file and look up its creation time; I think the location should be something like <PostgreSQL folder>/main/base/<database id>/<table filenode id> (not sure what it is on Windows).
You can't - the information isn't recorded anywhere. Looking at the table files won't necessarily give you the right information - there are table operations that will create a new file for you, in which case the date would reset.
I don't think it's possible from within PostgreSQL, but you'll probably find it in the underlying table file's creation time.
Suggested here :
SELECT oid FROM pg_database WHERE datname = 'mydb';
Then (assuming the oid is 12345) :
ls -l $PGDATA/base/12345/PG_VERSION
This workaround assumes that PG_VERSION is the least likely to be modified after the creation.
NB : If PGDATA is not defined, check Where does PostgreSQL store the database?
Check data dir location
SHOW data_directory;
Check For Postgres relation file path :
SELECT pg_relation_filepath('table_name');
you will get the file path of your relation
check for creation time of this file <data-dir>/<relation-file-path>
I tried a different approach to get table creation date which could help for keeping track of dynamically created tables. Suppose you have a table inventory in your database where you manage to save the creation date of the tables.
CREATE TABLE inventory (id SERIAL, tablename CHARACTER VARYING (128), created_at DATE);
Then, when a table you want to keep track of is created it's added in your inventory.
CREATE TABLE temp_table_1 (id SERIAL); -- A dynamic table is created
INSERT INTO inventory VALUES (1, 'temp_table_1', '2020-10-07 10:00:00'); -- We add it into the inventory
Then you could get advantage of pg_tables to run something like this to get existing table creation dates:
SELECT pg_tables.tablename, inventory.created_at
FROM pg_tables
INNER JOIN inventory
ON pg_tables.tablename = inventory.tablename
/*
tablename | created_at
--------------+------------
temp_table_1 | 2020-10-07
*/
For my use-case it is ok because I work with a set of dynamic tables that I need to keep track of.
P.S: Replace inventory in the database with your table name.
I'm trying to follow a different way for obtain this.
Starting from this discussion my solution was:
DROP TABLE IF EXISTS t_create_history CASCADE;
CREATE TABLE t_create_history (
gid serial primary key,
object_type varchar(20),
schema_name varchar(50),
object_identity varchar(200),
creation_date timestamp without time zone
);
--delete event trigger before dropping function
DROP EVENT TRIGGER IF EXISTS t_create_history_trigger;
--create history function
DROP FUNCTION IF EXISTS public.t_create_history_func();
CREATE OR REPLACE FUNCTION t_create_history_func()
RETURNS event_trigger
LANGUAGE plpgsql
AS $$
DECLARE
obj record;
BEGIN
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands () WHERE command_tag in ('SELECT INTO','CREATE TABLE','CREATE TABLE AS')
LOOP
INSERT INTO public.t_create_history (object_type, schema_name, object_identity, creation_date) SELECT obj.object_type, obj.schema_name, obj.object_identity, now();
END LOOP;
END;
$$;
--ALTER EVENT TRIGGER t_create_history_trigger DISABLE;
--DROP EVENT TRIGGER t_create_history_trigger;
CREATE EVENT TRIGGER t_create_history_trigger ON ddl_command_end
WHEN TAG IN ('SELECT INTO','CREATE TABLE','CREATE TABLE AS')
EXECUTE PROCEDURE t_create_history_func();
In this way you obtain a table that records all the creation tables.
--query
select pslo.stasubtype, pc.relname, pslo.statime
from pg_stat_last_operation pslo
join pg_class pc on(pc.relfilenode = pslo.objid)
and pslo.staactionname = 'CREATE'
Order By pslo.statime desc
will help to accomplish desired results
(tried it on greenplum)
You can get this from pg_stat_last_operation. Here is how to do it:
select * from pg_stat_last_operation where objid = 'table_name'::regclass order by statime;
This table stores following operations:
select distinct staactionname from pg_stat_last_operation;
staactionname
---------------
ALTER
ANALYZE
CREATE
PARTITION
PRIVILEGE
VACUUM
(6 rows)
In relation to my other question "What’s the best way to audit log DELETEs?". What's the PostgreSQL equivalent of CONTEXT_INFO?
I want to log deletes using a trigger, but since I'm not using the database user as my app's logical user, I cannot log the CURRENT_USER from the trigger code as the user who deleted the record. But for INSERT and UPDATE it is possible to log the record changes from trigger since you can just add a user field in the record, say inserted_by and last_updated_by, and use these fields to log to audit table.
http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks_II#Any_other_session_variables
PostgreSQL provides more variants, please, look to this site.
The accepted answer is outdated.
In recent versions of postgresql (since 9.3 or 9.4 I think), one can set / retrieve config variables are only alive for the current session [or transaction]. Documentation Reference
example of setting a session variable:
hal=# select set_config('myvar.foo', 'bar', false);
set_config
------------
bar
(1 row)
hal=# select current_setting('myvar.foo');
current_setting
-----------------
bar
(1 row)
It is possible to use the function current_setting as one would use other set returning functions.
example:
create table customers (id int);
insert into customers values (1), (2), (3), (4);
select c.*, s.*
from customers c
left join current_setting('myvar.foo') s
on c.id = length(s)