# generate psql input for benchmarking join-or-to-union-5.patch

use Data::Dumper;
use Getopt::Long;
use strict;
use warnings;

# Each dimension adds a join and adds an arm to each OR-clause.
my $dimensions = 2;
# WHERE clause consists of an AND of this many OR clauses.
my $or_clauses = 1;
# Self-join the fact table this many times.  The point is to make the join
# problem gratuitously more expensive in a way that persists into each branch
# of the UNION this optimization synthesizes.
my $self_joins = 0;
# Each dimension table has this many rows.
my $values_per_dimension = 100;
# Change settings to force an exhaustive search of join orders.
my $exhaustive;
# Comma-separated list of test names.
my $tests_arg;
GetOptions('dimensions=o' => \$dimensions,
		   'or-clauses=o' => \$or_clauses,
		   'self-joins=o' => \$self_joins,
		   'values-per-dimension=o' => \$values_per_dimension,
		   'exhaustive!' => \$exhaustive,
		   'tests=s' => \$tests_arg);
my @valid_tests = qw(const except rowmark var);
my %tests = map { $_ => 1 } (
	defined $tests_arg ? (split /,/, $tests_arg) : @valid_tests
);

my @dim_tables = map { sprintf "d%02d", $_ } 1 .. $dimensions;

print "BEGIN;\n";

$exhaustive && print <<EOSQL;
SET geqo = off;
SET join_collapse_limit = 100;
SET from_collapse_limit = 100;
EOSQL

for (1 .. $dimensions)
{
	my $tbl = $dim_tables[$_ - 1];
	print <<EOSQL;
CREATE TEMP TABLE $tbl (k, v) AS
	SELECT $_ * 1000000 + n, n FROM generate_series(1, $values_per_dimension) t(n);
ANALYZE $tbl;
EOSQL
}
printf("CREATE TEMP TABLE fact (%s) AS SELECT %s FROM %s LIMIT 50000; ANALYZE fact;\n",
	   join(', ', map { "${_}k" } @dim_tables),
	   join(', ', map { "${_}.k" } @dim_tables),
	   join(', ', @dim_tables));

my $explain = "\nEXPLAIN (ANALYZE)";
my $from_where = sprintf("
FROM fact%s, %s
WHERE %s",
(
 join('', (map { sprintf " NATURAL JOIN fact f%02d", $_ } 1 .. $self_joins)),
 join(', ', @dim_tables),
 join(' AND ',
	  (map { "fact.${_}k = $_.k" } @dim_tables),
	  (map { sprintf "%d in (%s)", $_, join(', ', map { "$_.v" } @dim_tables) } 1 .. $or_clauses))
));

print "\\timing on\n";

# --dimensions=11 crashes:
# TRAP: FailedAssertion("!(uniq_exprs != ((List *) ((void *)0)))", File: "planunionor.c", Line: 334)
# --dimensions=11 --exhaustive gets the optimization
$tests{const} &&
  print "$explain SELECT 1 $from_where;\n";

# --dimensions=11 crashes the aforementioned way
# --dimensions=11 --exhaustive still gets the optimization, by subquery pushdown
$tests{except} &&
  print "$explain SELECT 1 $from_where EXCEPT SELECT 0;\n";

# rowMarks suppresses the optimization
$tests{rowmark} &&
  print "$explain SELECT 1 $from_where FOR KEY SHARE OF fact;\n";

# --dimensions=11 crashes the aforementioned way
# --dimensions=11 --exhaustive --values-per-dimension=0 crashes differently:
# TRAP: FailedAssertion("!(list_length(dest_tlist) == list_length(src_tlist))", File: "tlist.c", Line: 344)
$tests{var} &&
  print "$explain SELECT d01.v $from_where;\n";
