Embedding Slonik in Shell Scripts

19. Embedding Slonik in Shell Scripts

As mentioned earlier, there are numerous Slony-I test scripts in src/ducttape that embed the generation of Slonik inside the shell script.

They mostly don't do this in a terribly sophisticated way. Typically, they use the following sort of structure:

DB1=slony_test1
DB2=slony_test2
slonik <<_EOF_
	cluster name = T1;
	node 1 admin conninfo = 'dbname=$DB1';
	node 2 admin conninfo = 'dbname=$DB2';

	try {
		create set (id = 1, origin = 1, comment = 
                            'Set 1 - pgbench tables');
		set add table (set id = 1, origin = 1,
			id = 1, fully qualified name = 'public.accounts',
			comment = 'Table accounts');
		set add table (set id = 1, origin = 1,
			id = 2, fully qualified name = 'public.branches',
			comment = 'Table branches');
		set add table (set id = 1, origin = 1,
			id = 3, fully qualified name = 'public.tellers',
			comment = 'Table tellers');
		set add table (set id = 1, origin = 1,
			id = 4, fully qualified name = 'public.history',
			comment = 'Table accounts');
	}
	on error {
		exit 1;
	}
_EOF_

A more sophisticated approach might involve defining some common components, notably the "preamble" that consists of the SLONIK CLUSTER NAME SLONIK ADMIN CONNINFO commands that are common to every Slonik script, thus:

CLUSTER=T1
DB1=slony_test1
DB2=slony_test2
PREAMBLE="cluster name = $CLUSTER
node 1 admin conninfo = 'dbname=$DB1';
node 2 admin conninfo = 'dbname=$DB2';
"

The PREAMBLE value could then be reused over and over again if the shell script invokes slonik multiple times. You might also consider using SLONIK INCLUDE and place the preamble in a file that is included.

Shell variables provide a simple way to assign names to sets and nodes:

origin=1
subscriber=2
mainset=1
slonik <<_EOF_
$PREAMBLE
try {
	create set (id = $mainset, origin = $origin, 
                    comment = 'Set $mainset - pgbench tables');
	set add table (set id = $mainset, origin = $origin,
		id = 1, fully qualified name = 'public.accounts',
		comment = 'Table accounts');
	set add table (set id = $mainset, origin = $origin,
		id = 2, fully qualified name = 'public.branches',
		comment = 'Table branches');
	set add table (set id = $mainset, origin = $origin,
		id = 3, fully qualified name = 'public.tellers',
		comment = 'Table tellers');
	set add table (set id = $mainset, origin = $origin,
		id = 4, fully qualified name = 'public.history',
		comment = 'Table accounts');
} on error {
	exit 1;
}
_EOF_

The script might be further enhanced to loop through the list of tables as follows:

# Basic configuration
origin=1
subscriber=2
mainset=1
# List of tables to replicate
TABLES="accounts branches tellers history"
ADDTABLES=""
tnum=1
for table in `echo $TABLES`; do
  ADDTABLES="$ADDTABLES
   set add table ($set id = $mainset, origin = $origin,
   id = $tnum, fully qualified name = 'public.$table',
   comment = 'Table $tname');
"
  let "tnum=tnum+1"
done
slonik <<_EOF_
$PREAMBLE
try {
	create set (id = $mainset, origin = $origin, 
                    comment = 'Set $mainset - pgbench tables');
$ADDTABLES
} on error {
	exit 1;
}
_EOF_

That is of somewhat dubious value if you only have 4 tables, but eliminating errors resulting from enumerating large lists of configuration by hand will make this pretty valuable for the larger examples you'll find in "real life."

You can do even more sophisticated things than this if your scripting language supports things like:

  • "Record" data structures that allow assigning things in parallel

  • Functions, procedures, or subroutines, allowing you to implement useful functionality once, and then refer to it multiple times within a script

  • Some sort of "module import" system so that common functionality can be shared across many scripts

If you can depend on having Bash, zsh, or Korn shell available, well, those are all shells with extensions supporting reasonably sophisticated data structures and module systems. On Linux, Bash is fairly ubiquitous; on commercial UNIX™, Korn shell is fairly ubiquitous; on BSD, "sophisticated" shells are an option rather than a default.

At that point, it makes sense to start looking at other scripting languages, of which Perl is the most ubiquitous, being widely available on Linux, UNIX™, and BSD.