--
-- Tests the spill files disk space accounting mechanism
--
-- CBDB_PARALLEL_FIXME: it's hard to make fault_injection work with prallel processes.
set enable_parallel = false;

-- check segspace before test
reset statement_mem;
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

--- create and populate the table

DROP TABLE IF EXISTS segspace_test_hj_skew;
CREATE TABLE segspace_test_hj_skew (i1 int, i2 int, i3 int, i4 int, i5 int, i6 int, i7 int, i8 int) DISTRIBUTED BY (i1);

set gp_autostats_mode = none;

-- many values with i1 = 1
INSERT INTO segspace_test_hj_skew SELECT 1,i,i,i,i,i,i,i FROM 
	(select generate_series(1, nsegments * 50000) as i from 
	(select count(*) as nsegments from gp_segment_configuration where role='p' and content >= 0) foo) bar;
-- some nicely distributed values
INSERT INTO segspace_test_hj_skew SELECT i,i,i,i,i,i,i,i FROM 
	(select generate_series(1, nsegments * 100000) as i from 
	(select count(*) as nsegments from gp_segment_configuration where role='p' and content >= 0) foo) bar;

ANALYZE segspace_test_hj_skew;

--
--  Testing that query cancelation during spilling updates the accounting
--

------------ Interrupting SELECT query that spills -------------------

-- enable the fault injector
select gp_inject_fault('exec_hashjoin_new_batch', 'reset', 2);
select gp_inject_fault('exec_hashjoin_new_batch', 'interrupt', 2);

set statement_mem=2048;
set gp_autostats_mode = none;

begin;
SELECT t1.* FROM segspace_test_hj_skew AS t1, segspace_test_hj_skew AS t2 WHERE t1.i1=t2.i2;
rollback;

-- 
-- GPDB parallel once got errors like: 
-- could not read from shared tuplestore temporary file: read only 0 of 8 bytes from file.
-- Enable parallel here to test it.
-- 
begin;
set local enable_parallel = true;
set local optimizer=off;
set local min_parallel_table_scan_size=0;
set local min_parallel_index_scan_size = 0;
set local force_parallel_mode=1;
EXPLAIN(COSTS OFF) SELECT t1.* FROM segspace_test_hj_skew AS t1, segspace_test_hj_skew AS t2 WHERE t1.i1=t2.i2;
SELECT count(t1.*) FROM segspace_test_hj_skew AS t1, segspace_test_hj_skew AS t2 WHERE t1.i1=t2.i2;
rollback;

select gp_inject_fault('exec_hashjoin_new_batch', 'status', 2);

-- check used segspace after test
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

-- Run the test without fault injection
begin;
-- Doing select count so output file doesn't have 75000 rows.
select count(*) from 
(SELECT t1.* FROM segspace_test_hj_skew AS t1, segspace_test_hj_skew AS t2 WHERE t1.i1=t2.i2) temp;
rollback;

-- check used segspace after test
reset statement_mem;
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;


------------ Interrupting INSERT INTO query that spills -------------------

drop table if exists segspace_t1_created;
create table segspace_t1_created (i1 int, i2 int, i3 int, i4 int, i5 int, i6 int, i7 int, i8 int) DISTRIBUTED BY (i1);

set statement_mem=2048;
set gp_autostats_mode = none;

-- enable the fault injector
select gp_inject_fault('exec_hashjoin_new_batch', 'reset', 2);
select gp_inject_fault('exec_hashjoin_new_batch', 'interrupt', 2);

begin;

insert into segspace_t1_created
SELECT t1.* FROM segspace_test_hj_skew AS t1, segspace_test_hj_skew AS t2 WHERE t1.i1=t2.i2;

rollback;

select gp_inject_fault('exec_hashjoin_new_batch', 'status', 2);

-- check used segspace after test
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

-- Run the test without fault injection
begin;

insert into segspace_t1_created
SELECT t1.* FROM segspace_test_hj_skew AS t1, segspace_test_hj_skew AS t2 WHERE t1.i1=t2.i2;

rollback;

-- check used segspace after test
reset statement_mem;
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

--start_ignore
drop table if exists segspace_t1_created;
--end_ignore

------------ Interrupting CREATE TABLE AS query that spills -------------------

drop table if exists segspace_t1_created;
set statement_mem=2048;
set gp_autostats_mode = none;

-- enable the fault injector
select gp_inject_fault('exec_hashjoin_new_batch', 'reset', 2);
select gp_inject_fault('exec_hashjoin_new_batch', 'interrupt', 2);

begin;

create table segspace_t1_created AS
SELECT t1.* FROM segspace_test_hj_skew AS t1, segspace_test_hj_skew AS t2 WHERE t1.i1=t2.i2;

rollback;

select gp_inject_fault('exec_hashjoin_new_batch', 'status', 2);

-- check used segspace after test
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

-- Run the test without fault injection
begin;

create table segspace_t1_created AS
SELECT t1.* FROM segspace_test_hj_skew AS t1, segspace_test_hj_skew AS t2 WHERE t1.i1=t2.i2;

rollback;

-- check used segspace after test
reset statement_mem;
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;


------------ workfile_limit_per_segment leak check during ERROR on UPDATE with CTE and MK_SORT -------------------

drop table if exists testsisc;
drop table if exists foo;

create table testsisc (i1 int, i2 int, i3 int, i4 int);
insert into testsisc select i, i % 1000, i % 100000, i % 75 from
	(select generate_series(1, nsegments * 100000) as i from
	(select count(*) as nsegments from gp_segment_configuration where role='p' and content >= 0) foo) bar;

create table foo (i int, j int);

set statement_mem=1024; -- 1mb for 3 segment to get leak.
set gp_resqueue_print_operator_memory_limits=on;
set gp_cte_sharing=on;

-- enable the fault injector
select gp_inject_fault('workfile_write_failure', 'reset', 2);
select gp_inject_fault('workfile_write_failure', 'error', 2);

-- LEAK in UPDATE: update with sisc xslice sort
update foo set j=m.cc1 from (
  with ctesisc as
    (select * from testsisc order by i2)
  select t1.i1 as cc1, t1.i2 as cc2
  from ctesisc as t1, ctesisc as t2
  where t1.i1 = t2.i2 ) as m;

select gp_inject_fault('exec_hashjoin_new_batch', 'status', 2);

select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

-- Run the test without fault injection
-- LEAK in UPDATE: update with sisc xslice sort
update foo set j=m.cc1 from (
  with ctesisc as
    (select * from testsisc order by i2)
  select t1.i1 as cc1, t1.i2 as cc2
  from ctesisc as t1, ctesisc as t2
  where t1.i1 = t2.i2 ) as m;
  
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

reset statement_mem;
reset gp_resqueue_print_operator_memory_limits;
reset gp_cte_sharing;

------------ workfile_limit_per_segment leak check during ERROR on DELETE with APPEND-ONLY table -------------------

drop table if exists testsisc;
drop table if exists foo;

create table testsisc (i1 int, i2 int, i3 int, i4 int) WITH (appendonly=true, compresstype=zlib) ;
insert into testsisc select i, i % 1000, i % 100000, i % 75 from
    (select generate_series(1, nsegments * 100000) as i from
	(select count(*) as nsegments from gp_segment_configuration where role='p' and content >= 0) foo) bar;

create table foo (i int, j int) WITH (appendonly=true, compresstype=zlib);
insert into foo select i, i % 1000 from
    (select generate_series(1, nsegments * 100000) as i from
	(select count(*) as nsegments from gp_segment_configuration where role='p' and content >= 0) foo) bar;


set statement_mem=1024; -- 1mb for 3 segment to get leak.

-- enable the fault injector
select gp_inject_fault('workfile_write_failure', 'reset', 2);
select gp_inject_fault('workfile_write_failure', 'error', 2);

-- LEAK in DELETE with APPEND ONLY tables
delete from testsisc using (
  select *
  from foo
) src  where testsisc.i1 = src.i;

select gp_inject_fault('exec_hashjoin_new_batch', 'status', 2);

select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

-- Run the test without fault injection
-- LEAK in DELETE with APPEND ONLY tables
delete from testsisc using (
  select *
  from foo
) src  where testsisc.i1 = src.i;

reset statement_mem;
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

------------ workfile_limit_per_segment leak check during UPDATE of SORT -------------------

drop table if exists testsort;
create table testsort (i1 int, i2 int, i3 int, i4 int);
insert into testsort select i, i % 1000, i % 100000, i % 75 from generate_series(0,1000000) i;
analyze testsort;

set statement_mem="1MB";

drop table if exists foo;
create table foo (c int, d int);
insert into foo values (1, 1);

-- enable the fault injector
select gp_inject_fault('workfile_write_failure', 'reset', 2);
select gp_inject_fault('workfile_write_failure', 'error', 2);

-- expect to see leak if we hit error
update foo set d = i1 from (select i1,i2 from testsort order by i2) x;

-- check counter leak
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

select gp_inject_fault('exec_hashjoin_new_batch', 'status', 2);

-- Run the test without fault injection
-- expect to see leak if we hit error
-- start_ignore
update foo set d = i1 from (select i1,i2 from testsort order by i2) x;
-- end_ignore

-- check counter leak
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

drop table testsort;

------------ workfile_limit_per_segment leak check during UPDATE of SHARE_SORT_XSLICE -------------------

drop table if exists testsisc;
create table testsisc (i1 int, i2 int, i3 int, i4 int);
insert into testsisc select i, i % 1000, i % 100000, i % 75 from generate_series(0,1000000) i;

set statement_mem="2MB";
set gp_cte_sharing=on;

drop table if exists foo;
create table foo (c int, d int);
-- expect to see leak if we hit error

-- enable the fault injector
select gp_inject_fault('workfile_write_failure', 'reset', 2);
select gp_inject_fault('workfile_write_failure', 'error', 2);

update foo set d = i1 from (with ctesisc as (select * from testsisc order by i2)
select * from
(select count(*) from ctesisc) x(a), ctesisc
where x.a = ctesisc.i1) y;

select gp_inject_fault('exec_hashjoin_new_batch', 'status', 2);

-- check counter leak
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

-- Run the test without fault injection
update foo set d = i1 from (with ctesisc as (select * from testsisc order by i2)
select * from
(select count(*) from ctesisc) x(a), ctesisc
where x.a = ctesisc.i1) y;

-- check counter leak
select max(bytes) as max, min(bytes) as min from gp_toolkit.gp_workfile_mgr_used_diskspace;

-- Check if we delete workfile_set at each subtransaction.
-- This test doesn't depend on the guc `gp_workfile_limit_per_segment` like rest
-- start_ignore
drop external table if exists exttest;
create external web table exttest (x int)
execute E'echo 1; echo 2; echo 3; echo bogus; echo 5'
on master
format 'text';
-- end_ignore

create or replace function workset_cleanup_test()
returns boolean as
$func$
declare                                          
  sub_work_set_cnt  integer;
  top_work_set_cnt 	integer;
begin
  select count(*) into top_work_set_cnt from gp_toolkit.gp_workfile_entries;
   for i in 1..2 loop
     begin
       select * from exttest a, exttest b;
     exception when others then
       raise notice 'caught exception: %', sqlerrm;
     end;
     select count(*) into sub_work_set_cnt from gp_toolkit.gp_workfile_entries;
   end loop;
  return top_work_set_cnt = sub_work_set_cnt;
end;
$func$ language plpgsql;

select workset_cleanup_test();

drop table segspace_test_hj_skew;

reset enable_parallel;

-- don't disturb other processes.
select cleanupAllGangs();
