Item8148: ODBC support for DatabasePlugin and fix to not error on NULL fields - and fix for Sybase and MSSQL

pencil
Priority: Normal
Current State: Confirmed
Released In: n/a
Target Release: n/a
Applies To: Extension
Component: DatabasePlugin
Branches:
Reported By: Foswiki:Main.SallyHoughton
Waiting For:
Last Change By: FlorianSchlichting
Using the Plugin Version: 0 (28 nov. 2008) on Foswiki-1.0.4, Thu, 19 Mar 2009, build 3201, Plugin API version 2.0

I found that out of the box, DatabasePlugin wouldn't work using an ODBC connection. So (apart from having to download freeTDS, DBD::ODBC and make with odbc support to give me libtsodbc.so.0) I looked around t'internet and found some other people had made changes to the original twiki/foswiki code. I've taken those, along with the sybase fix and put it in my version. I've also included a fix to spweing errors in the http logs when you reach a NULL field ( shows error message like view: Use of uninitialized value in substitution iterator) and being able to use functions in the output format (e.g. $format="$count(name)").

Note - I am doubling up the use of the sid variable as a DSN for ODBC.

myserver # diff -c Connection.pm Connection.pm.orig
*** Connection.pm     Wed Apr 29 13:33:17 2009
--- Connection.pm.orig  Fri Nov 28 14:09:06 2008
***************
*** 27,48 ****

      unless ( $this->{db} ) {
          my $sid = $this->{sid} ? ";sid=$this->{sid}" : '';
!       my $driver = $this->{driver};
!       my $hoststring="host";
!       my $connectstring="DBI:";
!
!       if ( $driver =~ /Sybase/i ) { $hoststring = "server"; }
!       if ( $driver eq 'ODBC' ) {
!               $connectstring = $connectstring."$this->{driver}:$this->{sid}";
!       } else {
!               $connectstring = $connectstring."$this->{driver}:datbase=$this->{database};$hoststring=$this->{hostname}$sid";
!       }
!
!       # not quite sure why, but don't put this next line within an if statement as that breaks it
!       my $db = DBI->connect($connectstring, $this->{username}, $this->{password},
!                       { PrintError => 1, RaiseError => 1 });
          if ( !$db ) {
!             die "Can't open database specified by description '$this->{description}'";
          }

          $this->{db} = $db;
--- 27,41 ----

      unless ( $this->{db} ) {
          my $sid = $this->{sid} ? ";sid=$this->{sid}" : '';
!
!         my $db = DBI->connect(
! "DBI:$this->{driver}:database=$this->{database};host=$this->{hostname}$sid",
!             $this->{username},
!             $this->{password},
!             { PrintError => 1, RaiseError => 1 }
!         );
          if ( !$db ) {
!             die "Can't open database specified by description '$description'";
          }

          $this->{db} = $db;
myserver # diff -c DATABASE_SQL.pm DATABASE_SQL.pm.orig
*** DATABASE_SQL.pm   Wed Apr 29 13:24:34 2009
--- DATABASE_SQL.pm.orig        Fri Nov 28 14:09:06 2008
***************
*** 22,31 ****

              # reverse sort so we handle longer keys first
              foreach my $k ( reverse sort keys %$res ) {
!               my $thisres = defined $res->{$k} ? $res->{$k} : '';
!               ($z = $k ) =~ s/\(/[\(]/g;
!               ($z = $k ) =~ s/\(/[\(]/g;
!                 $row =~ s/\$$z/$thisres/g;
              }
              $result .= $row . $separator;
          }
--- 22,28 ----

              # reverse sort so we handle longer keys first
              foreach my $k ( reverse sort keys %$res ) {
!                 $row =~ s/\$$k/$res->{$k}/g;
              }
              $result .= $row . $separator;
          }
myserver # 

I hope that these can help someone else and maybe get included (with whatever cleanup is required) in an updated version.

As an example, the Database setting in my LocalSite.cfg looks like this (pertinent pieces santised for security);
$Foswiki::cfg{Plugins}{DatabasePlugin}{Databases} = [
          {
            # hack to use the sid as a DSN when driver=ODBC
            'sid' => 'HPQC',
            'hostname' => 'myremoteserver',
            'description' => 'QualityCenter',
            'username' => 'Readonly',
            'database' => 'my_db',
            'password' => 'cleartextpassword',
            'table_name' => 'BUG',
            'driver' => 'ODBC'
          }
        ];

-- SallyHoughton - 29 Apr 2009

I've also made the following change so that I can extract large field entries from my ODBC database without being limited to using CAST and only 8000 characters. It would be nice to have this as a tunable, but I can live without that...20000 seems to be a high enough limit for me right now.

Basically I added a single line to the Connection.pm script;
fnet-rio1 # diff -c Connection.pm Connection.pm.orig
*** Connection.pm       Wed May  6 14:11:53 2009
--- Connection.pm.orig  Wed May  6 14:07:27 2009
***************
*** 44,50 ****
          if ( !$db ) {
              die "Can't open database specified by description '$this->{description}'";
          }
-       if ( $driver eq 'ODBC' ) { $db->{LongReadLen} = 20000; }

          $this->{db} = $db;
      }
--- 44,49 ----

-- SallyHoughton - 06 May 2009

Many thanks! Just in case anyone else is searching for how to get DatabasePlugin to work with MSSQL (Microsoft SQL Server), the "sybase fix" mentioned above is needed if your configuration setting for DatabasePlugin has 'driver' => 'Sybase'. (The fix is that Connection.pm has to say "server=" instead of "host=" when it does the DBI->connect.)

-- StevenKrahn - 26 Mar 2010

I've tested this again on a fresh installation of 1.1.0, and it is still true that you need the above fix in order to use Microsoft SQL Server with DatabasePlugin

-- StevenKrahn - 20 Oct 2010

I just fixed the warnings on NULL fields as Item11363

-- FlorianSchlichting - 19 Dec 2011

I find unified diffs (diff -u) much easier to read. Here's how I understand your patch:

--- a/DatabasePlugin/lib/Foswiki/Plugins/DatabasePlugin/Connection.pm
+++ b/DatabasePlugin/lib/Foswiki/Plugins/DatabasePlugin/Connection.pm
@@ -30,15 +32,26 @@ sub connect {
 
     unless ( $this->{db} ) {
         my $sid = $this->{sid} ? ";sid=$this->{sid}" : '';
+        my $data_source = "DBI:$this->{driver}:";
+        my $hoststring = 'host';
+
+        if ($this->{driver} =~ /Sybase/i) {
+            $hoststring = 'server';
+        }
+        if ($this->{driver} eq 'ODBC') {
+            $data_source .= "$this->{sid}";
+        } else {
+            $data_source .= "database=$this->{database};$hoststring=$this->{hostname}$sid";
+        }
 
         my $db = DBI->connect(
-"DBI:$this->{driver}:database=$this->{database};host=$this->{hostname}$sid",
+            $data_source,
             $this->{username},
             $this->{password},
             { PrintError => 1, RaiseError => 1 }
         );
         if ( !$db ) {
-            die "Can't open database specified by description '$description'";
+            die "Can't open database specified by description '$this->{description}'";
         }
 
         $this->{db} = $db;

The last chunk is an independent bugfix.

I don't like how sid is abused for something unrelated; probably a new variable, odbcdsn or the like, should be used.

Regarding LongReadLen, why is that only used for ODBC databases? And is that really useful to have in general, given that the standard formatting puts limits on the amount of text that can be displayed in a sensible manner? I guess I haven't fully understood your use case...

-- FlorianSchlichting - 19 Dec 2011
 

ItemTemplate edit

Summary ODBC support for DatabasePlugin and fix to not error on NULL fields - and fix for Sybase and MSSQL
ReportedBy Foswiki:Main.SallyHoughton
Codebase 1.1.0, 1.0.9, 1.0.4
SVN Range Foswiki-1.0.0, Thu, 08 Jan 2009, build 1878
AppliesTo Extension
Component DatabasePlugin
Priority Normal
CurrentState Confirmed
WaitingFor
Checkins distro:77c52e458072 distro:d9d3191c83b9
TargetRelease n/a
ReleasedIn n/a
CheckinsOnBranches
trunkCheckins
Release01x01Checkins
Topic revision: r8 - 19 Dec 2011, FlorianSchlichting
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. See Copyright Statement. Creative Commons License    Legal Imprint    Privacy Policy