Strange behavior of the informix procedure call - procedure

I have been writing the procedure in SPL for Informix database. I have one big procedure which calls smaller procedures. I observed strange behavior when I inserted the return statement after the first call.
CREATE PROCEDURE main( )
RETURNING INTEGER, INTEGER, CHAR (200);
call procedure1();
RETURN 0, 0, "OK";
call procedure2();
call procedure3();
END procedure;
When I compare running time with this variant:
CREATE PROCEDURE main( )
RETURNING INTEGER, INTEGER, CHAR (200);
call procedure1();
END procedure;
The differences in running time {time required for execution of these two procedures} are striking. The first program runs ten times more than second.
Does anybody know, what does Informix doing and why there is so long waiting time for procedures that are not executed?

Related

DB2 LUW - Get Error Line in Stored Procedure

I'm trying to determine the line in a stored procedure or the last SQL-statement which is causing an error. As a workaround I'm using temporary variables which I manually set to determine in which part of my stored procedure an error occurs.
See the following:
-- Create an ErrorLog table
Create Table SCHEMA.ErrorLog_lrc_test
(
ErrSQLCODE Integer ,
Codepart Char(1),
Type Char(1) ,
MsgText VarChar(1024));
CREATE OR REPLACE PROCEDURE SCHEMA.test_error(IN divisor INT)
LANGUAGE SQL
BEGIN
-- Define variables
DECLARE codepart_var Char(1);
DECLARE test_INT INT;
-- Define sqlcode
DECLARE SQLCODE INTEGER;
--Define Error-Handler
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
INSERT INTO SCHEMA.ErrorLog_lrc_test(ErrSQLCODE, Codepart, TYPE, MsgText)
VALUES(SQLCODE, codepart_var, 'E', SYSPROC.SQLERRM(SQLCODE));
END;
--Define Warning-Handler
DECLARE CONTINUE HANDLER FOR SQLWARNING, NOT FOUND
BEGIN
INSERT INTO SCHEMA.ErrorLog_lrc_test(ErrSQLCODE, Codepart, TYPE, MsgText)
VALUES(SQLCODE, codepart_var, 'W', SYSPROC.SQLERRM(SQLCODE));
END;
-- Set temporary variable to 'a' to get part of code where error occured
SET codepart_var = 'a';
-- Create Error
sELECT 1/divisor into test_INT
FROM SYSIBM.SYSDUMMY1;
SET codepart_var = 'b';
-- Create Error
sELECT 1/divisor into test_INT
FROM SYSIBM.SYSDUMMY1;
SET codepart_var = 'c';
-- Create Not Found (Sqlcode 100)
INSERT INTO SCHEMA.ErrorLog_lrc_test
SELECT NULL, NULL, NULL, NULL FROM "SYSIBM".SYSDUMMY1
WHERE 1 = 0 ;
END
call SCHEMA.test_error(0);
SELECT *
FROM SCHEMA.ErrorLog_lrc_test;
I get the following:
ERRSQLCODE
CODEPART
TYPE
MSGTEXT
-801
a
E
SQL0801N Division by zero was attempted.
-801
b
E
SQL0801N Division by zero was attempted.
100
c
W
SQL0100W No row was found for FETCH, UPDATE or DELETE; or the result of a query is an empty table.
So I am able to get the part of the code where an error or warning occurs, but it would be better to get the line or the SQL statement as I don't want to specify every part of the code with a temporary variable.
I already found this SQLCA --> sqlerrd(3): "...If an error is encountered during the compilation of an SQL routine, trigger, or dynamic compound SQL (inlined or compiled) statement, sqlerrd(3) contains the line number where the error was encountered". For now I didn't manage to make use of SQLCA variables. I don't know how to implement them in DB2 LUW in a stored procedure.
Is there another/better way to log the specific line or SQL-statement in a stored procedure which is causing an error?
My DB2 version is 10.5.0.
Thank you!
If your Db2-server platform is Linux/Unix/Windows, and you are using a recent version, then consider using DBMS_UTILITY.FORMAT_ERROR_BACKTRACE which may help you.
Documentation here. The documentation includes a worked example.
When using this for stored procedures or routines, it is wise to always create those routines with a meaningful specific name with the SPECIFIC clause on the create or replace statement. Otherwise the routine will have a system generated name which will not be meaningful to users when it appears in the output of DBMS_UTILITY.FORMAT_ERROR_BACKTRACE. There are other reasons you should always use a specific name for your routines.
The SQLCA is for calling programs (i.e. the program that calls the stored procedure).

How to call a procedure with a rowtype literal as parameter in PL/SQL?

Lets say I have a table and a procedure that accepts one argument of the tables rowtype:
CREATE TABLE t (a NUMBER, b NUMBER);
CREATE PROCEDURE p (x t%ROWTYPE) IS
BEGIN
NULL;
END;
Can I call that procedure using a rowtype literal, that is without explicitly creating a rowtype variable (or at least not explicitly listing and assigning every field of it)? The two following approaches both generate the below error:
p(1, 2);
p((1, 2));
PLS-00306: wrong number or types of arguments in call to 'P'
You could also construct the record from a cursor loop:
for r in (
select 1, 2 from dual
)
loop
p(r);
end loop;
Unfortunately PL/SQL records are just simple structures and don't come with constructors as object types do. (I wish they did.)
This is not an optimal solution, since it (a) requires the creation of a variable and (b) isn't very pretty. But at least it works:
DECLARE
x t%ROWTYPE;
BEGIN
SELECT 1, 2 INTO x FROM dual;
p(x);
END;
I am still interested in better solutions.

Unable to read memory address of variable that pass to function as call by reference

I am Delphi 6 developer. And trying to call C++ dll function from delphi.
Dll written in C++ and the have Char pointer as out parameter.
Eg.
Function doGetStatus(Var szStatusData : Char) : Integer; cdecl; external 'WhizCal.dll';
When I call function from DLL I send char variable 'szStatusData' and return value should be 'Down' or 'Live' but it retuens only 'D' or 'L' this is because I have passed Char variable reference.
My question is, what can I do for getting return data as 'Down' or 'Live' without changing datatype.
A char is just a single character. So you cannot expect more.
Most likely the C++ code expects to be passed a pointer to an array of characters. Perhaps it should be like this:
function doGetStatus(szStatusData: PAnsiChar): Integer; cdecl; external 'WhizCal.dll';
And you might call it like this:
var
StatusData: array [0..255] of AnsiChar;
RetVal: Integer;
....
RetVal := doGetStatus(StatusData);
// check RetVal
We cannot tell you what exactly your code should be because we don't have any details of the specified interface. Unless the caller also passes the length of the array, such a function is bait for buffer overrun errors. As written above, this is the case. How can the caller know how much space to allocate. Well designed functions also allow the caller to pass the length of the array that they supplied.
My general advice to you is that interop is a topic that demands precision and detail, and knowledge of both sides of the boundary. You need to find out that detail.

How can I receive a string from FPC DLL? [duplicate]

This question already has answers here:
Need simple demo call Delphi DLL in C++
(2 answers)
Closed 8 years ago.
How can I receive a string from a FPC DLL? I would like to send two pointers concat them and receive the result in another string in Delphi.
library Test;
{$mode Delphi}
uses
Classes;
function Concat(const S1, S2: PWideChar): String; cdecl;
begin
Result := S1 + S2;
end;
exports
Concat name 'Concat';
begin
end.
In Delphi, a String is a complex, structured type with many details managed for you by the compiler and RTL 'magic' that hides these details. In particular, for 'long strings' there is a reference count and a length and, depending on the Delphi version involved, possibly other information.
Any DLL cannot know the details of precisely what information is required to be returned (or may be present in) any 'string' variables (or results) that an application may require. The DLL may not even be called by a Delphi program at all, in which case the 'string' type will be quite different again.
For this reason, a DLL will usually choose to deal with strings as simple 'C'-style pointer to char types. That is, some pointer to a null terminated region of memory. The caller of the DLL must then also ensure to exchange 'string' values with the DLL accordingly.
In the case of some function returning a value, the issue is complicated by the fact that allocation of the area of memory required to hold the result must be performed by the caller, with the function in the DLL taking appropriate steps to ensure that the memory supplied is sufficient. Applying these principles in this case results in a DLL routine that might look similar to this:
function Concat(const S1, S2, DEST: PWideChar; const aMaxLen: Integer): Boolean; cdecl;
begin
// left as exercise
end;
This is a simple implementation that returns TRUE if aMaxLen is sufficient to accommodate the concatenated result. You should also consider other behaviours of the function under a variety of conditions (eg. S1 or S2 or both are NIL, aMaxLen is too big, etc).
Whatever implementation choices are made for performing the concatenation (left as an exercise for you), the result of the function call must be to place the result in the buffer pointed to by DEST.
The caller must then also ensure that a buffer of sufficient length is provided and the correct length indicated in the call:
var
a, b, ab: WideString; // Or: String or UnicodeString in Delphi 2009 and later
begin
a := 'foo';
b := 'bar';
// Make sure 'ab' is big enough to hold the concatenated result of a + b
SetLength(ab, Length(a) + Length(b));
if Concat(PWideChar(a), PWideChar(b), PWideChar(ab), Length(ab)) then
// success: ab == 'foobar'
else
// something went wrong
end;
The question has to be asked though: Why are you doing this in an FPC DLL when Delphi already handles the concatenation of strings quite comfortably ? O.o

Error Delphi XE2 - Exception class $C00000005

I am getting this error will debugging a project, which used to be in Delphi 7 and I have been upgrading to Delphi XE2, the same error happens in several methods.
First chance exception at $006DC660. Exception class $C0000005 with message 'access violation at 0x006dc660 read of address 0xffffffff'
This is one of the methods:
PFI = ^TFI;
TFI = record
Id : TToken;
Name : TName;
Parameters : string;
end;
function TListFI.IsIn(S: PChar): PFI;
function SearchName2(Item: PFI):Boolean;
var N1, N2: PChar;
begin
N1:= StrNew(Item^.Name);
N2:= StrNew(S); //Here is the issue
SearchName2:= (StrComp(StrUpper(N1), StrUpper(N2)) = 0);
StrDispose(N1);
StrDispose(N2);
end;
begin
IsIn:= PFI(FirstThat(#SearchName2));
end;
I have googled and I found someone describing a similar problem, and he affirms that when the incremental linker is disabled it works, can someone tell me what and where is it or give some advice to solve this situation.
[EDIT]
Removing the # now gives me the following error in IsIn:= PFI(FirstThat(SearchName2));
E2010 Incompatible types: 'TObject' and 'PFI'
I am adding the FirstThat procedure to see if it may help.
TFuncionColeccion = function (Elemento: TObject): Boolean;
function TColeccion.FirstThat (Rutina: TFuncionColeccion): TObject;
var
i: Integer;
begin
For i:=0 to Count-1 do
if Rutina(Items[i]) then
begin
FirstThat:=Items[i];
exit;
end;
FirstThat:=nil;
end;
It is (and always has been) an error to call local (nested) procedures by pointer, which is clearly what your FirstThat function does. The compiler has to do special things with the stack to call local functions and give them access to the parent scope's variables (S in your code), but the compiler can only know to do those special things when the local function is called directly. The compiler cannot know that the argument to FirstThat will be a local function, so it doesn't include the special code when FirstThat invokes the pointed-to function.
The bottom line is that the stack inside the function doesn't get set up the way it's supposed to, and that means any number of strange symptoms may appear. You'll have to use some other way. Maybe make SearchName2 be a two-argument function, and then write FirstThat to accept S as a parameter that it can forward to the function argument.
You shouldn't need to use the # operator when constructing a function pointer. When you do, the compiler tends to skip type checking, which is what allowed you to pass a local function pointer to FirstThat in the first place. When the function you're passing really matches the required prototype, the compiler will allow you to pass it without the # operator.
You are reporting an access violation in
StrNew(S)
where S is of type PChar. The explanation for that, with probability very close to 1, is that S is not in fact a pointer to null terminated array of WideChar.
In Delphi 7, PChar is an alias for PAnsiChar. That is a pointer to null terminated array of AnsiChar, i.e. 8 bit characters. In Delphi XE2, PChar is an alias for PWideChar, a pointer to null terminated array of WideChar, i.e. 16 bit characters.
It helps to understand what StrNew does. It walks the array until it finds a null character. For 8 bit text that is a single zero byte. For 16 bit text, the null is a zero 16 bit word. Then it allocates a new block of memory of the same length as the input string, and makes a copy into that new memory. The source code is:
function StrNew(const Str: PWideChar): PWideChar;
var
Size: Cardinal;
begin
if Str = nil then Result := nil else
begin
Size := StrLen(Str) + 1;
Result := StrMove(WideStrAlloc(Size), Str, Size);
end;
end;
The only plausible failure mode is that when StrLen walks the array, it attempts an invalid memory read. And that can only happen if your input parameter is invalid. In other words, this must be a programming error on your part.
One possible explanation is that you are in fact passing 8 bit text to this function despite promising to pass 16 bit text. An easy mistake to make, especially if you are not yet fully familiar with the Unicode change. The 8 bit text has a zero terminator, but the byte that follows happens not to be zero. Or the zero byte falls at an odd numbered offset from the start. And then StrNew continues walking the buffer, but now it is off the end and it so happens that it doesn't find a zero word before overrunning into an address that has not been allocated. And that is an access violation.
If that is so then solution will be either:
Change the function's parameter to be of type PAnsiChar, and fix the dubious casting at the call site.
Pass the function 16 bit text as it requires.
In your update you include the address which cannot be read, 0xffffffff. This is -1 in hex. And that would seem to be the most prosaic of errors. Your pointer is completely bogus! Your exact error message can be reproduced with this code: StrNew(PChar(-1)).
I don't have enough information here to tell you why your pointer is bogus. Hopefully you've learnt some debugging and diagnostic techniques that will enable you to solve the problem. At least you now know that the error is in your code.
Assuming that BuscaName2 and SearchName2 are one and the same thing, then you need look no further. Local procedures can only be called from a containing function. As #Rob correctly says, the use of # with procedures is almost always incorrect and is a warning sign of serious problems with your code.

Resources