Check if table exists in postgres - postgresql

In Firebird I have the following code to check if table exists:
SET TERM ^ ;
EXECUTE BLOCK AS
BEGIN
IF (NOT EXISTS(SELECT 1 FROM RDB$RELATIONS WHERE RDB$RELATION_NAME = 'TESTE')) THEN
BEGIN
EXECUTE STATEMENT
'CREATE TABLE TESTE( ' ||
'CDTESTE VARCHAR(7) NOT NULL, ' ||
'FATURA VARCHAR(7)); ';
EXECUTE STATEMENT 'ALTER TABLE TESTE ADD CONSTRAINT PK_TESTE PRIMARY KEY (CDTESTE)';
END
END^
SET TERM ; ^
How can I do that in Postgres using anonymous block? Sorry my english.

No need for a check:
CREATE TABLE IF NOT EXISTS TESTE(
CDTESTE VARCHAR(7) NOT NULL,
FATURA VARCHAR(7)
);
ALTER TABLE TESTE ADD CONSTRAINT PK_TESTE PRIMARY KEY (CDTESTE);

I found the solution:
DO $$
begin
if not (SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE
upper(table_name) = 'TB_INGREDIENTE')) then
CREATE TABLE tb_rep_ingredientes
(
cod_rep_ingrediente serial PRIMARY KEY,
cod_ingrediente bigint,
cod_filial bigint,
nome character varying(30),
preco_compra money,
saldo real
);
end if;
end
$$;

Related

How to execute delete statement inside a function

I want clients of my application to call DELETE statements indirectly or using a function. This is what I tried:
CREATE OR REPLACE FUNCTION layer_250_delete(layer_id integer)
RETURNS bool AS
$BODY$
BEGIN
EXECUTE 'DELETE FROM layer_250_ WHERE id = $1' USING layer_id;
RETURN TRUE;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION layer_250_delete(integer)
OWNER TO postgres;
I also tried a raw query:
...
DELETE FROM layer_250_ WHERE id = 1';
RETURN TRUE;
...
However, when I call this function (variant 1 or 2) like this:
select layer_250_delete(1);
it does not delete a row from layer_250_ table. I scanned dozens of threads here at stackoverflow, but could not find anything similar to my task.
STRUCTURE
CREATE TABLE public.layer_250_
(
id integer NOT NULL DEFAULT nextval('layer_250__id_seq'::regclass),
feature_type character varying(100) NOT NULL,
feature_hash character varying(500) NOT NULL,
feature_uid character varying(100) NOT NULL,
geom geometry,
radius integer,
group_id integer,
object_id integer NOT NULL DEFAULT 0,
row_id integer NOT NULL DEFAULT 0,
action_time timestamp without time zone NOT NULL DEFAULT now(),
action_type character varying(255),
action_user_id integer,
action_user_ip character varying(255),
CONSTRAINT layer_250__pkey PRIMARY KEY (id),
CONSTRAINT layer_250__feature_uid_key UNIQUE (feature_uid),
CONSTRAINT enforce_dims_geom_layer_250_ CHECK (st_ndims(geom) = 2),
CONSTRAINT enforce_srid_geom_layer_250_ CHECK (st_srid(geom) = 3857)
)
WITH (
OIDS=FALSE
);
ALTER TABLE public.layer_250_
OWNER TO postgres;
Yrs, it does:
t=# create table layer_250_ (id int);
CREATE TABLE
t=# insert into layer_250_ select 1;
INSERT 0 1
t=# CREATE OR REPLACE FUNCTION layer_250_delete(layer_id integer)
t-# RETURNS bool AS
t-# $BODY$
t$# BEGIN
t$# EXECUTE 'DELETE FROM layer_250_ WHERE id = $1' USING layer_id;
t$# RETURN TRUE;
t$# END;
t$# $BODY$
t-# LANGUAGE plpgsql VOLATILE
t-# COST 100;
CREATE FUNCTION
t=# ALTER FUNCTION layer_250_delete(integer)
t-# OWNER TO postgres;
ALTER FUNCTION
t=# begin;
BEGIN
t=# select layer_250_delete(1);
layer_250_delete
------------------
t
(1 row)
t=# select * from layer_250_;
id
----
(0 rows)
t=# end;
COMMIT

Using PL/pgSQL conditionals without creating a function

I would like to create a Postgres table and alter it, but only if it does not exist in the first place. I don't think this should be a function, as this is ran only once during the app startup. I tried to execute this code from pg-promise nodejs lib using client.none(sql, params), but it gave me an error syntax error at or near "IF", so I'm guessing it has to be some other execution method, or my SQL is fundamentally flawed. Thanks!
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = $1 AND table_name = $2
) THEN
CREATE TABLE $2~ (zoom smallint, idx bigint, tile bytea, PRIMARY KEY (zoom, idx));
ALTER TABLE $2~ ALTER COLUMN tile SET STORAGE EXTERNAL;
END IF;
END;
Your code is almost there
do $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = $1 AND table_name = $2
) THEN
CREATE TABLE $2~ (zoom smallint, idx bigint, tile bytea, PRIMARY KEY (zoom, idx));
ALTER TABLE $2~ ALTER COLUMN tile SET STORAGE EXTERNAL;
END IF;
END $$;

Insert record dynamically inside of Procedural Trigger

We are looking to convert our database over to Postgres (9.3.5), which I have no experience with, and I am trying to get our audit tables up and running. I understand that each table will need its own trigger, but all triggers can call a single function.
The trigger on the table is passing a list of the columns that need to be audited since some of our columns are not tracked.
Here are some of the posts I followed:
- https://stackoverflow.com/a/7915100/229897
- http://www.postgresql.org/docs/9.3/static/plpgsql-statements.html
- http://www.postgresql.org/docs/9.4/static/plpgsql-trigger.html
When I run this I get the error: ERROR: syntax error at or near "$1"
DROP TABLE IF EXISTS people;
DROP TABLE IF EXISTS a_people;
CREATE TABLE IF NOT EXISTS people (
record_id SERIAL PRIMARY KEY NOT NULL,
first_name VARCHAR NOT NULL,
last_name VARCHAR NOT NULL,
last_updated_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS a_people (
record_id SERIAL PRIMARY KEY NOT NULL,
a_record_id INT,
first_name VARCHAR NULL,
last_name VARCHAR NULL,
last_updated_on TIMESTAMP
);
/******************************************************/
--the function
CREATE OR REPLACE FUNCTION audit_func()
RETURNS TRIGGER AS
$BODY$
DECLARE
audit TEXT := TG_TABLE_SCHEMA || '.a_' || TG_TABLE_NAME;
cols TEXT := TG_ARGV[0];
BEGIN
EXECUTE format('INSERT INTO %1$s(a_%2$s) SELECT %2$s FROM ($1)', audit, cols) USING OLD;
NEW.last_updated_on = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
/******************************************************/
--the trigger calling the function to update inbound records
CREATE TRIGGER build_user_full_name_trg
BEFORE UPDATE
ON people
FOR EACH ROW WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE PROCEDURE audit_func('record_id,first_name,last_name');
/******************************************************/
INSERT INTO people (first_name, last_name) VALUES ('George','Lincoln');
UPDATE people SET last_name = 'Washington' WHERE first_name = 'George';
SELECT * FROM people;
I welcome your assistance (and patience)!
This subselect should work:
EXECUTE format('INSERT INTO %1$s(a_%2$s) SELECT %2$s FROM (select ($1).*) XX', audit, cols) USING OLD;

CONSTRAINT to check values from a remotely related table (via join etc.)

I would like to add a constraint that will check values from related table.
I have 3 tables:
CREATE TABLE somethink_usr_rel (
user_id BIGINT NOT NULL,
stomethink_id BIGINT NOT NULL
);
CREATE TABLE usr (
id BIGINT NOT NULL,
role_id BIGINT NOT NULL
);
CREATE TABLE role (
id BIGINT NOT NULL,
type BIGINT NOT NULL
);
(If you want me to put constraint with FK let me know.)
I want to add a constraint to somethink_usr_rel that checks type in role ("two tables away"), e.g.:
ALTER TABLE somethink_usr_rel
ADD CONSTRAINT CH_sm_usr_type_check
CHECK (usr.role.type = 'SOME_ENUM');
I tried to do this with JOINs but didn't succeed. Any idea how to achieve it?
CHECK constraints cannot currently reference other tables. The manual:
Currently, CHECK expressions cannot contain subqueries nor refer to
variables other than columns of the current row.
One way is to use a trigger like demonstrated by #Wolph.
A clean solution without triggers: add redundant columns and include them in FOREIGN KEY constraints, which are the first choice to enforce referential integrity. Related answer on dba.SE with detailed instructions:
Enforcing constraints “two tables away”
Another option would be to "fake" an IMMUTABLE function doing the check and use that in a CHECK constraint. Postgres will allow this, but be aware of possible caveats. Best make that a NOT VALID constraint. See:
Disable all constraints and table checks while restoring a dump
A CHECK constraint is not an option if you need joins. You can create a trigger which raises an error instead.
Have a look at this example: http://www.postgresql.org/docs/9.1/static/plpgsql-trigger.html#PLPGSQL-TRIGGER-EXAMPLE
CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- Who works for us when she must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- Remember who changed the payroll when
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
...i did it so (nazwa=user name, firma = company name) :
CREATE TABLE users
(
id bigserial CONSTRAINT firstkey PRIMARY KEY,
nazwa character varying(20),
firma character varying(50)
);
CREATE TABLE test
(
id bigserial CONSTRAINT firstkey PRIMARY KEY,
firma character varying(50),
towar character varying(20),
nazwisko character varying(20)
);
ALTER TABLE public.test ENABLE ROW LEVEL SECURITY;
CREATE OR REPLACE FUNCTION whoIAM3() RETURNS varchar(50) as $$
declare
result varchar(50);
BEGIN
select into result users.firma from users where users.nazwa = current_user;
return result;
END;
$$ LANGUAGE plpgsql;
CREATE POLICY user_policy ON public.test
USING (firma = whoIAM3());
CREATE FUNCTION test_trigger_function()
RETURNS trigger AS $$
BEGIN
NEW.firma:=whoIam3();
return NEW;
END
$$ LANGUAGE 'plpgsql'
CREATE TRIGGER test_trigger_insert BEFORE INSERT ON test FOR EACH ROW EXECUTE PROCEDURE test_trigger_function();

PostgreSQL drop constraint with unknown name

I have an SQL script that needs to drop several constraints and restore them at the end, but the constraint names are auto-generated and will be different each time the script is run.
I know how to get the constraint name from the table names, but it doesn't seem possible to use this information in the drop statement.
select conname from pg_constraint where
conrelid = (select oid from pg_class where relname='table name')
and confrelid = (select oid from pg_class where relname='reference table');
alter table something drop constraint (some subquery) is a syntax error.
Ideally I would like to get the constraint name and store it in a variable, but it doesn't seem that Postgres supports that and I can't make it work with psql \set.
Is this even possible?
To dynamically drop & recreate a foreign key constraint, you could wrap it all in a function or use the DO command:
DO
$body$
DECLARE
_con text := (
SELECT quote_ident(conname)
FROM pg_constraint
WHERE conrelid = 'myschema.mytable'::regclass
AND confrelid = 'myschema.myreftable'::regclass
LIMIT 1 -- there could be multiple fk constraints. Deal with it ...
);
BEGIN
EXECUTE '
ALTER TABLE wuchtel12.bet DROP CONSTRAINT ' || _con;
-- do stuff here
EXECUTE '
ALTER TABLE myschema.mytable
ADD CONSTRAINT ' || _con || ' FOREIGN KEY (col)
REFERENCES myschema.myreftable (col)';
END
$body$
You must own the table to use ALTER TABLE.
Else you can create a function with LANGUAGE plpgsql SECURITY DEFINER (using the same body) and
ALTER FUNCTION foo() OWNER TO postgres;
postgres being a superuser here - or the owner of the table.
But be sure to know what the manual has to say about security.
The manual also has more on dynamic commands.
You can use stored procedure also.
CREATE OR REPLACE PROCEDURE public.p_costraint()
LANGUAGE plpgsql
AS $procedure$
DECLARE _constrint text;
begin
-- for dynamic change the constraint.
_constrint := (
SELECT quote_ident(conname)
FROM pg_constraint
WHERE conrelid = 'test.contacts'::regclass
AND confrelid = 'test.customers'::regclass
LIMIT 1 -- there could be multiple fk constraints. Deal with it ...
);
_constrint := _constrint || 'test';
EXECUTE '
ALTER TABLE test.contacts
ADD CONSTRAINT ' || _constrint || ' FOREIGN KEY (customer_id)
REFERENCES test.customers (customer_id)';
RAISE NOTICE 'hello, world!';
end
$procedure$;
In here. constraint name is used as a text variable.
You can just call it: call public.p_costraint();
It will return :
NOTICE: hello, world!
CALL