import torrus 1.0.9
[freeside.git] / torrus / perllib / Torrus / SQL.pm
diff --git a/torrus/perllib/Torrus/SQL.pm b/torrus/perllib/Torrus/SQL.pm
new file mode 100644 (file)
index 0000000..de54cac
--- /dev/null
@@ -0,0 +1,234 @@
+#  Copyright (C) 2005  Stanislav Sinyagin
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+
+# $Id: SQL.pm,v 1.1 2010-12-27 00:03:38 ivan Exp $
+# Stanislav Sinyagin <ssinyagin@yahoo.com>
+
+# Package for RDBMS communication management in Torrus
+# Classes should inherit Torrus::SQL and execute Torrus::SQL->new(),
+# and then use methods of DBIx::Abstract.
+
+package Torrus::SQL;
+
+use strict;
+use DBI;
+use DBIx::Abstract;
+use DBIx::Sequence;
+
+use Torrus::Log;
+
+my %connectionArgsCache;
+
+# Obtain connection attributes for particular class and object subtype.
+# The attributes are defined in torrus-siteconfig.pl, in a hash
+# %Torrus::SQL::connections.
+# For a given Perl class and an optional subtype,
+# the connection attributes are derived in the following order:
+# 'Default', 'Default/[subtype]', '[Class]', '[Class]/[subtype]',
+# 'All/[subtype]'.
+# For a simple setup, the default attributes are usually defined for
+# 'Default' key.
+# The key attributes are: 'dsn', 'username', and 'password'.
+# Returns a hash reference with the same keys.
+
+sub getConnectionArgs
+{
+    my $class = shift;
+    my $objClass = shift;
+    my $subtype = shift;
+
+    my $cachekey = $objClass . ( defined( $subtype )? '/'.$subtype : '');
+    if( defined( $connectionArgsCache{$cachekey} ) )
+    {
+        return $connectionArgsCache{$cachekey};
+    }
+    
+    my @lookup = ('Default');
+    if( defined( $subtype ) )
+    {
+        push( @lookup, 'Default/' . $subtype );
+    }
+    push( @lookup, $objClass );
+    if( defined( $subtype ) )
+    {
+        push( @lookup, $objClass . '/' . $subtype, 'All/' . $subtype );
+    }    
+
+    my $ret = {};
+    foreach my $attr ( 'dsn', 'username', 'password' )
+    {
+        my $val;
+        foreach my $key ( @lookup )
+        {
+            if( defined( $Torrus::SQL::connections{$key} ) )
+            {
+                if( defined( $Torrus::SQL::connections{$key}{$attr} ) )
+                {
+                    $val = $Torrus::SQL::connections{$key}{$attr};
+                }
+            }
+        }
+        if( not defined( $val ) )
+        {
+            die('Undefined attribute in %Torrus::SQL::connections: ' . $attr);
+        }
+        $ret->{$attr} = $val;
+    }
+
+    $connectionArgsCache{$cachekey} = $ret;
+    
+    return $ret;
+}
+
+
+my %dbhPool;
+
+# For those who want direct DBI manipulation, simply call
+# Class->dbh($subtype) with optional subtype. Then you don't use
+# any other methods of Torrus::SQL.
+
+sub dbh
+{
+    my $class = shift;
+    my $subtype = shift;
+
+    my $attrs = Torrus::SQL->getConnectionArgs( $class, $subtype );
+
+    my $poolkey = $attrs->{'dsn'} . '//' . $attrs->{'username'} . '//' .
+        $attrs->{'password'};
+
+    my $dbh;
+    
+    if( exists( $dbhPool{$poolkey} ) )
+    {
+        $dbh = $dbhPool{$poolkey};
+        if( not $dbh->ping() )
+        {
+            $dbh = undef;
+            delete $dbhPool{$poolkey};
+        }
+    }
+
+    if( not defined( $dbh ) )
+    {
+        $dbh = DBI->connect( $attrs->{'dsn'},
+                             $attrs->{'username'},
+                             $attrs->{'password'},
+                             { 'PrintError' => 0,
+                               'AutoCommit' => 0 } );
+
+        if( not defined( $dbh ) )
+        {
+            Error('Error connecting to DBI source ' . $attrs->{'dsn'} . ': ' .
+                  $DBI::errstr);
+        }
+        else
+        {
+            $dbhPool{$poolkey} = $dbh;
+        }
+    }
+    
+    return $dbh;
+}
+
+
+END
+{
+    foreach my $dbh ( values %dbhPool )
+    {
+        $dbh->disconnect();
+    }
+}
+
+
+sub new
+{
+    my $class = shift;
+    my $subtype = shift;
+
+    my $self = {};
+
+    $self->{'dbh'} = $class->dbh( $subtype );
+    if( not defined( $self->{'dbh'} ) )
+    {
+        return undef;
+    }
+    
+    $self->{'sql'} = DBIx::Abstract->connect( $self->{'dbh'} );
+
+    $self->{'subtype'} = $subtype;
+    $self->{'classname'} = $class;
+    
+    bless ($self, $class);
+    return $self;    
+}
+
+
+
+sub sequence
+{
+    my $self = shift;
+
+    if( not defined( $self->{'sequence'} ) )
+    {
+        my $attrs = Torrus::SQL->getConnectionArgs( $self->{'classname'},
+                                                    $self->{'subtype'} );
+
+        $self->{'sequence'} = DBIx::Sequence->new({
+            dbh => $self->{'dbh'},
+            allow_id_reuse => 1 });
+    }
+    return $self->{'sequence'};
+}
+       
+
+sub sequenceNext
+{
+    my $self = shift;
+
+    return $self->sequence()->Next($self->{'classname'});
+}
+
+
+sub fetchall
+{
+    my $self = shift;
+    my $columns = shift;
+    
+    my $ret = [];
+    while( defined( my $row = $self->{'sql'}->fetchrow_arrayref() ) )
+    {
+        my $retrecord = {};
+        my $i = 0;
+        foreach my $col ( @{$columns} )
+        {
+            $retrecord->{$col} = $row->[$i++];
+        }
+        push( @{$ret}, $retrecord );
+    }
+    
+    return $ret;
+}
+
+
+1;
+
+
+# Local Variables:
+# mode: perl
+# indent-tabs-mode: nil
+# perl-indent-level: 4
+# End: