Matthias: Aufruf einer Oracle-Funktion mittels Perl

Hallo,

vielleicht kann mir hier jemand einen kleinen Tip geben, ich kriege es einfach nicht auf die Reihe :-(

Ich habe in Oracle eine Funktion in einem Package definiert:

IMPORT (FUNCTION) <return value>       VARCHAR2 OUT
IMPORT            ID                   NUMBER   IN
IMPORT            BRUTTO               NUMBER   IN
IMPORT            NETTO                NUMBER   IN
IMPORT            BESCHREIBUNG         VARCHAR2 IN
IMPORT            FIRMA                VARCHAR2 IN
IMPORT            X                    NUMBER   IN
IMPORT            Y                    NUMBER   IN
IMPORT            Z                    NUMBER   IN
IMPORT            ACHSE                VARCHAR2 IN
IMPORT            EINGANG              VARCHAR2 IN
IMPORT            FEHLER               VARCHAR2 IN
IMPORT            DATEI_1              BLOB     IN
IMPORT            DATEI_2              BLOB     IN

Über PL/SQL kann ich die Funktion ohne Fehler wie folgt aufrufen:

declare
  out VARCHAR2(500);
begin
  out := my_func(123, null, null, null, null, null, null, null, null, 'BEISPIEL', 'TEST', null, null);
  dbms_output.put_line('Function returns ' || out || '.');
end;

Das gleiche mittels DBI in Perl sieht so aus:

my $out;

my $sth = ${dbh}->prepare("BEGIN ? := my_func(?,?,?,?,?,?,?,?,?,?,?,?,?); END;") || exit 1;

${sth}->execute($out, $id, undef, undef, undef, undef, undef, undef, undef, undef, $eingang, $fehler, undef, undef) || exit 1;

id, eingang und fehler sind gleich wie oben.

Beim Aufruf erhalte ich allerdings den folgenden Fehler:

DBD::Oracle::st execute failed: ORA-06550: line 1, column 14:
PLS-00306: wrong number or types of arguments in call to 'MY_FUNC'
ORA-06550: line 1, column 14:
PLS-00306: wrong number or types of arguments in call to 'MY_FUNC'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored (DBD ERROR: error possibly near <*> indicator at char 13 in 'BEGIN :p1 := <*>my_func(:p2,:p3,:p4,:p5,:p6,:p7,:p8,:p9,:p10,:p11,:p12,:p13,:p14); END;') [for Statement "BEGIN ? := my_func(?,?,?,?,?,?,?,?,?,?,?,?,?); END;" with ParamValues: :p5=undef, :p12='TEST', :p8=undef, :p14=undef, :p10=undef, :p13=undef, :p2='123', :p3=undef, :p6=undef, :p7=undef, :p1=undef, :p4=undef, :p9=undef, :p11='BEISPIEL'] at ./test.pl line 91.

Anzahl und Typ der Argumente stimmt imho. Ich steh hier total auf dem Schlauch.

Vielen Dank im voraus,
Matthias

  1. Moin Moin!

    Ausgabe-Parameter kannst Du nicht einfach an execute() übergeben, denn dann zählen sie nur als Eingabeparameter. Dir fehlt bind_param_inout(), siehe auch PL/SQL-Examples in DBD::Oracle.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
    1. Hallo Alexander,

      vielen Dank für Deinen Tipp. Ich habe das Programm jetzt wir folgt abgeändert, ich erhalte aber immer noch die gleiche Fehlermeldung:

      my $sth = $dbh->prepare("BEGIN :out := my_func(:p1, :p2, :p3, :p4, :p5, :p6, :p7, :p8, :p9, :p10, :p11, :p12, :p13); END;") || exit 1;

      ${sth}->bind_param(":p1", $id);
      ${sth}->bind_param(":p2", undef);
      ${sth}->bind_param(":p3", undef);
      ${sth}->bind_param(":p4", undef);
      ${sth}->bind_param(":p5", undef);
      ${sth}->bind_param(":p6", undef);
      ${sth}->bind_param(":p7", undef);
      ${sth}->bind_param(":p8", undef);
      ${sth}->bind_param(":p9", undef);
      ${sth}->bind_param(":p10", $eingang);
      ${sth}->bind_param(":p11", $fehler);
      ${sth}->bind_param(":p12", undef);
      ${sth}->bind_param(":p13", undef);

      ${sth}->bind_param_inout(":out", $out, 500);

      ${sth}->execute;

      Hast Du noch einen Tipp, was ich hier falsch mache?

      Vielen Dank im voraus,
      Matthias

      1. Moin Moin!

        Hallo Alexander,

        vielen Dank für Deinen Tipp. Ich habe das Programm jetzt wir folgt abgeändert, ich erhalte aber immer noch die gleiche Fehlermeldung:

        Diese? "DBD::Oracle::st execute failed: ORA-06550: line 1, column 14:
        PLS-00306: wrong number or types of arguments in call to 'MY_FUNC'"

        Dann würde ich mal nachsehen, wie MY_FUNC definiert ist. Ganz offensichtlich hat Oracle mit der Anzahl oder dem Typ der Parameter ein Problem.

        my $sth = $dbh->prepare("BEGIN :out := my_func(:p1, :p2, :p3, :p4, :p5, :p6, :p7, :p8, :p9, :p10, :p11, :p12, :p13); END;") || exit 1;

        ${sth}->bind_param(":p1", $id);
        ${sth}->bind_param(":p2", undef);
        ${sth}->bind_param(":p3", undef);
        ${sth}->bind_param(":p4", undef);
        ${sth}->bind_param(":p5", undef);
        ${sth}->bind_param(":p6", undef);
        ${sth}->bind_param(":p7", undef);
        ${sth}->bind_param(":p8", undef);
        ${sth}->bind_param(":p9", undef);
        ${sth}->bind_param(":p10", $eingang);
        ${sth}->bind_param(":p11", $fehler);
        ${sth}->bind_param(":p12", undef);
        ${sth}->bind_param(":p13", undef);

        ${sth}->bind_param_inout(":out", $out, 500);

        ${sth}->execute;

        Das sieht auf den ersten Blick ok aus, soll heißen: 13 Parameter und bind_param_inout() für den Rückgabewert. ABER: Weil Du bei bind_param() keine expliziten Typangaben machst (dritter Parameter fehlt), wird DBI bzw. DBD::Oracle irgendeinen Default-Typ nehmen, bei DBD::Oracle ist das ohne weitere Einstellungen VARCHAR2.

        Deine Oracle-Funktion hat aber auch noch einige NUMBER- und BLOB-Argumente, und ich schätze, dass Oracle keinen Bock hat, die VARCHAR2s automatisch umzuwandeln.

        Ruf also bind_param() mit einem zum jeweiligen Parameter passenden Datentyp auf. Das ist in der Doku von DBD::Oracle ziemlich ausführlich beschrieben.

        Übrigens ist dort auch noch eine Perle zum dritten Parameter von bind_param_inout() zu finden: >>The third parameter of bind_param_inout_array, (0 in the example), "maxlen" is required by DBI but not used by DBD::Oracle.<<

        Wenn Du also nur mit Oracle arbeiten willst, kannst Du Dir das Größenraten komplett sparen, immer 0 nehmen, und Oracle / DBD::Oracle den Rest machen lassen.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
        1. Hallo Alexander,

          vielen Dank für deine Hilfe, jetzt geht es.

          Deine Oracle-Funktion hat aber auch noch einige NUMBER- und BLOB-Argumente, und ich schätze, dass Oracle keinen Bock hat, die VARCHAR2s automatisch umzuwandeln.

          Genau das war das Problem.

          Viele Grüße,
          Matthias

          1. Moin Moin!

            vielen Dank für deine Hilfe, jetzt geht es.

            Na also. Es lohnt sich also doch, gelegentlich mal die Dokumentation gelesen zu haben. ;-)

            Deine Oracle-Funktion hat aber auch noch einige NUMBER- und BLOB-Argumente, und ich schätze, dass Oracle keinen Bock hat, die VARCHAR2s automatisch umzuwandeln.

            Genau das war das Problem.

            Naja, wenn ich fies wäre, würde ich sagen, dass Du die Doku von DBI und DBD::Oracle nicht genau genug gelesen hast. Das ist allerdings wirklich eine Menge Stoff, und einige kleinere Details versteht man erst, wenn man sich lange genug mit dem DBI befaßt hat.

            Ganz allgemein gesagt ist einer der Tricks vom DBI, sich möglichst alle Daten von der jeweiligen Datenbank als String anliefern zu lassen und umgekehrt alle Daten als String in die Datenbank zu schieben. Die Umwandlung in irgendetwas anderes (Zahl, BLOB, Datum) erledigt dann jede Seite (Perl, Datenbank) für sich. Im SQL-Umfeld ist das der "natürliche" Weg, mit Daten umzugehen, und auch Perl hat damit nur sehr selten Probleme.

            Alexander

            --
            Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".