--
-- Test Set-returning functions
--

-- Test a function returning nothing
create function retnothing() returns setof record
language plphp as $$ ; $$;
select * from retnothing() as a(a int);

create type nothing as (a int[]);
create function retnothing2() returns setof nothing
language plphp as $$
pg_raise('notice', 'hello world');
return;
pg_raise('error', 'fatal error');
$$;
select * from retnothing2();

-- Return an anonymous record, resolving the type at query time
create function retset() returns setof record
language plphp as $$
	return_next(array(1, 'hello'));
	return_next(array(2, 'world'));
	return_next(array(3, 'plphp rocks!'));
$$;
select * from retset() as f(a int, b text);

-- Try a predeclared type
create type type_foo as (a int, b text);
create function retset2() returns setof type_foo
language plphp as $$
	return_next(array(1, 'hello'));
	return_next(array(2, 'world'));
	return_next(array(3, 'plphp rocks!'));
$$;
select * from retset2();

-- Return a scalar.  Note no array() construct
create function retset3(int, int) returns setof int
language plphp as $$
	for ($i = 1; $i <= $args[0]; $i++) {
		return_next($args[1] * $i);
	}
$$;
select * from retset3(10, 3);

-- Same as above but with array()
create function retset4(int, int) returns setof int
language plphp as $$
	for ($i = 1; $i <= $args[0]; $i++) {
		return_next(array($args[1] * $i));
	}
$$;
select * from retset4(10, 3);


-- Do we support OUT params?
create function retset5(out int, out int, int, int)
language plphp as $$
	for ($i = 1; $i <= $args[2]; $i++) {
		return_next($args[3] * $i);
	}
$$;
select * from retset5(5, 2);

-- Try to return setof array
create or replace function retset6(int, int) returns setof int[]
language plphp as $$
	for ($i = 1; $i <= $args[0]; $i++) {
		return_next(array(array($args[1] * $i, $i)));
	}
$$;
select * from retset6(10, 2);

-- What happens if we use the "avoid outer array hack" here?
create or replace function retset7(int, int) returns setof int[]
language plphp as $$
	for ($i = 1; $i <= $args[0]; $i++) {
		return_next(array($args[1] * $i, $i));
	}
$$;
select * from retset7(10, 2);

create type dual_int_arr as (label text, a int[], b int[]);
create or replace function retset8(int, int) returns setof dual_int_arr
language plphp as $$
	for ($i = 1; $i <= $args[0]; $i++) {
		return_next(
		array('one', array($args[1] * $i, $i),
			array(
				array($args[1] * $i * 2, $i * 2, $i * $i),
				array($args[1] * $i * 3, $i * 3, $i * 4)
			), 'foo'
		));
	}
$$;
select * from retset8(10, 2);

create table large_table (a int);
insert into large_table select * from generate_series(1, 10000);
create or replace function duplicate(text, text) returns setof int
language plphp as $$
  $r = spi_exec("select $args[1] as a from $args[0]");
  while ($row = spi_fetch_row($r)) {
  	return_next($row['a'] * 2);
  }
$$;

create table duplicated as select * from duplicate('large_table', 'a');
select min(duplicate) from duplicated;
select count(*) from duplicated;

-- Returning the whole result set as one array (one element per row),
-- like PL/Perl's "return a reference to an array" form
create function retwhole() returns setof int language plphp as $$
	return array(10, 20, 30);
$$;
select * from retwhole();

create type whole_row as (id int, name text);
create function retwhole_rows(int) returns setof whole_row language plphp as $$
	$rows = array();
	for ($i = 1; $i <= $args[0]; $i++)
		$rows[] = array('id' => $i, 'name' => "row $i");
	return $rows;
$$;
select * from retwhole_rows(3);

-- an empty array means an empty result set
create function retwhole_empty() returns setof int language plphp as $$
	return array();
$$;
select count(*) from retwhole_empty();

-- return_next and a returned array can be combined; the array rows are
-- appended after the return_next ones
create function retwhole_mixed() returns setof int language plphp as $$
	return_next(1);
	return_next(2);
	return array(3, 4);
$$;
select * from retwhole_mixed();
