Alter table failing when i execute a function that modifys the table - postgresql

I have written a generic function to change ids of students with wrong ids. Since the students table is referenced by other tables i had to drop the foreign keys and disable the triggers temporarily. This is my function.
CREATE OR REPLACE FUNCTION update_student_id(varchar, varchar) RETURNS varchar AS $$
DECLARE
old_id ALIAS FOR $1;
new_id ALIAS FOR $2;
msg varchar(120);
BEGIN
--disable triggers
ALTER TABLE students
DISABLE TRIGGER ALL;
ALTER TABLE studentdegrees
DISABLE TRIGGER ALL;
ALTER TABLE studentdegrees DROP CONSTRAINT studentdegrees_studentid_fkey;
UPDATE students
SET studentid=new_id
WHERE studentid=old_id;
ALTER TABLE studentdegrees
ADD CONSTRAINT studentdegrees_studentid_fkey
FOREIGN KEY (studentid)
REFERENCES students(studentid);
UPDATE studentdegrees
SET studentid=new_id
WHERE studentid=old_id;
UPDATE entitys
SET user_name=new_id
WHERE user_name=old_id;
--enable triggers
ALTER TABLE students
ENABLE TRIGGER ALL;
ALTER TABLE studentdegrees
ENABLE TRIGGER ALL;
msg:=old_id|| ' Changed to '||new_id;
RETURN msg;
END;
$$ LANGUAGE plpgsql;
When i call the function like this
SELECT update_student_id(
studentid, a.stdid)
FROM
(SELECT studentid, REPLACE(studentid, ' ', '') AS stdid
FROM students
WHERE studentid NOT IN (SELECT REPLACE(studentid, ' ', ''))) AS a
I get this error
ERROR: cannot ALTER TABLE "students" because it is being used by active queries in this session
CONTEXT: SQL statement "ALTER TABLE studentdegrees DROP CONSTRAINT studentdegrees_studentid_fkey"
PL/pgSQL function update_student_id(character varying,character varying) line 17 at SQL statement

Related

create a procedure based on column data change in postgrersql

Fire trigger on update of columnA or ColumnB or ColumnC
Set up the scene
begin;
create temp table account_details(email text primary key, username text, password text);
insert into account_details(email, username, password) values('a.com','b','c'),('b.com','d','e');
commit;
create function:
CREATE OR REPLACE FUNCTION notify_insert_account_details()
RETURNS trigger
LANGUAGE plpgsql AS
$$
BEGIN
RAISE NOTICE 'hello world';
END
$$;
create trigger:
CREATE TRIGGER trigger_update_account_details
AFTER UPDATE ON account_details
FOR EACH ROW
WHEN (OLD.email IS DISTINCT FROM NEW.email
OR OLD.username IS DISTINCT FROM NEW.username
OR OLD.password IS DISTINCT FROM NEW.password)
EXECUTE PROCEDURE notify_insert_account_details();
Trying to update:
update account_details set username = 'username1' where email = 'a.com';
The result: notify_insert_account_details() fired. but didn't execute the update clause.
NOTICE: hello world
ERROR: control reached end of trigger procedure without RETURN
CONTEXT: PL/pgSQL function notify_insert_account_details()
My question: how sophisticated this procedure/function notify_insert_account_details() Can become, let's say linked within 3 table. Can anyone showcase an example/demo?

Ignore constraint if already present on Postgres? [duplicate]

Does Postgres have any way to say ALTER TABLE foo ADD CONSTRAINT bar ... which will just ignore the command if the constraint already exists, so that it doesn't raise an error?
A possible solution is to simply use DROP IF EXISTS before creating the new constraint.
ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;
Seems easier than trying to query information_schema or catalogs, but might be slow on huge tables since it always recreates the constraint.
Edit 2015-07-13:
Kev pointed out in his answer that my solution creates a short window when the constraint doesn't exist and is not being enforced. While this is true, you can avoid such a window quite easily by wrapping both statements in a transaction.
This might help, although it may be a bit of a dirty hack:
create or replace function create_constraint_if_not_exists (
t_name text, c_name text, constraint_sql text
)
returns void AS
$$
begin
-- Look for our constraint
if not exists (select constraint_name
from information_schema.constraint_column_usage
where table_name = t_name and constraint_name = c_name) then
execute constraint_sql;
end if;
end;
$$ language 'plpgsql'
Then call with:
SELECT create_constraint_if_not_exists(
'foo',
'bar',
'ALTER TABLE foo ADD CONSTRAINT bar CHECK (foobies < 100);')
Updated:
As per Webmut's answer below suggesting:
ALTER TABLE foo DROP CONSTRAINT IF EXISTS bar;
ALTER TABLE foo ADD CONSTRAINT bar ...;
That's probably fine in your development database, or where you know you can shut out the apps that depend on this database for a maintenance window.
But if this is a lively mission critical 24x7 production environment you don't really want to be dropping constraints willy nilly like this. Even for a few milliseconds there's a short window where you're no longer enforcing your constraint which may allow errant values to slip through. That may have unintended consequences leading to considerable business costs at some point down the road.
You can use an exception handler inside an anonymous DO block to catch the duplicate object error.
DO $$
BEGIN
BEGIN
ALTER TABLE foo ADD CONSTRAINT bar ... ;
EXCEPTION
WHEN duplicate_table THEN -- postgres raises duplicate_table at surprising times. Ex.: for UNIQUE constraints.
WHEN duplicate_object THEN
RAISE NOTICE 'Table constraint foo.bar already exists';
END;
END $$;
http://www.postgresql.org/docs/9.4/static/sql-do.html http://www.postgresql.org/docs/9.4/static/plpgsql-control-structures.html
http://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
you can run query over pg_constraint table to find constraint exists or not.like:
SELECT 1 FROM pg_constraint WHERE conname = 'constraint_name'"
Creating constraints can be an expensive operation on a table containing lots of data so I recommend not dropping constraints only to immediately create them again immediately after - you only want to create that thing once.
I chose to solve this using an anonymous code block, very similar to Mike Stankavich, however unlike Mike (who catches an error) I first check to see if the constraint exists:
DO $$
BEGIN
IF NOT EXISTS ( SELECT constraint_schema
, constraint_name
FROM information_schema.check_constraints
WHERE constraint_schema = 'myschema'
AND constraint_name = 'myconstraintname'
)
THEN
ALTER TABLE myschema.mytable ADD CONSTRAINT myconstraintname CHECK (column <= 100);
END IF;
END$$;
Using information_schema.constraint_column_usage to check for the constraint doesn't work for foreign keys. I use pg_constraint to check for primary keys, foreign keys or unique constraints:
CREATE OR REPLACE FUNCTION add_constraint(t_name text, c_name text, constraint_sql text)
RETURNS void
AS $$
BEGIN
IF NOT EXISTS(
SELECT c.conname
FROM pg_constraint AS c
INNER JOIN pg_class AS t ON c.conrelid = t."oid"
WHERE t.relname = t_name AND c.conname = c_name
) THEN
EXECUTE 'ALTER TABLE ' || t_name || ' ADD CONSTRAINT ' || c_name || ' ' || constraint_sql;
END IF;
END;
$$
LANGUAGE plpgsql;
Examples:
SELECT add_constraint('client_grant_system_scopes', 'client_grant_system_scopes_pk', 'PRIMARY KEY (client_grants_id, tenant, "scope");');
SELECT add_constraint('client_grant_system_scopes', 'client_grant_system_scopes_fk', 'FOREIGN KEY (tenant,"scope") REFERENCES system_scope(tenant,"scope") ON DELETE CASCADE;');
SELECT add_constraint('jwt_assertion_issuers', 'jwt_assertion_issuers_issuer_key', 'UNIQUE (issuer);');
Take advantage of regclass to reduce verbosity, increase performance, and avoid errors related to table naming clashes between schemas:
DO $$ BEGIN
IF NOT EXISTS (SELECT FROM pg_constraint
WHERE conrelid = 'foo'::regclass AND conname = 'bar') THEN
ALTER TABLE foo ADD CONSTRAINT bar...;
END IF;
END $$;
This will also work for tables in other schemas, e.g.:
DO $$ BEGIN
IF NOT EXISTS (SELECT FROM pg_constraint
WHERE conrelid = 's.foo'::regclass AND conname = 'bar') THEN
ALTER TABLE s.foo ADD CONSTRAINT bar...;
END IF;
END $$;
In psql You can use metacommand \gexec for run generated query.
SELECT 'ALTER TABLE xx ADD CONSTRAINT abc' WHERE not EXISTS (SELECT True FROM pg_constraint WHERE conname = 'abc') \gexec
For me those solutions didn't work because the constraint was a primary key.
This one worked for me:
ALTER TABLE <table.name> DROP CONSTRAINT IF EXISTS <constraint.name> CASCADE;
Considering all the above mentioned answers , the below approach help if you just want to check if a constraint exist in the table in which you are trying to insert and raise a notice if there happens to be one
DO
$$ BEGIN
IF NOT EXISTS (select constraint_name
from information_schema.table_constraints
where table_schema='schame_name' and upper(table_name) =
upper('table_name') and upper(constraint_name) = upper('constraint_name'))
THEN
ALTER TABLE TABLE_NAME ADD CONSTRAINT CONTRAINT_NAME..... ;
ELSE raise NOTICE 'Constraint CONTRAINT_NAME already exists in Table TABLE_NAME';
END IF;
END
$$;
Don't know why so many lines of code ?
-- SELECT "Column1", "Column2", "Column3" , count(star) FROM dbo."MyTable" GROUP BY "Column1" , "Column2" , "Column3" HAVING count(*) > 1;
alter table dbo."MyTable" drop constraint if exists "MyConstraint_Name" ;
ALTER TABLE dbo."MyTable" ADD CONSTRAINT "MyConstraint_Name" UNIQUE("Column1", "Column3", "Column2");

How can i Alter TABLE all column from not null to null by one query of PostgreSQL 9.1

I migrate my data from MySQL Database to PostgreSQL Database and by mistaken i have all my column set not-null in PostgreSQL database.
After this i am facing issue for inserting data and have to uncheck not null manually, so is there any way to do for all column in table ( except id(PRIMARY KEY) ).
i have this query also for single column but its also time consuming,
ALTER TABLE <table name> ALTER COLUMN <column name> DROP NOT NULL;
I don't think there is built in functionality for this. But it's easy to write a function to do this. For example:
CREATE OR REPLACE FUNCTION set_nullable(relation TEXT)
RETURNS VOID AS
$$
DECLARE
rec RECORD;
BEGIN
FOR rec IN (SELECT * FROM pg_attribute
WHERE attnotnull = TRUE AND attrelid=relation::regclass::oid AND attnum > 0 AND attname != 'id')
LOOP
EXECUTE format('ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL', relation, rec.attname);
RAISE NOTICE 'Set %.% nullable', relation, rec.attname;
END LOOP;
END
$$
LANGUAGE plpgsql;
Use it like this:
SELECT set_nullable('my_table');
Just perform a new CREATE TABLE noNULL using the same structure as your current table and modify the NULLable field you need.
Then insert from old table
INSERT INTO noNULL
SELECT *
FROM oldTable
then delete oldTable and rename noNull -> oldTable

postgresql inheritance / insert in parent then same record only in child

I am trying to insert in child table ONLY the same record that is already exsisting in the parent table.
Because ONLY is not aplicable i've tried
to create before insert trigger on parent table that checks for existing id(primary key) but it seems it doesn`t work -> the data are still duplicated:
Example:
parent table:
CREATE TABLE public.store(
id serial PRIMARY KEY,
name text);
child table :
CREATE TABLE public.db_store(
) INHERITS(store);
alter table public.db_store
add constraint db_store_pkey_id primary key (id);
function trigger:
create or replace function store_before_insert()
returns trigger language plpgsql as $$
declare old_s_id integer;
begin
old_s_id=null;
if (new.id is not null ) then
select s.id into old_s_id from store s where s.id=new.id;
if (old_s_id is not null) then
return null;
end if;
end if;
return new;
end $$;
trigger itself:
create trigger insert_storehouse_trg
before insert on storehouse
for each row
execute procedure storehouse_before_insert();
if execute:
insert into store(name) ('test');
insert into db_store(id,name) (1,'test');
(1, 'test') appers two time in store.
Is there are way to achive it without delete record from parent table?

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