PostgreSQL 9.1 eller senare
format()
har ett inbyggt sätt att undkomma identifierare. Enklare än tidigare:
CREATE OR REPLACE FUNCTION foo_before()
RETURNS trigger AS
$func$
BEGIN
EXECUTE format('INSERT INTO %I.%I SELECT $1.*'
, TG_TABLE_SCHEMA, TG_TABLE_NAME || 'shadow')
USING OLD;
RETURN OLD;
END
$func$ LANGUAGE plpgsql;
Fungerar med en VALUES
uttryck också.
db<>spela här
Gammal sqlfiddle.
Huvudpunkter
- Använd
format()
ellerquote_ident()
att citera identifierare (automatiskt och endast när det är nödvändigt), och därmed försvara sig mot SQL-injektion och enkla syntaxbrott.
Detta är nödvändigt , även med dina egna tabellnamn! - Schema-kvalificera tabellnamnet. Beroende på den aktuella
search_path
Att ställa in ett tabellnamn kan annars lösas till en annan tabell med samma namn i ett annat schema. - Använd
EXECUTE
för dynamiska DDL-satser. - Placera värden säkert med
USING
klausul. - Konsultera den fina handboken om exekvering av dynamiska kommandon i plpgsql.
- Observera att
RETURN OLD;
i triggerfunktionen krävs för en triggerBEFORE DELETE
. Detaljer i manualen här.
Du får felmeddelandet i din nästan framgångsrika version eftersom OLD
är inte synlig inuti EXECUTE
. Och om du vill sammanfoga individuella värden för den dekomponerade raden som du försökte, måste du förbereda textrepresentationen för varje enskild kolumn med quote_literal()
för att garantera giltig syntax. Du måste också veta kolumnnamn i förväg för att hantera dem eller fråga i systemkatalogerna - vilket står emot din idé om att ha en enkel, dynamisk triggerfunktion ...
Min lösning undviker alla dessa komplikationer. Också lite förenklat.
PostgreSQL 9.0 eller tidigare
format()
är inte tillgänglig än, så:
CREATE OR REPLACE FUNCTION foo_before()
RETURNS trigger AS
$func$
BEGIN
EXECUTE 'INSERT INTO ' || quote_ident(TG_TABLE_SCHEMA)
|| '.' || quote_ident(TG_TABLE_NAME || 'shadow')
|| ' SELECT $1.*'
USING OLD;
RETURN OLD;
END
$func$ LANGUAGE plpgsql;
Relaterat:
- Hur använder man TG_TABLE_NAME dynamiskt i PostgreSQL 8.2?