CREATE TEMP TABLE x ( a serial, b int, c text not null default 'stuff', d text, e text ) ; CREATE FUNCTION fn_x_before () RETURNS TRIGGER AS ' BEGIN NEW.e := ''before trigger fired''::text; return NEW; END; ' LANGUAGE plpgsql; CREATE FUNCTION fn_x_after () RETURNS TRIGGER AS ' BEGIN UPDATE x set e=''after trigger fired'' where c=''stuff''; return NULL; END; ' LANGUAGE plpgsql; CREATE TRIGGER trg_x_after AFTER INSERT ON x FOR EACH ROW EXECUTE PROCEDURE fn_x_after(); CREATE TRIGGER trg_x_before BEFORE INSERT ON x FOR EACH ROW EXECUTE PROCEDURE fn_x_before(); COPY x (a, b, c, d, e) from stdin; 9999 \N \\N \NN \N 10000 21 31 41 51 \. COPY x (b, d) from stdin; 1 test_1 \. COPY x (b, d) from stdin; 2 test_2 3 test_3 4 test_4 5 test_5 \. COPY x (a, b, c, d, e) from stdin; 10001 22 32 42 52 10002 23 33 43 53 10003 24 34 44 54 10004 25 35 45 55 10005 26 36 46 56 \. -- non-existent column in column list: should fail COPY x (xyz) from stdin; -- redundant options COPY x from stdin (format CSV, FORMAT CSV); COPY x from stdin (freeze off, freeze on); COPY x from stdin (delimiter ',', delimiter ','); COPY x from stdin (null ' ', null ' '); COPY x from stdin (header off, header on); COPY x from stdin (quote ':', quote ':'); COPY x from stdin (escape ':', escape ':'); COPY x from stdin (force_quote (a), force_quote *); COPY x from stdin (force_not_null (a), force_not_null (b)); COPY x from stdin (force_null (a), force_null (b)); COPY x from stdin (convert_selectively (a), convert_selectively (b)); COPY x from stdin (encoding 'sql_ascii', encoding 'sql_ascii'); COPY x from stdin (on_error ignore, on_error ignore); -- incorrect options COPY x to stdin (format BINARY, delimiter ','); COPY x to stdin (format BINARY, null 'x'); COPY x from stdin (format BINARY, on_error ignore); COPY x from stdin (on_error unsupported); COPY x to stdin (format TEXT, force_quote(a)); COPY x from stdin (format CSV, force_quote(a)); COPY x to stdout (format TEXT, force_not_null(a)); COPY x to stdin (format CSV, force_not_null(a)); COPY x to stdout (format TEXT, force_null(a)); COPY x to stdin (format CSV, force_null(a)); COPY x to stdin (format BINARY, on_error unsupported); -- too many columns in column list: should fail COPY x (a, b, c, d, e, d, c) from stdin; -- missing data: should fail COPY x from stdin; \. COPY x from stdin; 2000 230 23 23 \. COPY x from stdin; 2001 231 \N \N \. -- extra data: should fail COPY x from stdin; 2002 232 40 50 60 70 80 \. -- various COPY options: delimiters, oids, NULL string, encoding COPY x (b, c, d, e) from stdin delimiter ',' null 'x'; x,45,80,90 x,\x,\\x,\\\x x,\,,\\\,,\\ \. COPY x from stdin WITH DELIMITER AS ';' NULL AS ''; 3000;;c;; \. COPY x from stdin WITH DELIMITER AS ':' NULL AS E'\\X' ENCODING 'sql_ascii'; 4000:\X:C:\X:\X 4001:1:empty:: 4002:2:null:\X:\X 4003:3:Backslash:\\:\\ 4004:4:BackslashX:\\X:\\X 4005:5:N:\N:\N 4006:6:BackslashN:\\N:\\N 4007:7:XX:\XX:\XX 4008:8:Delimiter:\::\: \. COPY x TO stdout WHERE a = 1; COPY x from stdin WHERE a = 50004; 50003 24 34 44 54 50004 25 35 45 55 50005 26 36 46 56 \. COPY x from stdin WHERE a > 60003; 60001 22 32 42 52 60002 23 33 43 53 60003 24 34 44 54 60004 25 35 45 55 60005 26 36 46 56 \. COPY x from stdin WHERE f > 60003; COPY x from stdin WHERE a = max(x.b); COPY x from stdin WHERE a IN (SELECT 1 FROM x); COPY x from stdin WHERE a IN (generate_series(1,5)); COPY x from stdin WHERE a = row_number() over(b); -- check results of copy in SELECT * FROM x; -- check copy out COPY x TO stdout; COPY x (c, e) TO stdout; COPY x (b, e) TO stdout WITH NULL 'I''m null'; CREATE TEMP TABLE y ( col1 text, col2 text ); INSERT INTO y VALUES ('Jackson, Sam', E'\\h'); INSERT INTO y VALUES ('It is "perfect".',E'\t'); INSERT INTO y VALUES ('', NULL); COPY y TO stdout WITH CSV; COPY y TO stdout WITH CSV QUOTE '''' DELIMITER '|'; COPY y TO stdout WITH CSV FORCE QUOTE col2 ESCAPE E'\\' ENCODING 'sql_ascii'; COPY y TO stdout WITH CSV FORCE QUOTE *; -- Repeat above tests with new 9.0 option syntax COPY y TO stdout (FORMAT CSV); COPY y TO stdout (FORMAT CSV, QUOTE '''', DELIMITER '|'); COPY y TO stdout (FORMAT CSV, FORCE_QUOTE (col2), ESCAPE E'\\'); COPY y TO stdout (FORMAT CSV, FORCE_QUOTE *); \copy y TO stdout (FORMAT CSV) \copy y TO stdout (FORMAT CSV, QUOTE '''', DELIMITER '|') \copy y TO stdout (FORMAT CSV, FORCE_QUOTE (col2), ESCAPE E'\\') \copy y TO stdout (FORMAT CSV, FORCE_QUOTE *) --test that we read consecutive LFs properly CREATE TEMP TABLE testnl (a int, b text, c int); COPY testnl FROM stdin CSV; 1,"a field with two LFs inside",2 \. -- test end of copy marker CREATE TEMP TABLE testeoc (a text); COPY testeoc FROM stdin CSV; a\. \.b c\.d "\." \. COPY testeoc TO stdout CSV; -- test handling of nonstandard null marker that violates escaping rules CREATE TEMP TABLE testnull(a int, b text); INSERT INTO testnull VALUES (1, E'\\0'), (NULL, NULL); COPY testnull TO stdout WITH NULL AS E'\\0'; COPY testnull FROM stdin WITH NULL AS E'\\0'; 42 \\0 \0 \0 \. SELECT * FROM testnull; BEGIN; CREATE TABLE vistest (LIKE testeoc); COPY vistest FROM stdin CSV; a0 b \. COMMIT; SELECT * FROM vistest; BEGIN; TRUNCATE vistest; COPY vistest FROM stdin CSV; a1 b \. SELECT * FROM vistest; SAVEPOINT s1; TRUNCATE vistest; COPY vistest FROM stdin CSV; d1 e \. SELECT * FROM vistest; COMMIT; SELECT * FROM vistest; BEGIN; TRUNCATE vistest; COPY vistest FROM stdin CSV FREEZE; a2 b \. SELECT * FROM vistest; SAVEPOINT s1; TRUNCATE vistest; COPY vistest FROM stdin CSV FREEZE; d2 e \. SELECT * FROM vistest; COMMIT; SELECT * FROM vistest; BEGIN; TRUNCATE vistest; COPY vistest FROM stdin CSV FREEZE; x y \. SELECT * FROM vistest; COMMIT; TRUNCATE vistest; COPY vistest FROM stdin CSV FREEZE; p g \. BEGIN; TRUNCATE vistest; SAVEPOINT s1; COPY vistest FROM stdin CSV FREEZE; m k \. COMMIT; BEGIN; INSERT INTO vistest VALUES ('z'); SAVEPOINT s1; TRUNCATE vistest; ROLLBACK TO SAVEPOINT s1; COPY vistest FROM stdin CSV FREEZE; d3 e \. COMMIT; CREATE FUNCTION truncate_in_subxact() RETURNS VOID AS $$ BEGIN TRUNCATE vistest; EXCEPTION WHEN OTHERS THEN INSERT INTO vistest VALUES ('subxact failure'); END; $$ language plpgsql; BEGIN; INSERT INTO vistest VALUES ('z'); SELECT truncate_in_subxact(); COPY vistest FROM stdin CSV FREEZE; d4 e \. SELECT * FROM vistest; COMMIT; SELECT * FROM vistest; -- Test FORCE_NOT_NULL and FORCE_NULL options CREATE TEMP TABLE forcetest ( a INT NOT NULL, b TEXT NOT NULL, c TEXT, d TEXT, e TEXT ); \pset null NULL -- should succeed with no effect ("b" remains an empty string, "c" remains NULL) BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c)); 1,,"" \. COMMIT; SELECT b, c FROM forcetest WHERE a = 1; -- should succeed, FORCE_NULL and FORCE_NOT_NULL can be both specified BEGIN; COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d)); 2,'a',,"" \. COMMIT; SELECT c, d FROM forcetest WHERE a = 2; -- should fail with not-null constraint violation BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL(b), FORCE_NOT_NULL(c)); 3,,"" \. ROLLBACK; -- should fail with "not referenced by COPY" error BEGIN; COPY forcetest (d, e) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b)); ROLLBACK; -- should fail with "not referenced by COPY" error BEGIN; COPY forcetest (d, e) FROM STDIN WITH (FORMAT csv, FORCE_NULL(b)); ROLLBACK; -- should succeed with no effect ("b" remains an empty string, "c" remains NULL) BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NULL *); 4,,"" \. COMMIT; SELECT b, c FROM forcetest WHERE a = 4; -- should succeed with effect ("b" remains an empty string) BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *); 5,,"" \. COMMIT; SELECT b, c FROM forcetest WHERE a = 5; -- should succeed with effect ("c" remains NULL) BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *); 6,"b","" \. COMMIT; SELECT b, c FROM forcetest WHERE a = 6; -- should fail with "conflicting or redundant options" error BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NOT_NULL(b)); ROLLBACK; -- should fail with "conflicting or redundant options" error BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *, FORCE_NULL(b)); ROLLBACK; \pset null '' -- test case with whole-row Var in a check constraint create table check_con_tbl (f1 int); create function check_con_function(check_con_tbl) returns bool as $$ begin raise notice 'input = %', row_to_json($1); return $1.f1 > 0; end $$ language plpgsql immutable; alter table check_con_tbl add check (check_con_function(check_con_tbl.*)); \d+ check_con_tbl copy check_con_tbl from stdin; 1 \N \. copy check_con_tbl from stdin; 0 \. select * from check_con_tbl; -- test with RLS enabled. CREATE ROLE regress_rls_copy_user; CREATE ROLE regress_rls_copy_user_colperms; CREATE TABLE rls_t1 (a int, b int, c int); COPY rls_t1 (a, b, c) from stdin; 1 4 1 2 3 2 3 2 3 4 1 4 \. CREATE POLICY p1 ON rls_t1 FOR SELECT USING (a % 2 = 0); ALTER TABLE rls_t1 ENABLE ROW LEVEL SECURITY; ALTER TABLE rls_t1 FORCE ROW LEVEL SECURITY; GRANT SELECT ON TABLE rls_t1 TO regress_rls_copy_user; GRANT SELECT (a, b) ON TABLE rls_t1 TO regress_rls_copy_user_colperms; -- all columns COPY rls_t1 TO stdout; COPY rls_t1 (a, b, c) TO stdout; -- subset of columns COPY rls_t1 (a) TO stdout; COPY rls_t1 (a, b) TO stdout; -- column reordering COPY rls_t1 (b, a) TO stdout; SET SESSION AUTHORIZATION regress_rls_copy_user; -- all columns COPY rls_t1 TO stdout; COPY rls_t1 (a, b, c) TO stdout; -- subset of columns COPY rls_t1 (a) TO stdout; COPY rls_t1 (a, b) TO stdout; -- column reordering COPY rls_t1 (b, a) TO stdout; RESET SESSION AUTHORIZATION; SET SESSION AUTHORIZATION regress_rls_copy_user_colperms; -- attempt all columns (should fail) COPY rls_t1 TO stdout; COPY rls_t1 (a, b, c) TO stdout; -- try to copy column with no privileges (should fail) COPY rls_t1 (c) TO stdout; -- subset of columns (should succeed) COPY rls_t1 (a) TO stdout; COPY rls_t1 (a, b) TO stdout; RESET SESSION AUTHORIZATION; -- test with INSTEAD OF INSERT trigger on a view CREATE TABLE instead_of_insert_tbl(id serial, name text); CREATE VIEW instead_of_insert_tbl_view AS SELECT ''::text AS str; COPY instead_of_insert_tbl_view FROM stdin; -- fail test1 \. CREATE FUNCTION fun_instead_of_insert_tbl() RETURNS trigger AS $$ BEGIN INSERT INTO instead_of_insert_tbl (name) VALUES (NEW.str); RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trig_instead_of_insert_tbl_view INSTEAD OF INSERT ON instead_of_insert_tbl_view FOR EACH ROW EXECUTE PROCEDURE fun_instead_of_insert_tbl(); COPY instead_of_insert_tbl_view FROM stdin; test1 \. SELECT * FROM instead_of_insert_tbl; -- Test of COPY optimization with view using INSTEAD OF INSERT -- trigger when relation is created in the same transaction as -- when COPY is executed. BEGIN; CREATE VIEW instead_of_insert_tbl_view_2 as select ''::text as str; CREATE TRIGGER trig_instead_of_insert_tbl_view_2 INSTEAD OF INSERT ON instead_of_insert_tbl_view_2 FOR EACH ROW EXECUTE PROCEDURE fun_instead_of_insert_tbl(); COPY instead_of_insert_tbl_view_2 FROM stdin; test1 \. SELECT * FROM instead_of_insert_tbl; COMMIT; -- tests for on_error option CREATE TABLE check_ign_err (n int, m int[], k int); COPY check_ign_err FROM STDIN WITH (on_error stop); 1 {1} 1 a {2} 2 3 {3} 3333333333 4 {a, 4} 4 5 {5} 5 \. COPY check_ign_err FROM STDIN WITH (on_error ignore); 1 {1} 1 a {2} 2 3 {3} 3333333333 4 {a, 4} 4 5 {5} 5 \. SELECT * FROM check_ign_err; -- test datatype error that can't be handled as soft: should fail CREATE TABLE hard_err(foo widget); COPY hard_err FROM STDIN WITH (on_error ignore); 1 \. -- test missing data: should fail COPY check_ign_err FROM STDIN WITH (on_error ignore); 1 {1} \. -- test extra data: should fail COPY check_ign_err FROM STDIN WITH (on_error ignore); 1 {1} 3 abc \. -- clean up DROP TABLE forcetest; DROP TABLE vistest; DROP FUNCTION truncate_in_subxact(); DROP TABLE x, y; DROP TABLE rls_t1 CASCADE; DROP ROLE regress_rls_copy_user; DROP ROLE regress_rls_copy_user_colperms; DROP FUNCTION fn_x_before(); DROP FUNCTION fn_x_after(); DROP TABLE instead_of_insert_tbl; DROP VIEW instead_of_insert_tbl_view; DROP VIEW instead_of_insert_tbl_view_2; DROP FUNCTION fun_instead_of_insert_tbl(); DROP TABLE check_ign_err; DROP TABLE hard_err; -- -- COPY FROM ... DEFAULT -- create temp table copy_default ( id integer primary key, text_value text not null default 'test', ts_value timestamp without time zone not null default '2022-07-05' ); -- if DEFAULT is not specified, then the marker will be regular data copy copy_default from stdin; 1 value '2022-07-04' 2 \D '2022-07-05' \. select id, text_value, ts_value from copy_default; truncate copy_default; copy copy_default from stdin with (format csv); 1,value,2022-07-04 2,\D,2022-07-05 \. select id, text_value, ts_value from copy_default; truncate copy_default; -- DEFAULT cannot be used in binary mode copy copy_default from stdin with (format binary, default '\D'); -- DEFAULT cannot be new line nor carriage return copy copy_default from stdin with (default E'\n'); copy copy_default from stdin with (default E'\r'); -- DELIMITER cannot appear in DEFAULT spec copy copy_default from stdin with (delimiter ';', default 'test;test'); -- CSV quote cannot appear in DEFAULT spec copy copy_default from stdin with (format csv, quote '"', default 'test"test'); -- NULL and DEFAULT spec must be different copy copy_default from stdin with (default '\N'); -- cannot use DEFAULT marker in column that has no DEFAULT value copy copy_default from stdin with (default '\D'); \D value '2022-07-04' 2 \D '2022-07-05' \. copy copy_default from stdin with (format csv, default '\D'); \D,value,2022-07-04 2,\D,2022-07-05 \. -- The DEFAULT marker must be unquoted and unescaped or it's not recognized copy copy_default from stdin with (default '\D'); 1 \D '2022-07-04' 2 \\D '2022-07-04' 3 "\D" '2022-07-04' \. select id, text_value, ts_value from copy_default; truncate copy_default; copy copy_default from stdin with (format csv, default '\D'); 1,\D,2022-07-04 2,\\D,2022-07-04 3,"\D",2022-07-04 \. select id, text_value, ts_value from copy_default; truncate copy_default; -- successful usage of DEFAULT option in COPY copy copy_default from stdin with (default '\D'); 1 value '2022-07-04' 2 \D '2022-07-03' 3 \D \D \. select id, text_value, ts_value from copy_default; truncate copy_default; copy copy_default from stdin with (format csv, default '\D'); 1,value,2022-07-04 2,\D,2022-07-03 3,\D,\D \. select id, text_value, ts_value from copy_default; truncate copy_default; -- DEFAULT cannot be used in COPY TO copy (select 1 as test) TO stdout with (default '\D');