blob: efebb69e770adab81458c4b187eda13749a46f76 [file] [log] [blame]
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_options_r;
CREATE READABLE EXTERNAL TABLE exttabtest_options_r(like exttabtest)
LOCATION('demoprot://exttabtest.txt')
FORMAT 'text'
OPTIONS (database 'redplum');
SELECT * FROM exttabtest_options_r
EXCEPT ALL
SELECT * FROM exttabtest;
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'
OPTIONS (database 'cloudberry', foo 'bar');
\d exttabtest_r
\d exttabtest_w
-- 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
-- Comment this out since output file is undeterministic
-- 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: Both STABLE and IMMUTABLE are supported for formatter.
-- When it is VALOTILE,
-- 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: Both STABLE and IMMUTABLE are supported for formatter.
-- When it is VALOTILE,
-- the expected error like:
-- ERROR: formatter function formatter_export_i is not declared STABLE or IMMUTABLE
-- Create RET and WET using IMMUTABLE functions will succeed
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);
-- 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
-- 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;
\d format_w
\d 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');
\d format_w_s1
\d format_w_s2
\d format_r_s1
\d format_r_s2
-- 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:183) (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:101) (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 column value1 from source table formatsource
ALTER TABLE formatsource DROP COLUMN value1;
-- Drop column 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:81) (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 ;
-- 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;