Thread
-
Re: on_error table, saving error info to a table
Zsolt Parragi <zsolt.parragi@percona.com> — 2026-05-14T17:14:29Z
Hello! + cstate->error_rel = table_open(err_relOid, NoLock); + ... and shortly after ... + + table_close(cstate->error_rel, NoLock); Bot this is still used by many other calls after closing. See the following example: SET debug_discard_caches = 1; CREATE TABLE src_tbl (a int, b int); CREATE TABLE err_tbl OF copy_error_saving; CREATE INDEX src_idx ON src_tbl(a); COPY src_tbl FROM STDIN (FORMAT csv, ON_ERROR table, ERROR_TABLE err_tbl); 1,2 xx,3 3,4 \. -- ERROR: relation with OID 0 does not exist + /* Handle queued AFTER triggers */ + AfterTriggerEndQuery(cstate->mtcontext->estate); Is the order of this correct? See the following snippet that crashes the server: CREATE TABLE target_tbl (id int, val int); CREATE TABLE err_tbl OF copy_error_saving; CREATE OR REPLACE FUNCTION err_stmt_trans_fn() RETURNS trigger AS $$ BEGIN END; $$ LANGUAGE plpgsql; CREATE TRIGGER err_stmt_trans AFTER INSERT ON err_tbl REFERENCING NEW TABLE AS new_rows FOR EACH STATEMENT EXECUTE FUNCTION err_stmt_trans_fn(); \echo === COPY === COPY target_tbl FROM stdin WITH (on_error 'table', error_table 'err_tbl'); 1 100 bad 200 3 notanumber 4 400 \. + + cstate->num_errors = cstate->num_errors + estate->es_processed; Counting seems to miss if a before trigger returns null: \set ON_ERROR_STOP 0 CREATE TABLE t2 (a int, b int, c int); CREATE TABLE err_tbl2 OF copy_error_saving; CREATE FUNCTION drop_all() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NULL; END; $$; CREATE TRIGGER drop_all_t BEFORE INSERT ON err_tbl2 FOR EACH ROW EXECUTE FUNCTION drop_all(); COPY t2 FROM STDIN WITH (FORMAT csv, ON_ERROR table, ERROR_TABLE err_tbl2); 1,2,a 3,4,b 5,6,c 7,8,d 9,10,e \. SELECT count(*) AS n FROM t2; SELECT count(*) AS n FROM err_tbl2; + typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid, + PointerGetDatum("copy_error_saving"), + ObjectIdGetDatum(PG_CATALOG_NAMESPACE)); ... + if (reloftype != typoid) + ereport(ERROR, ... + errhint("The COPY error saving table must be a typed table based on type \"%s\".", + format_type_be_qualified(typoid))); Isn't an if (!OidIsValid(typoid)) check missing between the two?