| From: | Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com> |
|---|---|
| To: | pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Subject: | Re: on_error table, saving error info to a table |
| Date: | 2026-05-14 17:14:29 |
| Message-ID: | CAN4CZFPOj5LhzwrmcvKd8D0v_D6EiKSBFazDBCzzq8zAP5a9Vw@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
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?
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Nikita Malakhov | 2026-05-14 17:22:55 | Re: [(known) BUG] DELETE/UPDATE more than one row in partitioned foreign table |
| Previous Message | Ayush Tiwari | 2026-05-14 17:05:51 | Re: [PATCH] Rebuild CHECK constraints after generated column SET EXPRESSION |