I'm unable to get a simple SQL function that run over rows of a tables and display it column info
Here how the SQL function looks like.
CREATE OR REPLACE FUNCTION iterators() RETURNS Void AS $$
DECLARE
t2_row call_records%ROWTYPE;
BEGIN
FOR t2_row IN (SELECT timestamp,plain_crn INTO call_records limit 2)
LOOP
RAISE NOTICE t2_row.timestamp;
END LOOP
END
$$ LANGUAGE plpgsql;
But I keep getting following error
ERROR: syntax error at or near "t2_row"
LINE 7: RAISE NOTICE t2_row.timestamp;
I'm not sure what possible syntax error the code has? Is it possible to get a bit more verbose error log or know as to what is the syntax error in code that I have to fix.
Statement RAISE requires format string. It should be trivial, but should be there.
RAISE NOTICE '%', t2_row.timestamp;
Related
I have a plpgsql Procedure where I am simply trying to handle any possible exceptions, since I will be running these Procedures on pg_cron (automated) and I do not want anything to fail. The basic skeleton of the procedure looks like this:
CREATE OR REPLACE PROCEDURE marketing_offers.stackover_overflow_ex_question(limit_size integer)
LANGUAGE plpgsql
AS
$procedure$
DECLARE
n_rec_cnt bigint;
BEGIN
LOOP
EXIT WHEN n_rec_cnt = 0;
WITH cte AS (SELECT *
FROM master_table mt
where mt.created_date <= '1999-01-01'::time
LIMIT limit_size)
INSERT INTO some_archive_table (SELECT * FROM cte)
COMMIT;
GET DIAGNOSTICS n_rec_cnt = row_count;
RAISE EXCEPTION 'Max retry count exceeded';
begin
EXCEPTION
WHEN OTHERS then
GET STACKED DIAGNOSTICS text_var1 = message_text,
text_var2 = PG_EXCEPTION_DETAIL,
text_var3 = PG_EXCEPTION_HINT;
RAISE NOTICE 'error msg is %', text_var1;
UPDATE job_log
SET error_msg = text_var1
return;
end;
END LOOP;
END;
$procedure$
;
The problem is the RAISE NOTICE with text_var1, which is supposed to hold the SQL exception message, never gets logged neither does the UPDATE statement to my job_log table that should hold the message also.
I also would like to add that , I had to surround the EXCEPTION block with another begin and end , because when I did not, I would get a syntax error.
I am simply trying to catch the exception from my SQL script - should I be using a different EXCEPTION type and should I be looking for specific SQL codes? I'm kind of confused what the best practices are here
You should really start indenting your code. This is not just about being pretty, but it would immediately show you the problem with your code.
Your code, properly indented, looks like this:
BEGIN
LOOP
EXIT WHEN n_rec_cnt = 0;
COMMIT;
RAISE EXCEPTION 'Max retry count exceeded';
begin
EXCEPTION
WHEN OTHERS then
RAISE NOTICE 'error msg is %', text_var1;
UPDATE job_log
SET error_msg = text_var1
return;
end;
END LOOP;
END;
There are two things obvious:
You forgot the semicolon in front of the RETURN → syntax error
The EXCEPTION clause is part of the block that starts with the BEGIN in the immediately preceding line.
Since an EXCEPTION clause will only catch exceptions thrown in the block to which it belongs, and that block is empty, execution can never reach the exception handler.
You are obviously fighting with the restriction that COMMIT cannot be executed inside a block with an EXCEPTION clause. But since it is not clear what you want to do (for example, the unconditional RAISE EXCEPTION seems pointless), it is difficult to help you.
I had the similar problem. when I removed commit and roll back from my code(I read postgres is auto commit) it worked fine. and also begin infront of exception is not neededI think.
I'm getting a syntax error but having issues identifying what exactly the issue is. I'm not sure if I'm misunderstanding things here -- but it looks like the INSERT INTO is breaking, but only when it's in the function.
I'm running the below through Hasura:
CREATE OR REPLACE FUNCTION custom_q_into_selected()
RETURNS trigger AS $BODY$
BEGIN
IF NEW.is_custom = true THEN
INSERT INTO selected_question(book_id,question_id)
VALUES(NEW.custom_for_book_id,NEW.id);
END IF;
RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql;
Keep getting this error when I run this:
postgres-error: syntax error at or near "\"
Database logs:
CREATE OR REPLACE FUNCTION custom_q_into_selected() \r
RETURNS trigger AS $BODY$\r
BEGIN\r
IF NEW.is_custom THEN \r
INSERT INTO selected_question(book_id,question_id) VALUES(NEW.custom_for_book_id,NEW.id);\r
END IF;\r
RETURN NEW;\r
END;\r
$BODY$ LANGUAGE plpgsql;\r
Any help is appreciated.
When i execute the function below (in the picture), i can't find the raise message anywhere even in execution logs: is there anyway to make it appear.
script:
SELECT helloworld('myname');
CREATE OR REPLACE FUNCTION helloWorld(name text) RETURNS void AS $helloWorld$
DECLARE
BEGIN
RAISE LOG 'Hello, %', name;
END;
$helloWorld$ LANGUAGE plpgsql;
Messages with the level LOG typicall don't get sent to the client.
Either use RAISE NOTICE or set client_min_messages to log.
also dont do a basic mistake like me that to see the raise notice messages inside function, you first have to call that function by select public.helloworld()
I am struggling to figure out how to best handle the return of results or errors to my application from Postgres stored functions.
Consider the following contrived psudeocode example:
app.get_resource(_username text)
RETURNS <???>
BEGIN
IF ([ ..user exists.. ] = FALSE) THEN
RETURN 'ERR_USER_NOT_FOUND';
END IF;
IF ([ ..user has permission.. ] = FALSE) THEN
RETURN 'ERR_NO_PERMISSION';
END IF;
-- Return the full user object.
RETURN QUERY( SELECT 1
FROM app.resources
WHERE app.resources.owner = _username);
END
The function can fail with a specific error or succeed and return 0 or more resources.
At first I tried creating a custom type to always use as a standard return type in eachh function:
CREATE TYPE app.appresult AS (
success boolean,
error text,
result anyelement
);
Postgres does not allow this however:
[42P16] ERROR: column "result" has pseudo-type anyelement
I then discovered OUT parameters and attempted the following uses:
CREATE OR REPLACE FUNCTION app.get_resource(
IN _username text,
OUT _result app.appresult -- Custom type
-- {success bool, error text}
)
RETURNS SETOF record
AS
$$
BEGIN
IF 1 = 1 THEN -- just a test
_result.success = false;
_result.error = 'ERROR_ERROR';
RETURN NULL;
END IF;
RETURN QUERY(SELECT * FROM app.resources);
END;
$$
LANGUAGE 'plpgsql' VOLATILE;
Postgres doesn't like this either:
[42P13] ERROR: function result type must be app.appresult because of OUT parameters
Also tried a similar function but reversed: Returning a custom app.appresult object and setting the OUT param to "SETOF RECORD". This was also not allowed.
Lastly i looked into Postgres exception handling using
RAISE EXCEPTION 'ERR_MY_ERROR';
So in the example function, i'd just raise this error and return.
This resulted in the driver sending back the error as:
"ERROR: ERR_MY_ERROR\nCONTEXT: PL/pgSQL function app.test(text) line 6 at RAISE\n(P0001)"
This is easy enough to parse but doing things this way feels wrong.
What is the best way to solve this problem?
Is it possible to have a custom AppResult object that i could return?
Something like:
{ success bool, error text, result <whatever type> }
//Edit 1 //
I think I'm leaning more towards #Laurenz Albe solution.
My main goal is simple: Call a stored procedure which can return either an error or some data.
Using RAISE seems to accomplish this and the C++ driver allows easy checking for an error condition returned from a query.
if ([error code returned from the query] == 90100)
{
// 1. Parse out my overly verbose error from the raw driver
// error string.
// 2. Handle the error.
}
I'm also wondering about using custom SQLSTATE codes instead of parsing the driver string.
Throwing '__404' might mean that during the course of my SPs execution, it could not continue because some record needed was not found.
When calling the sql function from my app, i have a general idea of what it failing with a '__404' would mean and how to handle it. This avoids the additional step of parsing driver error string.
I can also see the potential of this being a bad idea.
Bedtime reading:
https://www.postgresql.org/docs/current/static/errcodes-appendix.html
This is slightly opinion based, but I think that throwing an error is the best and most elegant solution. That is what errors are for!
To distinguish various error messages, you could use SQLSTATEs that start with 6, 8 or 9 (these are not used), then you don't have to depend on the wording of the error message.
You can raise such an error with
RAISE EXCEPTION SQLSTATE '90001' USING MESSAGE = 'my own error';
We do something similar to what you're trying to do, but we use TEXT rather than ANYELEMENT, because (almost?) any type can be cast to TEXT and back. So our type looks something like:
(errors our_error_type[], result TEXT)
The function which returns this stores errors in the errors array (it's just some custom error type), and can store the result (cast to text) in the result field.
The calling function knows what type it expects, so it can first check the errors array to see if any errors were returned, and if not it can cast the result value to the expected return type.
As a general observation, I think exceptions are more elegant (possibly because I come from a c# background). The only problem is in plpgsql exception handling is (relatively) slow, so it depends on the context - if you're running something many times in a loop, I would prefer a solution that doesn't use exception handling; if it's a single call, and/or especially when you want it to abort, I prefer raising an exception. In practice we use both at various points throughout our call stacks.
And as Laurenz Albe pointed out, you're not meant to "parse" exceptions, so much as raise an exception with specific values in specific fields, which the function that catches the exception can then extract and act on directly.
As an example:
Setup:
CREATE TABLE my_table (id INTEGER, txt TEXT);
INSERT INTO my_table VALUES (1,'blah');
CREATE TYPE my_type AS (result TEXT);
CREATE OR REPLACE FUNCTION my_func()
RETURNS my_type AS
$BODY$
DECLARE
m my_type;
BEGIN
SELECT my_table::TEXT
INTO m.result
FROM my_table;
RETURN m;
END
$BODY$
LANGUAGE plpgsql STABLE;
Run:
SELECT (m.result::my_table).*
FROM my_func() AS m
Result:
| id | txt |
-------------
| 1 | blah |
I've seen the answer to this question in a couple posts. However when I the below which was an answer coming from another post, I get an error. My objective is to simply write comments to the screen as I execute DML commands in a script. However I have not found a simple way to do this.
CREATE OR REPLACE FUNCTION raise_exception(text)
RETURNS void AS $$
BEGIN
RAISE EXCEPTION '%', $1;
END;
$$ LANGUAGE plpgsql;
Called like this: select * from sp_send_msg('go for it');
I would like to be able to do something like this:
SELECT send_comment('Writing widgets to temporary table');
SELECT * INTO t_widgets FROM widgets;
SELECT send_comment('Writing temporary table into new widget table');
INSERT INTO new_widgets
SELECT * FROM t_widgets;
Thanks in advance for any helpful guidance on this. I'm running PostgreSQL 8.4.7.
You should raise notice instead of raise exception. The former will show a message and the latter is treated as an error and will abort your transaction.