blob: 6495277606aa6bcf4c21546fd5c43a03d9a300cc [file] [log] [blame]
set timezone=UTC;
-- Create test tables which will be used for table function testing
DROP TABLE IF EXISTS t1 CASCADE;
CREATE TABLE t1 (a int, b int, c int, d int, e text)
DISTRIBUTED BY (a);
INSERT INTO t1 SELECT i, i/3, i%2, 100-i, 'text'||i
FROM generate_series(1,100) i;
select count(*) from t1;
DROP FUNCTION IF EXISTS sessionize(anytable, interval);
DROP FUNCTION IF EXISTS ud_project(anytable, text);
DROP FUNCTION IF EXISTS ud_project(anytable);
DROP FUNCTION IF EXISTS ud_project2(anytable, text);
DROP FUNCTION IF EXISTS ud_project2(anytable);
DROP FUNCTION IF EXISTS ud_describe(internal);
DROP FUNCTION IF EXISTS ud_describe2(internal);
DROP TYPE IF EXISTS outcomp CASCADE;
DROP TABLE IF EXISTS outtable CASCADE;
DROP TABLE IF EXISTS intable CASCADE;
DROP TABLE IF EXISTS randtable;
CREATE TABLE intable(id int, value text) distributed by (id);
CREATE TABLE outtable(a text, b int) distributed randomly;
CREATE TABLE randtable(id int, value text) distributed randomly;
INSERT INTO intable SELECT id::int, ('value_'||id)::text FROM generate_series(1, 10) id;
INSERT INTO randtable SELECT id::int, ('value_'||id)::text FROM generate_series(1, 10) id;
\d intable
\d outtable
\d randtable
select * from intable order by id,value;
DROP TABLE IF EXISTS history CASCADE;
CREATE TABLE history(id integer, time timestamp) DISTRIBUTED BY (id);
INSERT INTO history values
(1,'2011/08/21 10:15:02am'),
(1,'2011/08/21 10:15:30am'),
(1,'2011/08/22 10:15:04am'),
(1,'2011/08/22 10:16:10am'),
(2,'2011/08/21 10:15:02am'),
(2,'2011/08/21 10:15:02am'),
(2,'2011/08/21 10:16:02am'),
(2,'2011/08/21 10:16:02am'),
(3,'2011-08-19 19:05:13'),
(3,'2011-08-19 19:06:50'),
(3,'2011-08-19 19:07:35'),
(3,'2011-08-19 19:08:18'),
(3,'2011-08-19 19:09:07'),
(3,'2011-08-20 10:07:10'),
(3,'2011-08-20 10:07:35'),
(3,'2011-08-20 10:11:29'),
(3,'2011-08-20 10:17:10'),
(3,'2011-08-20 10:17:42');
SELECT * FROM history order by id, time;
-- Create Enhanced Table Function (ETF) using input syntax:
-- CREATE FUNCTION tabfunc(t anytable) RETURNS ...
-- MPP-16614, the SELECT query would fail
DROP FUNCTION IF EXISTS transform(anytable);
CREATE OR REPLACE FUNCTION transform(a anytable)
RETURNS SETOF outtable
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
\df transform
SELECT * FROM transform(
TABLE( SELECT * FROM randtable ORDER BY id, value SCATTER BY id)
) order by b;
-- The 1st workaround of MPP-16614
DROP FUNCTION IF EXISTS transform(anytable);
CREATE OR REPLACE FUNCTION transform(a anytable)
RETURNS SETOF RECORD
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
SELECT * FROM transform(
TABLE( SELECT * FROM randtable ORDER BY id, value SCATTER BY id)
) AS t (a text, b int) order by b;
-- The 2nd workaround of MPP-16614
DROP FUNCTION IF EXISTS transform(anytable);
CREATE OR REPLACE FUNCTION transform(a anytable)
RETURNS TABLE (a text, b int)
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
SELECT * FROM transform(
TABLE( SELECT * FROM randtable ORDER BY id, value SCATTER BY id)
) order by b;
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- Check callback function project_desc is registerred in pg_proc_callback
select * from pg_proc_callback
where profnoid='project'::regproc
and procallback='project_desc'::regproc;
-- ETF can only be created when using anytable as input type.
-- Negative: CREATE FUNCTION tabfunc_bad1 (x SETOF targettable) RETURNS ...
CREATE OR REPLACE FUNCTION transform3(a SETOF intable)
RETURNS SETOF outtable
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
-- Negative: CREATE FUNCTION tabfunc_bad2 (x TABLE(a int) ) RETURNS ...
CREATE OR REPLACE FUNCTION transform3(x TABLE(a int, b text))
RETURNS SETOF outtable
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
-- Create ETF using TABLE return syntax.
DROP FUNCTION IF EXISTS transform(anytable);
CREATE OR REPLACE FUNCTION transform(a anytable)
RETURNS TABLE (a text, b int)
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
SELECT * FROM transform(
TABLE( SELECT * FROM randtable ORDER BY id, value SCATTER BY id)
) order by b;
-- create ETF with output table "hai10" does not exist at the moment
CREATE OR REPLACE FUNCTION transform_new(a anytable)
RETURNS SETOF hai10
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
\df transform_new
drop function if exists transform_new(a anytable);
-- Create ETF using return syntax: TABLE LIKE table, however the "liked" table is non-existing
DROP FUNCTION IF EXISTS transform(anytable);
CREATE OR REPLACE FUNCTION transform (a anytable)
RETURNS TABLE (LIKE outtable_nonexist)
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
-- Create ETF using return syntax: TABLE LIKE table
CREATE OR REPLACE FUNCTION transform(a anytable)
RETURNS TABLE (LIKE outtable)
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
-- Verify ETF can be called successfully
select * from transform(TABLE(select * from intable where id<3));
-- Add a new column to output table, then drop the new added column.
alter table outtable add column newcol int;
alter table outtable drop column newcol;
\d outtable;
-- Calling ETF again, get ERROR: invalid output tuple
-- MPP-14231
select * from transform(TABLE(select * from intable where id<3));
-- Recreate outtable
DROP TABLE IF EXISTS outtable cascade;
CREATE TABLE outtable(a text, b int) distributed randomly;
-- Recreate transform function
CREATE OR REPLACE FUNCTION transform (a anytable)
RETURNS TABLE (a text, b int)
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
-- Create ETF using return syntax: TABLE (argname argtype).
-- CREATE FUNCTION tabfunc2(t anytable)
-- RETURNS TABLE (x int, y text) ...
CREATE OR REPLACE FUNCTION transform3(a anytable)
RETURNS TABLE (b text, a int)
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
select * from transform3(TABLE(select * from intable where id<3));
-- Currently ETF can take one only one anytable type input.
CREATE OR REPLACE FUNCTION transform3(a anytable, b anytable)
RETURNS TABLE (b text, a int)
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
-- Missing SETOF keyword in RETURNS
-- Note this is stll valid.
-- The result is one row of record is returned per each segment.
-- For a cluster of 2 segments, total 2 rows will be returned.
DROP FUNCTION IF EXISTS transform_outtable(anytable);
CREATE OR REPLACE FUNCTION transform_outtable(a anytable)
RETURNS TABLE (a text, b int)
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
select * from transform_outtable(TABLE(select * from intable)) order by b;
drop function if exists transform_outtable(anytable);
-- Missing TABLE keyword in RETURNS
CREATE OR REPLACE FUNCTION transform(a anytable)
RETURNS (a text, b int)
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
-- Negative: create ETF with distribution and/or ordering defined at function create time.
-- The followings should not be allowed.
CREATE OR REPLACE FUNCTION transform_tmp(a anytable)
scatter randomly
RETURNS SETOF outtable
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
CREATE OR REPLACE FUNCTION transform_tmp(a anytable)
RETURNS SETOF outtable
scatter randomly
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
CREATE OR REPLACE FUNCTION transform_tmp(a anytable)
order by a
RETURNS SETOF outtable
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
CREATE OR REPLACE FUNCTION transform_tmp(a anytable)
RETURNS SETOF outtable
order by a
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
CREATE OR REPLACE FUNCTION transform_tmp(a anytable)
partition by a
RETURNS SETOF outtable
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
CREATE OR REPLACE FUNCTION transform_tmp(a anytable)
RETURNS SETOF outtable
partition by a
AS '$libdir/gppc_test', 'mytransform'
LANGUAGE C;
-- TABLE value expression can only be used inside ETF call
-- Cannot be used in any other places.
-- TVE after select
SELECT TABLE(select a from t1);
-- ERROR: invalid use of TABLE value expression
-- TVE after select
SELECT TABLE(select a from t1) + 1;
-- ERROR: operator does not exist: anytable + integer
-- TVE in FROM clause
SELECT * FROM TABLE(select a from t1);
-- ERROR: syntax error at or near "TABLE"
-- ETF in IN clause
SELECT * FROM t1 WHERE a IN (
transform( TABLE(select a,e from t1 where a < 5) )
);
-- ERROR: operator does not exist: integer = outtable
-- TVE in IN clause
SELECT * FROM t1 WHERE a IN (
TABLE(select a from t1 where a < 5)
);
-- ERROR: operator does not exist: integer = anytable
-- TVE in NOT IN clause
SELECT * FROM t1 WHERE a NOT IN (
TABLE(select a from t1 where a < 5)
);
-- ERROR: operator does not exist: integer <> anytable
-- TVE in EXIST clause
SELECT * FROM t1 WHERE EXISTS (
TABLE(select a from t1 where a < 5)
);
-- ERROR: syntax error at or near "TABLE"
-- TVE in NOT EXIST clause
SELECT * FROM t1 WHERE NOT EXISTS (
TABLE(select a from t1 where a < 5)
);
-- ERROR: syntax error at or near "TABLE"
-- TVE in ANY/SOME clause
SELECT * FROM t1 WHERE a < ANY (
TABLE(select a from t1 where a < 5)
);
-- ERROR: op ANY/ALL (array) requires array on right side
-- TVE in ALL clause
SELECT * FROM t1 WHERE a > ALL (
TABLE(select a from t1 where a < 5)
);
-- ERROR: op ANY/ALL (array) requires array on right side
-- TVE in GROUP BY clause
SELECT avg(a) FROM t1 GROUP BY TABLE(select c from t1);
-- ERROR: could not identify an ordering operator for type anytable
-- TVE in HAVING clause
SELECT avg(a) FROM t1 GROUP BY c HAVING TABLE(select 51) > 50;
-- ERROR: operator does not exist: anytable > integer
-- TVE in window function
select a, c, TABLE (SELECT a from t1) over(partition by c) from t1 where a < 10;
-- ERROR: syntax error at or near "over"
-- TVE in window function
select a, c, avg(a) over(partition by TABLE (SELECT a from t1)) from t1 where a < 10;
-- ERROR: could not identify an ordering operator for type anytable
-- TVE in ORDER BY clause
SELECT a FROM t1 ORDER BY TABLE(select a from t1);
-- ERROR: could not identify an ordering operator for type anytable
-- TVE in LIMIT clause
SELECT a FROM t1 LIMIT TABLE(select 4);
-- ERROR: argument of LIMIT must be type bigint, not type anytable
-- nested TABLE() expression
SELECT a FROM TABLE(TABLE(select 4,'haha'::text));
-- ERROR: syntax error at or near "TABLE"
-- TABLE() value expressions can never be involved in an expression.
SELECT TABLE(select a from t1) + 1;
-- ERROR: operator does not exist: anytable + integer
SELECT * FROM t1 WHERE a IN (
TABLE(select a from t1 where a < 5) * 2
);
-- ERROR: operator does not exist: anytable * integer
-- Negative: test cases of using anytable as output of function in create time
-- Create output table outComp
drop table if exists outComp cascade;
create table outComp (b1 int, b2 text);
-- Create a non-enhanced table function
CREATE OR REPLACE FUNCTION tf_int2char(max integer)
RETURNS SETOF outComp AS $$
DECLARE f outComp%ROWTYPE;
BEGIN
FOR i IN 1..max
LOOP
f.b1 := CAST(i AS varchar(10));
f.b2 := 'tf_test '||CAST(i AS varchar(10));
RETURN NEXT f;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
-- This should succeed and function can be created successfully.
-- Using **anytable** as return type
CREATE OR REPLACE FUNCTION tf_int2char_bad1(max integer)
RETURNS anytable AS $$
DECLARE f outComp%ROWTYPE;
BEGIN
FOR i IN 1..max
LOOP
f.b1 := CAST(i AS varchar(10));
f.b2 := 'tf_test '||CAST(i AS varchar(10));
RETURN NEXT f;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
-- ERROR: PL/pgSQL functions cannot return type anytable
-- Using **anytable** as OUT parameter
CREATE OR REPLACE FUNCTION tf_int2char_bad2(IN max integer, OUT a anytable)
AS $$
DECLARE f outComp%ROWTYPE;
BEGIN
FOR i IN 1..max
LOOP
f.b1 := CAST(i AS varchar(10));
f.b2 := 'tf_test '||CAST(i AS varchar(10));
RETURN NEXT f;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
-- ERROR: functions cannot return "anytable" arguments
-- Using **anytable** as INOUT parameter
CREATE OR REPLACE FUNCTION tf_int2char_bad3(INOUT a anytable)
AS $$
DECLARE f outComp%ROWTYPE;
BEGIN
FOR i IN 1..max
LOOP
f.b1 := CAST(i AS varchar(10));
f.b2 := 'tf_test '||CAST(i AS varchar(10));
RETURN NEXT f;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
-- ERROR: functions cannot return "anytable" arguments
-- Negative: can't pass anytable as prepare argument
PREPARE neg_p(anytable) AS SELECT * FROM transform(
TABLE(SELECT * FROM intable ));
-- ERROR: type "anytable" is not a valid parameter for PREPARE
drop function tf_int2char(max integer);
drop table if exists outComp cascade;
-- Negative: using anytable as general data type should fail
-- NOTICE: Table doesn't have 'DISTRIBUTED BY' clause,
-- and no column type is suitable for a distribution key.
-- Creating a NULL policy entry.
CREATE TABLE tmpTable1 (a anytable);
select * from transform( TABLE(
select * from intable
order by value
scatter by id) )
order by b limit 2;
-- Verify TVE inside ETF can be casted
-- to anytable type and query still works fine
select * from transform( TABLE(
select * from intable
order by value
scatter by id)::anytable)
order by b limit 2;
-- Verify TVE inside ETF cannot be casted
-- to any type (such as anyelement) other than anytable
select * from transform( TABLE(
select * from intable
order by value
scatter by id)::anyelement)
order by b limit 2;
select * from transform( TABLE(
select * from intable
order by value
scatter by id)::anyarray)
order by b limit 2;
-- Verify array_append(anyarray, anyelement)
select array_append(array['value1','value2']::anyarray, 'value3'::anyelement);
-- Verify anyarray cannot be casted to anytable
select array_append(array['value1','value2']::anytable, 'value3');
-- Verify anyelement cannot be casted to anytable
select array_append(array['value1','value2'], 'value3'::anytable);
-- Verify pseudo types anytable and anyelement cannot be used for PREPARE
PREPARE p1(anyelement) AS SELECT $1;
-- ERROR: type "anyelement" is not a valid parameter for PREPARE
PREPARE p2(anytable) AS SELECT $1;
-- ERROR: type "anytable" is not a valid parameter for PREPARE
-- Negative: using SCATTER BY outside of sub-query of ETF call.
-- The followings should fail
SELECT * FROM transform( TABLE(select * from intable) scatter randomly );
SELECT * FROM transform( TABLE(select * from intable) ) scatter randomly;
SELECT * FROM transform( TABLE(select * from intable) scatter by a );
SELECT * FROM transform( TABLE(select * from intable) ) scatter by a, b;
SELECT * FROM transform( TABLE(select * from intable) distributed randomly );
SELECT * FROM transform( TABLE(select * from intable) ) distributed randomly;
SELECT * FROM transform( TABLE(select * from intable) distributed by (a) );
SELECT * FROM transform( TABLE(select * from intable) ) distributed by (a,b);
SELECT id,value FROM intable scatter by id;
SELECT id,value FROM intable scatter by (id);
SELECT id,value FROM intable scatter randomly;
SELECT id,value FROM intable scatter by id,value;
SELECT id,value FROM intable order by id scatter by id;
SELECT id,value FROM intable where id < 5 and scatter by id;
SELECT avg(a) FROM t1 scatter by c;
SELECT avg(a) FROM t1 group by c scatter by c;
SELECT avg(a) FROM t1 group by c having avg(a)>50 scatter by c;
SELECT a, c, avg(a) over(scatter by c scatter by c) FROM t1 where a <10;
SELECT a, c, avg(a) over(partition by c scatter by c) FROM t1 where a <10;
-- Negative: using SCATTER BY in create table DML
-- The following should fail
CREATE TABLE scatter_test1 (a int, b int)
SCATTER BY a;
CREATE TABLE scatter_test1 (a int, b int)
SCATTER BY a,b;
CREATE TABLE scatter_test1 (a int, b int)
SCATTER BY (a);
CREATE TABLE scatter_test1 (a int, b int)
SCATTER BY (a,b);
-- The input of ETF can only be TABLE value expression,
-- which only takes a subquery as an input
SELECT * FROM transform( TABLE(select * from intable) );
SELECT * FROM transform( TABLE(select distinct b,c from t1) );
-- CONSTANT input
SELECT * FROM transform( TABLE( SELECT 2 as id,'haha'::text as value FROM intable) );
SELECT * FROM transform( TABLE(select a,e from t1 scatter by a) ) order by b;
SELECT * FROM transform( TABLE(select a,e from t1 scatter by a, d) ) order by b;
SELECT * FROM transform( TABLE(select a,e from t1 scatter by a+d) ) order by b;
-- a: will be 1, 2, 3 ... 100
-- d: will be 100, 99, 98 ... 1
SELECT * FROM transform( TABLE(select a,e from t1 where a<=10 scatter randomly) ) order by b;
SELECT * FROM transform( TABLE(select a,e from t1 where a<=10 order by a) ) order by b;
SELECT * FROM transform( TABLE(select a,e from t1 where a<=10 order by a desc, b) ) order by a,b;
SELECT * FROM transform( TABLE(select a,e from t1 where a<=10 order by a + d) ) order by b;
-- a: will be 1, 2, 3 ... 100
-- d: will be 100, 99, 98 ... 1
SELECT * FROM transform( TABLE(select a,e from t1 where a<=10 order by a::text || b::text ) ) order by b desc;
SELECT * FROM transform( TABLE(select a,e from t1 where a<=10 order by a asc, b desc) )
order by a desc, b asc;
SELECT * FROM transform( TABLE(select a,e from t1 where a<=10 order by a scatter by a) ) order by b;
SELECT * FROM transform( TABLE(select a,e from t1 where a<=10 order by a scatter by e) ) order by b;
SELECT * FROM transform( TABLE(select a,e from t1 where a<=10
order by a,e scatter by a,e) ) order by b;
SELECT * FROM transform( TABLE(select b, e from t1 where a<=10
order by a scatter by a) ) order by b,a;
SELECT * FROM transform( TABLE(select a,e from t1 where a<=10
order by a scatter by b,c,d,e) ) order by b;
SELECT * FROM transform( TABLE(select a,e from t1 where a<=10
order by b,c,d,e scatter by b,c,d,e) ) order by b;
-- Test query using ETF with filters
-- MPP-14250
select a,b from transform( TABLE(
select id,value from intable
where id<8 ))
where b <3 order by b;
-- ETF call returns empty result set
-- ETF sub-query on an empty table: intable2
create table intable2 (id int, value text) distributed by (id);
SELECT * from transform( TABLE(select * from intable2) );
-- ETF sub-query input returns 0 row via filter
SELECT * from transform( TABLE(select a, e from t1 where a > 1000) );
SELECT * from transform( TABLE(select a, e from t1 where a is null) );
SELECT * from transform( TABLE(select a, e from t1 where a::text = e) );
-- Also checking outer layer
SELECT * from transform( TABLE(select a, e from t1 where a > 10) )
where a < 10::text;
drop table intable2;
-- ETF call returns duplicate rows
-- Have source table t1 contain some duplicated rows.
INSERT INTO t1 SELECT i/100*100, i/100*100, i/100*100, i/100*100, 'text'
FROM generate_series(101,110) i;
SELECT * FROM transform( TABLE (select a,e from t1 where a = 100 order by b scatter by a) ) order by b,a;
DELETE FROM t1 where e='text';
-- ETF call returns rows contain null values in column e
-- Check scatter by null value column
-- Check order by null value column
-- Have source table t1 contain some null values
INSERT INTO t1 SELECT i/200*200, i/200*200, i/200*200, i/200*200, null
FROM generate_series(200,210) i;
SELECT * FROM transform( TABLE (select a, e from t1 where a=200) ) order by b,a;
SELECT * FROM transform( TABLE (select a, e from t1 where a=200
order by a scatter by a ) ) order by b,a;
SELECT * FROM transform( TABLE (select a, e from t1 where a=200
scatter by e) ) order by b,a;
SELECT * FROM transform( TABLE (select a, e from t1 where a=200
order by e) ) order by b,a;
SELECT * FROM transform( TABLE (select a, e from t1 where a=200
order by e scatter by a ) ) order by b,a;
SELECT * FROM transform( TABLE (select a, e from t1 where a=200
order by e scatter by e ) ) order by b,a;
DELETE FROM t1 WHERE e is null;
-- ETF call returns rows contain null values in all columns
-- Have source table t1 contain some rows with all null values
INSERT INTO t1 SELECT null, null, null, null, null
FROM generate_series(200,210) i;
SELECT * FROM transform( TABLE (select a, e from t1 where a is null) );
SELECT * FROM transform( TABLE (select a, e from t1 where a is null
order by a scatter by a) );
SELECT * FROM transform( TABLE (select a, e from t1 where a is null
scatter by e) );
SELECT * FROM transform( TABLE (select a, e from t1 where a is null
order by e) );
SELECT * FROM transform( TABLE (select a, e from t1 where a is null
order by e scatter by a) );
SELECT * FROM transform( TABLE (select a, e from t1 where a is null
order by e scatter by e) );
DELETE FROM t1 where a is null;
-- ETF sub-query using view
DROP VIEW IF EXISTS t1_view;
CREATE VIEW t1_view as (
SELECT a, b, c, d ,e from t1
WHERE a <10 ORDER BY d);
SELECT * FROM transform( TABLE(select a, e from t1_view) ) ORDER BY b;
SELECT * FROM transform( TABLE(select a, e from t1_view
order by b scatter by a) ) ORDER BY b;
SELECT * FROM transform( TABLE(select a, e from t1_view
order by a) ) ORDER BY b;
SELECT * FROM transform( TABLE(select a, e from t1_view
where a < 6) ) ORDER BY b;
SELECT * FROM transform( TABLE(select a, e from t1_view) )
WHERE a < 6::text ORDER BY b;
SELECT * FROM transform( TABLE(select a, e from t1_view
where a > 10) ) ORDER BY b;
-- empty result set
SELECT * FROM transform( TABLE(select a, e from t1_view) )
WHERE b > 10 ORDER BY b;
-- empty result set
-- ETF sub-query contains correlated sub-query
select avg(a)::int,'haha'::text from t1;
SELECT * FROM transform(
TABLE( select avg(a)::int,'haha'::text from t1 ));
SELECT * FROM transform(
Table(select a,e from t1 t1 where d >
(select avg(a) from t1 t2 where t2.a=t1.a)
)) order by b;
-- This query should fail with following error message:
select a,e from t1 t1 where a >
(SELECT b FROM transform(
TABLE( select avg(a)::int,'haha'::text from t1 t2 where t2.a=t1.d)
)) ;
-- ERROR: subquery in TABLE value expression may not refer to relation of another query level
-- LINE 3: TABLE( select avg(a)::int,'haha'::text from t1 t2 where t2.a...
-- The following internal correlation sub-query works correctly:
SELECT * FROM transform(
Table(select a,e from t1 t1 where a >
(select avg(a) from t1 t2 where t2.a=t1.d)
)) order by b;
select avg(a) from t1
group by (select b from transform(TABLE(select a,e from t1 where a=10 )) );
SELECT min(b) FROM
transform( TABLE(select a,e from t1 where a <5) );
------------------------------
-- This query works correctly:
SELECT * FROM t1 WHERE a IN (
SELECT b FROM transform(
TABLE(select a,e from t1
order by a
scatter randomly))
) AND a < 10 order by d;
-- The following query should also work.
-- The only different between this one the one above is this uses "NOT IN"
SELECT * FROM t1 WHERE a NOT IN (
SELECT b FROM transform(
TABLE(select a,e from t1
order by a
scatter randomly))
) AND a < 10 order by a;
-------------------------------
-- For EXISTS and NOT EXISTS
SELECT * FROM t1 WHERE EXISTS (
SELECT 1 FROM transform(
TABLE(select a,e from t1
order by a
scatter randomly))
) AND a < 10 ORDER BY a;
SELECT * FROM t1 WHERE NOT EXISTS (
SELECT 1 FROM transform(
TABLE(select a,e from t1
order by a
scatter randomly))
) AND a < 10 ORDER BY a;
--------------------------------
-- ETF in ALL sub query expression
SELECT * FROM t1 WHERE a>ALL (
SELECT b FROM transform(
TABLE(select a,e from t1
where a>90 and a<98
order by a
scatter randomly))
) ORDER BY a;
--------------------------------
-- Calling ETF from WITH clause (CTE)
WITH first_5 AS (
SELECT b,a FROM transform( TABLE(
select a,e from t1 where a <= 5
))
)
select * from first_5 order by b;
---------------------------------
-- Test ETF as argument
-- The following query should fail with error as shown:
SELECT * FROM ARRAY_APPEND(array['value0','value1'],
(select a from transform( TABLE(select * from intable))));
-- ERROR: more than one row returned by a subquery used as an expression
-- The following query should succeed
SELECT * FROM ARRAY_APPEND(array['value0','value1'],
(select a from transform( TABLE(select * from intable where id=2))));
-- ETF sub-query contains join table / view
-- Create another table t2 so to test join tables t1 and t2.
DROP TABLE IF EXISTS t2;
CREATE TABLE t2 (a int, b int, c int, d int, e text)
DISTRIBUTED BY (a);
INSERT INTO t2 SELECT i, i/3, i%2, 100-i, 'text'||i
FROM generate_series(1,100) i;
-- ETF sub-query: join table works correctly when using format:
-- SELECT FROM t1 JOIN t2 ON t1.a=t2.a
select * from transform(
TABLE(select t1.a,t2.e from t1
join t2 on t1.a=t2.a
where t1.a <10 order by t1.a scatter by t2.c) )
order by b;
-- ETF sub-query joins table and putting join condition in where cause
select * from transform(
TABLE(select t1.a,t2.e from t1,t2 where t1.a=t2.a
and t1.a < 10 order by t1.a scatter by t2.c) )
order by b;
drop table t2;
-- ETF sub-query contains aggregation
SELECT * FROM transform( TABLE(select count(*)::int,'haha'::text from t1) ) ORDER BY b;
SELECT * FROM transform( TABLE(select avg(a)::int,'haha'::text from t1
where a < 51
group by c
order by avg(a)) ) ORDER BY b;
-- This query should fail since "scatter by a"
SELECT * FROM transform( TABLE(select avg(a)::int,'haha'::text from t1
where a < 51
group by c
order by avg(a)
scatter by a) ) ORDER BY b;
-- This query should succeed since "scatter by c"
SELECT * FROM transform( TABLE(select avg(a)::int,'haha'::text from t1
where a < 51
group by c
order by avg(a)
scatter by c) ) ORDER BY b;
SELECT * FROM transform( TABLE(select avg(a)::int,'haha'::text from t1 group by c scatter by c) ) ORDER BY b;
SELECT * FROM transform( TABLE(select avg(a)::int,'haha'::text from t1 group by c scatter by avg(a)::int) ) ORDER BY b;
SELECT * FROM transform( TABLE(select avg(a)::int,'haha'::text from t1 group by c scatter by 'haha'::text) ) ORDER BY b;
SELECT * FROM transform( TABLE(select avg(a)::int,'haha'::text from t1
where a < 51 group by c order by avg(a) scatter by c) ) ORDER BY b;
SELECT sum(b) FROM transform(
TABLE(select (rank() over (order by a))::int,'haha'::text
from t1 scatter by (rank() over (order by a))::int) );
-- ETF sub-query contains WINDOW function
select * from transform( TABLE(
select avg(a) over(partition by c)::int,e from t1
order by d
scatter by d
) )
order by a limit 5;
-- ETF call should fail for when input is not a TABLE value expression (TVE)
select * from transform( intable);
select * from transform( select id, value from intable);
select * from transform( t1_view);
select * from transform( select id, value from t1_view);
-- TABLE value expression only takes a select subquery as input.
-- Directly using a table (or view) as input to TVE should fail.
select * from transform( TABLE(intable));
select * from transform( TABLE(t1_view order by id scatter by id));
-- Negative: Some invalid usages of ETF
-- All following queries should fail
-- Using **DISTRIBUTED** keyword in sub-query
SELECT * FROM tabfunc( TABLE(select * from t1
DISTRIBUTED RANDOMLY
) );
-- ERROR: syntax error at or near "distributed"
-- Using **PARTITION** keyword in sub-query
SELECT * FROM tabfunc( TABLE(select * from t1
PARTITION BY a
) );
-- ERROR: syntax error at or near "partition"
-- Sub-query ending with semi-colon ';'
SELECT * FROM tabfunc( TABLE(select * from t1
SCATTER RANDOMLY ;
) );
-- ERROR: syntax error at or near ";"
-- source table does not exist
SELECT * FROM tabfunc( TABLE(select * from non_exist) );
-- ERROR: relation "non_exist" does not exist
-- sub-query is not a select query
SELECT * FROM tabfunc( TABLE(
update t1 set e='test_new' where a=200
) );
-- ERROR: syntax error at or near "update"
-- using multiple TABLE keyword
SELECT * FROM tabfunc( TABLE TABLE(select * from t1) );
-- ERROR: syntax error at or near "TABLE"
SELECT * FROM tabfunc( TABLE(select a from t1)
TABLE(select b from t1) );
-- ERROR: syntax error at or near "TABLE"
-- using multiple SCATTER keyword
SELECT * FROM tabfunc( TABLE(select * from t1
SCATTER BY a
SCATTER RANDOMLY) );
-- ERROR: syntax error at or near "scatter"
-- Using scatter before order by
SELECT * FROM tabfunc( TABLE(select * from t1
SCATTER BY a
ORDER BY a) );
-- ERROR: syntax error at or near "order"
SELECT * FROM tabfunc( TABLE(select * from t1
SCATTER RANDOMLY
ORDER BY a) );
-- ERROR: syntax error at or near "order"
-- using multiple ORDER keyword
SELECT * FROM tabfunc( TABLE(select * from t1
ORDER BY a
ORDER BY b) );
-- ERROR: syntax error at or near "order"
SELECT * FROM tabfunc( TABLE(select * from t1
ORDER BY a
SCATTER RANDOMLY
ORDER BY b) );
-- ERROR: syntax error at or near "order"
-- using incorrect keyword **SCATER**, **SCATTERED** instead of SCATTER
SELECT * FROM tabfunc( TABLE(select * from t1
SCATER RANDOMLY) );
-- ERROR: syntax error at or near "scater"
SELECT * FROM tabfunc( TABLE(select * from t1
SCATTERED BY a) );
-- ERROR: syntax error at or near "scattered"
-- using incorrect keyword **ORDERED** instead of ORDER
SELECT * FROM tabfunc( TABLE(select * from t1
ORDERED BY a) );
-- ERROR: syntax error at or near "by"
-- using incorrect parentheses for scatter by
SELECT * FROM tabfunc( TABLE(select * from t1
SCATTER BY (a,b)) );
-- ERROR: no equality operator for typid 2249 (cdbmutate.c:1177)
-- using incorrect parentheses for order by
SELECT * FROM tabfunc( TABLE(select * from t1
ORDER BY (a,b)) );
-- ERROR: could not identify an ordering operator for type record
-- Negative: using SCATTER outside of ETF sub-query
SELECT * FROM tabfunc( TABLE(select * from t1)
SCATTER BY a
);
-- ERROR: syntax error at or near "scatter"
SELECT * FROM tabfunc( TABLE(select * from t1) )
SCATTER BY a;
-- ERROR: syntax error at or near "scatter"
-- using ORDER BY outside of sub-query
SELECT * FROM tabfunc( TABLE(select * from t1)
ORDER BY a
);
-- ERROR: ORDER BY specified, but transform is not an ordered aggregate function
-- Negative: ETF function call missing TABLE keyword,
-- this effectively make the sub-query a value expression
SELECT * FROM tabfunc( (select count(*) from t1) );
SELECT * FROM tabfunc( (select id, value from t1) );
-- Negative: ETF function call missing or extra parentheses
SELECT * FROM tabfunc( TABLE select * from t1 );
-- ERROR: syntax error at or near "SELECT"
SELECT * FROM tabfunc TABLE (select * from t1);
-- ERROR: syntax error at or near "TABLE"
SELECT * FROM tabfunc TABLE select * from t1;
-- ERROR: syntax error at or near "TABLE"
SELECT * FROM tabfunc (TABLE) select * from t1;
-- ERROR: syntax error at or near ")"
-- cases of extra parentheses
SELECT * FROM transform( TABLE( SELECT * FROM intable) ) ORDER BY b; -- ok
SELECT * FROM transform( TABLE(( SELECT * FROM intable)) ) ORDER BY b; -- ok
SELECT * FROM transform( TABLE((( SELECT * FROM intable))) ) ORDER BY b; -- ok
SELECT * FROM transform( TABLE(( SELECT * FROM intable ORDER BY ID)) ) ORDER BY b; -- ok
SELECT * FROM transform( TABLE((( SELECT * FROM intable ORDER BY ID))) ) ORDER BY b; -- ok
SELECT * FROM transform( TABLE( SELECT * FROM intable SCATTER BY ID) ) ORDER BY b; -- ok
SELECT * FROM transform( TABLE(( SELECT * FROM intable SCATTER BY ID)) ); -- ERROR: syntax error at or near "SCATTER"
SELECT * FROM transform( TABLE(( SELECT * FROM intable ORDER BY ID SCATTER BY ID)) ); -- ERROR: syntax error at or near "SCATTER"
-- Note: SCATTER by applies to the TABLE value expression, it is not part of a normal SelectStmt.
-- Pushing the SCATTER clause into the select statement is not supported syntax.
-- The following is allowed syntax:
SELECT * FROM transform( TABLE( (SELECT * FROM intable) SCATTER BY id) ) order by b;
-- calling undefined ETF function
SELECT * FROM tabfunc_non( TABLE(select * from t1) );
SELECT * FROM transform( TABLE_non(select * from t1) );
-- Negative: ETF must be called via FROM
-- The following queries should fail
-- ETF after SELECT
select transform( TABLE(select * from intable));
-- ETF in ORDER BY
select * from t1 order by transform( TABLE(select * from intable));
-- ETF in LIMIT
select * from t1 LIMIT transform (TABLE(select * from intable));
-- ETF in GROUP BY
select avg(a) from t1 group by
(transform(TABLE(select * from t1 where a<10 )));
-- ETF in IN clause
select * from t1 where a in
(transform(TABLE(select a,e from t1 where a<10)));
-- Positive: ETF can be used for table JOIN operation
-- The followings should succeed
-- join table t1 with ETF
SELECT t1.* from t1, transform(
TABLE(select a,e from t1 where a <10 order by a scatter by a) ) t2
WHERE t1.a = t2.b order by t1.a;
-- join table t1 with ETF, a different format
SELECT t1.* from t1
JOIN transform( TABLE(select a,e from t1
where a <10 order by a scatter by a) ) t2
ON t1.a = t2.b
ORDER BY t1.a;
-- Create view using ETF function
CREATE VIEW t1viewetf AS
SELECT * FROM transform(
TABLE(select a,e from t1
where a < 10
order by a
scatter by a
)
);
-- This should succeed
-- Describe the created view
\d+ t1viewetf
-- directly using transform to create view
CREATE VIEW t1viewetf AS
transform(
TABLE(select a,e from t1
where a < 10
order by a
scatter by a
)
);
-- This should fail since ETF is not call via FROM
-- create view using ETF, where ETF itself is using another view
create view t1_etf_view as (
select * from transform(
table(select a,e from t1_view order by b scatter randomly)
) ) order by a;
-- This should succeed
\d+ t1_etf_view
-- Create temp table (CTAS) using ETF
create temp table tmp_t2 as select * from transform(
table(select a,e from t1 where a<=10) );
-- This should succeed
select * from tmp_t2 order by b;
drop view t1viewetf;
drop view t1_etf_view;
-- ETF can be used in the subquery
-- ETF in InitPlan
select array (select a from transform
( TABLE(select * from intable order by id scatter by value) ) order by a);
-- This works correctly
-- Use ETF as Qual
SELECT * FROM t1 WHERE a < (
SELECT max(b) FROM
transform( TABLE(select a,e from t1 where a <5) )
) ORDER BY a, b;
-- ETF can be called within following sub query expression:
-- IN/NOT IN, EXISTS / NOT EXISTS, ANY/SOME, ALL
-- ETF called in IN
SELECT * FROM t1 WHERE a IN (
SELECT b FROM transform(
TABLE(select a,e from t1
order by a
scatter randomly))
) AND a < 10 ORDER BY a, b;
-- ETF called in IN
SELECT * FROM t1 WHERE a NOT IN ( -- using NOT IN here
SELECT b FROM transform(
TABLE(select a,e from t1
order by a
scatter randomly))
) AND a < 10 ;
-- For EXISTS and NOT EXISTS
SELECT * FROM t1 WHERE EXISTS (
SELECT 1 FROM transform(
TABLE(select a,e from t1
order by a
scatter randomly))
) AND a < 10 ORDER BY a, b;
SELECT * FROM t1 WHERE NOT EXISTS (
SELECT 1 FROM transform(
TABLE(select a,e from t1
order by a
scatter randomly))
) AND a < 10 ;
-- For ANY/SOME
SELECT * FROM t1 WHERE a> ANY (
SELECT b FROM transform(
TABLE(select a,e from t1
where a>98
order by a
scatter randomly))
);
SELECT * FROM t1 WHERE a < SOME (
SELECT b FROM transform(
TABLE(select a,e from t1
where a<3
order by a
scatter randomly))
);
-- For ALL
SELECT * FROM t1 WHERE a > ALL (
SELECT b FROM transform(
TABLE(select a,e from t1
where a<98
order by a
scatter randomly))
) ORDER BY a;
-- ETF sub-query calling from CTE, i.e. WITH clause
WITH first_5 AS (
SELECT b,a FROM transform( TABLE(
select a,e from t1 where a <= 5
))
)
select * from first_5 order by b;
-- ETF sub-query recursive call
SELECT * FROM transform( TABLE(
select b,a from transform (TABLE(
select a,e from t1 where a < 5
) )
) ) order by a;
SELECT * FROM transform( TABLE(
select b,a from transform (TABLE(
select a,e from t1 where a < 5
order by d
scatter by c
) )
order by b
scatter by a
) )
order by b desc;
SELECT * FROM transform( TABLE(
SELECT b,a FROM transform( TABLE(
select b,a from transform (TABLE(
select a,e from t1 where a < 5
order by d
scatter randomly
) )
order by b
scatter by a
) )
order by b desc
scatter by b)) order by a;
-- ETF can be used for PREPARE statement
PREPARE pretransform (int) AS SELECT * FROM transform(
TABLE(SELECT * FROM intable WHERE ID <=$1 ORDER BY ID SCATTER BY value))
ORDER BY b;
EXECUTE pretransform(5);
EXECUTE pretransform(5);
EXECUTE pretransform(5);
EXECUTE pretransform(5);
DEALLOCATE pretransform;
-- ETF distribution is the same as underlying source table
-- Should not have redistribution motion, except for SCATTER RANDOMLY
-- Table t1 is distributed by column a
-- Table t3 is distributed by columns a and e
-- Table t4 is distributed randomly
DROP TABLE IF EXISTS t3;
CREATE TABLE t3 (a int, b int, c int, d int, e text)
DISTRIBUTED BY (a, e);
INSERT INTO t3 SELECT i, i/3, i%2, 100-i, 'text'||i
FROM generate_series(1,100) i;
DROP TABLE IF EXISTS t4;
CREATE TABLE t4 (a int, b int, c int, d int, e text)
DISTRIBUTED RANDOMLY;
INSERT INTO t4 SELECT i, i/3, i%2, 100-i, 'text'||i
FROM generate_series(1,100) i;
-- ETF sub-query distribution is different than source table
-- Create t1 distributed by a
-- Create t3 distributed by a, e
DROP TABLE IF EXISTS t3;
CREATE TABLE t3 (a int, b int, c int, d int, e text)
DISTRIBUTED BY (a, e);
-- start_ignore
create language plpython3u;
-- end_ignore
create or replace function find_operator(query text, operator_name text) returns text as
$$
rv = plpy.execute(query)
search_text = operator_name
result = ['false']
for i in range(len(rv)):
cur_line = rv[i]['QUERY PLAN']
if search_text.lower() in cur_line.lower():
result = ['true']
break
return result
$$
language plpython3u;
-- ETF sub-query contains join table / view
-- Table t1, t2 are distributed by column a
-- Table t3, t5 are distributed by columns a and e
-- Table t4, t6 are distributed randomly
DROP TABLE IF EXISTS t2;
DROP TABLE IF EXISTS t5;
DROP TABLE IF EXISTS t6;
DROP TABLE IF EXISTS t4;
DROP TABLE IF EXISTS t3;
CREATE TABLE t3 (a int, b int, c int, d int, e text)
DISTRIBUTED BY (a, e);
CREATE TABLE t2 (a int, b int, c int, d int, e text)
DISTRIBUTED BY (a);
INSERT INTO t2 SELECT i, i/3, i%2, 100-i, 'text'||i
FROM generate_series(1,100) i;
DROP TABLE IF EXISTS t4;
CREATE TABLE t4 (a int, b int, c int, d int, e text)
DISTRIBUTED RANDOMLY;
CREATE TABLE t5 (a int, b int, c int, d int, e text)
DISTRIBUTED BY (a, e);
INSERT INTO t5 SELECT i, i/3, i%2, 100-i, 'text'||i
FROM generate_series(1,100) i;
CREATE TABLE t6 (a int, b int, c int, d int, e text)
DISTRIBUTED RANDOMLY;
INSERT INTO t6 SELECT i, i/3, i%2, 100-i, 'text'||i
FROM generate_series(1,100) i;
-- Join t1 and t2 that are both distributed by single column a
-- SCATTER BY the same single distribution key a
-- of the source tables t1 and t2
-- No redistribution should be involved
SELECT find_operator('EXPLAIN SELECT * FROM transform( TABLE(select t1.a, t2.e from t1 join t2 on t1.a = t2.a scatter by t1.a) );','Redistribute Motion');
-- Join t3 and t5 that are both distributed by composite key columns a, e
-- SCATTER BY the same composite key a, e
-- of the source tables t3 and t5
-- No redistribution should be involved
SELECT find_operator('EXPLAIN SELECT * FROM transform( TABLE(select t3.a, t5.e from t3 join t5 on (t3.a = t5.a and t3.e = t5.e) scatter by t3.a, t3.e) );','Redistribute Motion');
-- Both source tables are DISTRIBUTED RANDOMLY
-- Redistribution is needed
SELECT find_operator('EXPLAIN SELECT * FROM transform( TABLE(select t4.a, t6.e from t4 join t6 on (t4.a = t6.a and t4.e = t6.e) scatter by t4.a, t6.e) );','Redistribute Motion');
-- start_ignore
drop function if exists find_operator(query text, operator_name text);
-- end_ignore
-- ETF query plan of projection
-- Note: Projection does not show up in plan
-- So needs to run queries
SELECT b FROM transform( TABLE(
select a, e from t1
scatter by a
) ) order by b limit 5;
SELECT b FROM transform( TABLE(
select a, e from t1
scatter randomly
) ) ORDER BY b desc limit 5;
-- ETF query plan for correlated sub-query
SELECT * FROM transform( TABLE(
select a,e from t1 where a >
(select avg(a) from t1)
) ) ORDER BY b;
-- Check catalog table pg_type for new type anytable
\x
select * from pg_type where typname='anytable';
-- Verify pg_proc catalog table for specific columns:
-- # prorettype
-- # proargtypes
-- # proallargtypes
-- # proargmodes
-- # proargnames
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
SELECT prorettype, proargtypes, proallargtypes
proargmodes, proargnames
FROM pg_proc
WHERE oid='project_desc'::regproc
or oid='project'::regproc
ORDER BY oid;
\x
-- After ETF is created and executed, verified regular table function can be created and executed (no regression)
DROP TYPE IF EXISTS outComp cascade;
CREATE TYPE outComp AS (b1 varchar(10), b2 varchar(10));
CREATE OR REPLACE FUNCTION tf_int2char(max integer)
RETURNS SETOF outComp AS $$
DECLARE f outComp%ROWTYPE;
BEGIN
FOR i IN 1..max
LOOP
f.b1 := CAST(i AS varchar(10));
f.b2 := 'tf_test '||CAST(i AS varchar(10));
RETURN NEXT f;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
SELECT t1.*, t2.*
FROM tf_int2char(5) t1
JOIN
tf_int2char(3) t2
ON t1.b1 = t2.b1;
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- explicit return type not suitable for dynamic type resolution
CREATE FUNCTION x() returns int
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- ERROR: DESCRIBE only supported for functions returning "record"
-- explicit return type (setof) not suitable for dynamic type resolution
CREATE FUNCTION x() returns setof int
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- ERROR: DESCRIBE only supported for functions returning "record"
-- explicit return type (TABLE) not suitable for dynamic type resolution
CREATE FUNCTION x() returns TABLE(id integer, "time" timestamp, sessionnum integer)
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- ERROR: DESCRIBE is not supported for functions that return TABLE
-- explicit return type (OUT PARAMS) not suitable for dynamic type resolution
CREATE FUNCTION x(OUT id integer, OUT "time" timestamp, OUT sessionnum integer)
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- ERROR: DESCRIBE is not supported for functions with OUT parameters
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- Using order by and scatter by inside ETF, with order by outside ETF
SELECT * FROM project(
TABLE( SELECT * FROM history order by id scatter by id), 1)
order by 1;
-- Using distinct outside ETF, scatter by multiple columns inside ETF
SELECT distinct id FROM project(
TABLE( SELECT id FROM history order by id scatter by id, time), 1)
order by 1 desc;
-- Using distinct filter inside ETF, and filter outside ETF
SELECT time FROM project(
TABLE( SELECT distinct * FROM history scatter by id), 2)
where time <'2011-08-20' order by 1;
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- Query returns empty result set because ETF returns empty result set
SELECT * FROM project( TABLE( SELECT id FROM history
where id > 4
order by id scatter by id, time), 1) order by 1 desc;
-- Query resturns empty result because of filter outside of ETF
SELECT * FROM project( TABLE( SELECT id FROM history
order by id scatter by id, time), 1) where id > 4;
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- Using constant input to ETF
SELECT * FROM project( TABLE( SELECT 'col_1','col_2','col_3' ), 2);
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- Rearrange column sequence of ETF input, order by multiple columns inside ETF
SELECT * FROM project( TABLE( SELECT time,id FROM history order by id,time scatter by id), 1) order by 1;
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- Valid operations on results
-- Using expression in scatter by and projected column
SELECT id+1 FROM project( TABLE( SELECT * FROM history where id >2 scatter by id+1), 1) order by 1;
-- Avg function
SELECT avg(id) FROM project( TABLE( SELECT * FROM history ), 1);
-- extract function, which takes timestamp type as input
SELECT extract(day from "time") FROM project( TABLE( SELECT * FROM history where time >'2011-08-21'), 2) order by 1;
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- Source table history contains null values
insert into history values (null,'2011-08-24'), (4,null), (null, null);
SELECT id FROM project( TABLE( SELECT id FROM history
where id is null
order by id scatter by id, time), 1) order by 1 desc;
delete from history where id is null or time is null;
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- ETF subquery using view
drop view if exists history_v;
create view history_v as (
select * from history order by id);
SELECT * FROM project(
TABLE( SELECT * FROM history_v order by id scatter by id), 1)
where id < 3 order by 1;
DROP FUNCTION project(anytable, integer);
DROP FUNCTION project_desc(internal);
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- ETF recursive call
SELECT * FROM project( TABLE( SELECT * FROM (
SELECT * FROM project (
TABLE (SELECT * FROM history where time is not null
order by id scatter by time), 2) as project_alias1
) as project_alias2
order by time scatter by time), 1)
order by 1;
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
drop view if exists history_v;
create view history_v as (
select * from history order by id);
-- History table self-join
SELECT * FROM project( TABLE(SELECT * FROM history h1, history h2
WHERE h1.id = h2.id and h1.time=h2.time
ORDER BY h1.id SCATTER BY h2.time), 3)
WHERE id <3 ORDER BY 1;
-- Join history table with history_v view
SELECT * FROM project( TABLE(SELECT * FROM history h1, history_v h2
WHERE h1.id = h2.id and h1.time=h2.time
ORDER BY h1.id SCATTER BY h2.time), 3)
WHERE id <3 ORDER BY 1;
-- Join history table with ETF, using join format
SELECT * from history h1 join project( TABLE(SELECT * FROM history h1, history_v h2
WHERE h1.id = h2.id and h1.time=h2.time
ORDER BY h1.id SCATTER BY h2.time), 3) h3
ON h1.id = h3.id
order by h1.time limit 5;
-- Join history table with ETF, put join condition in where clause
SELECT * from history h1, project( TABLE(SELECT * FROM history h1, history_v h2
WHERE h1.id = h2.id and h1.time=h2.time
ORDER BY h1.id SCATTER BY h2.time), 3) h3
WHERE h1.id = h3.id
order by h1.time desc limit 5;
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- Negative: Invalid column reference
SELECT time FROM project( TABLE( SELECT * FROM history ), 1);
SELECT id FROM project( TABLE( SELECT time,id FROM history ), 1);
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- Invalid projection position
SELECT * FROM project( TABLE( SELECT * FROM history ), NULL);
SELECT * FROM project( TABLE( SELECT * FROM history ), -1);
SELECT * FROM project( TABLE( SELECT * FROM history ), 0);
SELECT * FROM project( TABLE( SELECT * FROM history ), 25);
SELECT * FROM project( TABLE( SELECT time FROM history ), 2);
SELECT * FROM project( TABLE( SELECT * FROM t1 ), (ARRAY[2,3])[1]);
-- The following queries should work
SELECT * FROM project( TABLE( SELECT * FROM history ), 1+1) order by time;
SELECT * FROM project( TABLE( SELECT * FROM t1 ),
CASE 1 WHEN 2 THEN 1 ELSE GREATEST(1, COALESCE(1+1)) END)
order by b limit 10;
SELECT * FROM project( TABLE( SELECT * FROM t1 ),
CASE WHEN 3 IS NOT NULL AND 1 IN (1, 2) THEN floor(NULLIF(2, 3))::int END)
order by b limit 10;
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- DRT table function cannot be used to create view
CREATE VIEW project_v
AS SELECT *
FROM project ( TABLE (SELECT * FROM history ORDER BY id, time SCATTER BY id ), 1);
-- ERROR: CREATE VIEW statements cannot include calls to dynamically typed function
CREATE VIEW project_v
AS SELECT * FROM (SELECT *
FROM project ( TABLE (SELECT * FROM history ORDER BY id, time SCATTER BY id ), 1) as project_alias) as project_alias2;
-- ERROR: CREATE VIEW statements cannot include calls to dynamically typed function
-- An existing function that has views defined over it can not be allowed to
-- be altered to have a describe function for similar reasons outlined above.
-- Create table function project_plain
-- WITHOUT using the callback function
CREATE OR REPLACE FUNCTION project_plain (anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C;
-- Create a view using table function project_plain
-- which should be allowed
CREATE VIEW project_plain_v
AS SELECT *
FROM project_plain ( TABLE (SELECT * FROM history ORDER BY id, time SCATTER BY id ), 1)
AS project(id integer, "time" timestamp, sessionnum integer);
-- Now try to replace table func project_plain with one
-- actually is Dynamic Return Type table func
-- i.e. using callback function
CREATE OR REPLACE FUNCTION project_plain(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- ERROR: cannot add DESCRIBE callback to function used in view(s)
DROP VIEW project_plain_v;
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- Negative: $1 is not a constant
PREPARE p4 AS SELECT * FROM project( TABLE( SELECT * FROM history ), $1);
-- ERROR: unable to resolve type for function
-- LINE 1: PREPARE p4 AS SELECT * FROM project( TABLE( SELECT * FROM hi...
-- Negative: $1 is not a constant
PREPARE p5(integer) AS SELECT * FROM project( TABLE( SELECT * FROM history ), $1);
-- ERROR: unable to resolve type for function
-- LINE 1: PREPARE p5(integer) AS SELECT * FROM project( TABLE( SELECT ...
-- Positive: can prepare with a dynamic result set MPP-16643
PREPARE p6 AS SELECT * FROM project( TABLE( SELECT * FROM history ), 1) order by id;
EXECUTE p6;
EXECUTE p6;
EXECUTE p6;
EXECUTE p6;
DEALLOCATE p6;
-- Cannot drop describe (callback) function
-- while there is dyr table function (project) is using it
DROP FUNCTION project_desc(internal);
--start_ignore
-- Drop dyr table function first
DROP FUNCTION IF EXISTS project(anytable, integer);
DROP FUNCTION IF EXISTS project_desc(internal);
--end_ignore
-- create describe (callback) function
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION project_desc(internal)
RETURNS internal
AS '$libdir/gppc_test', 'project_describe'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION project(anytable, integer)
RETURNS setof record
AS '$libdir/gppc_test', 'project'
LANGUAGE C
WITH (describe = project_desc);
-- Check callback function project_desc is removed from pg_proc_callback
select * from pg_proc_callback
where profnoid='project'::regproc
and procallback='project_desc'::regproc;
-- Verify can drop describe (callback) function
-- when no other function is using it
-- Create Dynamic Return Type test table drt_test
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
select * from drt_test order by a;
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
CREATE OR REPLACE FUNCTION ud_describe(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe'
LANGUAGE C;
CREATE OR REPLACE FUNCTION ud_project1(anytable) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project'
LANGUAGE C
WITH (describe = ud_describe);
-- Simple check of DRT_UC_ETF
select * from ud_project1( table(select * from drt_test) );
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- Simple check of DRT_UC_ETF
select * from ud_project2(table(
select a from drt_test order by a scatter by (a)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position;
-- Check describe (callback) functions are registerred in pg_proc_callback
select * from pg_proc_callback
where ((profnoid='ud_project1'::regproc and procallback='ud_describe'::regproc)
or (profnoid='ud_project2'::regproc and procallback='ud_describe2'::regproc));
-- explicit return type not suitable for dynamic type resolution
CREATE FUNCTION x() returns int
AS '$libdir/gppc_test', 'userdata_project'
LANGUAGE C
WITH (describe = ud_describe);
-- ERROR: DESCRIBE only supported for functions returning "record"
-- explicit return type (setof) not suitable for dynamic type resolution
CREATE FUNCTION x() returns setof int
AS '$libdir/gppc_test', 'userdata_project'
LANGUAGE C
WITH (describe = ud_describe);
-- ERROR: DESCRIBE only supported for functions returning "record"
-- explicit return type (TABLE) not suitable for dynamic type resolution
CREATE FUNCTION x() returns TABLE(id integer, "time" timestamp, sessionnum integer)
AS '$libdir/gppc_test', 'userdata_project'
LANGUAGE C
WITH (describe = ud_describe);
-- ERROR: DESCRIBE is not supported for functions that return TABLE
-- explicit return type (OUT PARAMS) not suitable for dynamic type resolution
CREATE FUNCTION x(OUT id integer, OUT "time" timestamp, OUT sessionnum integer)
AS '$libdir/gppc_test', 'userdata_project'
LANGUAGE C
WITH (describe = ud_desc);
-- ERROR: DESCRIBE is not supported for functions with OUT parameters
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- ETF order by b scatter by (b)
select * from ud_project2(table(
select a from drt_test order by b scatter by (b)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position;
-- ETF has filter, order by b scatter by (c)
select * from ud_project2(table(
select a from drt_test where a<5 order by b scatter by (c)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position;
-- ETF has filter, order by a scatter by (a+b)
-- Also switch column positions
select message, position from ud_project2(table(
select a from drt_test where c=1 order by a desc scatter by (a+b)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position desc;
-- ETF inner filter that causes empty result set
select * from ud_project2(table(
select a from drt_test where a<0 order by b scatter by (c)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position;
-- Outter filter that causes empty result set
select * from ud_project2(table(
select a from drt_test order by c scatter by (c)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
where position >10
order by position;
-- Using constant input
select * from ud_project2(table(
select 3),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt');
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
-- insert records with null values
insert into drt_test values(null, null, 1, 2);
-- Verify null value can be handled
select message, position from ud_project2(table(
select a from drt_test where c=1 order by a desc scatter by (a+b)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position desc;
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- ETF subquery using view
create view drt_test_v as (
select * from drt_test order by a);
select * from ud_project2(table(
select a from drt_test where a<5 order by b scatter by (c)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position;
drop view if exists drt_test_v;
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- Negative: Create View Using DRT function with User Context
create view userdata_v as
select * from ud_project2(table(
select a from drt_test order by a scatter by (c)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt');
-- ERROR: CREATE VIEW statements cannot include calls to dynamically typed function
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
CREATE OR REPLACE FUNCTION ud_describe(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe'
LANGUAGE C;
CREATE OR REPLACE FUNCTION ud_project(anytable) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project'
LANGUAGE C
WITH (describe = ud_describe);
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- Positive: Using DRT function with User Context with PREPARE
-- MPP-16643
PREPARE userdata_pre as
select * from ud_project( table(select * from drt_test) );
EXECUTE userdata_pre;
EXECUTE userdata_pre;
EXECUTE userdata_pre;
EXECUTE userdata_pre;
PREPARE userdata_pre2 as
select * from ud_project2(table(
select a from drt_test order by a scatter by (c)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt') order by position;
EXECUTE userdata_pre2;
EXECUTE userdata_pre2;
EXECUTE userdata_pre2;
EXECUTE userdata_pre2;
DEALLOCATE userdata_pre;
DEALLOCATE userdata_pre2;
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- When sub query has dup rows
select * from ud_project2(table(
select b from drt_test order by a scatter by (a)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position;
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- Correlated sub query
select * from ud_project2(table(
select a from drt_test dt1 where a >
(select avg(a) from drt_test dt2 where dt1.c = dt2.c)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position;
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- Using aggregation avg()
select * from ud_project2(table(
select avg(a)::int from drt_test dt1 where a >
(select avg(a) from drt_test dt2 where dt1.c = dt2.c)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position;
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- Window Function
select * from ud_project2(table(
select avg(a) over(partition by b)::int from drt_test
order by d scatter by c),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position;
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- Called from subquery
select array(
select message from ud_project2(table(
select avg(a) over(partition by b)::int from drt_test
order by d scatter by c),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position);
select * from drt_test where a < (
select max(position) from
(select position from ud_project2(table(
select avg(a) over(partition by c)::int from drt_test
order by d scatter by c),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt') ) s
) order by a;
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
-- Delete the null record from DRT_TEST
delete from drt_test where a is null;
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- ETF called in IN/NOT IN
select * from drt_test where a in (
select position from ud_project2(table(
select avg(a) over(partition by b)::int from drt_test
order by d scatter by c),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt') )
order by a;
select * from drt_test where a not in (
select position from ud_project2(table(
select avg(a) over(partition by b)::int from drt_test
order by d scatter by c),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt') )
order by a;
-- For EXISTS and NOT EXISTS
select * from drt_test where exists (
select position from ud_project2(table(
select avg(a) over(partition by b)::int from drt_test
order by d scatter by c),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt') )
order by a;
select * from drt_test where not exists (
select position from ud_project2(table(
select avg(a) over(partition by b)::int from drt_test
order by d scatter by c),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt') )
order by a;
-- For ANY / SOME / ALL
select * from drt_test where a > any (
select position from ud_project2(table(
select avg(a) over(partition by c)::int from drt_test
order by d scatter by c),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt') )
order by a;
select * from drt_test where a < some (
select position from ud_project2(table(
select avg(a) over(partition by c)::int from drt_test
order by d scatter by c),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt') )
order by a;
select * from drt_test where a < all (
select position from ud_project2(table(
select avg(a) over(partition by c)::int from drt_test
order by d scatter by c),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt') )
order by a;
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- inside CTE
WITH first_4 AS (
select * from drt_test where a < all (
select position from ud_project2(table(
select avg(a) over(partition by c)::int from drt_test
order by d scatter by c),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt') ) )
select * from first_4 order by a;
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- Recursive call
select * from ud_project2(table(
select position from ud_project2(table(
select avg(a) over(partition by c)::int from drt_test
order by d scatter by c),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt') ) ,
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position desc;
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
-- Query table that only available on master (catalog tables)
select * from ud_project2(table(
select dbid::int from gp_segment_configuration
where dbid < 3
order by dbid scatter by dbid),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt')
order by position desc;
-- Positive: Using DRT function with User Context with PREPARE
-- MPP-16643
-- Change UDF after PREPARED. The expected behavior is the prepared statement
-- still using the old UDF definition.
-- Create Dynamic Return Type test table drt_test
DROP TABLE IF EXISTS drt_test;
CREATE TABLE drt_test (a int, b int, c int, d int)
DISTRIBUTED BY (a);
--
-- Load drt_test table with 10 records
--
INSERT INTO drt_test SELECT i, i/3, i%2, 10-i
FROM generate_series(1,10) i;
CREATE OR REPLACE FUNCTION ud_describe(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe'
LANGUAGE C;
CREATE OR REPLACE FUNCTION ud_project(anytable) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project'
LANGUAGE C
WITH (describe = ud_describe);
-- The second pair example (ud_describe2 and ud_project2) read text data
-- specified as 2nd argument to project function
-- create describe (callback) function with User Context
-- both input and output type must be internal
CREATE OR REPLACE FUNCTION ud_describe2(internal) RETURNS internal
AS '$libdir/gppc_test', 'userdata_describe2'
LANGUAGE C;
-- create dynamic return type (drt) table function
-- using the callback function with user context
-- the return type must be defined as set of record
CREATE OR REPLACE FUNCTION ud_project2(anytable, text) RETURNS setof RECORD
AS '$libdir/gppc_test', 'userdata_project2'
LANGUAGE C
WITH (describe = ud_describe2);
PREPARE userdata_pre as
select * from ud_project( table(select * from drt_test) );
EXECUTE userdata_pre;
-- The PREPARED statement should still work since it is cached
EXECUTE userdata_pre;
EXECUTE userdata_pre;
DEALLOCATE userdata_pre;
-- PREPARE using new UDF signature, should succeed
PREPARE userdata_pre as
select * from ud_project2(table(
select a from drt_test order by a scatter by (c)),
'/data/hhuang/cdbfast/main/etablefunc_gppc/data/shortMsg.txt') order by position;
EXECUTE userdata_pre;
DEALLOCATE userdata_pre;
-- Calling the error callback function (tfcallback()) from table function (project_errorcallback())
CREATE OR REPLACE FUNCTION project_errorcallback(anytable, OUT int, OUT int) RETURNS SETOF record AS '$libdir/gppc_test' LANGUAGE c;
SELECT * FROM project_errorcallback(TABLE(SELECT CASE WHEN a < 10 THEN a END, a FROM generate_series(1, 10)a SCATTER BY a));
SELECT * FROM project_errorcallback(TABLE(SELECT a, a FROM generate_series(1, 5)a SCATTER BY a)) ORDER BY 1;
-- Calling error callback function errorcallback from ETF describe function (tablefunc_describe)
CREATE OR REPLACE FUNCTION tablefunc_describe(internal) RETURNS internal AS '$libdir/gppc_test' LANGUAGE c;
CREATE OR REPLACE FUNCTION tablefunc_project(anytable, int) RETURNS SETOF record AS '$libdir/gppc_test' LANGUAGE c WITH(describe=tablefunc_describe);
SELECT * FROM tablefunc_project(TABLE(SELECT a, a / 10 FROM generate_series(1, 10)a SCATTER BY a), 2) ORDER BY 1;
-- ETF using SPI with describe function and user context
CREATE OR REPLACE FUNCTION describe_spi(internal) RETURNS internal AS '$libdir/gppc_test' LANGUAGE c;
CREATE OR REPLACE FUNCTION project_spi(anytable, text) RETURNS SETOF record AS '$libdir/gppc_test' LANGUAGE c WITH(describe=describe_spi);
SELECT * FROM project_spi(TABLE(SELECT a::text FROM generate_series(1, 10)a SCATTER BY a), 'SELECT $$foo$$') ORDER BY 1;
-- Negative: Test system behavior and error message when sub query is non-SELECT query
-- such as INSERT, INSERT ... INTO, UPDATE, DELETE,
-- or even DDL (CREATE/ALTER TABLE, CREATE/ALTER VIEW, CREATE/ALTER INDEX, VACUUM, and etc)
SELECT * FROM transform(
TABLE( INSERT INTO randtable values (11, 'value_11'))
);
SELECT * FROM transform(
TABLE( UPDATE randtable SET value='value_11' WHERE id=11)
);
SELECT * FROM transform(
TABLE( DELETE FROM randtable WHERE id=11;)
);
SELECT * FROM transform(
TABLE( select i, 'foo'||i into randtable2 from generate_series(11,15) i)
);
SELECT * FROM ud_project(
TABLE( CREATE TABLE neg_test (a int, b text)));
SELECT * FROM ud_project(
TABLE( ALTER TABLE randtable RENAME id TO id2));
SELECT * FROM ud_project(
TABLE( vacuum));