ARRAY declaration inside function in db2 iseries 7.1 - db2

I am unable to declare an array inside a function. It shows the below error.
Error: [SQ20441] Array type not valid where specified.
My code is below,
CREATE TYPE REV_QTY AS INTEGER ARRAY[] ;--ALLOW_ARRAY_VALUE_CHANGES
CREATE OR REPLACE FUNCTION GET_REV_QTY (IN_IPID INTEGER,QTY_TO_PLACE
INTEGER,INITIAL_REV_QTY INTEGER)
RETURNS INTEGER
LANGUAGE SQL
DETERMINISTIC
MODIFIES SQL DATA
--CONTAINS SQL
--RETURNS NULL ON NULL INPUT
NO EXTERNAL ACTION
BEGIN
DECLARE AVAILABLE_REV_QTY REV_QTY;
if exists(AVAILABLE_REV_QTY[IN_IPID]) then
CASE WHEN QTY_TO_PLACE > AVAILABLE_REV_QTY[IN_IPID] THEN
SET AVAILABLE_REV_QTY[IN_IPID] = 0
WHEN QTY_TO_PLACE < AVAILABLE_REV_QTY[IN_IPID] AND QTY_TO_PLACE >= 0
THEN
SET AVAILABLE_REV_QTY[IN_IPID] = AVAILABLE_REV_QTY[IN_IPID] -
QTY_TO_PLACE
END;
END IF;
RETURN AVAILABLE_REV_QTY[IN_IPID] ;
END |
Please help me on this. I am new to DB2 i7.1 and I am unable to
understand what is going wrong.

The IBM i 7.1 documentation for that sqlcode=-20441 does not list\include the SQL Scalar Function as where an array-type specification can be made [neither as an argument nor declared variable]. See:
Listing of SQL messages
These tables list SQL messages. Use these tables to find message text, cause text, recovery text, and corresponding SQLCODEs and SQLSTATEs.
SQ20441
Message Text: Array type not valid where specified.
Cause Text: An array type was used but is not allowed in the specified context. Array types can only be used:
• As an argument of an SQL or JAVA procedure.
• For an SQL variable declared in an SQL procedure.
• In a CAST specification in an SQL procedure.
Recovery Text: Remove the reference to the array type. Try the request again.
SQLCODE or SQLCODEs: -20441
SQLSTATE or SQLSTATEs: 428H2
As asides, if the references were code where allowed:
• the EXISTS predicate is not allowed syntax as written in the OP; presumably the following predicate is desired:
if cardinality(AVAILABLE_REV_QTY) >= IN_IPID then
• required semicolon after the statements in THEN of CASE are missing
• INITIAL_REV_QTY INTEGER is unreferenced
• DECLARE AVAILABLE_REV_QTY REV_QTY ; has DEFAULT NULL so even the CARDINALITY is of no use unless the array values are first constructed; perhaps passed as an argument
Note: the work of the routine could be composed as a SQL PROCEDURE, and perhaps that FUNCTION also could be rewritten to invoke the stored procedure to perform the array work -- unsure, as whence the data comes for the array is not clear from the OP.

Related

SQL0628N with MODIFIES SQL DATA when creating a table function

I am trying to encapsulate the functionality from this sample code here, inside a Table-Function.
I can run the sample alone without any problem.
But when I create a table function, just with a single call to OPEN_CURSOR , I receive SQL0577N
CREATE FUNCTION ROW_CHECKSUM
( IN sSchema VARCHAR(128) ,
IN sTable VARCHAR(128) ,
IN sColumnList VARCHAR(1024) ,
IN sWhere VARCHAR(1023),
IN iRows INTEGER
)
RETURNS TABLE (ROW_PK_VALUES VARCHAR(3000), CHECKSUM INTEGER )
LANGUAGE SQL
SPECIFIC ROW_CHECKSUM
--NO EXTERNAL ACTION
--MODIFIES SQL DATA
--NOT DETERMINISTIC
BEGIN
DECLARE iCheckSum INTEGER ;
DECLARE sKyes VARCHAR(1024) ;
DECLARE iCursor INTEGER;
DECLARE sQuery VARCHAR(32000) ;
SET sQuery = 'SELECT ' || sColumnList || ' FROM "' || sSchema || '"."' || sTable || '" WHERE ' || sWhere || ' FETCH FIRST ' || TO_CHAR(iRows) || ' ONLY' ;
CALL DBMS_SQL.OPEN_CURSOR(iCursor);
--CALL DBMS_SQL.PARSE(iCursor, sQuery, DBMS_SQL.native) ;
--PIPE (sKeys, iCheckSum) ;
--PIPE ('abcd', 1234) ;
RETURN ;
END
----
SQL0577N User defined routine "DB2ADMIN.ROW_CHECKSUM" (specific name "")
attempted to modify data but was not defined as MODIFIES SQL DATA. LINE
NUMBER=33. SQLSTATE=38002
it seems, OPEN_CURSOR demands to have the MODIFY SQL DATA specified.. ok.. let's go!
But, when I specify it, then I get the following error, instead:
SQL0628N Multiple or conflicting keywords involving the "MODIFIES SQL DATA"
clause are present. LINE NUMBER=33. SQLSTATE=42613
The error details for -628 error is too generic and does not help me to determine what's really going on here.
I need to perform dynamic SQL queries using DBMS_SQL module, and return the result set using PIPE , like this other sample here.
I have been reading spread documentations the entire day.. and so far was not able to determine exactly what rule I am violating.
Also, found some inconsistencies on documentation, which I don't understand:
This page, says:
SQL table functions cannot contain compiled compound statements.
While, the Rules from RETURN statement says the opposite, and matches with PIPE sample code:
In an SQL table function using a compound SQL (compiled) statement, an expression, NULL, or fullselectcannot be specified. Rows are returned from the function using the PIPE statement and the RETURN statement is required as the last statement to execute when the function exits (SQLSTATE 2F005).
Appreciate any help!
Look at the note about the MODIFIES SQL DATA in the CREATE FUNCTION statement description:
4 Valid only for compiled scalar function definition and an inlined
table function definition.
But you can't use PIPE in an inlined function.
So, you want to use different functionalities, which can't be used together.
The inconsistency you found in the documentation is not related to you problem.

How to pass VARGRAPHIC to Stored procedures in DB2?

How can I call a sp with VARGRAPHIC variable type as input?
I've create this super simple sp that dose nothing and just for test, with following statement:
CREATE PROCEDURE MYPROCEDURE (IN VARNAME vargraphic(5) )
LANGUAGE SQL
P1: BEGIN
END P1
but when I call the sp in IBM Data Studio,it raises this error:
{? = call SCHEMA.MYPROCEDURE (?)}
[SQL0189] Coded Character Set Identifier 37 not valid.
Run of routine failed.
- Roll back completed successfully.
is there any problem in my sp code?
should I define CCSIDs? How and where?
I suspect that you would want to specify the CCSID of your parameter like this
call SCHEMA.MYPROCEDURE( CAST(? AS VARGRAPHIC(5) CCSID 65535) )
picking the correct CCSID number as appropriate
https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_74/db2/rbafzcast.htm

Postgres: Returning Results or Error from Stored Functions

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 |

Why am I getting a syntax error when calling my stored procedure?

I am trying to call a stored procedure with Time variable as in parameter. But whenever i try to call the procedure i m getting error as:
db2 'call PASS_FAIL_CHECKDATE('2014-01-21','13:42:25','CSS1',Null,'4500096651','10',Null)'
SQL0104N An unexpected token ":42" was found following "CKDATE(2014-01-21,
13". Expected tokens may include: "+". SQLSTATE=42601
My Procedures input parameter are :
PASS_fail_checkdate (in post_date date,in post_time time,in destplant varchar(4), in destloc varchar(4), in transnum varchar(10), in translineitemnum varchar(6), in inboundconsignment varchar(35))
I am not sure if my declaration for time variable is correct in procedure or if i am calling the time variable correctly in the procedure.
Please give me suggestions on the same.
As already offered, in words, try the following example as a revision to what was noted in the OP as tried already but failing; i.e. change to use double-quote vs the apostrophe, specified as the delimiter for the DB2 SQL statement string:
db2 "call PASS_FAIL_CHECKDATE('2014-01-21','13:42:25','CSS1',Null,'4500096651','10',Null)"

I can create a stored procure with invalid user defined function names in it

I just noticed that I could alter my stored procedure code with a misspelled user defined function in it.
I noticed that at 1st time I execute the SP.
Is there any way to get a compile error when an SP include an invalid user-defined function name in it?
At compile time? No.
You can, however, use some of SQL's dependency objects (if using MS SQL) to find problems just after deployment, or as part of your beta testing. Aaron Bertran has a pretty nice article rounding up the options, depending upon the version of SQL Server.
Here is an example using SQL Server 2008 sys object called sql_expression_dependencies
CREATE FUNCTION dbo.scalarTest
(
#input1 INT,
#input2 INT
)
RETURNS INT
AS
BEGIN
-- Declare the return variable here
DECLARE #ResultVar int
-- Add the T-SQL statements to compute the return value here
SELECT #ResultVar = #input1 * #input2
-- Return the result of the function
RETURN #ResultVar
END
GO
--Fn Works!
SELECT dbo.ScalarTest(2,2)
GO
CREATE PROCEDURE dbo.procTest
AS
BEGIN
SELECT TOP 1 dbo.scalarTest(3, 3) as procResult
FROM sys.objects
END
GO
--Sproc Works!
EXEC dbo.procTest
GO
--Remove a dependency needed by our sproc
DROP FUNCTION dbo.scalarTest
GO
--Does anything have a broken dependency? YES
SELECT OBJECT_NAME(referencing_id) AS referencing_entity_name,
referenced_entity_name, *
FROM sys.sql_expression_dependencies
WHERE referenced_id IS NULL --dependency is missing
GO
--Does it work? No
EXEC dbo.procTest
GO