UDXs pour étendre le langage NZPLSQL

Les utilisateurs créent des procédures stockées en écrivant des applications qui utilisent le langage NZPLSQL. NZPLSQL est un langage interprété basé sur le langage PL/pgSQL de Postgres et conçu pour l'environnement hôte.

Vous pouvez étendre le langage NZPLSQL avec la fonctionnalité UDF C++ des fonctions définies par l'utilisateur. Ces UDF doivent être invoqués en utilisant un SQL structuré de manière à garantir que les UDF s'exécutent toujours sur l'hôte Netezza Performance Server à l'intérieur de Postgres. Comme les UDF sont limités à l'hôte, ils peuvent tirer parti de l'ensemble des fonctions LIBC, des caractéristiques et des autres bibliothèques standard qui ne sont pas présentes sur les SPU basées sur le Nucleus. Ces UDF doivent également être enregistrés pour fonctionner en mode non clôturé.

Les fonctions de la bibliothèque d'exécution C décrivent l'ensemble recommandé de bibliothèques prises en charge par le LIBC pour les UDF qui peuvent être exécutées sur les SPU et sur l'hôte. Il s'agit d'un sous-ensemble de la bibliothèque LIBC complète.

Pour vous assurer que vos UDF d'extension linguistique ne s'exécutent que sur l'hôte Netezza Performance Server au sein de Postgres, vous utilisez la fonction 'getCurrentLocus() Cette fonction détecte le lieu d'exécution de l'UDF. Concevez vos UDF de manière à ce qu'ils lèvent une exception si la valeur de retour n'est pas UDX_LOCUS_POSTGRES. (Les autres valeurs possibles sont UDX_LOCUS_DBOS ou UDX_LOCUS_SPU)

À titre d'exemple, le fichier UDF C++ suivant, nommé " dir.cpp, définit trois fonctions appelées " OpenDir, " ReadDir et " CloseDir (basées sur les fonctions LIBC), qui peuvent être utilisées pour ouvrir un répertoire local sur l'hôte, lire son contenu et fermer la connexion au répertoire. Ces fonctions sont définies de manière à pouvoir être appelées à partir de NZPLSQL et doivent être exécutées en mode non clôturé pour effectuer ces actions. Le fichier " dir.cpp suit :
#include <udxinc.h>
#include <dirent.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>

using namespace nz::udx;

class OpenDir : public Udf
{
public:

    Udf * instantiate();
    ReturnValue evaluate() {
        if (getCurrentLocus() != UDX_LOCUS_POSTGRES)
            throwUdxException("opendir only supported in frontend");

#ifndef FOR_SPU
    if (isArgNull(0))
        NZ_UDX_RETURN_NULL();
    StringArg *arg = stringArg(0);
    char path[2048];
    memcpy(path, arg->data, arg->length);
    path[arg->length] = 0;

    DIR *dir = opendir(path);
    if (dir == NULL) {
        char format[2500];
        sprintf(format, "Can't open dir %s: %s", path, 
strerror(errno));
        throwUdxException(format);
    }
    NZ_UDX_RETURN_INT32((int32)dir);
#endif
    }       
};

Udf* OpenDir::instantiate()
{
    return new OpenDir;
}

class ReadDir : public Udf
{
public:

    Udf * instantiate();
    ReturnValue evaluate() {
        if (getCurrentLocus() != UDX_LOCUS_POSTGRES)
            throwUdxException("readdir only supported in frontend");
#ifndef FOR_SPU
        if (isArgNull(0))
            NZ_UDX_RETURN_NULL();
        int32 arg = int32Arg(0);
        DIR *dir = (DIR*)arg;
        struct dirent *dp;

        dp = readdir(dir);
        if (dp == NULL)
            NZ_UDX_RETURN_NULL();

        StringReturn* info = stringReturnInfo();
        memcpy(info->data, dp->d_name, strlen(dp->d_name));
        info->size = strlen(dp->d_name);
        NZ_UDX_RETURN_STRING(info);
#endif
    }
};

Udf* ReadDir::instantiate()
{
    return new ReadDir;
}

class CloseDir : public Udf
{
public:

    Udf * instantiate();

    ReturnValue evaluate() {
        if (getCurrentLocus() != UDX_LOCUS_POSTGRES)
            throwUdxException("closedir only supported in frontend");
#ifndef FOR_SPU
        if (isArgNull(0))
            NZ_UDX_RETURN_NULL();
        int32 arg = int32Arg(0);
        DIR *dir = (DIR*)arg;
        closedir(dir);        
        NZ_UDX_RETURN_BOOL(true);
#endif
    }     
};

Udf* CloseDir::instantiate()
{
    return new CloseDir;
}

L'exemple de fichier " dir.cpp stocke plusieurs pointeurs dans un champ int32. Si le système d'exploitation Netezza Performance Server passe à une version 64 bits à l'avenir, ces pointeurs devront utiliser int64 à la place.

Vous pouvez compiler et enregistrer les trois UDF dans le fichier 'dir.cpp en utilisant les trois commandes suivantes ou en utilisant les commandes CREATE AND REPLACE FUNCTION :
nzudxcompile dir.cpp --sig "opendir(varchar(any))" --return int4 
--class OpenDir --unfenced
nzudxcompile dir.cpp --sig "readdir(int4)" --return "varchar(512)" 
--class ReadDir --unfenced 
nzudxcompile dir.cpp --sig "closedir(int4)" --return "bool" 
--class CloseDir --unfenced
Créez ensuite une procédure stockée similaire à la procédure suivante :
DEV.SCHEMA(MYUSER)=> CREATE OR REPLACE PROCEDURE sp_listdirs01() RETURNS BOOL 
LANGUAGE NZPLSQL AS 
BEGIN_PROC
  DECLARE
    dirp int4;
    nm varchar(512);
    cl bool;
    dir varchar(1024);
    num int4; 
    r record;
  BEGIN
        select count(*) INTO num from _t_object where upper(objname) = 
'SORTER' and objclass = 4905 and objdb = current_db;
            IF num = 1 THEN
                DROP TABLE SORTER;
            END IF;
        CREATE TABLE SORTER (grp int4, name varchar(2000));
        dir := '/tmp/udx_known';           
        dirp := opendir(dir);
        LOOP
            nm = readdir(dirp);
            exit when nm is null;
            EXECUTE IMMEDIATE 'INSERT INTO SORTER VALUES (1, ' || 
quote_literal(nm) || ')';
        END LOOP;
        FOR r in SELECT name from sorter order by name LOOP
        RAISE NOTICE 'got %/%',  dir, r.name;
        END LOOP;
        cl = closedir(dirp);
        DROP TABLE SORTER;
        RETURN cl;
      EXCEPTION WHEN OTHERS THEN
        IF dirp is not NULL THEN
        cl = closedir(dirp);
        RETURN cl;
        END IF;
  END;
END_PROC;
L'exemple de procédure appelle les nouveaux UDF " opendir(), " readdir() et " closedir() pour opérer sur un répertoire nommé " /tmp/udx_known. Par exemple, si " udx_known " contient le programme " dir.cpp et les fichiers objets de " nzudxcompile, un appel à " sp_listdirs01() renvoie les informations suivantes :
DEV.SCHEMA(MYUSER)=> CALL sp_listdirs01(); 
call sp_listdirs01();
NOTICE:  got /tmp/udx_known/.
NOTICE:  got /tmp/udx_known/..
NOTICE:  got /tmp/udx_known/dir.cpp
NOTICE:  got /tmp/udx_known/dir.o_diab_ppc
NOTICE:  got /tmp/udx_known/dir.o_ppc
NOTICE:  got /tmp/udx_known/dir.o_x86
 SP_LISTDIRS01
---------------
 t
(1 row)
Si vous tentez d'exécuter l'un des UDF 'OpenDir, 'ReadDir ou 'CloseDir sur les SPU ou dans DBOS, le système Netezza Performance Server signale une erreur similaire au message suivant :
DEV.SCHEMA(MYUSER)=> SELECT readdir(grp) FROM customers; 
ERROR:  readdir only supported in frontend