-- directory paths are passed to us in environment variables
\getenv abs_srcdir PG_ABS_SRCDIR
-- Clean up in case a prior regression run failed SET client_min_messages TO'warning'; DROP ROLE IFEXISTS regress_file_fdw_superuser, regress_file_fdw_user, regress_no_priv_user;
RESET client_min_messages;
CREATE ROLE regress_file_fdw_superuser LOGIN SUPERUSER; -- is a superuser CREATE ROLE regress_file_fdw_user LOGIN; -- has priv and user mapping CREATE ROLE regress_no_priv_user LOGIN; -- has priv but no user mapping
-- Install file_fdw CREATE EXTENSION file_fdw;
-- create function to filter unstable results of EXPLAIN CREATE FUNCTION explain_filter(text) RETURNS setof text
LANGUAGE plpgsql AS
$$ declare
ln text;
begin for ln in execute $1 loop -- Remove the path portion of foreign file names
ln := regexp_replace(ln, 'Foreign File: .*/([a-z.]+)$', 'Foreign File: .../\1'); return next ln;
end loop;
end;
$$;
-- regress_file_fdw_superuser owns fdw-related objects SET ROLE regress_file_fdw_superuser; CREATE SERVER file_server FOREIGN DATA WRAPPER file_fdw;
-- privilege tests SET ROLE regress_file_fdw_user; CREATEFOREIGN DATA WRAPPER file_fdw2 HANDLER file_fdw_handler VALIDATOR file_fdw_validator; -- ERROR CREATE SERVER file_server2 FOREIGN DATA WRAPPER file_fdw; -- ERROR CREATE USER MAPPING FOR regress_file_fdw_user SERVER file_server; -- ERROR
SET ROLE regress_file_fdw_superuser; GRANTUSAGEONFOREIGN SERVER file_server TO regress_file_fdw_user;
SET ROLE regress_file_fdw_user; CREATE USER MAPPING FOR regress_file_fdw_user SERVER file_server;
-- create user mappings and grant privilege to test users SET ROLE regress_file_fdw_superuser; CREATE USER MAPPING FOR regress_file_fdw_superuser SERVER file_server; CREATE USER MAPPING FOR regress_no_priv_user SERVER file_server;
\set filename :abs_srcdir '/data/agg.data' CREATEFOREIGNTABLE agg_text (
a int2CHECK (a >= 0),
b float4
) SERVER file_server
OPTIONS (format 'text', filename :'filename', delimiter ' ', null'\N'); GRANTSELECTON agg_text TO regress_file_fdw_user;
\set filename :abs_srcdir '/data/agg.csv' CREATEFOREIGNTABLE agg_csv (
a int2,
b float4
) SERVER file_server
OPTIONS (format 'csv', filename :'filename', header 'true', delimiter ';', quote '@', escape '"', null''); ALTERFOREIGNTABLE agg_csv ADDCHECK (a >= 0);
\set filename :abs_srcdir '/data/agg.bad' CREATEFOREIGNTABLE agg_bad (
a int2,
b float4
) SERVER file_server
OPTIONS (format 'csv', filename :'filename', header 'true', delimiter ';', quote '@', escape '"', null''); ALTERFOREIGNTABLE agg_bad ADDCHECK (a >= 0);
-- test header matching
\set filename :abs_srcdir '/data/list1.csv' CREATEFOREIGNTABLE header_match ("1"int, foo text) SERVER file_server
OPTIONS (format 'csv', filename :'filename', delimiter ',', header 'match'); SELECT * FROM header_match; CREATEFOREIGNTABLE header_doesnt_match (a int, foo text) SERVER file_server
OPTIONS (format 'csv', filename :'filename', delimiter ',', header 'match'); SELECT * FROM header_doesnt_match; -- ERROR
-- per-column options tests
\set filename :abs_srcdir '/data/text.csv' CREATEFOREIGNTABLE text_csv (
word1 text OPTIONS (force_not_null 'true'),
word2 text OPTIONS (force_not_null 'off'),
word3 text OPTIONS (force_null 'true'),
word4 text OPTIONS (force_null 'off')
) SERVER file_server
OPTIONS (format 'text', filename :'filename', null'NULL'); SELECT * FROM text_csv; -- ERROR ALTERFOREIGNTABLE text_csv OPTIONS (SET format 'csv');
\pset null _null_ SELECT * FROM text_csv;
-- force_not_null and force_null can be used together on the same column ALTERFOREIGNTABLE text_csv ALTERCOLUMN word1 OPTIONS (force_null 'true'); ALTERFOREIGNTABLE text_csv ALTERCOLUMN word3 OPTIONS (force_not_null 'true');
-- force_not_null is not allowed to be specified at any foreign object level: ALTERFOREIGN DATA WRAPPER file_fdw OPTIONS (ADD force_not_null '*'); -- ERROR ALTER SERVER file_server OPTIONS (ADD force_not_null '*'); -- ERROR CREATE USER MAPPING FOR public SERVER file_server OPTIONS (force_not_null '*'); -- ERROR CREATEFOREIGNTABLE tbl () SERVER file_server OPTIONS (force_not_null '*'); -- ERROR
-- force_null is not allowed to be specified at any foreign object level: ALTERFOREIGN DATA WRAPPER file_fdw OPTIONS (ADD force_null '*'); -- ERROR ALTER SERVER file_server OPTIONS (ADD force_null '*'); -- ERROR CREATE USER MAPPING FOR public SERVER file_server OPTIONS (force_null '*'); -- ERROR CREATEFOREIGNTABLE tbl () SERVER file_server OPTIONS (force_null '*'); -- ERROR
-- basic query tests SELECT * FROM agg_text WHERE b > 10.0ORDERBY a; SELECT * FROM agg_csv ORDERBY a; SELECT * FROM agg_csv c JOIN agg_text t ON (t.a = c.a) ORDERBY c.a;
-- on_error, log_verbosity and reject_limit tests ALTERFOREIGNTABLE agg_bad OPTIONS (ADD on_error 'ignore'); SELECT * FROM agg_bad; ALTERFOREIGNTABLE agg_bad OPTIONS (ADD log_verbosity 'silent'); SELECT * FROM agg_bad; ALTERFOREIGNTABLE agg_bad OPTIONS (ADD reject_limit '1'); -- ERROR SELECT * FROM agg_bad; ALTERFOREIGNTABLE agg_bad OPTIONS (SET reject_limit '2'); SELECT * FROM agg_bad; ANALYZE agg_bad;
-- misc query tests
\t on SELECT explain_filter('EXPLAIN (VERBOSE, COSTS FALSE) SELECT * FROM agg_csv');
\t off
PREPARE st(int) ASSELECT * FROM agg_csv WHERE a = $1;
EXECUTE st(100);
EXECUTE st(100);
DEALLOCATE st;
-- tableoid SELECT tableoid::regclass, b FROM agg_csv;
-- updates aren't supported INSERTINTO agg_csv VALUES(1,2.0); UPDATE agg_csv SET a = 1; DELETEFROM agg_csv WHERE a = 100;
TRUNCATE agg_csv; -- but this should be allowed SELECT * FROM agg_csv FORUPDATE;
-- copy from isn't supported either
COPY agg_csv FROM STDIN; 123.4
\.
-- constraint exclusion tests
\t on SELECT explain_filter('EXPLAIN (VERBOSE, COSTS FALSE) SELECT * FROM agg_csv WHERE a < 0');
\t off SELECT * FROM agg_csv WHERE a < 0; SET constraint_exclusion = 'on';
\t on SELECT explain_filter('EXPLAIN (VERBOSE, COSTS FALSE) SELECT * FROM agg_csv WHERE a < 0');
\t off SELECT * FROM agg_csv WHERE a < 0;
RESET constraint_exclusion;
-- table inheritance tests CREATETABLE agg (a int2, b float4); ALTERFOREIGNTABLE agg_csv INHERIT agg; SELECT tableoid::regclass, * FROM agg; SELECT tableoid::regclass, * FROM agg_csv; SELECT tableoid::regclass, * FROM ONLY agg; -- updates aren't supported UPDATE agg SET a = 1; DELETEFROM agg WHERE a = 100; -- but this should be allowed SELECT tableoid::regclass, * FROM agg FORUPDATE; ALTERFOREIGNTABLE agg_csv NO INHERIT agg; DROPTABLE agg;
-- declarative partitioning tests SET ROLE regress_file_fdw_superuser; CREATETABLE pt (a int, b text) partition by list (a);
\set filename :abs_srcdir '/data/list1.csv' CREATEFOREIGNTABLE p1 partition of pt forvaluesin (1) SERVER file_server
OPTIONS (format 'csv', filename :'filename', delimiter ','); CREATETABLE p2 partition of pt forvaluesin (2); SELECT tableoid::regclass, * FROM pt; SELECT tableoid::regclass, * FROM p1; SELECT tableoid::regclass, * FROM p2;
\set filename :abs_srcdir '/data/list2.bad'
COPY pt FROM :'filename'with (format 'csv', delimiter ','); -- ERROR
\set filename :abs_srcdir '/data/list2.csv'
COPY pt FROM :'filename'with (format 'csv', delimiter ','); SELECT tableoid::regclass, * FROM pt; SELECT tableoid::regclass, * FROM p1; SELECT tableoid::regclass, * FROM p2; INSERTINTO pt VALUES (1, 'xyzzy'); -- ERROR INSERTINTO pt VALUES (2, 'xyzzy'); UPDATE pt set a = 1where a = 2; -- ERROR SELECT tableoid::regclass, * FROM pt; SELECT tableoid::regclass, * FROM p1; SELECT tableoid::regclass, * FROM p2;
-- Test DELETE/UPDATE/MERGE on a partitioned table when all partitions -- are excluded and only the dummy root result relation remains. The -- operation is a no-op but should not fail regardless of whether the -- foreign child was processed (pruning off) or not (pruning on). DROPTABLE p2; SET enable_partition_pruning TO off; DELETEFROM pt WHEREfalse; UPDATE pt SET b = 'x'WHEREfalse;
MERGE INTO pt t USING (VALUES (1, 'x'::text)) AS s(a, b) ONfalseWHEN MATCHED THENUPDATESET b = s.b;
SET enable_partition_pruning TOon; DELETEFROM pt WHEREfalse; UPDATE pt SET b = 'x'WHEREfalse;
MERGE INTO pt t USING (VALUES (1, 'x'::text)) AS s(a, b) ONfalseWHEN MATCHED THENUPDATESET b = s.b;
DROPTABLE pt;
-- generated column tests
\set filename :abs_srcdir '/data/list1.csv' CREATEFOREIGNTABLE gft1 (a int, b text, c text GENERATED ALWAYS AS ('foo') STORED) SERVER file_server
OPTIONS (format 'csv', filename :'filename', delimiter ','); SELECT a, c FROM gft1; DROPFOREIGNTABLE gft1;
-- copy default tests
\set filename :abs_srcdir '/data/copy_default.csv' CREATEFOREIGNTABLE copy_default (
id integer,
text_value text notnulldefault'test',
ts_value timestamp without time zone notnulldefault'2022-07-05'
) SERVER file_server
OPTIONS (format 'csv', filename :'filename', default'\D'); SELECT id, text_value, ts_value FROM copy_default; DROPFOREIGNTABLE copy_default;
-- privilege tests SET ROLE regress_file_fdw_superuser; SELECT * FROM agg_text ORDERBY a; SET ROLE regress_file_fdw_user; SELECT * FROM agg_text ORDERBY a; SET ROLE regress_no_priv_user; SELECT * FROM agg_text ORDERBY a; -- ERROR SET ROLE regress_file_fdw_user;
\t on SELECT explain_filter('EXPLAIN (VERBOSE, COSTS FALSE) SELECT * FROM agg_text WHERE a > 0');
\t off -- file FDW allows foreign tables to be accessed without user mapping DROP USER MAPPING FOR regress_file_fdw_user SERVER file_server; SELECT * FROM agg_text ORDERBY a;
-- privilege tests for object SET ROLE regress_file_fdw_superuser; ALTERFOREIGNTABLE agg_text OWNER TO regress_file_fdw_user; ALTERFOREIGNTABLE agg_text OPTIONS (SET format 'text'); SET ROLE regress_file_fdw_user; ALTERFOREIGNTABLE agg_text OPTIONS (SET format 'text'); SET ROLE regress_file_fdw_superuser;
-- cleanup
RESET ROLE; DROP EXTENSION file_fdw CASCADE; DROP ROLE regress_file_fdw_superuser, regress_file_fdw_user, regress_no_priv_user;
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-08-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.