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?