En la siguiente publicación se verá a manera de ejemplo una función del tipo trigger, genérica, que será usada para registrar los cambios de los datos de una tabla en otra tabla de auditoria.
Grabará los registros antiguos y nuevos, la tabla afectada, el usuario de base de datos que realizó el cambio y la fecha en que se está realizando el cambio.
Tabla de auditoria
CREATE schema audit; REVOKE CREATE ON schema audit FROM public; CREATE TABLE audit.logged_actions ( schema_name text NOT NULL, TABLE_NAME text NOT NULL, user_name text, action_tstamp TIMESTAMP WITH TIME zone NOT NULL DEFAULT CURRENT_TIMESTAMP, action TEXT NOT NULL CHECK (action IN ('I','D','U')), original_data text, new_data text, query text ) WITH (fillfactor=100); REVOKE ALL ON audit.logged_actions FROM public;
Permisos en tabla de auditoria
GRANT SELECT ON audit.logged_actions TO public; CREATE INDEX logged_actions_schema_table_idx ON audit.logged_actions(((schema_name||'.'||TABLE_NAME)::TEXT)); CREATE INDEX logged_actions_action_tstamp_idx ON audit.logged_actions(action_tstamp); CREATE INDEX logged_actions_action_idx ON audit.logged_actions(action);
Función de auditoria
CREATE OR REPLACE FUNCTION audit.if_modified_func() RETURNS TRIGGER AS $body$ DECLARE v_old_data TEXT; v_new_data TEXT; BEGIN IF (TG_OP = 'UPDATE') THEN v_old_data := ROW(OLD.*); v_new_data := ROW(NEW.*); INSERT INTO audit.logged_actions (schema_name,table_name,user_name,action,original_data,new_data,query) VALUES (TG_TABLE_SCHEMA::TEXT,TG_TABLE_NAME::TEXT,session_user::TEXT,substring(TG_OP,1,1),v_old_data,v_new_data, current_query()); RETURN NEW; ELSIF (TG_OP = 'DELETE') THEN v_old_data := ROW(OLD.*); INSERT INTO audit.logged_actions (schema_name,table_name,user_name,action,original_data,query) VALUES (TG_TABLE_SCHEMA::TEXT,TG_TABLE_NAME::TEXT,session_user::TEXT,substring(TG_OP,1,1),v_old_data, current_query()); RETURN OLD; ELSIF (TG_OP = 'INSERT') THEN v_new_data := ROW(NEW.*); INSERT INTO audit.logged_actions (schema_name,table_name,user_name,action,new_data,query) VALUES (TG_TABLE_SCHEMA::TEXT,TG_TABLE_NAME::TEXT,session_user::TEXT,substring(TG_OP,1,1),v_new_data, current_query()); RETURN NEW; ELSE RAISE WARNING '[AUDIT.IF_MODIFIED_FUNC] - Other action occurred: %, at %',TG_OP,now(); RETURN NULL; END IF; EXCEPTION WHEN data_exception THEN RAISE WARNING '[AUDIT.IF_MODIFIED_FUNC] - UDF ERROR [DATA EXCEPTION] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM; RETURN NULL; WHEN unique_violation THEN RAISE WARNING '[AUDIT.IF_MODIFIED_FUNC] - UDF ERROR [UNIQUE] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM; RETURN NULL; WHEN OTHERS THEN RAISE WARNING '[AUDIT.IF_MODIFIED_FUNC] - UDF ERROR [OTHER] - SQLSTATE: %, SQLERRM: %',SQLSTATE,SQLERRM; RETURN NULL; END; $body$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog, audit;
Tabla de prueba de audtoria
La siguiente tabla “categories” servirá de ejemplo para ser auditada.
CREATE TABLE categories ( category_id smallint NOT NULL, category_name character varying(15) NOT NULL, description text, picture bytea );
Trigger de auditoria
CREATE TRIGGER t_if_modified_trg AFTER INSERT OR UPDATE OR DELETE ON categories FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func();
Insertando datos de prueba
INSERT INTO categories VALUES (1, 'Beverages', 'Soft drinks, coffees, teas, beers, and ales', '\x'); INSERT INTO categories VALUES (2, 'Condiments', 'Sweet and savory sauces, relishes, spreads, and seasonings', '\x');
Los datos de la tabla de auditoria se verán realizando la siguiente consulta.
SELECT * FROM audit.logged_actions