web-dev-qa-db-fra.com

Pl / pgsql RegClass Citing de table nommée comme mot clé

Je souhaite créer un nouveau nom de table basé sur un existant en ajoutant un suffixe. Une fonction POSTGRES 9.5 PL/PGSQL obtient le nom de la table existant transmis sous la forme regclass Tapez et renvoie le nouveau nom en tant que chaîne. J'utilise format() pour construire le nouveau nom et utiliserais généralement l'espace réservé %I. Cela fonctionne aussi longtemps que le nom de la table transmis ne correspond à aucun mot-clé PL/PGSQL. Dans ce cas, peu importe la composante type que j'ai choisie (%I ou %s), la citation est fausse.

Considérez la fonction suivante:

CREATE OR REPLACE FUNCTION make_name(old_name regclass)
RETURNS text                                           
LANGUAGE plpgsql AS                                    
$$                                                     
BEGIN                                                  
    RETURN format('%I_new', old_name);                 
END;                                                   
$$;                                                    

Supposons en outre qu'il y a deux tables: treenode et overlay. Appelez cette fonction pour les deux résultats dans les nouveaux noms suivants:

SELECT make_name('overlay'::regclass);
     make_name     
-------------------
 """overlay"""_new
(1 row)

SELECT make_name('treenode'::regclass);
  make_name   
--------------
 treenode_new
(1 row)

Comme il s'avère overlay est également une fonction PL/PGSQL (juste comme format, mais treenode n'est pas), ce qui semble changer de comportement de cite. Si %s est utilisé avec format(), le résultat serait "overlay"_new. Il en va de même lorsque j'utilise l'opérateur ||. Si j'utilise text comme type de paramètre d'entrée, tout fonctionne comme prévu, mais je préférerais utiliser regclass.

Existe-t-il un moyen de formater une chaîne avec un nom de table regclass (par exemple, overlay) sans guillemets, tout comme c'est le cas pour une correspondance non clés regclass Nom de la table (par exemple treenode)?

5
tomka

Explication

Je vais passer à travers les multiples couches de malentendus un par un - arrivant à une solution simple et sécurisée.

La raison pour laquelle overlay est cité est pas car c'est un nom de la fonction, mais parce que c'est un mot réservé .
[.____] aussi, overlay() est non A "Fonction PL/PGSQL" (NOR PL/PGSQL Mot-clé), c'est une norme Fonction SQL intégrée au noyau Postgres (LANGUAGE internal, Donc c derrière les rideaux). En fait, PL/PGSQL n'a rien à voir avec aucun de cela.

Une simple erreur logique.

format('%I_new', old_name)

Cela échapperait à tout identifiant non standard avec des citations doubles environnantes avant _ nouveau ' est ajouté et échouerait même avec text comme type d'entrée. Vous devez appendez '_ NOUVEAU ' Avant de citer l'ensemble de celui-ci:

format('%I', old_name || '_new')

Mais cela ne fonctionnera toujours pas pour regclass. Continue de lire.

Il est absurde d'ajouter des citations à une variable regclass avec %I, Car sa représentation de texte est déjà citée automatiquement si nécessaire. (Semblable à quote_ident(), mais ne peut pas être nul; puisque l'entrée est regclass _ Cela ne peut pas être null pour commencer.) Vous l'appliqueriez deux fois. Utilisez toujours %s Pour regclass entrée (la chaîne telle quelle).

format() est surchargée pour cela. Il suffit d'utiliser quote_ident() :

quote_ident(old_name || '_new')

surtout, comme mentionné ci-dessus, la valeur text représentation d'un regclass La variable est automatiquement échappée. Cela est intégré à la distribution. Pour accéder au texte brut, récupérez-le de catalogue système pg_class directement. A regclass valeur est juste le oid de cette table en interne.

SELECT relname FROM pg_class WHERE oid = $1;

Solution

Cela fait ce que vous recherchez:

CREATE OR REPLACE FUNCTION make_name(old_name regclass)
  RETURNS text AS
$func$
SELECT quote_ident(relname || '_new') FROM pg_class WHERE oid = $1;
$func$  LANGUAGE sql STABLE;

Test

CREATE TEMP TABLE "sUICiDAL' namE"();
CREATE TEMP TABLE overlay();

SELECT make_name('overlay') AS n1
     , make_name('"sUICiDAL'' namE"')  AS n2;

     n1      |          n2
-------------+----------------------
 overlay_new | "sUICiDAL' namE_new"

Notez que overlay_new Est un identifiant parfaitement légal afin que ce ne soit cité.

Si vous voulez le nom non évalué (pas de citations doubles), utilisez simplement:

SELECT relname || '_new' FROM pg_class WHERE oid = $1;
7
Erwin Brandstetter

La seule façon dont je pouvais penser était d'envelopper l'appel à la fonction format dans un translate comme suit:

RETURN translate(format('%I_new',old_name),'"','');

Cet article traite la sortie du format et des bandes les "caractères.

Je ne sais pas si quelqu'un a une solution plus élégante. Je pensais que Btrim, Rtrim, etc. Mais il aurait besoin d'un appel à chacun.

1
smbennett1974