Firebird 2.5 UDF PChar visszatérési érték

Firebird 2.5 UDF PChar visszatérési érték
2015-01-01T18:16:03+01:00
2015-01-06T00:27:51+01:00
2022-10-15T22:06:06+02:00
gforce
A problémám a következő:

Van egy UDF függvényem (Delphi 7), ami stringet alakít át.
Firebird 2.1 alatt hibátlanul működött. Szeretném az adatbázist 2.5 alá áttenni, és ott a függvény meghívásakor az SQL szerver összeomlik.

A Delphi függvény:

function FKSz_Convert( cFKSz : PChar ) : PChar;
var
   cSeged : string;
   cOut : string;
begin
   cSeged := String( cFKSz );
   if Length( Trim( cSeged )) > 6 then begin
      cOut := Copy( cSeged,1,6 ) + '-' + Copy( cSeged,7,5 );
   end else begin
      cOut := Copy( cSeged,1,6 ) + ' ' + Copy( cSeged,7,5 );
   end;
   Result := ib_util_malloc( Length( cOut ) + 1 );
   StrPCopy( Result, cOut );
end;

Az adatbázisban az UDF definíciója pedig:
DECLARE EXTERNAL FUNCTION FKSZCONV
   CSTRING (11)
   RETURNS CSTRING (12) FREE_IT
   ENTRY_POINT 'FKSZ_Convert' MODULE_NAME 'AlmiraSQL_UDF.dll';

Egy kis nyomozgatás után kiderítettem, hogy az ib_util_malloc-függvény meghívása után a Result változó mérete 0-byte.

Mi lehet a gond ?
Mutasd a teljes hozzászólást!
A hiba valódi okát valószínű, hogy teljesen máshol kell keresni! Az sem elképzelhetetlen, hogy a stack-ed esetleg összeomlott vagy az adatterület valamilyen túlírás miatt megsérült.  A szerver akkor biztos, hogy összeomlik, ha mondjuk kivétel keletkezik a függvényedben és az továbbgyűrűzik a szervíz alkalmazáshoz.
Mindenesetre az alábbi kód nálam nem okozott semmiféle problémát pedig a 2.5-ös firebird-öt telepítettem egy virtuális XP-re.

library AlmiraSQL_UDF; uses SysUtils; function ib_util_malloc(Size: integer): pointer; cdecl; external 'ib_util.dll'; function FKSz_Convert(cFKSz : PAnsiChar) : PAnsiChar; cdecl; var tmp : AnsiString; begin tmp := Trim(cFKSz); if (Length(tmp) > 6) then Insert('-', tmp, 7); Result := ib_util_malloc(Length(tmp) + 1); StrPCopy(Result, tmp); end; exports FKSz_Convert; begin end.

Teszt:

select fkszconv(null), fkszconv(' xyz '), fkszconv('abcdefghxyz') from rdb$database;
Mutasd a teljes hozzászólást!

  • Hello!

    A 2.1 és a 2.5 között volt struktúra változás (ODS 11.1->11.2) Egyszer valamiért nekem is gondom volt vele. Már nem emlékszem, hogy mi volt a gond melyik verzióról kellett váltanom és hogyan csináltam (Az is lehet, hogy egy Backup és Restore is elég volt).

    Először ezt mindenképp kipróbálnám, sajna a módszere nem emlékszem ezt rád bízom :)

     Tomi
    Mutasd a teljes hozzászólást!
  • Valószínűleg az a AlmiraSQL_UDF.dll lehet a ludas, újra kéne fordítani a 2.5 s headerjeivel. Én csak Postgres alá szoktam kiegészítő függvényeket csinálni c ben vagy delphiben de a lefordított dll vagy so fájlok nem használhatóak a server következő verziójához.
    Mutasd a teljes hozzászólást!
  • az SQL szerver összeomlik

    Ennek sok oka lehet. Nem kizárt, hogy valami miatt nem tudja betölteni a .DLL-edet. Pl. mert a könyvtárad 32 bites, de a szerver 64 bites vagy nem találja az ib_util.dll-t stb. Esetleg még az is elképzelhető, hogy olyan módszereket is használsz, amelyek nem szálbiztosak vagy nem megfelelő hívási konvencióval deklarálod az exportált függvényedet. Szükség esetén a memóriafoglaláshoz esetleg használd inkább az msvcrt.dll könyvtár malloc függvényét!
    Egy kis segítség: Writing Clean and Safe UDFs In Delphi
    Mutasd a teljes hozzászólást!
  • Az msvcrt.dll mallocja nem igazán jó ötlet, mivel a legtöbb sql szervereknek saját memória menedzsere van, amiben regisztrálni kell a memóriafoglalást, ezt végzi például a ib_util_malloc . Valószínűleg a régi dll van még belőle a projekt mellett vagy az elérési út elején
    Mutasd a teljes hozzászólást!
  • legtöbb sql szervereknek saját memória menedzsere van

    Alapvetően igazad van, de szükség esetén az ember támaszkodhat a tapasztalatokra is, mint jelen esetben én is az előző hozzászólásomban a csatolt leírásra...

    Amúgy meg: ib_util.cpp
    Mutasd a teljes hozzászólást!
  • Nos én az általad belinkelt kódból is azt olvasom ki, hogy csak abban az esetben lehet használni a mallocot, ha memóriamenedzser nélkül fordították a szervert és nem egy bármikor bevethető dzsóker, már csak azért is mert azt a lefoglalt memóriát valakinek fel kell szabadítania, és a memória menedzser csak azt fogja felszabadítani amit nála bejegyeztek 
    Mutasd a teljes hozzászólást!
  • a memória menedzser csak azt fogja felszabadítani amit nála bejegyeztek

    Ez így van. Nem beszélve arról, hogy az általam csatolt dokumentációnak van egy PDF-es változata is, amely ha jól emlékszem1998-as (!) keltezésű. Tehát nem éppen friss, pedig többféle weboldalon is visszakacsint.
    Az ajánlásom természetesen nem egy "örök érvényű" joker formára vonatkozik, amelynek rendszeres használatára bárkit is bátorítani szeretnék, csupán csak arra a rövid időre szól, amíg az összeomlás okának felderítése megtörténik. Ugyanis régebben volt egy olyan hibája is a szervernek, hogy abban az esetben omlott össze, ha olyan memóriát kellett felszabadítani, amelyet nem az ib_util_malloc-kal foglaltak. Gondolom ennek kiküszöbölésére javították ki az általad is említett működésre. Vagyis jelenleg már emiatt nem omlik össze, csak memóriaszivárgás lép fel, amely természetesen a végleges változatban nem maradhat!
    Akit érdekel a folyamat, az a fun.epp file alapján tájékozódhat (ld. tryLibrary).
    Mutasd a teljes hozzászólást!
  • Nem vagyok teljesen biztos abban, hogy az ib_util_malloc hívása a hibás.

    Ugyanis egy olyan függvény, aminek csak visszatérő értéke van, hiba nélkül lefut.

    function Mai_Datum : PChar;
    var
    cSeged : string;
    begin
    cSeged := FormatDateTime( 'yyyy.mm.dd', Now );
    Result := ib_util_malloc( Length( cSeged ) + 1 );
    StrPCopy( Result, cSeged );
    end;

    Firebirdben pedig:

    DECLARE EXTERNAL FUNCTION MAIDATUM
    RETURNS CSTRING (11) FREE_IT
    ENTRY_POINT 'Mai_Datum' MODULE_NAME 'AlmiraSql_UDF';

    Amúgy megnéztem, az egész rendszer 32bites, nincs másik ib_util.dll az egész gépen, csak a 'bin' könyvtárban. A saját dll-em pedig az UDF könyvtárban van, ha letörölöm, akkor korrekt hibaüzenetet kapok.
    Mutasd a teljes hozzászólást!
  • Szerintem nem ártana a cOut stringet üresre inicializálni mivel ha a feltétel nem teljesül bármilyen szemét lehet azon a memóriaterületen és a mérete akár 2 Gb is lehet a visszatérési típusként definiált 12 karakter helyett ami tényleg kifektetheti a szervert.
    Mutasd a teljes hozzászólást!
  • A hiba valódi okát valószínű, hogy teljesen máshol kell keresni! Az sem elképzelhetetlen, hogy a stack-ed esetleg összeomlott vagy az adatterület valamilyen túlírás miatt megsérült.  A szerver akkor biztos, hogy összeomlik, ha mondjuk kivétel keletkezik a függvényedben és az továbbgyűrűzik a szervíz alkalmazáshoz.
    Mindenesetre az alábbi kód nálam nem okozott semmiféle problémát pedig a 2.5-ös firebird-öt telepítettem egy virtuális XP-re.

    library AlmiraSQL_UDF; uses SysUtils; function ib_util_malloc(Size: integer): pointer; cdecl; external 'ib_util.dll'; function FKSz_Convert(cFKSz : PAnsiChar) : PAnsiChar; cdecl; var tmp : AnsiString; begin tmp := Trim(cFKSz); if (Length(tmp) > 6) then Insert('-', tmp, 7); Result := ib_util_malloc(Length(tmp) + 1); StrPCopy(Result, tmp); end; exports FKSz_Convert; begin end.

    Teszt:

    select fkszconv(null), fkszconv(' xyz '), fkszconv('abcdefghxyz') from rdb$database;
    Mutasd a teljes hozzászólást!
  • Az eljárás hívás típusával volt baj. 'stdcall'-helyett a 'cdecl' típust kell alkalmazni.
    Érdekes a dolog, hisz az összes példaprogram 'stdcall'-t használ, 2.1-es szerverrel működik is rendesen.
    Sőt utána olvastam egy kicsit, és elvileg a 'stdcall' hívási convenciót használja a Win32 API is.

    A lényeg, hogy meg lett oldva a probléma.

    Köszönöm mindenkinek az építő hozzászólásokat.
    Mutasd a teljes hozzászólást!
  •  az összes példaprogram 'stdcall'-t használ

    Sajnos a valódi helyzet ennél sokkal rosszabb. Ugyanis attól függ, hogy éppen hol nézed ezeket a példákat. Egymással teljesen ellentmondó példák vannak erre. Márpedig a hívási konvenció nagyon nem mindegy... Én "ősidőktől" fogva cdecl-et használok az interbase/firebird UDF-hez. A meglévő ib_udf és fbudf forrásásából is ezt "hámoztam" ki...

     elvileg a 'stdcall' hívási convenciót használja a Win32 API is

    Ez jelenleg teljesen érdektelen. Ideális esetben az interface-ek hívási konvencióit a dokumentációnak tartalmaznia kell!
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd