#!/usr/bin/perl -w
# MasterInit
# Vadim Mikheev, (c) 2000, PostgreSQL Inc.

eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
    & eval 'exec perl -S $0 $argv:q'
    if 0;

use strict;
use Pg;
use Getopt::Long;

$| = 1;

my ($debug,$verbose) = (0,0);
my ($help,$masterhost,$masterport,$masteruser,$masterpassword);
my $lib;

my $result = GetOptions(
	"debug!" => \$debug, "verbose!" => \$verbose, "help" => \$help,
	"masterhost=s" => \$masterhost, "masterport=i" => \$masterport,
	"masteruser=s" => \$masteruser, "masterpassword=s" => \$masterpassword,
	"lib=s" => \$lib);

if (defined($help) || (scalar(@ARGV) < 1)) {
    print "Usage: $0 [options] masterdb
Options:
	--masterhost=hostname --masterport=port
	--masteruser=username --masterpassword=string
	--lib=libpath
";
    exit ((scalar(@ARGV) < 1)? 1:0);
}

my $master = $ARGV[0] || "master";

my $minfo = "dbname=$master";
$minfo = "$minfo host=$masterhost" if (defined($masterhost));
$minfo = "$minfo port=$masterport" if (defined($masterport));
$minfo = "$minfo user=$masteruser" if (defined($masteruser));
$minfo = "$minfo password=$masterpassword" if (defined($masterpassword));

if (!defined($lib) || !-e $lib) {
	# find my compiled rserv.so module
	$lib = $0; $lib =~ s#/[^/]+$#/../lib#;
	if ($lib =~ m#^\.#) {
		my $pwd = `pwd`;
		chomp($pwd);
		$lib = "$pwd/$lib/";
	}
	while ($lib =~ s#/[^/]+/\.\./#/#g) {};
	while ($lib =~ s#/\./#/#g) {};
	$lib =~ s#//#/#g;

	if (-e "$lib/rserv.so") {
		$lib .= "rserv.so";
	} else {
		print STDERR "Can't find compiled rserv.so in $lib. Go there and type make.\n";
		exit 1;
	}
}
print "Using lib '$lib'\n" if ($verbose);

sub RollbackAndQuit {
    my $conn = shift @_;

    print STDERR "Error in query: ", $conn->errorMessage;
    $conn->exec("ROLLBACK");
    exit (-1);
}

my $conn = Pg::connectdb($minfo);
if ($conn->status != PGRES_CONNECTION_OK) {
    print STDERR "Failed opening $minfo\n";
    exit 1;
}

$result = $conn->exec("BEGIN");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

$result = $conn->exec("set transaction isolation level serializable");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

# List of slave servers
$result = $conn->exec("create table _RSERV_SERVERS_" .
		      " (server serial primary key, host text not ".
		      "null, port int4 default 5432, dbase text not ".
		      "null, unique(host,port,dbase))");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

# List of replicated tables
$result = $conn->exec("create table _RSERV_TABLES_" .
		      " (tname name not null, cname name not null, ".
		      "reloid oid primary key, key int4 not null)");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

## should always call MasterDelTable
#$result = $conn->exec("CREATE RULE _rserv_deltrig_ AS ON delete to _RSERV_TABLES_ DO (DELETE FROM pg_trigger WHERE tgname='_rserv_trigger_t_' AND tgrelid=(SELECT oid FROM pg_class WHERE relname=old.tname);UPDATE pg_class SET reltriggers=reltriggers-1 WHERE relname=old.tname)");
#RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

# Bookkeeping log for row replication	
$result = $conn->exec("create table _RSERV_LOG_" .
		      " (reloid oid REFERENCES _RSERV_TABLES_(reloid) ON ".
		      "DELETE CASCADE ON UPDATE CASCADE, logid int4 not ".
		      "null, logtime timestamp not null, insert smallint, ".
		      "update smallint, delete smallint, key text, ".
		      "CONSTRAINT only_one CHECK (insert+update+delete=1))");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

# This is to speedup lookup of deleted tuples
$result = $conn->exec("create index _RSERV_LOG_INDX_DLT_ID_ on _RSERV_LOG_ ".
		      "(delete, logid) WHERE delete = 1");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

# This is to speedup lookup of updated tuples
$result = $conn->exec("create index _RSERV_LOG_INDX_UPD_ID_ on _RSERV_LOG_ ".
		      "(update, logid) WHERE update = 1");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

# This is to speedup lookup of insert tuples
$result = $conn->exec("create index _RSERV_LOG_INDX_INS_ID_ on _RSERV_LOG_ ".
		      "(insert, logid) WHERE insert = 1");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

# This is to speedup cleanup
$result = $conn->exec("create index _RSERV_LOG_INDX_TM_ID_ on _RSERV_LOG_ ".
		      "(logtime, logid)");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

# This is to speedup trigger 
$result = $conn->exec("create index _RSERV_LOG_INDX_REL_KEY_ on _RSERV_LOG_ ".
		      "(reloid, key)");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

# View to help managing _rserv_log_ table
$result = $conn->exec("CREATE VIEW _RSERV_HUMAN_LOG_ AS SELECT log.logid, ".
		      "tab.tname AS table_name, tab.cname AS column_name, ".
		      "log.key AS column_value, log.insert, log.update, ".
		      "log.delete, log.logtime FROM _RSERV_LOG_ log, ".
		      "_RSERV_TABLES_ tab WHERE tab.reloid ".
		      "= log.reloid ORDER BY log.logtime");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

# View to help logging daily transactions
$result = $conn->exec("CREATE VIEW _RSERV_DAILY_LOG_ AS ".
		      "SELECT count(*) AS \"# records\", ".
		      "to_char(_rserv_log_.logtime, 'YYYY-MM-DD') ".
		      "AS day FROM _rserv_log_ GROUP BY day");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

# Sync point for each slave server
$result = $conn->exec("create table _RSERV_SYNC_ " .
		      "(server int REFERENCES _RSERV_SERVERS_(server) ".
		      "ON DELETE CASCADE ON UPDATE CASCADE, syncid int4 ".
		      "not null, synctime timestamp, status int4 not null,".
		      " minid int4 not null, maxid int4 not null, active text)");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

$result = $conn->exec("create index _RSERV_SYNC_INDX_SRV_ID_ on _RSERV_SYNC_ ".
		      "(server, syncid)");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

# Sync point reference numbers
$result = $conn->exec("create sequence _rserv_sync_seq_");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

$result = $conn->exec("CREATE FUNCTION _rserv_log_() RETURNS opaque" .
		      " AS '$lib' LANGUAGE 'c'");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

$result = $conn->exec("CREATE FUNCTION _rserv_sync_(int4) RETURNS int4" .
		      " AS '$lib' LANGUAGE 'c'");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

$result = $conn->exec("CREATE FUNCTION _rserv_debug_(int4) RETURNS int4" .
		      " AS '$lib' LANGUAGE 'c'");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

$result = $conn->exec("COMMIT");
RollbackAndQuit($conn) if ($result->resultStatus ne PGRES_COMMAND_OK);

exit (0);
