Calling the column in call method in PostgreSQL - postgresql

Why is not working for executing the alter table in stored procedure? I tried everything but nothing is working
CREATE OR REPLACE PROCEDURE change_datatypes(columnname VARCHAR(255), datatype VARCHAR(255))
language plpgsql
as $$
BEGIN
EXECUTE
ALTER TABLE public.Sales_2019 ALTER COLUMN columnname TYPE datatype USING columnname :: datatype;
END $$;
CALL change_datatypes(Sales_2019.orderid, 'INTEGER') FROM Sales_2019;
UPDATED
CREATE OR REPLACE PROCEDURE change_datatypes(column_name VARCHAR(255), datatype VARCHAR(255))
language plpgsql
as $$
BEGIN
EXECUTE('ALTER TABLE Sales_2019 ALTER COLUMN'
|| quote_ident(column_name)
||'TYPE' || quote_literal(datatype) || 'USING '
||quote_ident(column_name) || '::'
||quote_literal(datatype));
END $$;
CALL change_datatypes(OrderID, INTEGER);

Related

Deleting a row with dynamic SQL in a procedure

How to delete a row in a stored procedure in PostgreSQL with dynamic table and column names?
delete_word will be the one that should be deleted.
CREATE OR REPLACE PROCEDURE delete_row(tablename VARCHAR(255),columnname VARCHAR(255), delete_word VARCHAR(255))
language plpgsql
as $$
BEGIN
EXECUTE 'DELETE FROM ' || quote_ident(tablename) || ' WHERE ' || columnname || ' = ' || quote_ident(delete_word);
END $$;
CALL delete_row('sales_2019','orderid', 'Order ID');
Handling dynamic SQL properly:
CREATE OR REPLACE PROCEDURE delete_row(tablename text, columnname text, delete_word text)
LANGUAGE plpgsql AS
$proc$
DECLARE
_sql text := format('DELETE FROM %I WHERE %I = $1', $1, $2);
BEGIN
-- RAISE NOTICE '%', _sql; -- debug first?!
EXECUTE _sql
USING delete_word;
END
$proc$;
Call:
CALL delete_row('sales_2019', 'orderid', 'Order ID');
See:
Format specifier for integer variables in format() for EXECUTE?
Table name as a PostgreSQL function parameter
How to use variable as table name in plpgsql
Dynamic SQL with format() and EXECUTE is only needed if table and column name must indeed be variable. The answer is a proof of concept for more complex tasks. A simple command like in the example, I would rather just concatenate and execute as plain SQL.
Also using data type text for arguments. varchar(255) does not do anything useful there, and generally tends to be a misunderstanding carried over from other RDBMS. See:
Refactor foreign key to fields

Create multiple stored procedure by running Postgresql script

I wanted to execute a Postgresql scripts file in my vm containing 3 stored procedure. But only the first one gets executed. Any workaround for this? Scripts file:
alter table ex add column if not exists json_column jsonb;
alter table im add column if not exists json_column jsonb;
alter table imag add column if not exists json_column jsonb;
create or replace procedure update_ex( //first stored procedure
js jsonb,
id bigint
)
language plpgsql
as $$
begin
update ex set json_column = js where ex_id=id;
end;$$ // error: SQL Error [42601]: ERROR: syntax error at or near "create"
create or replace procedure update_im( //second stored procedure
js jsonb,
id bigint
)
language plpgsql
as $$
begin
update im set json_column = js where im_set_id=id;
end;$$
create or replace procedure update_imag(
js jsonb,
id bigint
)
language plpgsql
as $$
begin
update imag set json_column = js where imag_id=id;
end;$$
You missing a ";" at the end.
end;$$;

Check to see if a record exists postgres function

I am attempting to create a function that will determine if a record exists for our applications developers to help simplify things.
CREATE FUNCTION records_exist(schema_name VARCHAR(255), table_name VARCHAR(255), field_name VARCHAR(255), field_value VARCHAR(255))
RETURNS BOOLEAN
LANGUAGE plpgsql
AS $$
DECLARE
_schema_name ALIAS FOR $1;
_table_name ALIAS FOR $2;
_field_name ALIAS FOR $3;
_field_value ALIAS FOR $4;
_sql_string VARCHAR(5000);
BEGIN
_sql_string= 'SELECT EXISTS(SELECT 1 FROM ' || _schema_name || '.' || _table_name || ' WHERE ' || _field_name || '=' || _field_value || ');';
RETURN BOOLEAN EXECUTE _sql_string;
--RETURN TABLE EXECUTE _sql_string; doesn't work
END
$$;
The following should work, but I keep getting ERROR: syntax error at or near "EXECUTE"
Please let me know the error of my ways.
also, your dynamic string is bad, better use:
select format('select exists from %I.%I where %I = %L',schema_name,table_name,field_name, field_value) into _sql_string;
also, you realize your _field_value has no check for data type?..
so in short, it could be smth similar to:
db=# CREATE or replace FUNCTION records_exist(schema_name VARCHAR(255), table_name VARCHAR(255), field_name VARCHAR(255), field_value VARCHAR(255))
RETURNS BOOLEAN
LANGUAGE plpgsql
AS $$
DECLARE
_sql text;
_b boolean;
BEGIN
_sql := format('select exists (select null from %I.%I where %I = %L)',schema_name,table_name,field_name, field_value);
execute _sql into _b;
return _b;
END
$$;
CREATE FUNCTION
Time: 10.680 ms
db=# select * from records_exist('pg_catalog','pg_database','datname','postgres'); records_exist
---------------
t
(1 row)
Time: 59.472 ms

PostgreSQL passing variable in function for select into

We are creating trigger before inserting in users relation that call below function.
In this function we are selecting from users depends on function argument and insert into a temp table for logging.
But result of execute select is null!
create or REPLACE function auth._role_policy_admin(_role text, _province text, _type user_group, _services json) returns void
LANGUAGE plpgsql
AS $$
DECLARE
_contract_id INTEGER;
_user_id INTEGER;
_test INTEGER;
BEGIN
if _type = 'customer' then
EXECUTE 'SELECT id FROM auth.users WHERE users.username = '' ' || _role || ' '' ' INTO _user_id;
INSERT INTO auth.logs(user_id, role) VALUES (_user_id, _role);
end if;
END;
$$;

multi table auditing trigger function

I want to keep all changes of my tables. I have a working solution for making a trigger per table, but it seems silly to copy the code foreach table. Is there any way to create a single trigger function that does this?
Example of my working per-table trigger (including table definitions):
CREATE TABLE departments (
id bigserial Primary Key,
name varchar not null,
created bigint not null default date_part('epoch', NOW()),
created_by bigint references Employees (id) not null
);
create table Departments_hist ("action" varchar not null, change_date bigint not null, rev bigserial not null, like Departments);
CREATE OR REPLACE FUNCTION add_to_history_Departments() RETURNS TRIGGER AS $$
BEGIN
IF(TG_OP='INSERT' OR TG_OP='UPDATE') THEN
INSERT INTO Departments_hist values (TG_OP,date_part('epoch', NOW()),DEFAULT,NEW.*);
END IF;
IF (TG_OP='DELETE') THEN
INSERT INTO Departments_hist values (TG_OP,date_part('epoch', NOW()),DEFAULT,OLD.*);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_history_Departments AFTER INSERT OR UPDATE OR DELETE ON Departments FOR EACH ROW EXECUTE PROCEDURE add_to_history_Departments();
I've tried to make it multi-table by concatenating '_hist' to TG_TABLE_NAME:
CREATE OR REPLACE FUNCTION add_to_hist_table() RETURNS TRIGGER AS $$
DECLARE
histTable text :=TG_TABLE_NAME || '_hist';
BEGIN
IF (TG_OP='INSERT' OR TG_OP='UPDATE') THEN
INSERT INTO histTable values (TG_OP,date_part('epoch', NOW()),DEFAULT,NEW.*);
ELSIF TG_OP='DELETE' THEN
INSERT INTO histTable values (TG_OP,date_part('epoch', NOW()),DEFAULT,OLD.*);
END IF;
RETURN null; --ignored since it is an AFTER triggger.
END;
$$ LANGUAGE plpgsql;
But I get an error:
ERROR: syntax error at or near "$1"
LINE 1: INSERT INTO $1 values ( $2 ,date_part('epoch', NOW()),DEFA...
^
QUERY: INSERT INTO $1 values ( $2 ,date_part('epoch', NOW()),DEFAULT, $3 .*)
CONTEXT: SQL statement in PL/PgSQL function "add_to_hist_table" near line 5
I guess it is a problem with variable substitution ( http://www.postgresql.org/docs/8.4/static/plpgsql-implementation.html ).
How can this functionality be achieved?
PS. I'm using postgresql 8.4 but will likely upgrade to 9.3 soon.
I found a solution on this "related question" https://stackoverflow.com/a/1997417/844731
I didn't think of doing 'EXECUTE USING' with NEW and OLD. So now a working solution is:
CREATE OR REPLACE FUNCTION add_to_hist_table() RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP='INSERT' OR TG_OP='UPDATE') THEN
execute 'INSERT INTO '|| TG_TABLE_NAME ||'_hist values (''' || TG_OP || ''',date_part(''epoch'', NOW()),DEFAULT,$1.*)' using NEW;
ELSIF TG_OP='DELETE' THEN
execute 'INSERT INTO '|| TG_TABLE_NAME ||'_hist values (''' || TG_OP || ''',date_part(''epoch'', NOW()),DEFAULT,$1.*)' using OLD;
END IF;
RETURN null; --ignored since it is an AFTER triggger.
END;
$$ LANGUAGE plpgsql;
#Pascal_dher, somebody can create table with name with containing attacker code. Due Postgresql this probably dosnt do some really bad, only failed queries. But if your trigger will be more complex, then impacts can be much more worse.