my table is aaa (a char(5))
CREATE OR REPLACE PROCEDURE zzz ()
DYNAMIC RESULT SETS 1
P1: BEGIN
Declare cID char(5) ;
Declare cc char(5) ;
Declare csql varchar(200) ;
Declare s1 varchar(200) ;
Set cID = 'a' ;
SET Csql = 'SET ? = (select a from aaa where a = :cID)' ;
PREPARE S1 FROM Csql ;
EXECUTE S1 into cc using cID ; {error this line}
print (cc) ;
END P1
I get error :
Deploy [sss]A.ZZZ
Running A.ZZZ - Deploy started. Create stored procedure returns SQLCODE: -104, SQLSTATE: 42601. A.ZZZ: 14: An unexpected token "" was found following "". Expected tokens may include: "S1 into cc using cID".. SQLCODE=-104, SQLSTATE=42601, DRIVER=3.69.56 An unexpected token "" was found following "". Expected tokens may include: "S1 into cc using cID".. SQLCODE=-104, SQLSTATE=42601, DRIVER=3.69.56 A.ZZZ - Deploy failed. A.ZZZ - Roll back completed successfully.
Multiple problems.
S1 must either have the STATEMENT type or not be declared at
all, but not be declared as VARCHAR.
print (cc) is not a valid statement at all.
If you want to have some debug output, read about
DBMS_OUTPUT.PUT_LINE.
The tool you call your routine from must have an ability to display
contents of the message buffer.
Your routine doesn't return any
cursors to caller / client. There is no need to use the DYNAMIC RESULT SETS 1 clause. This problem is not critical.
Related
Without schema name its not able to call user defined function.
CREATE PROCEDURE TEST_PROC (
IN IN_EMP_ID INT
)
LANGUAGE SQL
DYNAMIC RESULT SETS 1
ISOLATION LEVEL CS
DISABLE DEBUG MODE
VALIDATE BIND
DYNAMICRULES BIND
QUALIFIER ABC
BEGIN
DECLARE STMT VARCHAR(500) ;
DECLARE emp_curr CURSOR WITH RETURN FOR TRANSACTIONS;
SET STMT = '';
SET STMT = STMT ||'SELECT ' ||
'EMP_ID, ' ||
'ABC.TEST_FUNCTION(EMP_ID), ' ||
'EMP_NAME, ' ||
'SALARY, ' ||
'COMPANY ' ||
'FROM ' ||
'EMP_DETAILS ' ||
'WHERE 1=1 ';
IF IN_EMP_ID IS NOT NULL THEN
SET STMT = STMT || ' AND EMP_ID =' || IN_EMP_ID ;
END IF ;
PREPARE TRANSACTIONS FROM STMT ;
OPEN emp_curr;
END;
when i am trying to execute this procedure its working but if i am remove alias name from my function as highlighted so its unable to call user define function. pls help me out thanks in advance.
You can use SET PATH statement to help resolve unqualified function names (and data type names), and this works on all platforms, with some variations in rules and syntax. So choose your Db2-server platform from one of the links below.
The idea is that you tell Db2 how to search for the unqualified function name. Notice that this is different from unqualified tables/views etc.
For Db2 for Z/OS see the documentation here.
For Db2 for i-series see this page.
For Db2 for Linux/Unix/Windows, see here for details of changing the special register CURRENT PATH.
Name resolution for program objects, such as functions, in dynamic SQL is controlled by the CURRENT PATH variable:
The CURRENT PATH (or CURRENT_PATH) special register specifies a VARCHAR(2048) value that identifies the SQL path used when resolving unqualified function names, procedure names, data type names, global variable names, and module object names in dynamically prepared SQL statements.
SET PATH effect on dynamic statement inside the routine example:
--#SET TERMINATOR #
CREATE OR REPLACE FUNCTION S1.TEST_SCALAR() RETURNS INT RETURN 1#
CREATE OR REPLACE FUNCTION S2.TEST_SCALAR() RETURNS INT RETURN 2#
CREATE OR REPLACE PROCEDURE TEST_DYN()
DYNAMIC RESULT SETS 1
BEGIN
DECLARE C1 CURSOR WITH RETURN FOR S1;
PREPARE S1 FROM 'SELECT TEST_SCALAR() AS RES FROM (VALUES 1) T(I)';
OPEN C1;
END#
SET PATH = "S1", USER, SYSTEM PATH#
CALL TEST_DYN#
SET PATH = "S2", USER, SYSTEM PATH#
CALL TEST_DYN#
The result is:
SET PATH = "S1", USER, SYSTEM PATH
DB20000I The SQL command completed successfully.
CALL TEST_DYN
Result set 1
--------------
RES
-----------
1
1 record(s) selected.
Return Status = 0
SET PATH = "S2", USER, SYSTEM PATH
DB20000I The SQL command completed successfully.
CALL TEST_DYN
Result set 1
--------------
RES
-----------
2
1 record(s) selected.
Return Status = 0
I have been researching this for a few days now and can not specifically find an answer to my issue. I want to be able to dynamically create sql statements to run against my DB2 database. Below is some test code I have playing with to see how the DB2 interprets my sql, but I get an error every time I run this. Please help.
Begin
Declare Monthcol integer;
Declare TXPage integer;
Declare TXYear integer;
Declare text varchar(2000);
set Monthcol = 11;
set TXPage = 10190;
set TXYear = 2018;
set text = 'Select GLMN'|| Monthcol || 'from gldbfa.glpgl where glyear =
2018 and glpage = 10190';
Print text;
end;
I have tried casting the variables to varchar and I have tried moving the print section to after the End. I get
"SQL State: 42601 Vendor Code: -104 Message: [SQL0104] Token TEXT was
not valid. Valid tokens: :. Cause . . . . . : A syntax error was
detected at token TEXT. Token TEXT is not a valid token."
if print is before end.
I get
"SQL State: 42601 Vendor Code: -104 Message: [SQL0104] Token PRINT was
not valid. Valid tokens: ( CL END GET SET CALL DROP FREE HOLD LOCK
OPEN WITH ALTER. Cause . . . . . : A syntax error was detected at
token PRINT."
if print is after the End.
This is super easy to do in SQL Server, but I am very new to DB2. Thank you.
I know this may not be an ideal solution but one workaround would be to use case logic on all twelve rows as follows:
SELECT
CASE
WHEN :Monthcol = 1 THEN GLMN01
WHEN :Monthcol = 2 THEN GLMN02
---
--- etc
---
WHEN :Monthcol = 12 THEN GLMN12
END AS "GLMN"
FROM gldbfa.glpgl
WHERE glyear = :TXYear AND glpage = :TXPage
If the example code you posted is copy/pasted from your actual code then your problem is probably just the missing spaces in your concatenation.
Begin
Declare Monthcol integer;
Declare TXPage integer;
Declare TXYear integer;
Declare text varchar(2000);
set Monthcol = 11;
set TXPage = 10190;
set TXYear = 2018;
set text = 'Select GLMN' || Monthcol || ' from gldbfa.glpgl
where glyear = 2018 and glpage = 10190'; <----- You were missing a space after Monthcol so your select statements was 'Select GLMN11from gldbfa...'
-- execute statment here
end;
Db2 for IBM i does not have a print
You need to be looking at the Db2 for IBM i docs, not Db2 for LUW.
https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzahg/rzahgsql.htm
In answer to your comment, yes you can create dynamic queries...
Howver, doing so inside an SQL Stored procedure can be tricky...all depends on what you want to do with the results. See this question...
Refer to table name dynamically in DB2/400 SQL query..?
What do you want to do with the row returned?
When trying to join two tables and update one of them, I'm receiving an unexpected error from this function right here:
CREATE OR REPLACE FUNCTION tsi.update_data(_creation_time int)
RETURNS VOID
AS $$
BEGIN
EXECUTE format($sql$
UPDATE tsi.forecasts_%s a SET
a."incidents # 01:00" = b.n_incid,
a."road # 01:00" = b.n_roads
FROM tgi_tmp b WHERE a.link_ix = b.link_id;
$sql$,_creation_time);
END $$ LANGUAGE plpgsql;
It gives me this error message:
syntax error at or near "#"
cidents # 01:00" = n_incid,
^
Do anyone know why I'm getting this error? The tables do contain the mentioned columns, so that is not the problem. Is postgres having a hard time dealing with string-columns in an Execute-format?
Postgres version: 10.5
Simplified table structure:
CREATE TABLE tsi.forecasts_%s (
link_ix int PRIMARY KEY,
"slipincidents # 00:00" SMALLINT NOT NULL,
"roadcoverage # 00:00" SMALLINT NOT NULL,
);
and tgi_tmp:
CREATE TEMPORARY TABLE tgi_tmp (
link_id TEXT,
n_road SMALLINT,
n_incid SMALLINT
CONSTRAINT tgi_tmp_pkey PRIMARY KEY (link_id)
);
Weird it complaints about the # doesn't do that for me. What however is wrong is specifying the table (alias) for the columns you are assigning to in the set. You should only specify the column names.
CREATE OR REPLACE FUNCTION tsi.update_data(_creation_time int)
RETURNS VOID
AS $$
BEGIN
EXECUTE format($sql$
UPDATE tsi.forecasts_%s a SET
"incidents # 01:00" = b.n_incid,
"road # 01:00" = b.n_roads
FROM tgi_tmp b WHERE a.link_ix = b.link_id;
$sql$,_creation_time);
END $$ LANGUAGE plpgsql;
Trying to debug your function, I get these error messages, one after the other:
ERROR: operator does not exist: integer = text
ERROR: column b.n_roads does not exist
ERROR: column "a" of relation "tsi_forecasts_1" does not exist
ERROR: column "incidents # 01:00" of relation "tsi_forecasts_1" does not exist
Each after fixing the previous error.
I arrive at this working version:
CREATE OR REPLACE FUNCTION tsi_update_data(_creation_time int)
RETURNS VOID AS
$func$
BEGIN
EXECUTE format($sql$
UPDATE tsi_forecasts_%s a
SET "slipincidents # 00:00" = b.n_incid -- don't table-qualify target cols
, "roadcoverage # 00:00" = b.n_road -- col names in q don't match
FROM tgi_tmp b
WHERE a.link_ix = b.link_id::int; -- your db design forces a cast
$sql$, _creation_time);
END
$func$ LANGUAGE plpgsql;
But I cannot reproduce your error:
syntax error at or near "#"
cidents # 01:00" = n_incid,
^
Which must be invoked by something that's not in the question, like outer double-quoting or special meaning of characters in your unnamed client program.
All that aside, it might pay to reconsider your naming convention and your db design. Use legal, lower-case, unquoted identifiers and matching data types (link_ix is int while link_ix is text).
Works for some reason when I'm not specifying the offset. Like this:
CREATE OR REPLACE FUNCTION tsi.update_data(_creation_time int)
RETURNS VOID
AS $$
BEGIN
EXECUTE format($sql$
UPDATE tsi.forecasts_%s a SET
"incidents # %s" = b.n_incid,
"road # %s" = b.n_roads
FROM tgi_tmp b WHERE a.link_ix = b.link_id;
$sql$,_creation_time, '01:00', '01:00');
END $$ LANGUAGE plpgsql;
I wrote a procedure STRING_SESTAVLJEN_ENAKOST_TABEL('MERILA_STRANKE') that generates part of code that i want to execute (some long if statement)
IF ((new.LOKACIJA IS DISTINCT FROM old.LOKACIJA )
OR (new.MODIFIED IS DISTINCT FROM old.MODIFIED )
OR (new.KARAKTERISTIKE IS DISTINCT FROM old.KARAKTERISTIKE )
OR (new.LETNIK IS DISTINCT FROM old.LETNIK )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS ))
and I want to call it in trigger, then add some code and run it all together.
The code is:
SET TERM ^ ;
ALTER TRIGGER BI_MERILA_STRANKE ACTIVE
BEFORE INSERT OR UPDATE POSITION 0
AS
declare variable besedilo_primerjave varchar(5000);
BEGIN
begin
if (new.ID_MERILA_STRANKE is null OR new.ID_MERILA_STRANKE = 0) then new.ID_MERILA_STRANKE = gen_id(GEN_ID_MERILA_STRANKE,1);
end
begin
execute procedure STRING_SESTAVLJEN_ENAKOST_TABEL('MERILA_STRANKE')
returning_values :besedilo_primerjave;
execute statement besedilo_primerjave || ' THEN BEGIN INSERT INTO SYNC_INFO(TABLE_NAME,ID_COLUMN_NAME,ID_VALUE,DATETIME)
VALUES (
''MERILA_STRANKE'',
''ID_MERILA_STRANKE'',
NEW.ID_MERILA_STRANKE,
CURRENT_TIMESTAMP
);
END ELSE BEGIN
exception ENAK_RECORD;
END';
end
END^
SET TERM ; ^
Now when I run the update and trigger triggers I get this error:
SQL Message : -104 Invalid token
Engine Code : 335544569 Engine Message : Dynamic SQL Error SQL
error code = -104 Token unknown - line 1, column 1 IF
On the other hand if I write it like this:
SET TERM ^ ;
ALTER TRIGGER BI_MERILA_STRANKE ACTIVE
BEFORE INSERT OR UPDATE POSITION 0
AS
BEGIN
begin
if (new.ID_MERILA_STRANKE is null OR new.ID_MERILA_STRANKE = 0) then new.ID_MERILA_STRANKE = gen_id(GEN_ID_MERILA_STRANKE,1);
end
begin
IF ((new.LOKACIJA IS DISTINCT FROM old.LOKACIJA )
OR (new.MODIFIED IS DISTINCT FROM old.MODIFIED )
OR (new.KARAKTERISTIKE IS DISTINCT FROM old.KARAKTERISTIKE )
OR (new.LETNIK IS DISTINCT FROM old.LETNIK )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS ))
THEN BEGIN
INSERT INTO SYNC_INFO(TABLE_NAME,ID_COLUMN_NAME,ID_VALUE,DATETIME)
VALUES (
'MERILA_STRANKE',
'ID_MERILA_STRANKE',
NEW.ID_MERILA_STRANKE,
CURRENT_TIMESTAMP
);
END ELSE BEGIN
exception ENAK_RECORD;
END
end
END^
SET TERM ; ^
It works as it should. I do not understand why it doesn't run if is more or less the same code.
As I also mentioned in your previous question, execute statement cannot be used to execute snippets of PSQL (procedural SQL) like that, it can only execute normal DSQL (dynamic SQL). And as it doesn't understand PSQL, you get the "token unknown - if" error, because if is not valid in DSQL.
execute statement is equivalent to executing SQL yourself from a query tool or application (it uses the same API), you can't use if there either.
There is a loophole by using an execute block statement, but that still would not allow you to gain access to the NEW (or OLD) trigger context variables unless explicitly passed as parameters, which would negate most of the usefulness of dynamically generated code in this context.
The only real solution is to write the trigger and not do it dynamically, maybe using a code generator (I'm not sure if any exist, otherwise you need to write that yourself).
I want to return the error message from a procedure when an exception happens. In SQL Server you would select the Error_Number() and Error_Message(). How would I do it in FirebirdSql
SET TERM ^ ;
CREATE PROCEDURE sprocname
( id int )
RETURNS
( gcode int, errmsg varchar(250) )
AS
BEGIN
gcode = 0;
errmsg = '';
-- do procedure code here
WHEN ANY DO
BEGIN
gcode = gdscode; -- ??
errmsg = ??;
END
SUSPEND;
END^
SET TERM ; ^
Unfortunately, you will need to do that at the client side, as it is currently not possible to obtain this in PSQL. There is a feature request in the Firebird tracker, which has been implemented for Firebird 4, which is expected to be released in 2019.
See Firebird 4 release notes, section System Function RDB$ERROR():
The function RDB$ERROR() takes a PSQL error context as input and
returns the specific context of the active exception. Its scope is
confined to the context of the exception-handling block in PSQL.
Outside the exception handling block, RDB$ERROR always contains
NULL.
The type of the return value depends on the context.
Syntax Rules
RDB$ERROR ( context )
context ::= { GDSCODE | SQLCODE | SQLSTATE | EXCEPTION | MESSAGE }
[..]
Example
BEGIN
...
WHEN ANY DO
EXECUTE PROCEDURE P_LOG_EXCEPTION(RDB$ERROR(MESSAGE));
END
CREATE PROCEDURE ADD_COUNTRY (
ACountryName COUNTRYNAME,
ACurrency VARCHAR(10) )
AS
BEGIN
INSERT INTO country (country,
currency)
VALUES (:ACountryName,
:ACurrency);
WHEN ANY DO
BEGIN
-- write an error in log
IN AUTONOMOUS TRANSACTION DO
INSERT INTO ERROR_LOG (PSQL_MODULE,
GDS_CODE,
SQL_CODE,
SQL_STATE)
VALUES ('ADD_COUNTRY',
GDSCODE,
SQLCODE,
SQLSTATE);
-- Re-throw exception
EXCEPTION;
END
END
http://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-psql-handleexceptions.html