J'ai une fonction PL/PGSQL (voir ci-dessous) qui répertorie certains champs et efface leur contenu à l'aide de commandes de mise à jour construites de manière dynamique.
Lorsque j'ai défini log_statement = 'mod'
, Je ne vois rien sur le journal lors de l'exécution de la fonction avec SELECT fnct_clear_temp_fields();
. Lorsque j'ai défini log_statement = 'all'
Et exécutez la fonction que je peux voir SELECT fnct_clear_temp_fields();
dans le journal, mais pas les commandes de mise à jour sous-jacentes.
Y a-t-il un moyen de faire apparaître les commandes de mise à jour dans le journal?
Pour info, voici la fonction:
CREATE OR REPLACE FUNCTION fnct_clear_temp_fields() RETURNS VOID AS $$
DECLARE
--Put into a cursor a view dynamically listing all user-defined fields beginning with 'temp_'
dataset_1 CURSOR FOR
SELECT
column_name,
table_name
FROM information_schema.tables
NATURAL JOIN information_schema.columns
WHERE
table_schema='public'
AND table_type='BASE TABLE'
AND column_name ~ '^temp_'
ORDER BY table_name,column_name;
--Record variable to go through each line of the view above
dataset_1_row RECORD;
BEGIN
OPEN dataset_1; --Open the cursor
FETCH dataset_1 INTO dataset_1_row; --first row of the view
WHILE FOUND LOOP
RAISE NOTICE 'Table: %, Column: %', dataset_1_row.table_name,dataset_1_row.column_name;
--Set to NULL the contents of the current 'temp_' column
EXECUTE 'UPDATE '||dataset_1_row.table_name||' SET '||dataset_1_row.column_name||'=NULL WHERE '||dataset_1_row.column_name||' IS NOT NULL';
FETCH dataset_1 INTO dataset_1_row; --next row please.
END LOOP; --while end
CLOSE dataset_1;
RETURN;
END;
$$ LANGUAGE plpgsql;
Donc, ma suggestion comme réponse réelle:
Si vous en avez besoin uniquement dans cette fonction, vous pouvez faire un RAISE LOG '%', your_statement;
, ou dans votre code actuel:
...
DECLARE
exec_str text;
...
--Set to NULL the contents of the current 'temp_' column
exec_str := 'UPDATE '||dataset_1_row.table_name||
'SET '||dataset_1_row.column_name||'=NULL
WHERE '||dataset_1_row.column_name||' IS NOT NULL';
RAISE LOG 'Query executed: %', exec_str;
EXECUTE exec_str;
...
Aussi, je trouve le
FOR dataset_1_row IN SELECT ...
LOOP
END LOOP;
construire beaucoup plus doux.
Là est une manière intégrée pour enregistrer toutes les instructions à l'intérieur des fonctions PLPGSQL: auto-explain
LOAD 'auto_explain';
SET auto_explain.log_min_duration = 1; -- exclude very fast trivial queries
SET auto_explain.log_nested_statements = ON; -- statements inside functions
Détails Sous cette question étroitement liée:
[.____] plan de requête Postgres d'une invocation UDF écrite dans PGPSQL
Potentiellement génère beaucoup de la sortie du journal. Je n'utiliserais que pour le débogage, pas en production.
[.____] Si vous avez juste besoin de la déclaration unique enregistrée, allez-y Conseil de @ DezSo .
Considérez cette fonction réécrite:
CREATE OR REPLACE FUNCTION fnct_clear_temp_fields()
RETURNS void AS
$func$
DECLARE
rec record;
qry text;
BEGIN
FOR rec IN
SELECT quote_ident(c.relname) AS tbl, quote_ident(a.attname) AS col
FROM pg_namespace n
JOIN pg_class c ON c.relnamespace = n.oid
JOIN pg_attribute a ON a.attrelid = c.oid
WHERE n.nspname = 'public'
AND c.relkind = 'r'
AND a.attname LIKE 'temp_%' -- LIKE is faster than ~
AND a.attnum > 0
AND NOT a.attisdropped
ORDER BY 1,2
LOOP
RAISE NOTICE 'Table: %, Column: %', rec.tbl, rec.col;
qry := format('UPDATE %1$s SET %2$s = NULL WHERE %2$s IS NOT NULL', rec.tbl, rec.col);
RAISE LOG 'Query: %', qry;
EXECUTE qry;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Vous doit assainir tous les identificateurs que vous construisez dans Dynamic SQL, sinon cela peut échouer avec des noms non standard qui nécessitent Double-cite. Pire pire, vous êtes ouvert à l'injection SQL.
Démontrant quote_ident()
, puisque vous utilisez les identificateurs désinfectés à plusieurs reprises. Il y a plus d'options avec regclass
ou format()
:
Nom de la table en tant que paramètre de fonction PostgreSQL
Je préfère baser de telles requêtes sur le catalogue système au lieu des vues douloureusement lentes du schéma d'information. C'est une question d'exigences et de goût, cependant. Démontrer l'équivalent, qui est à 10 fois plus rapide (pas en ce qui concerne les commandes UPDATE
). Suite:
[.____] Comment vérifier si une table existe dans un schéma donné
LIKE
est généralement plus rapide que la correspondance d'expression régulière la plus puissante (~
). Si LIKE
peut faire le travail, l'utiliser.
Quelques autres simplifications mineures.
Réponse associée avec plus de détails:
[.____] mise à jour de l'enregistrement d'un curseur où le nom de la table est un paramètre
Great Dezso, ça marche! Voici la version finale de ma fonction:
CREATE OR REPLACE FUNCTION fnct_clear_temp_fields() RETURNS VOID AS $$
DECLARE
dataset_1_row RECORD; --Record variable to go through each row of the view below
update_query TEXT; --The dynamic UPDATE query to be executed
BEGIN
FOR dataset_1_row IN --Cycle through rows of query below
SELECT
column_name,
table_name
FROM information_schema.tables
NATURAL JOIN information_schema.columns
WHERE
table_schema='public'
AND table_type='BASE TABLE'
AND column_name ~ '^temp_'
ORDER BY table_name,column_name
LOOP
RAISE NOTICE 'Table: %, Column: %', dataset_1_row.table_name,dataset_1_row.column_name;
--Create a dynamic update query to set to NULL the contents of the current 'temp_' column
update_query :='UPDATE '||dataset_1_row.table_name||' SET '||dataset_1_row.column_name||'=NULL WHERE '||dataset_1_row.column_name||' IS NOT NULL;';
RAISE LOG 'Query executed: %', update_query; --Put query def in log
EXECUTE update_query; --Run the query
END LOOP; --Next line of SELECT query above
RETURN;
END;
$$ LANGUAGE plpgsql;