Préparer
Vos formules ressemblent à ceci:
d*b+(l*4+r)+(i/d)+s
Je remplacerais les variables par une $n
notation afin qu'elles puissent être remplacées par des valeurs directement dans plpgsql EXECUTE
(voir ci-dessous):
$1*$5+($3*4+$2)+($6/$1)+$4
Vous pouvez également stocker vos formules originales (pour l'œil humain) ou générer ce formulaire dynamiquement avec une expression comme:
SELECT regexp_replace(regexp_replace(regexp_replace(
regexp_replace(regexp_replace(regexp_replace(
'd*b+(l*4+r)+(i/d)+s'
, '\md\M', '$1', 'g')
, '\mr\M', '$2', 'g')
, '\ml\M', '$3', 'g')
, '\ms\M', '$4', 'g')
, '\mb\M', '$5', 'g')
, '\mi\M', '$6', 'g');
Assurez-vous simplement que votre traduction est bonne. Quelques explications pour les expressions regexp :
\ m .. ne correspond qu'au début d'un mot
\ M .. ne correspond qu'à la fin d'un mot
4ème paramètre 'g'
.. remplacer globalement
Fonction principale
CREATE OR REPLACE FUNCTION f_calc(
d int -- days worked that month
,r int -- new nodes accuired
,l int -- loyalty score
,s numeric -- subagent commission
,b numeric -- base rate
,i numeric -- revenue gained
,formula text
,OUT result numeric
) RETURNS numeric AS
$func$
BEGIN
EXECUTE 'SELECT '|| formula
INTO result
USING $1, $2, $3, $4, $5, $6;
END
$func$ LANGUAGE plpgsql SECURITY DEFINER IMMUTABLE;
Appel:
SELECT f_calc(1, 2, 3, 4.1, 5.2, 6.3, '$1*$5+($3*4+$2)+($6/$1)+$4');
Retour:
29.6000000000000000
Points majeurs
La fonction prend 6 paramètres de valeur et formula text
7e. J'ai mis la formule en dernier, afin que nous puissions utiliser à la $1 .. $6
place de $2 .. $7
. Juste pour des raisons de lisibilité.
J'ai attribué des types de données pour les valeurs comme bon me semblait. Attribuez les types appropriés (pour implémenter les contrôles de base de santé mentale) ou faites-les tous numeric
:
Passez des valeurs pour l'exécution dynamique avec la USING
clause. Cela évite de lancer dans les deux sens et rend tout plus simple, plus sûr et plus rapide.
J'utilise un OUT
paramètre parce qu'il est plus élégant et rend la syntaxe plus claire et plus courte. Une finale RETURN
n'est pas nécessaire, la valeur du ou des paramètres OUT est retournée automatiquement.
Considérez la conférence sur la sécurité par @Chris et le chapitre "Écrire des fonctions de DEFINER DE SECURITE en toute sécurité" dans le manuel. Dans ma conception, le point d'injection unique est la formule elle-même.
Vous pouvez utiliser des valeurs par défaut pour certains paramètres pour simplifier davantage l'appel.
CREATE FUNCTION foo(text) returns text IMMUTABLE LANGUAGE SQL SECURITY DEFINER AS $$...
) ou vous pouvezALTER FUNCTION foo(text) SECURITY DEFINER
a+b
- dire qu'il soit stocké dans une colonne de type texte dans un tableau, alors j'ai une fonctionfoo(a int, b int,formula text)
si elle obtient la formule est a + b comment puis-je faire en sorte que la fonction fasse réellement un + b au lieu de -je devoir avoir un énoncé de cas très long pour toutes les formules possibles et répéter le code dans tous les segments?Une alternative au simple stockage de la formule puis à son exécution (qui, comme Chris l'a mentionné, a des problèmes de sécurité ) serait d'avoir une table distincte appelée
formula_steps
qui contiendrait essentiellement les variables et les opérateurs et la séquence dans laquelle ils sont exécutés. Ce serait un peu plus de travail, mais ce serait plus sûr. Le tableau pourrait ressembler à ceci:Une autre option serait d'utiliser une bibliothèque / un outil tiers pour évaluer les expressions mathématiques. Cela rendrait votre base de données moins vulnérable à l'injection SQL, mais maintenant vous venez de déplacer les problèmes de sécurité possibles vers votre outil externe (qui pourrait encore être assez sûr).
La dernière option serait d'écrire (ou de télécharger) une procédure qui évalue les expressions mathématiques. Il existe des algorithmes connus pour ce problème, il ne devrait donc pas être difficile de trouver des informations en ligne.
la source