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
)?
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;
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;
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;
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.