I have this function
DO
$do$
BEGIN
IF EXISTS (SELECT FROM pg_database WHERE name = 'MydB') THEN
RAISE NOTICE 'db already created';
ELSE
CREATE DATABASE MydB;
END IF;
END
$do$;
However when this runs I get the error:
CREATE DATABASE cannot be executed from a function
can anyone recommend a good way around this?
You cannot run CREATE DATABASE inside a transaction, so you cannot run it in a function.
Run it without using the DO statement, and if you get an error with SQLSTATE 42P04, ignore it – it means that the database already exists.
Related
I am working with PostgreSQL with DBeaver. I would like for test purposes to make a procedure call in a transaction; then to Rollback it. I have written the following script:
DO $$
BEGIN
call MyProcedure();
ROLLBACK $$;
I have tried to run it in DBeaver console. I have got the following error:
SQL Error [42601]: ERROR: syntax error at end of input
It pointed out to the final $$.
What is wrong; and how could I reach my goal: to make a procedure call in a transaction; then to Rollback it?
For the DO block, you are missing the end
DO $$
BEGIN
RAISE NOTICE 'test';
ROLLBACK;
END $$;
But since you want to display a table content, you can't use an anonymous block but instead just start/end the transaction.
BEGIN;
select * from test;
call my_procedure();
select * from test;
ROLLBACK;
I'm new to Postgres, but with experience from Oracle. Trying to create a stored procedure which is going to:
Insert a row
Handle exceptions and in case of an exception insert a row into a log table by calling dedicated procedure
Emit an audit log record into a log table in case the whole procedure ran successfully
By pseudo code:
CREATE OR REPLACE PROCEDURE test.p_insert(IN p_test_param character varying)
LANGUAGE 'plpgsql'
SECURITY DEFINER
AS $BODY$
DECLARE
-- some declarations
BEGIN
BEGIN
INSERT INTO test.a(a) VALUES (p_test_param);
EXCEPTION
WHEN OTHERS THEN
-- GET STACKED DIAGNOSTICS
CALL test.p_insert_log(...); -- Inserts a row into a log table, another COMMIT may be required?
RAISE;
END;
COMMIT; -- CAN'T DO
BEGIN
IF (SELECT test.f_debug()) THEN
CALL test.p_insert_log(...); -- Audit the execution
END IF;
END;
COMMIT; -- CAN'T DO EITHER
END;
$$BODY$$;
However when I try to test the procedure out from an anonymous block in PgAdmin such as:
BEGIN;
DO
LANGUAGE plpgsql
$$
BEGIN
CALL test.p_insert(
p_test_param => 'test'
);
END;
$$
I'm getting an error ERROR: invalid transaction termination. How can I get rid of it? My objective is to let the procedure carry out the transaction control, I don't want the caller to COMMIT or ROLLBACK anything. If I remove both COMMIT commands from the code of the procedure, it executes well, however the invoker must explicitly COMMIT or REVOKE the transaction afterwards, which is not desired. In Oracle the pseudo code with COMMIT statements would work, in Postgres it doesn't seem to work as I would like to. Could you please help me out? Thanks
Your code will work as intended. Perhaps you made some mistake in calling the code:
you cannot call the procedure from a function
you cannot call the procedure in an explicitly started transaction:
BEGIN;
CALL p_insert('something); -- will fail
COMMIT;
I have a table in Redshift (let's call it a status table) where I set the status of tables which I want to truncate. I created a Redshift Stored Procedure in order to achieve that. Here is my code for the SP:
CREATE OR REPLACE PROCEDURE <schema>.truncate_table()
AS $$
DECLARE
v_tpsl RECORD;
exec_statement VARCHAR(256);
BEGIN
FOR v_tpsl in SELECT * from <schama>.tablename_process_status_log WHERE status = 'TRUE' LOOP
exec_statement = 'TRUNCATE TABLE <schema>.' + quote_ident(v_tpsl.staging_table_name) + '_test;';
RAISE INFO 'statement = %', exec_statement;
EXECUTE exec_statement;
END LOOP;
END;
$$
LANGUAGE plpgsql;
Now when I am CALLING the Stored Procedure, I am getting this error:
SQL Error [500310] [34000]: [Amazon](500310) Invalid operation: cursor does not exist;
I looked at the documentation of the SP to check if Truncate is possible or not. By looking at the examples, it looks like it's possible.
I am not sure what is going wrong in this. I am using RedshiftJDBC42-no-awssdk-1.2.34.1058.jar and connecting via DBeaver.
It looks like I have found the answer. According to this, Any cursor that is open (explicitly or implicitly) is closed automatically when a COMMIT, ROLLBACK, or TRUNCATE statement is processed. In my next iteration of the loop, it's trying to accessing the cursor which is already closed.
I'm trying to set up a trigger in postgresql 9.6 that will use dblink to insert a row into another database when a row is inserted into its own table. Since there can be a large volume of these inserts, I don't want to connect and disconnect to the database for every insertion, so I would prefer to have a persistent connection that gets used by every insert. But I think I would then have to test if the connection is available since a number of things could cause the connection to drop.
My pseudo-code is as follows:
-- 1. test if named dblink connection already exists
-- 2. if it does not, create a named dblink connection
-- 3. insert data via dblink
What I have so far is like this:
CREATE OR REPLACE FUNCTION db_link_trigger()
RETURNS trigger AS
$BODY$
BEGIN
-- 1. test if named dblink connection already exists
IF (SELECT COALESCE('dblinktest' = ANY (dblink_get_connections()), false)) = false THEN
-- 2. if it does not, create a named dblink connection
RAISE NOTICE 'dblink connection not established. Connecting now';
PERFORM dblink_connect('dblinktest', 'hostaddr=192.168.1.30 port=5433 dbname=otherdb user=myuser password=mypassword');
ELSE
RAISE NOTICE 'dblink connection already established';
END IF;
-- 3. insert data via dblink
PERFORM dblink_exec('dblinktest', 'insert into mytable(data) values(''' || NEW.data || ''');');
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
My main concerns are how to handle errors and how to handle near-simultaneous invocation of the trigger. Consider if two INSERTS come in at the same time, and there is no previous dblink named 'dblinktest'. When the first sees that the connection does not exist, it proceeds to set it up. Then the second could see that the link is also not established and try to connect itself, but it will fail since the first will connect before it, and it will raise an error as:
ERROR: duplicate connection name
How can I handle an error like that? Does postgresql have something like this python-inspired pseudo-code?
try:
if dblink connection is not established:
establish dblink connection
except 'ERROR: duplicate connection name':
pass # do nothing
finally:
insert row into other db via dblink connection
You can get the error code from this link https://www.postgresql.org/docs/10/errcodes-appendix.html i this case is:
42710 -> duplicate_object
and manage this error with PL/pgSQL BEGIN block with an EXCEPTION clause
for example:
CREATE OR REPLACE FUNCTION db_link_function()
RETURNS void AS
$BODY$
declare
rec record;
BEGIN
BEGIN
PERFORM dblink_connect('dblinktest', 'hostaddr=127.0.0.1 port=5435 dbname=dell user=postgres password=password');
--the EXCEPTION
EXCEPTION
WHEN duplicate_object THEN --code error 42710
RAISE NOTICE 'this connections exists';
END;
--select data via dblink
SELECT * FROM dblink('dblinktest','SELECT * FROM categories where category=1') AS t(cid int, cname text) into rec;
raise notice 'value of rec: %,%', rec.cid, rec.cname;
END;
$BODY$
LANGUAGE plpgsql;
I suggest using FDW instead of dblink https://www.postgresql.org/docs/10/postgres-fdw.html
With SQL Server, I can execute code ad hoc T-SQL code with full procedural logic through SQL Server Management Studio, or any other client. I've begun working with PostgreSQL and have run into a bit of a difference in that PGSQL requires any logic to be embedded in a function.
Is there a way to execute PL/PGSQL code without creating an executing a function?
Postgres 9
DO $$
-- declare
BEGIN
/* pl/pgsql here */
END $$;
No, not yet. Version 9.0 (still alpha) will have this option (do), you a have to wait until it's released.
I struggled to get this working because it's fairly strict about adding semi colons in exactly the right places. But once you get used to that it works well. Besides the inability to return records of course, however you can raise notices & exceptions and do the other workarounds like using temp tables as #ErwinBrandstetter pointed out in a comment above.
e.g.:
DO
$$
BEGIN
IF EXISTS(SELECT 'any rows?'
FROM {your_table}
WHERE {your_column} = 'blah')
THEN
RAISE NOTICE 'record exists';
ELSE
RAISE EXCEPTION 'record does not exist';
END IF;
DROP TABLE IF EXISTS foo;
CREATE TEMP TABLE foo AS
SELECT 'bar'::character varying(5) as baz;
END
$$;
SELECT * FROM foo;