problems with writing function in db 2 - db2

I am trying to write a function using modifies sql data.
Here is my code:
create function n1n2n3ToCityName()
returns table (cityname varchar(40),name1 varchar(40),name2
varchar(40),name3 varchar(40))
modifies sql data
language sql
begin
declare tempCityName varchar(40);
end;
When I try to compile this an error occurs:
[42613][-628] Multiple or conflicting keywords involving the "MODIFIES SQL DATA" clause are present.. SQLCODE=-628, SQLSTATE=42613, DRIVER=4.26.14
How can I fix it?

Table functions that modify SQL data can only be inlined, as stated in the manual (see note 4). Inlined functions are characterised by BEGIN ATOMIC.
Replace begin with begin atomic.

Related

%rowtype in nested `declare...begin...end`

inside an anonymous block, I have nested a declare...begin...end. Inner block cannot "see" a temp table created in outer block. What is best way to solve, still maintaining creating of temp table inside?
The below FAILs with: CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 152
do
$$
declare
xyz...
being
create temp table x ... <depending on xyz>
...
declare
r x%rowtype -> FAIL
begin
...
end;
end;
$$
It cannot to work. This error is not related to block scopes, but it is related to timing. The temp table is created in runtime, but the x%rowtype is evaluated in validation time (before runtime). There is very simple and robust solution in Postgres. Use record type:
declare r record;
begin
The variable of record type takes real composite type in assign time.
Although PL/pgSQL is similar to PL/SQL, inside it is absolutely different technology, and not all patterns from Oracle are possible or are effective. And sometimes there are different (and sometimes much more comfortable) ways.

execute immediate errors

I'm using Amazon RedShift SQL (used to use Oracle many years ago) and am getting constant syntax errors every way that I try EXECUTE IMMEDIATE. I have even copied and posted code from PostgreSQL documentation pages (e.g. using sprintf) but to no avail. I reckon the below should work...?
declare
stm varchar(200);
begin
stm = 'GRANT SELECT, INSERT, TRIGGER, UPDATE, DELETE, REFERENCES, RULE ON public.angus TO adcd;';
execute immediate stm;
end;
Amazon Invalid operation: syntax error at or near "varchar"
There are two problems:
You use EXECUTE IMMEDIATE, which you cannot have found in the PostgreSQL documentation, since it is Oracle syntax. The PL/pgSQL statement is EXECUTE.
You are trying to execute PL/pgSQL code as SQL statement, which won't work.
Use a DO statement:
DO $$DECLARE
...
END;$$;
Syntax for a standalone anonymous block slightly different, also the code is actually submitted as a string. See Postgres DO and search Dollar Quoting. Try
Do $$
declare
stm varchar(200);
begin
stm = 'GRANT SELECT, INSERT, TRIGGER, UPDATE, DELETE, REFERENCES, RULE ON public.angus TO adcd;';
execute immediate stm;
end;
$$

How to use DO in postgres

I am attempting to get a better understanding of the DO command in postgreSQL 9.1
I have following code block,
DO
$do$
BEGIN
IF 1=1 THEN
SELECT 'foo';
ELSE
SELECT 'bar';
END IF;
END
$do$
However it returns the following error:
[42601] ERROR: query has no destination for result data Hint: If you want to discard the results of a SELECT, use PERFORM instead. Where: PL/pgSQL function "inline_code_block" line 4 at SQL statement
PostgreSQL DO command creates and executes some specific short life function. This function has not any interface, and then it cannot to return any output other then changes data in tables and some debug output.
Your example has more than one issues:
Only PostgreSQL table functions can returns some tabular data. But the mechanism is significantly different than MSSQL. PostgreSQL user's should to use RETURN NEXT or RETURN QUERY commands.
CREATE OR REPLACE FUNCTION foo(a int)
RETURNS TABLE(b int, c int) AS $$
BEGIN
RETURN QUERY SELECT i, i + 1 FROM generate_series(1,a) g(i);
END;
$$ LANGUAGE plpgsql;
SELECT * FROM foo(10);
DO anonymous functions are not table functions - so no output is allowed.
PostgreSQL 9.1 is not supported version, please upgrade
If you have some experience only from MSSQL, then forget it. A concept of stored procedures of PostgreSQL is very similar to Oracle or DB2, and it is significantly different to MSSQL does. T-SQL integrates procedural and SQL constructs to one set. Oracle, PostgreSQL, ... procedural functionality can embedded SQL, but procedural functionality is not integrated to SQL.
Please, read PostgreSQL PLpgSQL documentation for better imagine about this technology.

Temp table for user defined functions

I am trying to use temp table in user defined function for DB2. I am trying to do this in data studio but the code below is not working. How can I make this work?
Thanks.
CREATE FUNCTION BELSIZE.TEST (aSTRING VARCHAR(50))
RETURNS TABLE(
column1 INTEGER
)
F1: BEGIN ATOMIC
DECLARE c1 CURSOR WITH RETURN TO CLIENT FOR stmt;
SET v_dynStmt = 'SELECT 1 column1 from sysibm.sysdummy1';
PREPARE stmt FROM v_dynStmt;
OPEN c1;
RETURN
END
You have syntax errors in your code, more details below.
Apart from syntax errors, your title mentions temp table but your code does not, so your question is poor.
Never write "...is not working", instead write the exact SQLCODE and SQLSTATE and message that you see.
When asking for help with Db2, always write in the question the Db2-version and the operating-system (Z/OS, i-Series, Linux/Unix/Windows) on which the Db2-server runs, because the answer can depend on those facts. Different versions of Db2 for different operating systems have different capabilities and different syntax.
If you want to use cursors for result-sets then use SQL PL stored-procedures, because there are fewer restrictions.
SQL table functions are suitable when you don't need to declare a cursor for result-set.
Db2-LUW prevents you from declaring a cursor in an SQL table function when you use BEGIN ATOMIC.
If you are not using BEGIN ATOMIC, Db2-LUW (current versions, i.e. v11.1) lets you declare a cursor in an SQL UDF but you cannot use that cursor directly to return the result-set, as you can do inside SQL PL stored procedures.
For your example, the syntax below is valid and also useless, so consider using an SQL PL procedure instead:
--#SET TERMINATOR #
CREATE or replace FUNCTION BELSIZE.TEST (aSTRING VARCHAR(50))
RETURNS TABLE( column1 INTEGER)
language sql
specific belsize.test
BEGIN atomic
RETURN select 1 as column1 from sysibm.sysdummy1 ;
END
#
select * from table(belsize.test('a')) as t
#

How to do variable substitution in plpgsql?

I've got a bit of complex sql code that I'm converting from MSSql to Postgres (using Entity Framework Core 2.1), to deal with potential race conditions when inserting to a table with a unique index. Here's the dumbed-down version:
const string QUERY = #"
DO
$$
BEGIN
insert into Foo (Field1,Field2,Field3)
values (#value1,#value2,#value3);
EXCEPTION WHEN others THEN
-- do nothing; it's a race condition
END;
$$ LANGUAGE plpgsql;
select *
from Foo
where Field1 = #value1
and Field2 = #value2;
";
return DbContext.Foos
.FromSql(QUERY,
new NpgsqlParameter("value1", value1),
new NpgsqlParameter("value2", value2),
new NpgsqlParameter("value3", value3))
.First();
In other words, try to insert the record, but don't throw an exception if the attempt to insert it results in a unique index violation (index is on Field1+Field2), and return the record, whether it was created by me or another thread.
This concept worked fine in MSSql, using a TRY..CATCH block. As far as I can tell, the way to handle Postgres exceptions is as I've done, in a plpgsql block.
BUT...
It appears that variable substitution in plpgsql blocks doesn't work. The code above fails on the .First() (no elements in sequence), and when I comment out the EXCEPTION line, I see the real problem, which is:
Npgsql.PostgresException : 42703: column "value1" does not exist
When I test using regular Sql, i.e. doing the insert without using a plpgsql block, this works fine.
So, what is the correct way to do variable substitution in a plpgsql block?
The reason this doesn't work is that the body of the DO statement is actually a string, a text. See reference
$$ is just another way to delimit text in postgresql. It can be just as well be replaced with ' or $somestuff$.
As it is a string, Npgsql and Postgresql have no reason to mess with #value1 in it.
Solutions? Only a very ugly one, so not using this construction, as you're not able to pass it any values. And messing with string concatenation is no different than doing concatenation in C# in the first place.
Alternatives? Yes!
You don't need to handle exceptions in plpgsql blocks. Simply insert, use the ON CONFLICT DO NOTHING, and be on your way.
INSERT INTO Foo (Field1,Field2,Field3)
VALUES (#value1,#value2,#value3)
ON CONFLICT DO NOTHING;
select *
from Foo
where Field1 = #value1
and Field2 = #value2;
Or if you really want to keep using plpgsql, you can simply create a temporary table, using the ON COMMIT DROP option, fill it up with these parameters as one row, then use it in the DO statement. For that to work all your code must execute as part of one transaction. You can use one explicitly just in case.
The only ways to pass parameters to plpgsql code is via these 2 methods:
Declaring a function, then calling it with arguments
When already inside a plpgsql block you can call:
EXECUTE $$ INSERT ... VALUES ($1, $2, $3); $$ USING 3, 'text value', 5.234;
End notes:
As a fellow T-SQL developer who loved its freedom, but transitioned to Postgresql, I have to say that the BIG difference is that on one side there's T-SQL which gives the power, and on the other side it's a very powerful Postgresql-flavored SQL. plpgsql is very rarely warranted. In fact, in a code base of megabytes of complex SQL stuff, I can rewrite pretty much every plpgsql code in SQL. That's how powerful it really is compared to MSSQL-flavored SQL. It just takes some getting used to, and befriending the very ample documentation. Good luck!