SET search_path TO 'exttableext'; -- Test 3: create RET and WET using created protocol -- Create external RET and WET DROP EXTERNAL TABLE IF EXISTS exttabtest_w; CREATE WRITABLE EXTERNAL TABLE exttabtest_w(like exttabtest) LOCATION('demoprot://exttabtest.txt') FORMAT 'text' DISTRIBUTED BY (id); DROP EXTERNAL TABLE IF EXISTS exttabtest_r; CREATE READABLE EXTERNAL TABLE exttabtest_r(like exttabtest) LOCATION('demoprot://exttabtest.txt') FORMAT 'text'; -- Checking pg_exttable for new created RET and WET select location,fmttype,fmtopts,encoding,writable from pg_exttable where reloid='exttabtest_r'::regclass or reloid='exttabtest_w'::regclass; -- write to WET SELECT * FROM clean_exttabtest_files; INSERT INTO exttabtest_w (SELECT * FROM exttabtest); -- read from RET SELECT * FROM exttabtest_r EXCEPT ALL SELECT * FROM exttabtest; -- verify data should be evenly distributed with t as ( SELECT gp_segment_id as segid, count(*) as cnt from exttabtest_r group by gp_segment_id ) select max(cnt) - min(cnt) > 20 from t; -- Test 4.1: create uni-directional write protocol -- create WET using created protocol DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( writefunc = write_to_file_stable ); -- Create WET with uni-directional protocol DROP EXTERNAL TABLE IF EXISTS exttabtest_w_uni; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_uni(like exttabtest) LOCATION('demoprot://exttabtest_uni.txt') FORMAT 'text' DISTRIBUTED BY (id); -- write to WET INSERT INTO exttabtest_w_uni (SELECT * FROM exttabtest); -- Test 4.2: create uni-directional read protocol -- create RET using created protocol DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunc = read_from_file_stable ); -- Create RET with uni-directional protocol DROP EXTERNAL TABLE IF EXISTS exttabtest_r_uni; CREATE READABLE EXTERNAL TABLE exttabtest_r_uni(like exttabtest) LOCATION('demoprot://exttabtest_uni.txt') FORMAT 'text'; -- read from RET SELECT * FROM exttabtest_r_uni EXCEPT ALL SELECT * FROM exttabtest; -- Test 5: using bi-directional protocol, create ext table with different -- distribution policy than the source table -- When exporting, date file (exttabtest_dist.txt) will only be created as necessary. -- When data is not evenly distributed (as for this test case), -- some segments will not have data file created. -- -- When importing, data file is required for each primary segment. -- Otherwise "ERROR: demoprot_import: could not open file " will be thrown. DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); DROP EXTERNAL TABLE IF EXISTS exttabtest_w_dist; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_dist(like exttabtest) LOCATION('demoprot://exttabtest_dist.txt') FORMAT 'text' DISTRIBUTED BY (value2); DROP EXTERNAL TABLE IF EXISTS exttabtest_r_dist; CREATE READABLE EXTERNAL TABLE exttabtest_r_dist(like exttabtest) LOCATION('demoprot://exttabtest_dist.txt') FORMAT 'text'; -- write to WET INSERT INTO exttabtest_w_dist (SELECT * FROM exttabtest); -- read from RET SELECT * FROM exttabtest_r_dist EXCEPT ALL SELECT * FROM exttabtest; -- verify data should be evenly distributed with t as ( SELECT gp_segment_id as segid, count(*) as cnt from exttabtest_r_dist group by gp_segment_id ) select max(cnt) - min(cnt) > 20 from t; -- Test 6: using two urls and using CSV format DROP EXTERNAL TABLE IF EXISTS exttabtest_r_2url; CREATE READABLE EXTERNAL TABLE exttabtest_r_2url (like exttabtest) LOCATION('demoprot://exttabtest_2url_1.csv', 'demoprot://exttabtest_2url_2.csv') FORMAT 'csv'; DROP EXTERNAL TABLE IF EXISTS exttabtest_w_2url; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_2url (like exttabtest) LOCATION('demoprot://exttabtest_2url_1.csv', 'demoprot://exttabtest_2url_2.csv') FORMAT 'csv' DISTRIBUTED BY (id); -- write to WET INSERT INTO exttabtest_w_2url (SELECT * FROM exttabtest); -- read from RET SELECT * FROM exttabtest_r_2url EXCEPT ALL SELECT * FROM exttabtest; -- verify data should be evenly distributed with t as ( SELECT gp_segment_id as segid, count(*) as cnt from exttabtest_r_2url group by gp_segment_id ) select max(cnt) - min(cnt) > 20 from t; -- Check the output file at each segments -- ! gpssh -f allsegs ls -l /data/hhuang/MAIN/main_debug/primary/gpseg*/exttabtest_2url*.csv -- Test 7: using two urls and text format DROP EXTERNAL TABLE IF EXISTS exttabtest_r_2url; CREATE READABLE EXTERNAL TABLE exttabtest_r_2url (like exttabtest) LOCATION('demoprot://exttabtest_2url_1.txt', 'demoprot://exttabtest_2url_2.txt') FORMAT 'text'; DROP EXTERNAL TABLE IF EXISTS exttabtest_w_2url; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_2url (like exttabtest) LOCATION('demoprot://exttabtest_2url_1.txt', 'demoprot://exttabtest_2url_2.txt') FORMAT 'text' DISTRIBUTED BY (id); -- write to WET INSERT INTO exttabtest_w_2url (SELECT * FROM exttabtest); -- read from RET SELECT * FROM exttabtest_r_2url EXCEPT ALL SELECT * FROM exttabtest; -- verify data should be evenly distributed with t as ( SELECT gp_segment_id as segid, count(*) as cnt from exttabtest_r_2url group by gp_segment_id ) select max(cnt) - min(cnt) > 20 from t; -- Checking the output files on segments -- ! gpssh -f allsegs ls -l /data/hhuang/MAIN/main_debug/primary/gpseg*/exttabtest_2url*.txt -- Test 8: Negative - using 5 urls, exceeding number of primary segments (4) DROP EXTERNAL TABLE IF EXISTS exttabtest_w_5url; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_5url (like exttabtest) LOCATION('demoprot://exttabtest_5url_1.txt', 'demoprot://exttabtest_5url_2.txt', 'demoprot://exttabtest_5url_3.txt', 'demoprot://exttabtest_5url_4.txt', 'demoprot://exttabtest_5url_5.txt') FORMAT 'text' DISTRIBUTED BY (id); -- write to WET INSERT INTO exttabtest_w_5url (SELECT * FROM exttabtest); -- @skip Skipping this test because of a duplicate key violation error. -- Test 9: Negative - duplicte protocol name (check pg_extprotocol) SELECT count(*) FROM pg_extprotocol WHERE ptcname='demoprot'; CREATE PROTOCOL demoprot ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- Test 10: Negative: create external table using non-existing protocol CREATE WRITABLE EXTERNAL TABLE exttabtest_w_neg10 (like exttabtest) LOCATION('demoprot_nonexist://exttabtest_neg10.txt') FORMAT 'text' DISTRIBUTED BY (id); -- Test 11: Negative - Using invalid protocol attribute name -- attribute names must be readproc, write proc, and validatorproc DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunction = read_from_file, writefunction = write_to_file ); -- Test 12: Negatvie - using undefined function when defining protocol DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunc = read_from_file_badname, writefunc = write_to_file_badname ); -- Test 13: Negatvie - syntax error: missing '=' when defining protocol DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunc read_from_file, writefunc write_to_file ); -- @skip Test disabled in cdbfast as well -- Test 14: Negative - switching read function and write function -- This is user error. GPDB should display meaningful error message. -- Not running this test. Comment from cdbfast - Comment this out since it doesn't make much sense and it creates big output files DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( writefunc = read_from_file_stable, readfunc = write_to_file_stable ); DROP EXTERNAL TABLE IF EXISTS exttabtest_w_switched; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_switched (like exttabtest) LOCATION('demoprot://exttabtest_switched.txt') FORMAT 'text' DISTRIBUTED BY (id); DROP EXTERNAL TABLE IF EXISTS exttabtest_r_switched; CREATE READABLE EXTERNAL TABLE exttabtest_r_switched(like exttabtest) LOCATION('demoprot://exttabtest_switched.txt') FORMAT 'text'; -- write to WET INSERT INTO exttabtest_w_switched (SELECT * FROM exttabtest); -- read from RET --SELECT * FROM exttabtest_r_switched --EXCEPT ALL --SELECT * FROM exttabtest; -- Test 15: Negative - circular reference -- write to WET while selecting from RET, and WET and RET are using the same data source files DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); DROP EXTERNAL TABLE IF EXISTS exttabtest_w_circle; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_circle(like exttabtest) LOCATION('demoprot://exttabtest.txt') FORMAT 'text' DISTRIBUTED BY (id); DROP EXTERNAL TABLE IF EXISTS exttabtest_r_circle; CREATE READABLE EXTERNAL TABLE exttabtest_r_circle(like exttabtest) LOCATION('demoprot://exttabtest.txt') FORMAT 'text'; -- First to create exttabtest.txt using WET INSERT INTO exttabtest_w_circle (SELECT * FROM exttabtest); -- write to WET while reading from RET, -- using limit to avoid infinit loop -- This is to test that using RET and WET inappropriately can get yourself into trouble INSERT INTO exttabtest_w_circle (SELECT * FROM exttabtest_r_circle order by id limit 100); -- Test 16: Negative - invalid URL: missing path drop external table if exists exttabtest_w_misspath; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_misspath(like exttabtest) LOCATION('demoprot://') FORMAT 'text' DISTRIBUTED BY (id); insert into exttabtest_w_misspath (select * from exttabletest); -- Test 17: Negative - invalid URL: missing protocol CREATE READABLE EXTERNAL TABLE exttabtest_r_missprot(like exttabtest) LOCATION('exttabtest.txt') FORMAT 'text'; -- Test 18: Negative - invalid URL: invalid path CREATE READABLE EXTERNAL TABLE exttabtest_r_invalidpath(like exttabtest) LOCATION('demoprot:\\exttabtest.txt') FORMAT 'text'; -- Test 19: Negative - invalid URL: invalid protocol name CREATE READABLE EXTERNAL TABLE exttabtest_r_invalidprot(like exttabtest) LOCATION('badprotocol://exttabtest.txt') FORMAT 'text'; -- Test 20: Small dataset - 20 records DROP EXTERNAL TABLE IF EXISTS exttabtest_w_20records; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_20records (like exttabtest) LOCATION('demoprot://exttabtest_20records.txt') FORMAT 'text' DISTRIBUTED BY (id); DROP EXTERNAL TABLE IF EXISTS exttabtest_r_20records; CREATE READABLE EXTERNAL TABLE exttabtest_r_20records (like exttabtest) LOCATION('demoprot://exttabtest_20records.txt') FORMAT 'text'; -- write to WET INSERT INTO exttabtest_w_20records (SELECT * FROM exttabtest where id<=20); -- read from RET SELECT * FROM exttabtest_r_20records order by id; -- verify data should be evenly distributed with t as ( SELECT gp_segment_id as segid, count(*) as cnt from exttabtest_r_20records group by gp_segment_id ) select max(cnt) - min(cnt) > 20 from t; -- Drop External Tables DROP EXTERNAL TABLE IF EXISTS exttabtest_r_20records; DROP EXTERNAL TABLE IF EXISTS exttabtest_w_20records; -- Test 21: Small dataset - 1 record DROP EXTERNAL TABLE IF EXISTS exttabtest_w_1record; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_1record (like exttabtest) LOCATION('demoprot://exttabtest_1record.txt') FORMAT 'text' DISTRIBUTED BY (id); DROP EXTERNAL TABLE IF EXISTS exttabtest_r_1record; CREATE READABLE EXTERNAL TABLE exttabtest_r_1record (like exttabtest) LOCATION('demoprot://exttabtest_1record.txt') FORMAT 'text'; -- write to WET INSERT INTO exttabtest_w_1record (SELECT * FROM exttabtest where id = 4); -- read from RET -- The implemented example protocol (demoprot) requires data file must be available -- (even it is empty) for each (primary) segment. -- This is the limitation of this example (MPP-13811) -- and will cause following SElECT query to fail SELECT * FROM exttabtest_r_1record order by id; -- Drop External Tables DROP EXTERNAL TABLE IF EXISTS exttabtest_r_1record; DROP EXTERNAL TABLE IF EXISTS exttabtest_w_1record; -- Test 22: Using /dev/null -- Using /dev/null for output and input DROP EXTERNAL TABLE IF EXISTS exttabtest_w_null; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_null (like exttabtest) LOCATION('demoprot:///dev/null') FORMAT 'text' DISTRIBUTED BY (id); DROP EXTERNAL TABLE IF EXISTS exttabtest_r_null; CREATE READABLE EXTERNAL TABLE exttabtest_r_null (like exttabtest) LOCATION('demoprot:///dev/null') FORMAT 'text'; -- write to WET INSERT INTO exttabtest_w_null (SELECT * FROM exttabtest); -- read from RET SELECT count(id) FROM exttabtest_r_null; -- Test 23: Performance - 1M records -- Load 1M rows of data TRUNCATE TABLE exttabtest; INSERT INTO exttabtest SELECT i, 'name'||i, i*2, i*3 FROM generate_series(1,1000000) i; -- Using demoprot, import and export 1M records DROP EXTERNAL TABLE IF EXISTS exttabtest_w_1M; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_1M (like exttabtest) LOCATION('demoprot://exttabtest_1M.txt') FORMAT 'text' DISTRIBUTED BY (id); DROP EXTERNAL TABLE IF EXISTS exttabtest_r_1M; CREATE READABLE EXTERNAL TABLE exttabtest_r_1M (like exttabtest) LOCATION('demoprot://exttabtest_1M.txt') FORMAT 'text'; -- write to WET INSERT INTO exttabtest_w_1M (SELECT * FROM exttabtest); -- Time: 1369.028 ms -- read from RET SELECT count(id) FROM exttabtest_r_1M; -- Time: 869.793 ms -- Compare to using demoprot protocol and output to /dev/null (no disk IO, thanks to Alan) DROP EXTERNAL TABLE IF EXISTS exttabtest_w_1M_null; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_1M_null (like exttabtest) LOCATION('demoprot:///dev/null') FORMAT 'text' DISTRIBUTED BY (id); DROP EXTERNAL TABLE IF EXISTS exttabtest_r_1M_null; CREATE READABLE EXTERNAL TABLE exttabtest_r_1M_null (like exttabtest) LOCATION('demoprot:///dev/null') FORMAT 'text'; -- write to WET INSERT INTO exttabtest_w_1M_null (SELECT * FROM exttabtest); -- Time: 1368.173 ms (about same as writing to file - CPU intensive) -- Test 30: UDF dependency - drop UDF when it does not have dependent protocol -- Create new UDF that has no dependent CREATE OR REPLACE FUNCTION write_udf_todrop() RETURNS integer AS '$libdir/gpextprotocol.so', 'demoprot_export' LANGUAGE C STABLE; CREATE OR REPLACE FUNCTION read_udf_todrop() RETURNS integer AS '$libdir/gpextprotocol.so', 'demoprot_import' LANGUAGE C STABLE; -- Check pg_proc catalog table for new created functions SELECT proname, prolang,proisstrict,provolatile,pronargs,prorettype,prosrc,proacl FROM pg_proc WHERE proname like 'write_udf_todrop' or proname like 'read_udf_todrop' ORDER BY proname; -- Drop two UDFs DROP FUNCTION write_udf_todrop(); DROP FUNCTION read_udf_todrop(); -- Check pg_proc catalog table for after drop the UDFs SELECT proname, prolang,proisstrict,provolatile,pronargs,prorettype,prosrc,proacl FROM pg_proc WHERE proname like 'write_udf_todrop' or proname like 'read_udf_todrop' ORDER BY proname; -- Test 31: UDF dependency - drop UDF when it has dependent dependent protocol -- Cannot drop UDF when it has protocol dependent. -- Check protocol demoprot is depending on UDF select count(*) from pg_extprotocol where ptcreadfn='read_from_file_stable'::regproc; -- Try to drop UDF read_from_file_stable() that has protocol demoprot depends on it drop function read_from_file_stable(); -- Check pg_proc catalog table to verify UDF is NOT dropped SELECT proname, prolang,proisstrict,provolatile,pronargs,prorettype,prosrc,proacl FROM pg_proc WHERE proname like 'write_to_file%' or proname like 'read_from_file%' ORDER BY proname; -- Test 35: UDF dependency - drop function cascade -- Restore (recreate) protocol with the same protocol name demoprot DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- CREATE PROTOCOL -- Drop function cascade. -- Showing notice message of cascade drop of dependent protocol demoprot drop function read_from_file_stable() cascade; -- NOTICE: drop cascades to protocol demoprot -- DROP FUNCTION -- Check pg_proc for function read_from_file_stable SELECT oid, proname FROM pg_proc WHERE proname = 'read_from_file_stable'; -- returns 0 -- Check dependency: pg_depend table select count(*) from pg_depend where objid in ( select oid from pg_extprotocol where ptcname='demoprot'); -- Verified other catalog tables: pg_extprotocol, pg_depend -- Check pg_extprotocol table, the entry should be dropped select count(*) from pg_extprotocol where ptcname='demoprot'; -- returns 0 -- Test 36: Regression - Alter Ext Table when using custom protocol -- Recreate function read_from_file_stable CREATE OR REPLACE FUNCTION read_from_file_stable() RETURNS integer AS '$libdir/gpextprotocol.so', 'demoprot_import' LANGUAGE C STABLE; -- Restore (recreate) protocol with the same protocol name demoprot DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- CREATE PROTOCOL CREATE READABLE EXTERNAL TABLE exttabtest_r(like exttabtest) LOCATION('demoprot://exttabtest.txt') FORMAT 'text'; CREATE WRITABLE EXTERNAL TABLE exttabtest_w(like exttabtest) LOCATION('demoprot://exttabtest.txt') FORMAT 'text' DISTRIBUTED BY (id); -- truncate table exttabtest and load 100 records TRUNCATE TABLE exttabtest; INSERT INTO exttabtest SELECT i, 'name'||i, i*2, i*3 FROM generate_series(1,100) i; -- Check existing WET that using protocol demoprot SELECT * FROM clean_exttabtest_files; insert into exttabtest_w (select * from exttabtest); -- Check existing RET that using protocol demoprot select * from exttabtest_r where id <=4 order by id; -- returns 4 records -- Change ext table name when using custom protocol ALTER TABLE exttabtest_r rename to exttabtest_r_newname; -- Check for Add|Drop|Rename column ALTER EXTERNAL TABLE exttabtest_r_newname ADD COLUMN value3 int; ALTER TABLE exttabtest_r_newname RENAME COLUMN value3 to value3_newname; ALTER EXTERNAL TABLE exttabtest_r_newname DROP COLUMN value3_newname; -- Check external table is still accessible after alter operation select * from exttabtest_r_newname where id <=4 order by id; -- returns 4 records -- Test 37: Regression - Drop Ext Table when using custom protocol -- Check external table exttabtest_r_newname exists -- and is using demoprot protocol \d exttabtest_r_newname select count(*) from pg_class where relname = 'exttabtest_r_newname'; -- Drop external table 'exttabtest_r_newname' DROP EXTERNAL TABLE exttabtest_r_newname; -- DROP EXTERNAL TABLE -- Test 38: Negative - handling invalid data formate when using custom protocol -- Recreate RET DROP EXTERNAL TABLE IF EXISTS exttabtest_r_invalid; CREATE READABLE EXTERNAL TABLE exttabtest_r_invalid(like exttabtest) LOCATION('demoprot://exttabtest.txt') FORMAT 'text'; -- Using WET to create data source file exttabtest.txt SELECT * FROM clean_exttabtest_files; INSERT INTO exttabtest_w (select * from exttabtest); -- Check RET is accessible SELECT count(*) from exttabtest_r_invalid; -- returns count = 100 -- Add a column (value3) to RET ALTER EXTERNAL TABLE exttabtest_r_invalid ADD COLUMN value3 int; -- Access RET again with changed structure, the data file format is invalid now SELECT count(*) from exttabtest_r_invalid; -- Drop a column (id) from RET ALTER EXTERNAL TABLE exttabtest_r_invalid DROP COLUMN id; -- Access RET again with changed structure, the data file format is also invalid -- Comment this out since output file is undeterministic -- SELECT count(*) from exttabtest_r_invalid where gp_segment_id=0; -- Recreate WET DROP EXTERNAL TABLE IF EXISTS exttabtest_w_invalid; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_invalid(like exttabtest) LOCATION('demoprot://exttabtest_invalid.txt') FORMAT 'text'; -- Drop a column (id) from WET ALTER EXTERNAL TABLE exttabtest_w_invalid DROP COLUMN id; -- Write to WET INSERT INTO exttabtest_w_invalid (SELECT * from exttabtest); -- Test 60: setup source table -- This table is our example database table for formatter test DROP TABLE IF EXISTS formatsource CASCADE; CREATE TABLE formatsource( name varchar(40), id float8, value1 float8, value2 float8 ) DISTRIBUTED BY (id); -- Loading 100 records \echo 'loading data...' INSERT INTO formatsource SELECT 'name'||i, i, i*2, i*3 FROM generate_series(1,100) i; -- Check data distribution with t as ( SELECT gp_segment_id as segid, count(*) as cnt from formatsource group by gp_segment_id ) select max(cnt) - min(cnt) > 20 from t; -- Test 61: create STABLE read and write functions based on example gpformatter.so -- Note: Only STABLE is supported for formatter. -- When it is not STABLE (VALOTILE, or IMMUTABLE), -- the expected output of CREATE EXTERNAL TABLE should be an error. CREATE OR REPLACE FUNCTION formatter_export_s(record) RETURNS bytea AS '$libdir/gpformatter.so', 'formatter_export' LANGUAGE C STABLE; CREATE OR REPLACE FUNCTION formatter_import_s() RETURNS record AS '$libdir/gpformatter.so', 'formatter_import' LANGUAGE C STABLE; CREATE OR REPLACE FUNCTION formatter_export_v(record) RETURNS bytea AS '$libdir/gpformatter.so', 'formatter_export' LANGUAGE C VOLATILE; CREATE OR REPLACE FUNCTION formatter_import_v() RETURNS record AS '$libdir/gpformatter.so', 'formatter_import' LANGUAGE C VOLATILE; CREATE OR REPLACE FUNCTION formatter_export_i(record) RETURNS bytea AS '$libdir/gpformatter.so', 'formatter_export' LANGUAGE C IMMUTABLE; CREATE OR REPLACE FUNCTION formatter_import_i() RETURNS record AS '$libdir/gpformatter.so', 'formatter_import' LANGUAGE C IMMUTABLE; -- Check pg_proc catalog table for new created functions SELECT proname, prolang,proisstrict,provolatile,pronargs,prorettype,prosrc,proacl FROM pg_proc WHERE proname like 'formatter%' ORDER BY proname; -- Test 62: Drop function without dependent external table CREATE OR REPLACE FUNCTION formatter_export_todrop(record) RETURNS bytea AS '$libdir/gpformatter.so', 'formatter_export' LANGUAGE C STABLE; CREATE OR REPLACE FUNCTION formatter_import_todrop() RETURNS record AS '$libdir/gpformatter.so', 'formatter_import' LANGUAGE C STABLE; DROP FUNCTION formatter_export_todrop(record); DROP FUNCTION formatter_import_todrop(); -- Test 63: create RET and WET using demoprot protocol and STABLE formatter -- Note: Only STABLE is supported for formatter, this is enforced -- When it is not STABLE (VOLATILE, or IMMUTABLE), -- the expected error like: -- ERROR: formatter function formatter_export_i is not declared STABLE. (seg1 rh55-qavm55:7533 pid=14816) -- Create RET and WET using IMMUTABLE functions will succeed -- However query such RET or WET should fail DROP EXTERNAL TABLE IF EXISTS format_w; CREATE WRITABLE EXTERNAL TABLE format_w(like formatsource) LOCATION ('demoprot://exttabtest_test63') FORMAT 'CUSTOM' (FORMATTER='formatter_export_i'); DROP EXTERNAL TABLE IF EXISTS format_r; CREATE READABLE EXTERNAL TABLE format_r(like formatsource) LOCATION ('demoprot://exttabtest_test63') FORMAT 'CUSTOM' (FORMATTER='formatter_import_i'); INSERT INTO format_w (SELECT * FROM formatsource); -- ERROR: formatter function formatter_export_i is not declared STABLE. (seg1 rh55-qavm55:7533 pid=14816) -- Create RET and WET using STABLE functions DROP EXTERNAL TABLE IF EXISTS format_w; CREATE WRITABLE EXTERNAL TABLE format_w(like formatsource) LOCATION ('demoprot://exttabtest_test63') FORMAT 'CUSTOM' (FORMATTER='formatter_export_s'); DROP EXTERNAL TABLE IF EXISTS format_r; CREATE READABLE EXTERNAL TABLE format_r(like formatsource) LOCATION ('demoprot://exttabtest_test63') FORMAT 'CUSTOM' (FORMATTER='formatter_import_s'); -- Displaying table info \d format_w \d format_r -- Checking pg_exttable select pg_class.relname, fmttype, fmtopts, encoding, writable from pg_class, pg_exttable where pg_class.oid = pg_exttable.reloid and (pg_class.relname='format_w' or pg_class.relname='format_r' or pg_class.relname='format_text' or pg_class.relname='format_csv'); -- Write to WET SELECT * FROM clean_exttabtest_files; INSERT INTO format_w (SELECT * FROM formatsource); -- read from RET SELECT * FROM format_r EXCEPT ALL SELECT * FROM formatsource; -- Read from RET SELECT count(*) FROM format_r; -- verify data should be evenly distributed with t as ( SELECT gp_segment_id as segid, count(*) as cnt from format_r group by gp_segment_id ) select max(cnt) - min(cnt) > 20 from t; -- Test 64: Drop format function with external table using the function DROP FUNCTION formatter_export_i(record); DROP FUNCTION formatter_import_i(); DROP FUNCTION formatter_export_s(record); DROP FUNCTION formatter_import_s(); -- Check pg_proc catalog table for the format functions -- should return 0 SELECT proname, prolang,proisstrict,provolatile, pronargs,prorettype,prosrc,proacl FROM pg_proc WHERE proname='formatter_import_s' or proname='formatter_export_s' ORDER BY proname; -- Checking pg_exttable, should return 0 select pg_class.relname, fmttype, fmtopts, encoding, writable from pg_class, pg_exttable where pg_class.oid = pg_exttable.reloid and (pg_class.relname='format_w' or pg_class.relname='format_r'); -- Write to WET, should fail SELECT * FROM clean_exttabtest_files; INSERT INTO format_w (SELECT * FROM formatsource); -- Read from RET, should fail SELECT count(*) FROM format_r; -- Test 65: Restore (recreate) functions with same name, external table should work again -- Recreate UDFs with same function names CREATE OR REPLACE FUNCTION formatter_export_s(record) RETURNS bytea AS '$libdir/gpformatter.so', 'formatter_export' LANGUAGE C STABLE; CREATE OR REPLACE FUNCTION formatter_import_s() RETURNS record AS '$libdir/gpformatter.so', 'formatter_import' LANGUAGE C STABLE; -- Check pg_proc catalog table for new created functions SELECT proname, prolang,proisstrict,provolatile, pronargs,prorettype,prosrc,proacl FROM pg_proc WHERE proname='formatter_import_s' or proname='formatter_export_s' ORDER BY proname; -- Write to WET SELECT * FROM clean_exttabtest_files; INSERT INTO format_w (SELECT * FROM formatsource); -- Read from RET SELECT count(*) FROM format_r; -- returns count = 100 -- Test 67: Multiple external tables can use same UDF independently -- Recreate STABLE import and export UDFs CREATE OR REPLACE FUNCTION formatter_export_s(record) RETURNS bytea AS '$libdir/gpformatter.so', 'formatter_export' LANGUAGE C STABLE; CREATE OR REPLACE FUNCTION formatter_import_s() RETURNS record AS '$libdir/gpformatter.so', 'formatter_import' LANGUAGE C STABLE; -- First pair of RET and WET that using -- formatter_import_s and formatter_export_s DROP EXTERNAL TABLE IF EXISTS format_w_s1; CREATE WRITABLE EXTERNAL TABLE format_w_s1(like formatsource) LOCATION ('demoprot://exttabtest_test67_s1') FORMAT 'CUSTOM' (FORMATTER='formatter_export_s'); DROP EXTERNAL TABLE IF EXISTS format_r_s1; CREATE READABLE EXTERNAL TABLE format_r_s1(like formatsource) LOCATION ('demoprot://exttabtest_test67_s1') FORMAT 'CUSTOM' (FORMATTER='formatter_import_s'); -- Second pair of RET and WET that using -- same formatter_import_s and formatter_export_s DROP EXTERNAL TABLE IF EXISTS format_w_s2; CREATE WRITABLE EXTERNAL TABLE format_w_s2(like formatsource) LOCATION ('demoprot://exttabtest_test67_s2') FORMAT 'CUSTOM' (FORMATTER='formatter_export_s'); DROP EXTERNAL TABLE IF EXISTS format_r_s2; CREATE READABLE EXTERNAL TABLE format_r_s2(like formatsource) LOCATION ('demoprot://exttabtest_test67_s2') FORMAT 'CUSTOM' (FORMATTER='formatter_import_s'); -- Checking pg_exttable select pg_class.relname, fmttype, fmtopts, encoding, writable from pg_class, pg_exttable where pg_class.oid = pg_exttable.reloid and (pg_class.relname like 'format_w_s%' or pg_class.relname like 'format_r_s%'); -- Write to WET INSERT INTO format_w_s1 (SELECT * FROM formatsource); INSERT INTO format_w_s2 (SELECT * FROM formatsource); -- read from RET SELECT * FROM format_r_s1 EXCEPT ALL SELECT * FROM formatsource; SELECT * FROM format_r_s2 EXCEPT ALL SELECT * FROM formatsource; -- Read from RET SELECT count(*) FROM format_r_s1; SELECT count(*) FROM format_r_s2; -- Test 71: Check limit of MAX_FORMAT_STRING 4096 bytes for variable length strings - text type -- Create format_long table for formatter long record test DROP TABLE IF EXISTS format_long CASCADE; CREATE TABLE format_long ( id float8, name text ) DISTRIBUTED by (id); -- Check the atttypmod of column name is -1 SELECT atttypmod FROM pg_attribute WHERE attrelid = 'format_long'::regclass AND attname = 'name'; -- returns atttypmod = -1 -- Create RET and WET using demoprot and custom format UDFs DROP EXTERNAL TABLE IF EXISTS format_long_w; CREATE WRITABLE EXTERNAL TABLE format_long_w(like format_long) LOCATION ('demoprot://exttabtest_test71.txt') FORMAT 'CUSTOM' (FORMATTER='formatter_export_s'); DROP EXTERNAL TABLE IF EXISTS format_long_r; CREATE READABLE EXTERNAL TABLE format_long_r(like format_long) LOCATION ('demoprot://exttabtest_test71.txt') FORMAT 'CUSTOM' (FORMATTER='formatter_import_s'); -- Loading each record with 4000 characters, less than 4096 bytes truncate table format_long; insert into format_long select 1.0,i from repeat('oxo1', 1000) i; insert into format_long select 2.0,i from repeat('oxo2', 1000) i; insert into format_long select 3.0,i from repeat('oxo3', 1000) i; insert into format_long select 4.0,i from repeat('oxo4', 1000) i; insert into format_long select 5.0,i from repeat('oxo5', 1000) i; insert into format_long select 6.0,i from repeat('oxo6', 1000) i; insert into format_long select 7.0,i from repeat('oxo7', 1000) i; insert into format_long select 8.0,i from repeat('oxo8', 1000) i; insert into format_long select 9.0,i from repeat('oxo9', 1000) i; -- Check distribution is even with t as ( SELECT gp_segment_id as segid, count(*) as cnt from format_long group by gp_segment_id ) select max(cnt) - min(cnt) > 20 from t; -- Write to WET -- insert should be successful INSERT INTO format_long_w (SELECT * FROM format_long); -- Read from RET select count(*) from format_long_r; -- returns count = 4 -- Now loading each record with 5000 characters, more than 4096 bytes for each record truncate table format_long; insert into format_long select 1.0,i from repeat('oxox1', 1000) i; insert into format_long select 2.0,i from repeat('oxox2', 1000) i; insert into format_long select 3.0,i from repeat('oxox3', 1000) i; insert into format_long select 4.0,i from repeat('oxox4', 1000) i; -- Write to WET -- insert should fail since MAX_FORMAT_STRING (4096) is exceeded INSERT INTO format_long_w (SELECT * FROM format_long); -- ERROR: formatter_export: buffer too small (gpformatter.c:182) (seg2 rh55-qavm58:5532 pid=20093) -- Test 72: Verify limit of MAX_FORMAT_STRING 4096 bytes does not apply to fixed length strings - varchar() -- MAX_FORMAT_STRING limit should not apply to fixed length strings (char or varchar) -- Create format_long table for formatter long record test DROP TABLE IF EXISTS format_long CASCADE; CREATE TABLE format_long ( id float8, name varchar(6000) ) DISTRIBUTED by (id); -- Check the atttypmod of name > 0 SELECT atttypmod FROM pg_attribute WHERE attrelid = 'format_long'::regclass AND attname = 'name'; -- returns atttypmod = 6004 -- Create RET and WET using demoprot and custom format UDFs DROP EXTERNAL TABLE IF EXISTS format_long_w; CREATE WRITABLE EXTERNAL TABLE format_long_w(like format_long) LOCATION ('demoprot://exttabtest_test72.txt') FORMAT 'CUSTOM' (FORMATTER='formatter_export_s'); DROP EXTERNAL TABLE IF EXISTS format_long_r; CREATE READABLE EXTERNAL TABLE format_long_r(like format_long) LOCATION ('demoprot://exttabtest_test72.txt') FORMAT 'CUSTOM' (FORMATTER='formatter_import_s'); -- Now loading each record with 5000 characters, more than 4096 bytes for each record truncate table format_long; insert into format_long select 1.0,i from repeat('oxox1', 1000) i; insert into format_long select 2.0,i from repeat('oxox2', 1000) i; insert into format_long select 3.0,i from repeat('oxox3', 1000) i; insert into format_long select 4.0,i from repeat('oxox4', 1000) i; insert into format_long select 5.0,i from repeat('oxox5', 1000) i; insert into format_long select 6.0,i from repeat('oxox6', 1000) i; insert into format_long select 7.0,i from repeat('oxox7', 1000) i; insert into format_long select 8.0,i from repeat('oxox8', 1000) i; insert into format_long select 9.0,i from repeat('oxox9', 1000) i; -- Write to WET -- insert should be successful INSERT INTO format_long_w (SELECT * FROM format_long); -- Read from RET select count(*) from format_long_r; -- returns count = 9 -- Test 73: Interlacing short and long record, testing FMT_NEED_MORE_DATA -- When loading data from RET, long record may trigger FORMATTER_RETURN_NOTIFICATION(fcinfo, FMT_NEED_MORE_DATA). -- Verify the data can be loaded successfully and should exactly match the source records. -- Create format_long table for formatter long record test DROP TABLE IF EXISTS format_long CASCADE; CREATE TABLE format_long ( id float8, name varchar(5000) ) DISTRIBUTED by (id); -- Check the atttypmod of name > 0 SELECT atttypmod FROM pg_attribute WHERE attrelid = 'format_long'::regclass AND attname = 'name'; -- returns atttypmod = 5004 -- Create RET and WET using demoprot and custom format UDFs DROP EXTERNAL TABLE IF EXISTS format_long_w; CREATE WRITABLE EXTERNAL TABLE format_long_w(like format_long) LOCATION ('demoprot://exttabtest_test73.txt') FORMAT 'CUSTOM' (FORMATTER='formatter_export_s'); DROP EXTERNAL TABLE IF EXISTS format_long_r; CREATE READABLE EXTERNAL TABLE format_long_r(like format_long) LOCATION ('demoprot://exttabtest_test73.txt') FORMAT 'CUSTOM' (FORMATTER='formatter_import_s'); -- Loading short and long records alternatively. -- This should trigger FORMATTER_RETURN_NOTIFICATION(fcinfo, FMT_NEED_MORE_DATA) -- However not really sure about this. truncate table format_long; insert into format_long select 1.0,'oxo1'; insert into format_long select 2.0,i from repeat('oxo2', 1000) i; insert into format_long select 3.0,'oxo3'; insert into format_long select 4.0,i from repeat('oxo4', 1000) i; insert into format_long select 5.0,'oxo5'; insert into format_long select 6.0,i from repeat('oxo6', 1000) i; insert into format_long select 7.0,'oxo7'; insert into format_long select 8.0,i from repeat('oxo8', 1000) i; insert into format_long select 9.0,'oxo9'; -- Write to WET -- insert should be successful INSERT INTO format_long_w (SELECT * FROM format_long); -- Read from RET select count(*) from format_long_r; -- returns count = 9 -- read from RET, both should return 0 SELECT * FROM format_long_r EXCEPT ALL SELECT * FROM format_long; -- Test 74: Unsupported data format, using INT -- SAS example formatter only support String and Float types. For other types of data (like INT), it throws error "unsupported data type" -- Create format_long table for formatter long record test DROP TABLE IF EXISTS format_long CASCADE; CREATE TABLE format_long ( id int, name text ) DISTRIBUTED by (id); -- Create RET and WET using demoprot and custom format UDFs DROP EXTERNAL TABLE IF EXISTS format_long_w; CREATE WRITABLE EXTERNAL TABLE format_long_w(like format_long) LOCATION ('demoprot://format_long_test14') FORMAT 'CUSTOM' (FORMATTER='formatter_export_s'); DROP EXTERNAL TABLE IF EXISTS format_long_r; CREATE READABLE EXTERNAL TABLE format_long_r(like format_long) -- using source data file format_long_test13 created by previous test LOCATION ('demoprot://format_long_test13') FORMAT 'CUSTOM' (FORMATTER='formatter_import_s'); -- Now loading each record with 4000 characters, less than 4096 bytes for each record truncate table format_long; insert into format_long select 1,'oxo1'; insert into format_long select 2,'oxo2'; insert into format_long select 3,'oxo3'; insert into format_long select 4,'oxo4'; -- Write to WET -- insert should fail since INT type is not supported INSERT INTO format_long_w (SELECT * FROM format_long); -- ERROR: formatter_export error: unsupported data type (gpformatter.c:100) (seg2 rh55-qavm58:5532 pid=20668) (cdbdisp.c:1458) -- Read from RET using data file format_long_test13 create by previous test -- select should fail since INT type is not supported select count(*) from format_long_r; -- ERROR: formatter_import error: unsupported data type (gpformatter.c:256) (seg1 slice1 rh55-qavm57:5533 pid=20204) (cdbdisp.c:1458) -- Test 75: External table contains dropped columns -- SAS example formatter does NOT support dropping column from external table (both RET and WET). -- Both import and export operations will fail. This is expected. -- Create example source table formatsource DROP TABLE IF EXISTS formatsource CASCADE; CREATE TABLE formatsource ( id float8, name text, value1 float8 ) DISTRIBUTED by (id); -- Create RET and WET using demoprot and custom format UDFs DROP EXTERNAL TABLE IF EXISTS format_w; CREATE WRITABLE EXTERNAL TABLE format_w(like formatsource) LOCATION ('demoprot://format_test15') FORMAT 'CUSTOM' (FORMATTER='formatter_export_s'); DROP EXTERNAL TABLE IF EXISTS format_r; CREATE READABLE EXTERNAL TABLE format_r(like formatsource) -- using source data file format_long_test13 created by previous test LOCATION ('demoprot://format_long_test13') FORMAT 'CUSTOM' (FORMATTER='formatter_import_s'); -- Drop colum value1 from source table formatsource ALTER TABLE formatsource DROP COLUMN value1; -- Drop colum value1 from RET and WET ALTER EXTERNAL TABLE format_r DROP COLUMN value1; ALTER EXTERNAL TABLE format_w DROP COLUMN value1; -- Loading records truncate table formatsource; insert into formatsource select 1,'oxo1'; insert into formatsource select 2,'oxo2'; insert into formatsource select 3,'oxo3'; insert into formatsource select 4,'oxo4'; -- Write to WET -- insert should failed because of dropped column value1 INSERT INTO format_w (SELECT * FROM formatsource); -- ERROR: formatter_export: dropped columns (gpformatter.c:80) (seg1 rh55-qavm57:5533 pid=20454) (cdbdisp.c:1458) -- Read from RET using data file format_long_test13 create by previous test select count(*) from format_r; -- ERROR: formatter_import: dropped columns (gpformatter.c:244) (seg2 slice1 rh55-qavm58:5532 pid=20911) (cdbdisp.c:1458) -- Test 76: External table contains added column -- Add column to external table is fine. -- The value of the added column would be either null or NaN, depends on the data type. -- Create example source table formatsource DROP TABLE IF EXISTS formatsource CASCADE; CREATE TABLE formatsource ( id float8, name char(10) ) DISTRIBUTED by (id); -- Create RET and WET using demoprot and custom format UDFs DROP EXTERNAL TABLE IF EXISTS format_w; CREATE WRITABLE EXTERNAL TABLE format_w(like formatsource) LOCATION ('demoprot://exttabtest_test76.txt') FORMAT 'CUSTOM' (FORMATTER='formatter_export_s'); DROP EXTERNAL TABLE IF EXISTS format_r; CREATE READABLE EXTERNAL TABLE format_r(like formatsource) LOCATION ('demoprot://exttabtest_test76.txt') FORMAT 'CUSTOM' (FORMATTER='formatter_import_s'); -- Add column value1 to RET and WET ALTER EXTERNAL TABLE format_r ADD COLUMN value1 float8; ALTER EXTERNAL TABLE format_w ADD COLUMN value1 float8; -- Loading records truncate table formatsource; insert into formatsource select 1,'oxo1'; insert into formatsource select 2,'oxo2'; insert into formatsource select 3,'oxo3'; insert into formatsource select 4,'oxo4'; insert into formatsource select 5,'oxo5'; insert into formatsource select 6,'oxo6'; insert into formatsource select 7,'oxo7'; insert into formatsource select 8,'oxo8'; insert into formatsource select 9,'oxo9'; -- Write to WET -- insert should be successful INSERT INTO format_w (SELECT * FROM formatsource); -- Read from RET select id, value1 from format_r order by id; -- Test 77: data with null values -- SAS example formatter casts null value to: -- * null for String -- * NaN for Float8 -- Create example source table formatsource DROP TABLE IF EXISTS formatsource CASCADE; CREATE TABLE formatsource ( id float8, name text, value1 float8 ) DISTRIBUTED by (id); -- Create RET and WET using demoprot and custom format UDFs DROP EXTERNAL TABLE IF EXISTS format_w; CREATE WRITABLE EXTERNAL TABLE format_w(like formatsource) LOCATION ('demoprot://exttabtest_test77.txt') FORMAT 'CUSTOM' (FORMATTER='formatter_export_s'); DROP EXTERNAL TABLE IF EXISTS format_r; CREATE READABLE EXTERNAL TABLE format_r(like formatsource) LOCATION ('demoprot://exttabtest_test77.txt') FORMAT 'CUSTOM' (FORMATTER='formatter_import_s'); -- Loading records truncate table formatsource; insert into formatsource values (1,null,null); insert into formatsource values (2,null,null); insert into formatsource values (3,null,null); insert into formatsource values (4,null,null); insert into formatsource values (5,null,null); insert into formatsource values (6,null,null); insert into formatsource values (7,null,null); insert into formatsource values (8,null,null); insert into formatsource values (9,null,null); -- Write to WET -- insert should be successful INSERT INTO format_w (SELECT * FROM formatsource); -- Read from RET select * from format_r where name is null order by id; -- Test 78: Read from empty data file -- SAS example formatter can correctly handle empty input, either from /dev/null or from empty input data files. -- Create example source table formatsource DROP TABLE IF EXISTS formatsource CASCADE; CREATE TABLE formatsource ( id float8, name text, value1 float8 ) DISTRIBUTED by (id); -- Create RET with custom format using /dev/null as source file DROP EXTERNAL TABLE IF EXISTS format_r; CREATE READABLE EXTERNAL TABLE format_r(like formatsource) LOCATION ('demoprot:///dev/null') FORMAT 'CUSTOM' (FORMATTER='formatter_import_s'); -- Read from RET using /dev/null select * from format_r; -- Test 81: Protocol validator - create protocol with validator -- create the protocol read and write STABLE functions CREATE OR REPLACE FUNCTION write_to_file() RETURNS integer AS '$libdir/gpextprotocol.so', 'demoprot_export' LANGUAGE C STABLE; CREATE OR REPLACE FUNCTION read_from_file() RETURNS integer AS '$libdir/gpextprotocol.so', 'demoprot_import' LANGUAGE C STABLE; -- create validation STABLE function CREATE OR REPLACE FUNCTION url_validator() RETURNS void AS '$libdir/gpextprotocol.so', 'demoprot_validate_urls' LANGUAGE C STABLE; -- declare the protocol name along with in/out funcs and validator func DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunc = read_from_file, writefunc = write_to_file, validatorfunc = url_validator ); -- Test 82: At ext table create time, validate number of URLs cannot exceed number of primary segments CREATE READABLE EXTERNAL TABLE exttabtest_5url_r(like exttabtest) LOCATION('demoprot://test1.txt', 'demoprot://test2.txt', 'demoprot://test3.txt', 'demoprot://test4.txt', 'demoprot://test5.txt') FORMAT 'text'; -- ERROR: more than 2 urls aren't allowed in this protocol -- Test 83: At ext table create time, url string cannot contain "secured_directory" CREATE READABLE EXTERNAL TABLE exttabtest_3url_r(like exttabtest) LOCATION('demoprot://test1.txt', 'demoprot://secured_directory/test2.txt') FORMAT 'text'; -- ERROR: using 'secured_directory' in a url isn't allowed -- Test 4: Negative - validator protocol function must return void -- create the validator function and returns integer, which is invalid DROP FUNCTION IF EXISTS url_validator(); CREATE OR REPLACE FUNCTION url_validator() RETURNS integer AS '$libdir/gpextprotocol.so', 'demoprot_validate_urls' LANGUAGE C STABLE; -- declare the protocol name along with in/out funcs and validator func DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunc = read_from_file, writefunc = write_to_file, validatorfunc = url_validator ); -- Test 5: Negative - invalid protocol attribute name for validator function: must be "validatorfunc" -- declare the protocol using invalid attribute name "validatorproc" DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunc = read_from_file, writefunc = write_to_file, validatorproc = url_validator ); -- ERROR: protocol attribute "validatorproc" not recognized -- ERROR: using 'secured_directory' in a url isn't allowed -- Create multiple roles with login option so that they can be used for protocol permission tests and alter protocol tests -- Create another suerpuer user demoprot_super drop role if exists demoprot_super; create role demoprot_super with SUPERUSER LOGIN; -- Create a non-privileged user demoprot_nopriv drop role if exists demoprot_nopriv; create role demoprot_nopriv with login ; -- Create a gphdfs_user with CREATEEXTTABLE privilege using gphdfs protocol drop role if exists gphdfs_user; create role gphdfs_user with login CREATEEXTTABLE (protocol='gphdfs'); -- WARNING: GRANT/REVOKE on gphdfs is deprecated -- HINT: Issue the GRANT or REVOKE on the protocol itself -- NOTICE: resource queue required -- using default resource queue "pg_default" -- CREATE ROLE -- Test 92: Rename existing protocol DROP FUNCTION IF EXISTS url_validator(); CREATE OR REPLACE FUNCTION url_validator() RETURNS void AS '$libdir/gpextprotocol.so', 'demoprot_validate_urls' LANGUAGE C STABLE; CREATE PROTOCOL demoprot ( readfunc = read_from_file, writefunc = write_to_file, validatorfunc = url_validator ); -- Create external RET and WET DROP EXTERNAL TABLE IF EXISTS exttabtest_w; CREATE WRITABLE EXTERNAL TABLE exttabtest_w(like exttabtest) LOCATION('demoprot://exttabtest.txt') FORMAT 'text' DISTRIBUTED BY (id); DROP EXTERNAL TABLE IF EXISTS exttabtest_r; CREATE READABLE EXTERNAL TABLE exttabtest_r(like exttabtest) LOCATION('demoprot://exttabtest.txt') FORMAT 'text'; -- write to WET SELECT * FROM clean_exttabtest_files; INSERT INTO exttabtest_w (SELECT * FROM exttabtest); -- Rename existing protocol ALTER PROTOCOL demoprot RENAME to demoprot_new; -- checking pg_extprotocol select ptcname, ptctrusted from pg_extprotocol where ptcname like 'demoprot%' order by ptcname; -- Check existing ext table that created still refer to old protocol name \d exttabtest_r -- Create a new ext table using the new protocol name DROP EXTERNAL TABLE IF EXISTS exttabtest_w_new; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_new(like exttabtest) LOCATION('demoprot_new://exttabtest.txt') FORMAT 'text' DISTRIBUTED BY (id); DROP EXTERNAL TABLE IF EXISTS exttabtest_r_new; CREATE READABLE EXTERNAL TABLE exttabtest_r_new(like exttabtest) LOCATION('demoprot_new://exttabtest.txt') FORMAT 'text'; -- Verify access old ext table that referencing old protocol name would fail -- This is expected. select * from exttabtest_r; -- ERROR: protocol "demoprot" does not exist (seg1 slice1 rh55-qavm57:5533 pid=8558) -- Verify access new ext table would be successful -- However demoprot implementation prevents using any other protocol name than "demoprot" -- therefore the error is expected. select count(*) from exttabtest_r_new; -- Rename protocol name back to demoprot ALTER PROTOCOL demoprot_new RENAME to demoprot; -- Verify access old ext table that referencing old protocol name would succeed select count(*) from exttabtest_r; -- Test 93: Trusted protocol - Change ownership -- The owner of trusted protocol (not a superuser) can create external table -- using the protocol, even without SELECT or INSERT permission granted -- login as superuser huangh5 -- create trusted protocol demoprot DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE TRUSTED PROTOCOL demoprot ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- Check the owner of trusted protocl demoprot is current user select ptcname, ptctrusted from pg_extprotocol join pg_user on pg_extprotocol.ptcowner=pg_user.usesysid where ptcname='demoprot' and usename=(select user); -- Change protocol demoprot owner to non-privileged user "demoprot_nopriv" ALTER PROTOCOL demoprot OWNER TO demoprot_nopriv; -- Drop the existing external RET and WET DROP EXTERNAL TABLE IF EXISTS exttabtest_r_new; DROP EXTERNAL TABLE IF EXISTS exttabtest_w_new; -- Check the owner of demoprot is demoprot_nopriv -- and no protocol permission has been granted select ptcname, ptctrusted,ptcacl, usename from pg_extprotocol join pg_user on pg_extprotocol.ptcowner=pg_user.usesysid where ptcname='demoprot'; -- Check a dependency entry is added into pg_shdepend table select count(*) from pg_shdepend, pg_extprotocol, pg_user where pg_extprotocol.ptcowner=pg_user.usesysid and pg_shdepend.objid=pg_extprotocol.oid and pg_extprotocol.ptcname='demoprot' and pg_user.usename='demoprot_nopriv'; -- As superuser, GRANT SELECT permission on heap table "exttabtest" -- to non-privileged user "demoprot_nopriv" so that this user -- can try to create external table using format: (like exttabtest) GRANT SELECT ON exttabtest TO demoprot_nopriv; -- As superuser, REVOKE ALL privileges on protocol from owner demoprot_nopriv REVOKE ALL ON PROTOCOL demoprot FROM demoprot_nopriv; -- connect as non-privileged user "demoprot_nopriv" -- which is the owner of trusted demoprot SET ROLE demoprot_nopriv; select user; -- Verify that even though no permission has been -- granted to non-privileged user "demoprot_nopriv", -- this user can still create new ext tables -- using trusted protocol demoprot, because the user -- is the owner of the protocol demoprot. CREATE READABLE EXTERNAL TABLE exttabtest_r_new (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text'; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_new(like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text' DISTRIBUTED BY (id); -- Verify non-privileged user "demoprot_nopriv" can export data via new created WET exttabtest_w_new SELECT * FROM clean_exttabtest_files; INSERT INTO exttabtest_w_new (SELECT * FROM exttabtest); -- Verify non-privileged user "demoprot_nopriv" can load data via new created RET exttabtest_r_new select count(*) from exttabtest_r_new; -- Verified owner (non superuser) can drop the protocol DROP PROTOCOL demoprot CASCADE; RESET ROLE; -- Test 94: Untrusted protocol - Change ownership -- The owner of untrusted protocol (not a superuser) can still create external table -- using the untrusted protocol. -- connect as superuser -- create untrusted protocol demoprot DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- Try to change protocol demoprot owner to non-privileged user "demoprot_nopriv" -- Should get: ERROR: untrusted protocol "demoprot" can't be owned by non superuser -- Therefore the owner of trusted protocl demoprot is still the current superuser ALTER PROTOCOL demoprot OWNER TO demoprot_nopriv; -- As superuser, GRANT SELECT permission on heap table "exttabtest" -- to non-privileged user "demoprot_nopriv" so that this user -- can try to create external table using format: (like exttabtest) GRANT SELECT ON exttabtest TO demoprot_nopriv; -- As superuser, REVOKE ALL privileges on protocol from owner demoprot_nopriv -- The error is correctly shown REVOKE ALL ON PROTOCOL demoprot FROM demoprot_nopriv; -- ERROR: protocol "demoprot" is not trusted -- login as non-privileged user "demoprot_nopriv" SET ROLE demoprot_nopriv; select user; -- Verify that no permission has been -- granted to non-privileged user "demoprot_nopriv", -- this user can cannot create new ext tables -- using untrusted protocol demoprot. CREATE READABLE EXTERNAL TABLE exttabtest_r_new2 (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text'; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_new2 (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text' DISTRIBUTED BY (id); -- Verified non superuser cannot drop the protocol DROP PROTOCOL demoprot; -- Test 95: Alter protocol negative tests ALTER PROTOCOL demoprot ( readfunc = read_from_file_immutable, writefunc = write_to_file_immutable ); -- ERROR: syntax error at or near "(" ALTER PROTOCOL demoprot update readfunc = read_from_file_immutable; -- ERROR: syntax error at or near "update" ALTER PROTOCOL demoprot trusted; -- ERROR: syntax error at or near "trusted" RESET ROLE; -- Test 96: Untrusted protocol - Superuser -- Non-owner superuser does not have any limitation when using non-trusted protocol. -- login as superuser huangh5 -- create untrusted protocol demoprot DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE PROTOCOL demoprot ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- Check the owner of trusted protocl demoprot is current superuser select ptcname, ptctrusted from pg_extprotocol join pg_user on pg_extprotocol.ptcowner=pg_user.usesysid where ptcname='demoprot' and usename=(select user); -- connect as a different superuser "demoprot_super" SET ROLE demoprot_super; select user; SELECT * FROM clean_exttabtest_files; -- Verify superuser "demoprot_super" can create new ext table using untrusted protocol demoprot DROP EXTERNAL TABLE IF EXISTS exttabtest_r_new; CREATE READABLE EXTERNAL TABLE exttabtest_r_new(like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text'; DROP EXTERNAL TABLE IF EXISTS exttabtest_w_new; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_new(like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text' DISTRIBUTED BY (id); -- Verify superuser demoprot_super can still export data via new created WET exttabtest_w_new INSERT INTO exttabtest_w_new (SELECT * FROM exttabtest); -- Verify superuser demoprot_super can access new created RET exttabtest_r_new select count(*) from exttabtest_r_new; RESET ROLE; -- Test 97: Non-trusted protocol - non-priv user -- Non-privileged user cannot use non-trusted protocol to create external table. -- With granted permissions on existing external table, Non-privileged user can access existing WET and RET DROP EXTERNAL TABLE IF EXISTS exttabtest_r; CREATE READABLE EXTERNAL TABLE exttabtest_r(like exttabtest) LOCATION('demoprot://exttabtest.txt') FORMAT 'text'; DROP EXTERNAL TABLE IF EXISTS exttabtest_w; CREATE WRITABLE EXTERNAL TABLE exttabtest_w(like exttabtest) LOCATION('demoprot://exttabtest.txt') FORMAT 'text' DISTRIBUTED BY (id); -- As superuser, GRANT SELECT permission on RET -- and INSERT permission on WET to on-privileged user "demoprot_nopriv" -- to non-privileged user "demoprot_nopriv" GRANT SELECT ON exttabtest_r TO demoprot_nopriv; GRANT INSERT ON exttabtest_w TO demoprot_nopriv; -- As superuser, GRANT SELECT permission on heap table "exttabtest" -- to non-privileged user "demoprot_nopriv" so that this user -- can try to create external table using format: (like exttabtest) GRANT SELECT ON exttabtest TO demoprot_nopriv; -- connect as non-privileged user "demoprot_nopriv" SET ROLE demoprot_nopriv; select user; -- Check current user has the SELECT permission on 'exttabtest_r' select has_table_privilege('exttabtest_r','select'); -- Check current user has the INSERT permission on 'exttabtest_w' select has_table_privilege('exttabtest_w','insert'); -- Check current user has the SELECT permission on 'exttabtest' select has_table_privilege('exttabtest','select'); -- Verify with INSERT permission granted, non-privileged user "demoprot_nopriv" -- can export data via WET that was created using non-trusted protocol SELECT * FROM clean_exttabtest_files; INSERT INTO exttabtest_w (SELECT * FROM exttabtest); -- Verify with SELECT permission granted, non-privileged user "demoprot_nopriv" -- can load data via RET that was created using non-trusted protocol select count(*) from exttabtest_r; -- Verify non-privileged user "demoprot_nopriv" cannot create new ext table -- using untrusted protocol demoprot DROP EXTERNAL TABLE IF EXISTS exttabtest_r_new2; CREATE READABLE EXTERNAL TABLE exttabtest_r_new2 (like exttabtest) LOCATION('demoprot://exttabtest.txt') FORMAT 'text'; -- Test 98: Trusted Protocol - Negative Tests -- create protocol using incorrect keyword TRUST instead of TRUSTED DROP PROTOCOL IF EXISTS demoprot_trusted_bad; CREATE TRUST PROTOCOL demoprot_trusted_bad ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- create protocol using incorrect keyword UNTRUSTED DROP PROTOCOL IF EXISTS demoprot_trusted_bad; CREATE UNTRUSTED PROTOCOL demoprot_trusted_bad ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- create protocol using TRUSTED at wrong position DROP PROTOCOL IF EXISTS demoprot_trusted_bad; CREATE PROTOCOL TRUSTED demoprot_trusted_bad ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- create protocol using TRUSTED at wrong position DROP PROTOCOL IF EXISTS demoprot_trusted_bad; CREATE PROTOCOL demoprot_trusted_bad TRUSTED ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); RESET ROLE; -- Test 99: Trusted protocol - Grant All -- Grant all permissions ON trusted protocol to non-privileged user -- Non-privileged user can use trusted protocol to create external table. -- connect as superuser -- create trusted protocol demoprot DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE TRUSTED PROTOCOL demoprot ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- As superuser, GRANT SELECT permission on heap table "exttabtest" -- to non-privileged user "demoprot_nopriv" so that this user -- can try to create external table using format: (like exttabtest) GRANT SELECT ON exttabtest TO demoprot_nopriv; -- Drop existing WET and RET DROP EXTERNAL TABLE IF EXISTS exttabtest_r_new; DROP EXTERNAL TABLE IF EXISTS exttabtest_w_new; -- As superuser, GRANT ALL privileges on protocol to non-privileged user demoprot_nopriv GRANT ALL ON PROTOCOL demoprot TO demoprot_nopriv; -- login as non-privileged user "demoprot_nopriv" SET ROLE demoprot_nopriv; select user; -- Verify non-privileged user "demoprot_nopriv" can create new ext table -- using trusted protocol demoprot CREATE READABLE EXTERNAL TABLE exttabtest_r_new (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text'; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_new (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text' DISTRIBUTED BY (id); -- Verify non-privileged user "demoprot_nopriv" can export data via new created WET exttabtest_w_new SELECT * FROM clean_exttabtest_files; INSERT INTO exttabtest_w_new (SELECT * FROM exttabtest); -- Verify non-privileged user "demoprot_nopriv" can load data via new created RET exttabtest_r_new select count(*) from exttabtest_r_new; RESET ROLE; -- Test 99a: Trusted protocol - Revoke All -- Revoke all permissions ON trusted protocol from non-privileged user -- connect as superuser -- create trusted protocol demoprot DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE TRUSTED PROTOCOL demoprot ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- As superuser, GRANT SELECT permission on heap table "exttabtest" -- to non-privileged user "demoprot_nopriv" so that this user -- can try to create external table using format: (like exttabtest) GRANT SELECT ON exttabtest TO demoprot_nopriv; -- As superuser, REVOKE ALL privileges on protocol from non-privileged user demoprot_nopriv -- Both SELECT and INSERT permissions are revoked REVOKE ALL ON PROTOCOL demoprot FROM demoprot_nopriv; -- login as non-privileged user "demoprot_nopriv" SET ROLE demoprot_nopriv; select user; -- Verify after permissions have been revoked -- non-privileged user "demoprot_nopriv" cannot create new ext table -- using the trusted protocol demoprot CREATE READABLE EXTERNAL TABLE exttabtest_r_new (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text'; CREATE WRITABLE EXTERNAL TABLE exttabtest_w_new (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text' DISTRIBUTED BY (id); RESET ROLE; -- Test 101: Trusted protocol - Grant Select -- Grant SELECT permission ON trusted protocol to non-privileged user -- Non-privileged user can use trusted protocol to create readable external table. -- Create exttabtest_new.txt data file CREATE WRITABLE EXTERNAL TABLE exttabtest_w (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text' DISTRIBUTED BY (id); SELECT * FROM clean_exttabtest_files; INSERT INTO exttabtest_w (SELECT * FROM exttabtest); -- As superuser, demoport is created as a trusted readonly protocol DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE TRUSTED PROTOCOL demoprot ( readfunc = read_from_file_stable ); -- As superuser, GRANT SELECT permission on heap table "exttabtest" -- to non-privileged user "demoprot_nopriv" so that this user -- can try to create external table using format: (like exttabtest) GRANT SELECT ON exttabtest TO demoprot_nopriv; -- Drop existing WET and RET DROP EXTERNAL TABLE IF EXISTS exttabtest_r_new; DROP EXTERNAL TABLE IF EXISTS exttabtest_w_new; -- As superuser, GRANT SELECT permission on read protocol to non-privileged user demoprot_nopriv GRANT SELECT ON PROTOCOL demoprot TO demoprot_nopriv; -- login as non-privileged user "demoprot_nopriv" SET ROLE demoprot_nopriv; select user; -- Verify non-privileged user "demoprot_nopriv" can create new readable ext table -- using trusted protocol demoprot CREATE READABLE EXTERNAL TABLE exttabtest_r_new (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text'; -- Verify non-privileged user "demoprot_nopriv" cannot create new writable ext table CREATE WRITABLE EXTERNAL TABLE exttabtest_w_new (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text' DISTRIBUTED BY (id); -- Verify non-privileged user "demoprot_nopriv" can load data via new created RET exttabtest_r_new select count(*) from exttabtest_r_new; RESET ROLE; -- Test 102: Trusted protocol - Revoke Select -- Revoke SELECT permission ON trusted protocol from non-privileged user -- connect as superuser -- create trusted protocol demoprot DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE TRUSTED PROTOCOL demoprot ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- As superuser, GRANT SELECT permission on heap table "exttabtest" -- to non-privileged user "demoprot_nopriv" so that this user -- can try to create external table using format: (like exttabtest) GRANT SELECT ON exttabtest TO demoprot_nopriv; -- As superuser, REVOKE SElECT privilege on protocol from non-privileged user demoprot_nopriv REVOKE SELECT ON PROTOCOL demoprot FROM demoprot_nopriv; -- login as non-privileged user "demoprot_nopriv" SET ROLE demoprot_nopriv; select user; -- Verify after SELECT permission has been revoked -- non-privileged user "demoprot_nopriv" cannot create new readable ext table -- using the trusted protocol demoprot CREATE READABLE EXTERNAL TABLE exttabtest_r_new2 (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text'; RESET ROLE; -- Test 103: Trusted protocol - Grant Insert -- Grant INSERT permission ON trusted protocol to non-privileged user -- Non-privileged user can use trusted protocol to create writable external table. -- As superuser, demoport is created as a trusted readonly protocol DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE TRUSTED PROTOCOL demoprot ( writefunc = write_to_file_stable ); -- As superuser, GRANT SELECT permission on heap table "exttabtest" -- to non-privileged user "demoprot_nopriv" so that this user -- can try to create external table using format: (like exttabtest) GRANT SELECT ON exttabtest TO demoprot_nopriv; -- Drop existing WET and RET DROP EXTERNAL TABLE IF EXISTS exttabtest_r_new; DROP EXTERNAL TABLE IF EXISTS exttabtest_w_new; -- As superuser, GRANT INSERT permission on read protocol to non-privileged user demoprot_nopriv GRANT INSERT ON PROTOCOL demoprot TO demoprot_nopriv; -- login as non-privileged user "demoprot_nopriv" SET ROLE demoprot_nopriv; select user; -- Verify non-privileged user "demoprot_nopriv" cannot create new readable ext table -- using trusted protocol demoprot CREATE READABLE EXTERNAL TABLE exttabtest_r_new (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text'; -- Verify non-privileged user "demoprot_nopriv" can create new writable ext table CREATE WRITABLE EXTERNAL TABLE exttabtest_w_new (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text' DISTRIBUTED BY (id); -- Verify non-privileged user "demoprot_nopriv" can export data via new created WET exttabtest_w_new SELECT * FROM clean_exttabtest_files; INSERT INTO exttabtest_w_new (SELECT * FROM exttabtest); RESET ROLE; -- Test 104: Trusted protocol - Revoke Insert -- Revoke INSERT permission ON trusted protocol from non-privileged user -- connect as superuser -- create trusted protocol demoprot DROP PROTOCOL IF EXISTS demoprot CASCADE; CREATE TRUSTED PROTOCOL demoprot ( readfunc = read_from_file_stable, writefunc = write_to_file_stable ); -- As superuser, GRANT SELECT permission on heap table "exttabtest" -- to non-privileged user "demoprot_nopriv" so that this user -- can try to create external table using format: (like exttabtest) GRANT SELECT ON exttabtest TO demoprot_nopriv; -- As superuser, REVOKE INSERT privilege on protocol from non-privileged user demoprot_nopriv REVOKE INSERT ON PROTOCOL demoprot FROM demoprot_nopriv; -- login as non-privileged user "demoprot_nopriv" SET ROLE demoprot_nopriv; select user; -- Verify after INSERT permission has been revoked -- non-privileged user "demoprot_nopriv" cannot create new writable ext table -- using the trusted protocol demoprot CREATE WRITABLE EXTERNAL TABLE exttabtest_w_new2 (like exttabtest) LOCATION('demoprot://exttabtest_new.txt') FORMAT 'text' DISTRIBUTED BY (id); RESET ROLE;