0

J'essaye d'écrire une fonction pour obtenir la liste des objets dans le schéma à partir de Redshift. J'ai créé un lien de RDS PostgreSQL vers Redshift. La requête fonctionne très bien lorsqu'elle est invoquée individuellement, mais ne fonctionne pas lorsqu'elle est écrite dans une fonction avec des arguments. Je veux passer plusieurs arguments (noms de schéma), donc j'ai utilisé les arguments VARIADIC. La fonction ressemble ci-dessous -Fonctions Postgresql - Utilisation des arguments VARIADIC de type texte

CREATE FUNCTION f_fetch_tables(VARIADIC list text[]) 
RETURNS VOID 
AS $$ 
    DECLARE 
     begin_time TIMESTAMP; 
     expire_time TIMESTAMP; 
    BEGIN 
     /* To fetch the list of all objects from Redshift */ 
     EXECUTE 'drop table if exists tmp_rs_obj_list; 
     create table tmp_rs_obj_list as 
     SELECT * FROM dblink(''rs_link'',$REDSHIFT$ select * from (select schemaname, 
     tablename from pg_tables UNION select schemaname, viewname from pg_views) where schemaname 
     not in (array_to_string($1,'','')) $REDSHIFT$) AS t1 (schema_nm varchar(30), obj_nm varchar(100))' using list; 
    END; 
    $$ 
    LANGUAGE plpgsql 
; 

La fonction compile bien et est créé avec succès, mais je ne suis pas en mesure de trouver un moyen de l'appeler -

utilisé ces appels jusqu'à présent, sans chance -

  1. sélectionnez f_fetch_tables ('{public, pg_catalog}')

    eRREUR: il n'y a pas de paramètre 1 $ Où: une erreur est survenue sur dblink Connec nommée "unnamed": impossible d'exécuter la requête .

  2. select * from f_fetch_tables (VARIADIC '{public, pg_catalog}')

    ERREUR: il n'y a aucun paramètre 1 $ Où: Une erreur est survenue sur la connexion dblink nommé "sans nom": ne pouvait pas exécuter la requête.

Toutes les suggestions seraient vraiment utiles.

Merci, Kamlesh

Répondre

2

Votre fonction présente quelques problèmes. Je suggère d'utiliser:

  • la fonction format() pour passer le paramètre facile,
  • dollar cité ($fmt$) requêtes à l'intérieur execute,
  • <> all(array) au lieu de l'opérateur not in (vous ne devez pas convertir un tableau en chaîne).

La fonction avec les modifications proposées:

create or replace function f_fetch_tables(variadic list text[]) 
returns void 
as $$ 
    declare 
     begin_time timestamp; 
     expire_time timestamp; 
    begin 
     /* to fetch the list of all objects from redshift */ 
     execute format($fmt$ 
      drop table if exists tmp_rs_obj_list; 
      create table tmp_rs_obj_list as 
       select * 
       from dblink(
        'rs_link', $redshift$ 
         select * 
         from (
          select schemaname, tablename 
          from pg_tables 
          union 
          select schemaname, viewname 
          from pg_views) s 
         where schemaname <> all(%L) 
        $redshift$) 
        as t1 (schema_nm varchar(30), obj_nm varchar(100)) 
      $fmt$, list); 
    end; 
$$ 
language plpgsql; 

Notez également la bonne façon de passer des arguments à une fonction avec le paramètre variadique:

select f_fetch_tables('pg_catalog', 'information_schema'); 
select * from tmp_rs_obj_list; 
+0

Merci @klin. Cela a fonctionné pour moi ..! –

0

Ce problème est lié aux paramètres ne variadique - même se comportent vous obtiendrez si vous utilisez trop paramètres normaux. Il est lié au SQL dynamique - les requêtes exécutées par la commande EXECUTE de PLpgSQL ont leur propre environnement. Vous ne pouvez donc pas utiliser de variables ou de références param provenant de l'environnement de la fonction.

Ce code ne fonctionne pas:

CREATE OR REPLACE FUNCTION fx(a int) 
RETURNS void AS $$ 
BEGIN 
    EXECUTE 'SELECT * FROM foo WHERE foo.a = $1'; 
END; 
$$ LANGUAGE plpgsql; 

Dans ce cas, il n'y avait pas passé un paramètre à la requête exécutée. $1 n'est pas valide. Vous devez utiliser une clause USING, lorsque vous souhaitez transmettre certains paramètres à SQL dynamique.

Ce code doit travailler:

CREATE OR REPLACE FUNCTION fx(a int) 
RETURNS void AS $$ 
BEGIN 
    EXECUTE 'SELECT * FROM foo WHERE foo.a = $1' USING a; 
END; 
$$ LANGUAGE plpgsql; 

Mais il n'a pas à résoudre votre problème aussi, parce que vous utilisez la clause USING. Mais vous utilisez la clause USING seulement au niveau EXECUTE commande - pas sur dblink niveau - où il n'est pas pris en charge. dblink API n'a rien de similaire à USING clause de EXECUTE commande. Vous devez donc créer une chaîne SQL native avec des paramètres décompressés (pré-évalués) avant de l'envoyer à l'API dblink.

Vous utilisez deux niveaux de SQL dynamique

  • EXECUTE
    • dblink

dblink ne prend pas en charge la requête paramétrisation - vous ne devriez pas utiliser lieu de paramètre détenteurs là .. $ x.

Dans ce cas, il est préférable de sérialiser un tableau d'entrée en chaîne au niveau supérieur de plpgsql et de transmettre cette chaîne comme un paramètre SQL dynamique.

DECLARE serialized_params text; 
BEGIN 
    serialized_params = (SELECT array_agg(quote_literal(quote_ident(v))) FROM unnest(ARRAY['A','b']) g(v)); 
    EXECUTE ' ....' USING serialized_params; 
END