SQL : UDF(fonctions utilisateur) UDT(types Util.)

BoTTom |    Changer de couleur
 
SQL , V4R40 admet maintenant
 
 les fonctions définies par l'utilisateur        [UDF]
 
 les types de données définis par l'utilisateur  [UDT]
 
une fonction est un programme ou une procédure dans un programme de service
 enregistré(e) dans les catalogues SQL par CREATE FUNCTION.
 
 par exemple :
 
    CREATE FUNCTION AF4TEST/DT8_CHAR8 (DEC(8 , 0) )
                              RETURNS  CHAR(8)
           EXTERNAL NAME 'AF4TEST/DT_SQL(DT8_CHAR8)'    (1)
           PARAMETER STYLE GENERAL                      (2)
           RETURNS NULL ON NULL INPUT  ;                (3)
 
(1) fait référence à DT8_CHAR8 dans DT_SQL
(2) le passage de paramètres se fait sans gestion de la val. nulle
(3) la fonction retourne nul si un des argument est nul
     (il n'y aura pas d'appel)


|    Changer de couleur
 
 détail de la syntaxe :
 
 (1) external name : a/ bib/nom           ==> *PGM
 
                     b/ bib/nom(fonction) ==> fonction dans un *SRVPGM
 
 
 (2) type de paramètres
 
                    a/ SQL   convient pour les appels de PGM
 
 
                             a1/ paramètres recus (x fois)
 
                             a2/ val. retour en tant que paramètre en E/S
 
                             a3/ indicateurs(binaires) pour val nulle de a1.
 
                             a4/ indicateur (binaire) pour val. nulle de a2.
 
 


|    Changer de couleur
                             a5/ SQLSTATE
 
                                 '00000' = fonction terminée sans erreur
                                 '01Hxx' = Warning (xx n'importe pas)
                                 '38Ixx'à '38Zxx'= fonction terminée en
                                           erreur ==> génère SQLCODe négatif
 
                             a6/ nom qualifié de la fonction (139 car.)
 
                             a7/ nom complet, non qualifié   (128 car.)
 
                             a8/ message de diagnostique (VARCHAR)
 
                    b/ DB2SQL gère les appels succéssifs
 
                             contient de a1 à a8 , plus
 
                             b9/ scratchpad = zone de dialogue , conservé
                                               entre deux appels
                             b10/ type d'appel -1= premier , 1 =dernier
                                                0 = autre
                             b11/ dbinfo (voir sqludf dans QSYSINC/H)


|    Changer de couleur
 
                    C/ GENERAL appel d'une fonction (*SRVPGM)
 
                       c1/ paramètres recus
 
                           (la valeur resulta est la valeur retournée
                             par la fonction)
 
 
                           RETURNS NULL ON NULL INPUT évite un appel si
                           l'un des paramètres est null, SQL assumant
                           automatiquement un résultat NULL.
 
 
                    D/ GENERAL WITH NULL
 
                         idem GENERAL avec réception des indicateurs nulls
                          et gestion par le programme (comme en a et b)
 
 
 
 


|    Changer de couleur
 
 voici le source RPG de la procédure associée à la fonction vue au début :
 
 H nomain
  * prototype  =========================================================
 D dt8_char8       pr             8
 D                                8  0 const
  * fonction  ==========================================================
 Pdt8_char8        b                   export
 d                 pi             8
 d dt8_recue                      8  0 const
 
 d date            s               D   datfmt(*dmy/)
 c     *iso          test(d e)               dt8_recue
 
 c                   if        %error
 c                   return    '01/01/40'
 c                   else
 c     *iso          move      dt8_recue     date
 c                   return    %char(date)
 c                   endif
 pdt8_char8        e


|    Changer de couleur
 
la création se réalise par :
 
   CRTRPGMOD MODULE(DT8_CHAR8)
 et
   CRTSRVPGM SRVPGM(DT_SQL) MODULE(DT8_CHAR8) EXPORT(*ALL)
___________________________________________________________________________
 
 cela permet maintenant de passer des ordres SQL comme :
 
     [soit un fichier client avec DATCRT DEC(8 , 0) ]
 
 SELECT * FROM  CLIENTS WHERE DT8_CHAR8(DATCRT) <> '01/01/40'
 
 SELECT * FROM CLIENTS WHERE DATE( DT8_CHAR8(DATCRT) ) = current date
 
  ..................................................................
  :                                                                :
  : la fonction SQL date attend une chaîne de caractères au format :
  :   du JOB, soit en interactif 'JJ/MM/AA'.                       :
  :                                                                :
  :................................................................:


|    Changer de couleur
 
 Deuxième exemple retournant une date au format caractère en ISO
 
d dt8_char10      pr            10
d                                8  0 const
 *---------------------------------------------------------
Pdt8_char10       b                   export
d                 pi            10
d dt8_recue                      8  0 const
 
d date            s               D   datfmt(*iso)
c     *iso          test(d e)               dt8_recue
 
c                   if        %error
c                   return    '0001-01-01'
c                   else
c     *iso          move      dt8_recue     date
c                   return    %char(date)
c                   endif
c
Pdt8_char10       e
 


|    Changer de couleur
 
déclaration :
 
 CREATE FUNCTION AF4TEST/DT8_CHAR10 (DEC(8 , 0) )
                           RETURNS  CHAR(10)
        EXTERNAL NAME 'AF4TEST/DT_SQL(DT8_CHAR10)'
        PARAMETER STYLE GENERAL
        RETURNS NULL ON NULL INPUT  ;
 
 lors de la déclaration de la fonction vous pouvez convertir (caster)
   un/les argument(s) ou la valeur retour
 
 CREATE FUNCTION AF4TEST/DT8_DATE (DEC(8 , 0) )
                           RETURNS  DATE CAST FROM CHAR(10) 
        EXTERNAL NAME 'AF4TEST/DT_SQL(DT8_CHAR10)'
        PARAMETER STYLE GENERAL
        RETURNS NULL ON NULL INPUT  ;
 
 ce qui permet directement :
 
  SELECT * FROM CLIENTS WHERE DT8_DATE(DATCRT) = current date
 


|    Changer de couleur
 
* deux autres exemples, l'un sous forme de *SRVPGM l'autre de *PGM :
 
 ........................................................................
 : RAPPEL : un programme de service est l'équivalent d'une DLL Windows. :
 :                                                                      :
 : c'est à dire un objet contenant PLUSIEURS routines autonomes, créé   :
 :  à partir du langage C ou RPG4.                                      :
 :                                                                      :
 : Pour le GAP4, le source commence par NOMAIN (pas de pgm principal)   :
 :  et chaque routine est encadrées de deux "spécifs" P (début/fin).    :
 :......................................................................:
 
 
CREATE FUNCTION AF4SRCT/NBCAR (CHAR(50) )
                          RETURNS  INTEGER
       EXTERNAL NAME 'AF4SRCT/NBCAR(NBCAR)'
       PARAMETER STYLE GENERAL RETURNS NULL ON NULL INPUT
 
      **********************************************************************
      * compte le nbr de caractères non blancs                             *
      **********************************************************************


|    Changer de couleur
 
     h nomain 
 
     D nbcar           pr            10I 0
     D  chaine                       50    const
 
     Pnbcar            B                   export 
     D nbcar           pi            10I 0
     D  chaine                       50    const
 
     D w               s             10I 0
     D i               s             10I 0
 
     c                   for       i = 1 to %len(%trim(chaine))
     c                   if         %subst(chaine : i : 1) <> ' '
     c                   eval       w = w +1
     c                   endif
     c                   endfor
 
     c                   return    w
     Pnbcar            E 
 


|    Changer de couleur
 
CREATE FUNCTION AF4SRCT/NBNUM (CHAR(50) )
                          RETURNS  INTEGER
       EXTERNAL NAME 'AF4SRCT/NBNUM'
       LANGUAGE RPGLE NO SQL
       PARAMETER STYLE SQL RETURNS NULL ON NULL INPUT
 
...........................................................................
:                                                                         :
: Dans le cas d'un programme (tout langage), vous recevez des paramètres  :
:  de manière traditionnelle. Autant de paramètres qu'il y en a dans la   :
:  fonctions, plus UN pour la valeur retour.                              :
:                                                                         :
: Ainsi que ceux correspondant à votre choix (SQL, DB2SQL, ...)           :
:.........................................................................:
 
 ici, le programme va signaler un caractère invalide (lettre) en renseignant
  SQLSTATE (38I01) et le message de diagnostique :
 
      **********************************************************************
      * compte le nbr de chiffre avant premier blanc                       *
      **********************************************************************


|    Changer de couleur
      *paramètres
     D chaine          s             50
     D retour          s             10I 0
      *indicateurs SQL
     D chaine_ind      s              5I 0
     D retour_ind      s                         like(chaine_ind)
      *
     D SQLSTATE        s              5
     D fonction_qual   s            139
     D fonction_nom    s            128
     D msg_diag        s             70          varying
      * autres variables de travail
     D w               s             10I 0
     D i               s             10I 0
 
     C     *entry        plist
     C                   parm                    chaine
     C                   parm                    retour
     C                   parm                    chaine_ind
     C                   parm                    retour_ind
     C                   parm                    sqlstate
     C                   parm                    fonction_qual


|    Changer de couleur
     C                   parm                    fonction_nom
     C                   parm                    msg_diag
      * début du code
     C                   eval      SQLSTATE = '00000'
     c                   for       i = 1 to %len(%trim(chaine))
     c                   if         %subst(chaine : i : 1) < '0'
     c                              or %subst(chaine : i : 1) > '9'
 
      * car autre que blanc ==> erreur
     c                   if         %subst(chaine : i : 1) <> ' '
     c                   eval       SQLSTATE = '38I01'
     c                   eval       msg_diag = 'zone contient des car.+
     c                               invalides'
     c                   endif
 
     c                   else
     c                   eval       w = w +1
     c                   endif
     c                   endfor
 
     c                   eval      retour = w
     c                   eval      *inlr = *on


|    Changer de couleur
 Il peut exister plusieurs versions d'une même fonction
 
 par exemple   CALECHEANCE( date) et CALECHEANCE( dec(8,0) ),
  dans le fichier SYSFUNCS, cela s'appele une signature (PARM_SIGNATURE)
 
 c'est le type qui fait référence, pas la longueur
 (par exemple transmettre dec(8,0) ou dec(6,0) c'est pareil)
 
 Donc si vous essayez
 
  > SELECT NBNUM('123456')  from QSQPTABL
    NBNUM de type *N dans *LIBL non trouvé. (SQL0204)
 
 SQL n'a pas trouvé la version de NBNUM attendant du VARCHAR
 (en effet les constantes sont toujours traitées par SQL comme du VARCHAR)
 
le plus simple semble être de créer une deuxième version de la fonction :
 
CREATE FUNCTION AF4TEST/NBNUM (p1 VARCHAR(50) )
                RETURNS INTEGER
LANGUAGE SQL
RETURN NBNUM( CAST(P1 as CHAR(50) ) )


|    Changer de couleur
 
 Vous pouvez aussi créer des fonctions SQL à l'aide du PL/SQL (ou SQL PSM).
 
 ATTENTION, avant la V5R10, il faut avoir le compilateur C.
 
 En effet une fonction écrite complétement en SQL génère un source C
  contenant du SQL (dans QTEMP) et lance la commande CRTSQLCI afin
  de créer un programme de service.
 
exemple :
 
  create function af4test/dec_franc_euros (VALEUR decimal(9, 2))
                  returns decimal(9, 2)
  LANGUAGE SQL RETURNS NULL ON NULL INPUT
 
  BEGIN
  DECLARE VALEUROS DECIMAL ( 9 , 2 ) ;
   SET VALEUROS = VALEUR / 6.55957;
   RETURN VALEUROS ;
  END;
 
C'est le même langage que pour l'ordre CREATE PROCEDURE.


|    Changer de couleur
 
 et bien évidement la fonction inverse :
 
 create function af4test/dec_euro_francs (VALEUR decimal(9, 2))
                 returns decimal(9, 2)
 LANGUAGE SQL RETURNS NULL ON NULL INPUT
 
 BEGIN (1)
(3) DECLARE VALFRANCS DECIMAL ( 9 , 2 ) ;(2)
(4) SET VALFRANCS= VALEUR * 6.55957;
(5) RETURN VALFRANCS ;
 END;
 
(1)  le corps de la fonction est encadré de BEGIN  - END;
 
(2) chaque ligne se termine par ";"
 
(3) les déclarations se font par DECLARE (même types que CREATE TABLE)
 
(4) les manipulations se font par SET ou VALUES
 
(5) RETURN termine la fonction en indiquant la valeur retournée.


|    Changer de couleur
 
 Et pour terminer les types de données définis par l'utilisateur
 
 ils sont créés par :
 
     CREATE DISTINCT TYPE IMAGE AS BLOB(512K)
 
     CREATE DISTINCT TYPE EUROS AS DECIMAL(9, 2)
 
     CREATE DISTINCT TYPE FRANCS AS DECIMAL(9, 2)
 
vous pouvez maintenant créer une table par
 
 
     CREATE TABLE VOITURES
 
            (CODE CHAR(10) NOT NULL PRIMARY KEY,
 
             PRIXf FRANCS NOT NULL, PRIXe EUROS,
 
             PHOTO IMAGE)
 


|    Changer de couleur
 
 la particularité de ces types est qu'ils sont fortement typés 
 
    c'est à dire que l'on ne PEUT PAS COMPARER DES FRANCS et DES EUROS.
 
 
   WHERE PRIXF > PRIXE   est syntaxiquement invalide !!!
 
   WHERE PRIXF > 10000 aussi !
 
seules des FONCTIONS supportant ces types sont habilitées à les manipuler.
 
 on parle alors de SGBD-OO (ou orientés objet)
 
le système assure une convertion avec le type d'origine, par CAST
 
 
 WHERE CAST(PRIXF as DECIMAL(9, 2)) > 10000 est admis
 
 ainsi que
 
 WHERE PRIXF > FRANCS(10000)


|    Changer de couleur
 
 pour la comparaison prixe / prixf le plus simple est d'écrire des fonctions
 
 une fonction de conversion francs/euros
  une fonction de conversion euros/francs
 
 
 
 create function af4test/franc_euros (VALEUR FRANCS)
                 returns EUROS
 LANGUAGE SQL RETURNS NULL ON NULL INPUT   STATIC DISPATCH
 
 BEGIN
 DECLARE VALEUROS DECIMAL ( 9 , 2 ) ;
  SET VALEUROS = CAST(VALEUR as decimal(9,2) ) / 6.55957;
  RETURN CAST(VALEUROS as euros);
 END;
 
 
 
L'option STATIC DISPATCH est obligatoire quand une fonction manipule des UDT
 


|    Changer de couleur
 
  create function af4test/euro_francs (VALEUR EUROS)
                  returns FRANCS
  LANGUAGE SQL RETURNS NULL ON NULL INPUT   STATIC DISPATCH
 
  BEGIN
  DECLARE VALFRANCS DECIMAL ( 9 , 2 ) ;
   SET VALFRANCS= cast(VALEUR as decimal(9,2) ) * 6.55957;
   RETURN cast(VALFRANCS as FRANCS);
  END;
 
  > select * from voitures
    where prixe = prixf
    Opérandes de l'opérateur = incompatibles. 
 
  > select * from voitures
     where prixe <> franc_euros(prixf)
   donne un résultat , ainsi que
 
  > update voitures set prixe = franc_euros(prixf)
     where prixe = euros(0)
 


|    Changer de couleur
 
 il vous faut aussi, refédinir toutes les fonctions SUM, AVG, etc...
 
 on parle alors de fonctions dérivées.(parlerons nous de surcharge ??)
 
 il peut donc y avoir plusieurs fonctions ayant le même nom, si elles
   acceptent des types de données différents.(notion de polymorphisme)
 
 SQL choisi la bonne fonction, suivant le type manipulé.
 
 create function af4test/SUM (francs)
                  returns francs
        source   QSYS2/SUM (decimal() ) ;
 create function af4test/AVG (francs)
                  returns francs
        source   QSYS2/AVG (decimal() ) ;
 create function af4test/MAX (francs)
                 returns francs
        source   QSYS2/MAX (decimal() ) ;
 create function af4test/MIN (francs)
                 returns francs
        source   QSYS2/MIN (decimal() ) ;


|    Changer de couleur
 
 Toutes les fonctions SQL sont réputées pour être stockées dans QSYS2.
 
 (ne cherchez pas un programme physique, il n'y en a pas)
 
 
 toutes vos fonctions, tous vos types :
 
 1/ peuvent être qualifiés en convention SQL [biblio.fonction]
 
 
 2/ seront recherchés dans *LIBL, sinon.
 
 
 
 3/ vous pouvez préciser le chemin au préalable par
 
    SET PATH  .....
 
    et retrouver cette valeur par le registre CURRENT_PATH
 
 





©AF400