Index: main/trunk/greenstone2/perllib/gssql.pm
===================================================================
--- main/trunk/greenstone2/perllib/gssql.pm	(revision 32576)
+++ main/trunk/greenstone2/perllib/gssql.pm	(revision 32578)
@@ -33,4 +33,8 @@
 use DBI; # the central package for this module used by GreenstoneSQL Plugout and Plugin
 
+# singleton connection
+my $_dbh_instance = undef; # calls undef() function. See https://perlmaven.com/undef-and-defined-in-perl
+my $ref_count = 0;
+
 # Need params_map keys:
 # - collection_name
@@ -49,4 +53,10 @@
 # For port, see https://stackoverflow.com/questions/2248665/perl-script-to-connect-to-mysql-server-port-3307
 
+# TODO: remove unnecessary warn() since PrintError is active
+# https://perldoc.perl.org/perlobj.html#Destructors
+
+# TODO: drop table if exists and create table if exists are available in MySQL. Use those cmds
+# instead of always first checking for existence ourselves?
+
 sub new
 {  
@@ -80,4 +90,5 @@
     return $self;
 }
+
 
 
@@ -109,10 +120,40 @@
 # TODO: Consider AutoCommit status (and Autocommit off allowing commit or rollback for GS coll build cancel) later
 
+
+
+# SINGLETON / GET INSTANCE PATTERN
+# https://stackoverflow.com/questions/16655603/perl-objects-class-variable-initialization
+# https://stackoverflow.com/questions/7587157/how-can-i-set-a-static-variable-that-can-be-accessed-by-all-subclasses-of-the-sa
+# Singleton without Moose: https://www.perl.com/article/52/2013/12/11/Implementing-the-singleton-pattern-in-Perl/
+
+sub connect_to_db
+{
+    my $self= shift (@_);
+    my ($params_map) = @_;
+    
+    $params_map->{'db_encoding'} = $self->{'db_encoding'};
+    $params_map->{'verbosity'} = $self->{'verbosity'};
+    
+    $self->{'db_handle'} = &_get_connection_instance($params_map); # getting singleton (class method)
+    if($self->{'db_handle'}) {
+	$ref_count++; # if successful, keep track of the number of refs to the single db connection
+	return $self->{'db_handle'};
+    }
+    return undef;
+}
+
+# SINGLETON METHOD #
 # TODO: where should the defaults for these params be, here or in GS-SQLPlugin/Plugout?
-sub connect_to_db {
-    my $self= shift (@_);
+sub _get_connection_instance
+{
+    #my $self= shift (@_); # singleton method doesn't use self, but callers don't need to know that
     my ($params_map) = @_;
+
+    return $_dbh_instance if($_dbh_instance);
+
+    # or make the connection
+    
     # For proper utf8 support in MySQL, encoding should be 'utf8mb4' as 'utf8' is insufficient
-    my $db_enc = "utf8mb4" if $self->{'db_encoding'} eq "utf8";
+    my $db_enc = "utf8mb4" if $params_map->{'db_encoding'} eq "utf8";
 
     # these are the params for connecting to MySQL
@@ -130,5 +171,5 @@
     my $connect_str = "dbi:$db_driver:host=$db_host"; # don't provide db - allows checking the db exists later when loading the db
 
-    if($self->{'verbosity'}) {
+    if($params_map->{'verbosity'}) {
 	print STDERR "Away to make connection to $db_driver database with:\n";
 	print STDERR " - hostname $db_host; username: $db_user";
@@ -183,7 +224,40 @@
     
     # if we're here, then connection succeeded, store handle
-    $self->{'db_handle'} = $dbh;
-    return 1;
-}
+    $_dbh_instance = $dbh;
+    return $_dbh_instance;
+    
+}
+
+# Will disconnect if this instance of gssql holds the last reference to the db connection
+sub finished() {
+    my $self= shift (@_);
+
+    $ref_count--;
+    if($ref_count == 0) {
+	$self->force_disconnect_from_db();
+    }    
+}
+
+# Call this method on die(), so that you're sure the perl process has disconnected from SQL db
+# Disconnect from db - https://metacpan.org/pod/DBI#disconnect
+# TODO: make sure to have committed or rolled back before disconnect
+# and that you've call finish() on statement handles if any fetch remnants remain
+sub force_disconnect_from_db {
+    my $self= shift (@_);
+
+    if($_dbh_instance) {
+	# make sure any active stmt handles are finished
+	# NO: "When all the data has been fetched from a SELECT statement, the driver will automatically call finish for you. So you should not call it explicitly except when you know that you've not fetched all the data from a statement handle and the handle won't be destroyed soon."
+	
+	print STDERR "    GSSQL disconnecting from database\n";
+	# Just go through the singleton db handle to disconnect
+	$_dbh_instance->disconnect or warn $_dbh_instance->errstr;
+	$_dbh_instance = undef;
+    }
+    # Number of gssql objects that share a live connection is now 0, as the connection's dead
+    # either because the last gssql object finished() or because connection was killed (force)
+    $ref_count = 0;
+}
+
 
 # Load the designated database, i.e. 'use <dbname>;'.
@@ -260,19 +334,4 @@
 }
 
-# disconnect from db - https://metacpan.org/pod/DBI#disconnect
-# TODO: make sure to have committed or rolled back before disconnect
-# and that you've call finish() on statement handles if any fetch remnants remain
-sub disconnect_from_db {
-    my $self= shift (@_);    
-    my $dbh = $self->{'db_handle'};
-
-    # make sure any active stmt handles are finished
-    # NO: "When all the data has been fetched from a SELECT statement, the driver will automatically call finish for you. So you should not call it explicitly except when you know that you've not fetched all the data from a statement handle and the handle won't be destroyed soon."
-    
-    print STDERR "Disconnecting from database\n" if($self->{'verbosity'} > 1);
-    
-    my $rc = $dbh->disconnect or warn $dbh->errstr; # The handle is of little use after disconnecting. Possibly PrintError already prints a warning and this duplicates it?
-    return $rc;
-}
 
 sub create_db {
