#!/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;

BEGIN {
	my $basedir = $0; $basedir =~ s#/[^/]+$##;
	unshift(@INC, "$basedir/../share");
}

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

my $basedir = $0; $basedir =~ s#/[^/]+$#/../#;
if ($basedir =~ m#^\.#) {
	my $pwd = `pwd`;
	chomp($pwd);
	$basedir = "$pwd/$basedir/";
}
while ($basedir =~ s#/[^/.]+/\.\./#/#g) {};
while ($basedir =~ s#/\./#/#g) {};
$basedir =~ s#//#/#g;

$| = 1;

my ($debug,$verbose,$quiet) = (0,0,0);
my ($help,$masterhost,$masterport,$masteruser,$masterpassword,
	$slavehost,$slaveport,$slaveuser,$slavepassword);
my $lib;

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

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

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

my $minfo = MkInfo($master,$masterhost,$masterport,$masteruser,$masterpassword);
my $sinfo = MkInfo($slave,$slavehost,$slaveport,$slaveuser,$slavepassword);

$masterhost = "localhost" if (! $masterhost);
$slavehost = "localhost" if (! $slavehost);

$RServ::debug = $debug;
$RServ::verbose = $verbose;
$RServ::quiet = $quiet;

if (!defined($lib) || !-e $lib) {
	$lib = "$basedir/lib/";

	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);

my $mconn = Connect($minfo);
my $sconn = Connect($sinfo);

Exec2($mconn,$sconn,"BEGIN");

Exec2($mconn,$sconn,"set transaction isolation level serializable");

# List of slave servers
Exec2($mconn,$sconn, "create table _RSERV_SERVERS_" .
		      " (server serial primary key, host text not ".
		      "null, port int4 default 5432, dbase text not ".
		      "null, unique(host,port,dbase))");

# insert master server in list
Exec2($mconn,$sconn,"insert into _RSERV_SERVERS_" .
		      " (server, host, port, dbase) ".
		      " values (0,'$masterhost',".
		      ($masterport || 5432).",".
		      "'$master')");

# List of replicated tables
Exec2($mconn,$sconn,"create table _RSERV_TABLES_" .
		      " (tname name not null, cname name not null, ".
		      "reloid oid primary key, key int4 not null)");

## should always call MasterDelTable
#Exec($mconn,"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)");

# Bookkeeping log for row replication	
Exec2($mconn,$sconn,"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, ".
		      "server int4, ".
		      "CONSTRAINT only_one CHECK (insert+update+delete=1))");

# This is to speedup lookup of deleted tuples
Exec2($mconn,$sconn,"create index _RSERV_LOG_INDX_DLT_ID_ on _RSERV_LOG_ ".
		      "(delete, logid) WHERE delete = 1");

# This is to speedup lookup of updated tuples
Exec2($mconn,$sconn,"create index _RSERV_LOG_INDX_UPD_ID_ on _RSERV_LOG_ ".
		      "(update, logid) WHERE update = 1");

# This is to speedup lookup of insert tuples
Exec2($mconn,$sconn,"create index _RSERV_LOG_INDX_INS_ID_ on _RSERV_LOG_ ".
		      "(insert, logid) WHERE insert = 1");

# This is to speedup cleanup
Exec2($mconn,$sconn,"create index _RSERV_LOG_INDX_TM_ID_ on _RSERV_LOG_ ".
		      "(logtime, logid)");

# This is to speedup trigger 
Exec2($mconn,$sconn,"create index _RSERV_LOG_INDX_REL_KEY_ on _RSERV_LOG_ ".
		      "(reloid, key)");

# View to help managing _rserv_log_ table
Exec2($mconn,$sconn,"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");

# View to help logging daily transactions
Exec2($mconn,$sconn,"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");

# Sync point for each slave server
Exec2($mconn,$sconn,"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)");

Exec2($mconn,$sconn,"create index _RSERV_SYNC_INDX_SRV_ID_ on _RSERV_SYNC_ ".
		      "(server, syncid)");

# Sync point reference numbers
Exec2($mconn,$sconn,"create sequence _rserv_sync_seq_");

Exec2($mconn,$sconn,"CREATE FUNCTION _rserv_log_() RETURNS opaque" .
		      " AS '$lib' LANGUAGE 'c'");

Exec2($mconn,$sconn,"CREATE FUNCTION _rserv_sync_(int4) RETURNS int4" .
		      " AS '$lib' LANGUAGE 'c'");

Exec2($mconn,$sconn,"CREATE FUNCTION _rserv_debug_(int4) RETURNS int4" .
		      " AS '$lib' LANGUAGE 'c'");

Exec2($mconn,$sconn,"CREATE FUNCTION _rserv_xid_() RETURNS int4" .
		      " AS '$lib' LANGUAGE 'c'");

# Now, lets add the needed information in the slave database

Exec2($mconn,$sconn,"create table _RSERV_SLAVE_TABLES_" .
		      " (tname name not null, cname name not null, reloid oid not null, key int4 not null)");

Exec2($mconn,$sconn,"create table _RSERV_SLAVE_SYNC_" .
		      " (syncid int4 not null, synctime timestamp)");

# next, let's tell the master that the slave is set up.

Exec2($mconn,$sconn,"INSERT INTO _RSERV_SERVERS_ (host,dbase) VALUES ('$slavehost','$slave')");

# then commit the slave transaction

Exec2($mconn,$sconn,"COMMIT");

exit (0);
