## 16. 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 {
    		table add key (node id = 1, fully qualified name =
                                   'public.history');
    	}
    	on error {
    		exit 1;
    	}

    	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',
    			key = serial, 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 CLUSTER NAME 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 INCLUDE and place the preamble in a file that is `include`d.

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

    origin=1
    subscriber=2
    mainset=1
    slonik <<_EOF_
    $PREAMBLE
    try {
        table add key (node id = $origin, fully qualified name =
                       'public.history');
    } on error {
        exit 1;
    }
    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',
    		key = serial, 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 {
        table add key (node id = $origin, fully qualified name =
                       'public.history');
    } on error {
        exit 1;
    }
    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.