Is there an equivalent (or workaround) for the RAISE EXCEPTION statement for the function written below in LANGUAGE sql?
CREATE OR REPLACE FUNCTION fn_interpolation (p_yearinteger integer,
p_admin_id integer, p_crop_id integer, p_cropparameter integer)
RETURNS TABLE (value double precision, remark text)
AS $$
WITH
yearvalues AS (SELECT yearinteger, value FROM cropvalues WHERE crops_id =
p_crop_id AND admin_id = p_admin_id AND parameter_id = p_cropparameter),
I need the function to abort and to RETURN an error message if the arguments entered into the function do not exist. e.g. IF parameter_id != p_cropparameter THEN RAISE EXCEPTION ‘invalid cropparameter’ END IF
Just define a trivial wrapper function.
CREATE OR REPLACE FUNCTION raise_exception(text) RETURNS text AS $$
BEGIN
RAISE EXCEPTION '%',$1;
END;
$$ LANGUAGE plpgsql VOLATILE;
then use CASE:
SELECT CASE
WHEN parameter_id != p_cropparameter
THEN raise_exception("blah")
ELSE parameter_id
END;
This only works if the CASE otherwise returns text though, e.g. if parameter_id is integer you get:
regress=> SELECT CASE WHEN 1 = 2 THEN raise_exception('blah') ELSE 1 END;
ERROR: CASE types integer and text cannot be matched
LINE 1: SELECT CASE WHEN 1 = 2 THEN raise_exception('blah') ELSE 1 E...
You can work around this with a hack using polymorphic functions. Define:
CREATE OR REPLACE FUNCTION raise_exception(anyelement, text) RETURNS anyelement AS $$
BEGIN
RAISE EXCEPTION '%',$2;
RETURN $1;
END;
$$ LANGUAGE plpgsql VOLATILE;
then pass a fake value of the case type to it so PostgreSQL type-matches it correctly, e.g.
SELECT CASE WHEN 1 = 1 THEN raise_exception(0, 'blah') ELSE 1 END;
or
SELECT CASE WHEN 1 = 1 THEN raise_exception(NULL::integer, 'blah') ELSE 1 END;
All seem too hard? That's because really, this sort of thing is usually just better done in PL/PgSQL.
raise_exception.sql
Function to throwing an error for unhandled/impossible value.
Uses in SQL language.
Wrapper for RAISE command with EXCEPTION level in PL/pgSQL language.
Tests and using examples are included.
Related
I am creating a function that allow me to conditionally update specific columns in a table. However, I get an error indicating that there is a syntax error at or near "IF" when I try to run the following code. I'm a bit new to Postgres so it's quite possible. I can't understand some concept/syntax thing in Postgres. Can someone help me by pointing out the mistake I must be making?
CREATE OR REPLACE FUNCTION profiles.do_something(
p_id UUID,
p_condition1 BOOLEAN,
p_condition2 BOOLEAN,
p_condition3 BOOLEAN
)
RETURNS void AS $$
BEGIN
IF p_condition1 IS TRUE THEN
UPDATE tablename SET column1 = null WHERE member_id = p_id;
END IF;
IF p_condition2 IS TRUE THEN
UPDATE tablename SET column2 = null WHERE member_id = p_id;
END IF;
IF p_condition3 IS TRUE THEN
UPDATE tablename SET column3 = null WHERE member_id = p_id;
END IF;
END;
$$ LANGUAGE 'sql';
tl;dr $$ LANGUAGE 'plpgsql'
$$ LANGUAGE 'sql';
^^^^^
You're tell it to parse the body of the function as sql. In SQL, begin is a statement which starts a transaction.
create or replace function test1()
returns void
language sql
as $$
-- In SQL, begin starts a transaction.
-- note the ; to end the statement.
begin;
-- Do some valid SQL.
select 1;
-- In SQL, end ends the transaction.
end;
$$;
In SQL you wrote begin if ... which is a syntax error.
The language you're using is plpgsql. In plpgsql, begin is a keyword which starts a block.
create or replace function test1()
returns void
language plpgsql
as $$
-- In PL/pgSQL, begin starts a block
-- note the lack of ;
begin
-- Do some valid SQL.
select 1;
-- In PL/pgSQL, end ends the block
end;
$$;
I need to check whether the given text is numeric or not from the
function.
Creating function for isnumeric():
CREATE OR REPLACE FUNCTION isnumeric(text) RETURNS BOOLEAN AS $$
DECLARE x NUMERIC;
BEGIN
x = $1::NUMERIC;
RETURN TRUE;
EXCEPTION WHEN others THEN
RETURN FALSE;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
Function from which I am calling the isnumeric() function:
create or replace function tm(var text)
returns varchar as
$$
begin
if (select isnumeric(var))=t::BOOLEAN then
raise info 'Is numeric value';
else
raise info 'Not numeric';
end if;
end;
$$
language plpgsql;
Calling functon:
select tm('1');
Getting an error:
Here is the error details:
ERROR: column "t" does not exist
LINE 1: SELECT (select isnumeric(var))=t::BOOLEAN
You don't need a select (and it's actually wrong, as the error indicates) - just call isnumeric directly.
Also, by the way, your function is missing a return statement.
To sum it all up:
create or replace function tm(var text)
returns varchar as
$$
begin
if (isnumeric(var)) then -- call isnumeric directly
raise info 'Is numeric value';
else
raise info 'Not numeric';
end if;
return '0'; -- missing return value in the OP
end;
$$
language plpgsql;
this will help you to identify your field is numeric or not:
select * from Table where field_name ~ '^[0-9]*$'
for decimal values you can use^[0-9.]*$ instead ^[0-9]*$
select getDataType('2021'); == Number
select getDataType('2021-05-12 23:12:10'); == Date
select getDataType('2021-05-12'); == Date
select getDataType('2X'); == String
CREATE
OR REPLACE FUNCTION getDataType ( TEXT ) RETURNS TEXT AS $$ DECLARE
x VARCHAR;
BEGIN
x = $1 :: NUMERIC;
RETURN 'Number';
EXCEPTION
WHEN OTHERS THEN
BEGIN
x = $1 :: DATE;
RETURN 'Date';
EXCEPTION
WHEN OTHERS THEN
RETURN 'String';
END;
END;
$$ STRICT LANGUAGE plpgsql IMMUTABLE;
Is there an equivalent (or workaround) for the RAISE EXCEPTION statement for the function written below in LANGUAGE sql?
CREATE OR REPLACE FUNCTION fn_interpolation (p_yearinteger integer,
p_admin_id integer, p_crop_id integer, p_cropparameter integer)
RETURNS TABLE (value double precision, remark text)
AS $$
WITH
yearvalues AS (SELECT yearinteger, value FROM cropvalues WHERE crops_id =
p_crop_id AND admin_id = p_admin_id AND parameter_id = p_cropparameter),
I need the function to abort and to RETURN an error message if the arguments entered into the function do not exist. e.g. IF parameter_id != p_cropparameter THEN RAISE EXCEPTION ‘invalid cropparameter’ END IF
Just define a trivial wrapper function.
CREATE OR REPLACE FUNCTION raise_exception(text) RETURNS text AS $$
BEGIN
RAISE EXCEPTION '%',$1;
END;
$$ LANGUAGE plpgsql VOLATILE;
then use CASE:
SELECT CASE
WHEN parameter_id != p_cropparameter
THEN raise_exception("blah")
ELSE parameter_id
END;
This only works if the CASE otherwise returns text though, e.g. if parameter_id is integer you get:
regress=> SELECT CASE WHEN 1 = 2 THEN raise_exception('blah') ELSE 1 END;
ERROR: CASE types integer and text cannot be matched
LINE 1: SELECT CASE WHEN 1 = 2 THEN raise_exception('blah') ELSE 1 E...
You can work around this with a hack using polymorphic functions. Define:
CREATE OR REPLACE FUNCTION raise_exception(anyelement, text) RETURNS anyelement AS $$
BEGIN
RAISE EXCEPTION '%',$2;
RETURN $1;
END;
$$ LANGUAGE plpgsql VOLATILE;
then pass a fake value of the case type to it so PostgreSQL type-matches it correctly, e.g.
SELECT CASE WHEN 1 = 1 THEN raise_exception(0, 'blah') ELSE 1 END;
or
SELECT CASE WHEN 1 = 1 THEN raise_exception(NULL::integer, 'blah') ELSE 1 END;
All seem too hard? That's because really, this sort of thing is usually just better done in PL/PgSQL.
raise_exception.sql
Function to throwing an error for unhandled/impossible value.
Uses in SQL language.
Wrapper for RAISE command with EXCEPTION level in PL/pgSQL language.
Tests and using examples are included.
I am using PostgreSQL 9.3, working with plpgsql functions:
CREATE TEMP TABLE uuid_inside (
id uuid PRIMARY KEY
);
CREATE FUNCTION uuid_test_func(id uuid)
RETURNS uuid_inside AS
$$
DECLARE
res_uuid_inside uuid_inside;
BEGIN
IF (id = '00000000-0000-0000-0000-000000000001'::uuid) THEN
SELECT uuid_test_func('00000000-0000-0000-0000-000000000000'::uuid)
INTO res_uuid_inside;
RETURN res_uuid_inside;
END IF;
res_uuid_inside.id := id;
RETURN res_uuid_inside;
END;
$$
LANGUAGE plpgsql;
Call:
SELECT uuid_test_func('00000000-0000-0000-0000-000000000001'::uuid);
Message:
ERROR: invalid input syntax for uuid: "(00000000-0000-0000-0000-000000000000)"
SQL-state: 22P02
But this works fine:
SELECT uuid_test_func('00000000-0000-0000-0000-000000000002'::uuid);
The problem is not with recursive function calling - the original code is refering to some other function inside.
Simple function
The recursion in your function seems pointless. This simple sql function does the job (without nesting and without the composite type wrapper):
CREATE FUNCTION uuid_test_func(id uuid)
RETURNS uuid AS
$func$
SELECT CASE WHEN $1 = '00000000-0000-0000-0000-000000000001'::uuid
THEN '00000000-0000-0000-0000-000000000000'::uuid
ELSE $1
END
$func$ LANGUAGE plpgsql;
But that's probably just due to simplification for the demo.
Address problem in original
As for the error message. You are running into a confusing "feature" of PL/pgSQL:
Composite type assignment expects one column per type column. Assigning composite types as a whole is not an option.
This has nothing to do with the UUID type per se.
This modified version would work:
CREATE OR REPLACE FUNCTION uuid_test_func(id uuid)
RETURNS uuid_inside AS
$func$
DECLARE
res_uuid_inside uuid_inside;
BEGIN
IF (id = '00000000-0000-0000-0000-000000000001'::uuid) THEN
SELECT * FROM uuid_test_func('00000000-0000-0000-0000-000000000000'::uuid)
INTO res_uuid_inside.id;
-- INTO res_uuid_inside; -- would work, too - 1st col is assigned
RETURN res_uuid_inside;
END IF;
res_uuid_inside.id := id;
RETURN res_uuid_inside;
END
$func$ LANGUAGE plpgsql;
Closely related question:
Passing array of a composite type to stored procedure
Simpler function
That said, I'd suggest this simplified form (keeping the recursion and the composite result):
CREATE OR REPLACE FUNCTION uuid_test_func(id uuid)
RETURNS uuid_inside AS
$func$
BEGIN
IF (id = '00000000-0000-0000-0000-000000000001'::uuid) THEN
RETURN (SELECT f FROM uuid_test_func('00000000-0000-0000-0000-000000000000'::uuid) f);
ELSE
RETURN row(id)::uuid_inside;
END IF;
END
$func$ LANGUAGE plpgsql;
I am new to postgres and trying to setup a function that returns a bit.
I keep getting the error
Function's final statement must be SELECT or INSERT/UPDATE/DELETE
RETURNING.
I understand that
Unless the function is declared to return void, the last statement must be a SELECT, or an INSERT, UPDATE, or DELETE that has a RETURNING clause.
here is the code
CREATE OR REPLACE FUNCTION "f"(...)
RETURNS bit AS
DO $$
Begin
IF someStuff
THEN
0; //also tried select 0 //also tried return 0
ELSE
1; //also tried select 1 //also tried return 0
END IF;
0; //also tried select 0 //also tried return 0
END $$
Where am I going wrong with the syntax?
There are several errors:
the DO is wrong in a function definition
you are missing the specification of the language
in PL/pgSQL you use return to return the function's result
So your function becomes:
CREATE OR REPLACE FUNCTION f(some_value integer)
RETURNS bit AS
$$
Begin
IF (some_value = 1)
THEN
return 0;
ELSE
return 1;
END IF;
END $$
language plpgsql
But you should use boolean instead of bit to return true/false flags:
CREATE OR REPLACE FUNCTION f(some_value integer)
RETURNS boolean AS
$$
Begin
IF (some_value = 1)
THEN
return false;
ELSE
return true;
END IF;
END $$
language plpgsql
If you want to use plpgsql then do as in the a_horse's answer but if you don't need plpgsql do in sql:
create or replace function f(some_value integer)
returns boolean as $$
select some_value = 1;
$$
language sql;
If the function is the one from this question then this will do it:
create or replace function isPersonQualifiedForJob(pid integer, jid)
returns boolean as $$
select exists (
select 1
from
getskillsforjob(jid) j
inner join
getskillsforperson(pid) p on j.skillid = p.skillid
)
$$
language sql;
Checking for exists is much faster then counting since it is enough to find the first match.