Magazine

Debian, FreeTDS et SQL Server

Publié le 17 janvier 2008 par Olivier Duval

Contexte

Lorsqu’on travaille dans un environnement hétérogène composé de plateformes Windows et Linux, il peut s’avérer utile d’accéder à partir d’un serveur Unix à un serveur de base de données de type SQL Server (2000, 2005, ...), j’aime l’interop…

Le développement de SQL Server est notamment né à l’origine de la collaboration entre Microsoft et Sybase. Le protocole de communication utilisé pour les échanges clients/serveur se nomme TDS, et sa version Opensource FreeTDS, avec une communauté toujours active.

Dans notre environnement, nous utilisons le serveur de listes SYMPA. Ce dernier permet de synchroniser les [emails des] membres des listes qu’il gère avec une base externe (synchro automatique toutes les n minutes, ou lors de l’envoi d’un message). Dans notre système d’information, celle-ci est une base SQL Server qui est utilisé par la plateforme d’applications mais aussi par le serveur de listes (en consultation ou en insertion).

Debian, installation

Sous Debian, le paquetage à installer est libct3 :

apt-get install libct3

Le paquetage installe le tout dans le répertoire /etc/freetds.

Configuration

Le fichier freetds.conf est une base de départ. Parmi les paramétrages intéressants, nous avons la section globale ([global]), où l’on peut varier la version du protocole à utiliser (selon le serveur SQL Server en face), fixer explicitement l’encodage à utiliser pour la communication client/serveur, ou créer des sections (de la forme [masection]) spécifiques à vos serveurs SGBD (IP d’accès, protocole, port, ...).

Pour un dialogue avec un SQL Server 2000, en UTF, ci-après, un exemple de configuration freetds.conf :

$ less /etc/freetds.conf

[global]
        # TDS protocol version
        tds version = 8.0
        client charset = UTF-8
        # si besoin de traces
;       dump file = /var/log/freetds.log
;       debug level = 10

        # si besoin de gérer les timeout d'exécution des commandes SQL et du timeout de connexion
;       timeout = 10
;       connect timeout = 10

[monserveur-sql]
host = 10.75.40.31
port = 1433
tds version = 8.0

Dans le client (Perl, PHP, ...) qui utilisera FreeTDS, celui-ci fera appelle au libellé du serveur défini dans freetds.conf, ici monserveur-sql, cela a l’avantage d’abstraire l’IP réelle.

Exemple de client en Perl

Le but de ce programme est de lire un mail stocké sur le disque, de le parser, et d’inscrire les divers éléments (entêtes, corps, ...) qui le composent dans une table en base SQL Server.


use DBI;
use Encode;

use lib '/home/sympa/bin';
use Log;

# infos. sql
# le nom du serveur du freetds.conf
my $server = "monserveur-sql";
my $user = "loginsql";
my $pwd = "pwdsql";
my $bdd = "mabase";

sub encoding
{
    my $body = shift;
    &Encode::encode_utf8($body);
}

sub msgtosql
{
   my $file = $ENV{'PATHMSG'};
   my $adrlist = $ENV{'LISTADDR'};
   my $listname='';
   my $domain='';
   my $bodyencode='';

   if ($adrlist =~ /^(.*)\@(.*)$/) {
        $listname = $1;
        $domain = $2;
   } else {
        &do_log('err',"Match of list address '$adrlist' failed");
        return undef;
   } 

   # lecture du message (fichier texte)
   # et prise en compte des champs a inserer dans la base 
   my $parser = new MIME::Parser;
   $parser -> output_to_core(1);
   my $msg = $parser -> parse_open("$file");

   my $head = $msg -> head;
   my $body = $msg -> body_as_string;
   my $hdrall = $msg -> header_as_string;
   my $hdrdate = $head->get('Date');
   my $hdrfrom = $head->get('From');
   my $hdrsubject = $head->get('Subject');
   my $hdrto = $head->get('To');
   my $messageid = $head->get('Message-Id');

   $bodyencode = $body;

   $dbh = DBI->connect("dbi:Sybase:$server","$user","$pwd") or die 'connect';
   $dbh->do("use $bdd");     

   # insertion du message en base

   $SQL = sprintf "INSERT INTO msgs (body,creatstamp,hdrall,hdrdate,hdrfrom,hdrfromspc,hdrsubject,hdrto,list) VALUES (%s,getdate(),%s,%s,%s,%s,%s,%s,%s)", $dbh->quote($bodyencode),$dbh->quote($hdrall), $dbh->quote($hdrdate), $dbh->quote($hdrfrom), $dbh->quote($hdrfrom),$dbh->quote($hdrsubject),$dbh->quote($hdrto),$dbh->quote($listname);

  # on test l'insert, si catch alors on force l'encodage a utf-8
  # l'erreur vient de freeTDS qui ne pt encoder (via iconv) directement en UTF-8, ne connaissant pas parfois l'encodage origine 
  # pas d'encoding automatique car des msgs peuvent etre deja en utf-8
  {
          # trap die pour logguer l'erreur SQL
          local $SIG{'__DIE__'} =
                sub { do_log('err',$_[0]); die $_[0]; };
          eval { $dbh->do($SQL) || die "Error SQL $DBI::errstr"; };
          if($@) {
                do_log('notice',"[catch sql] try to encode in utf-8...");
                $bodyencode = &Encode::encode_utf8($body);
                $SQL = sprintf "INSERT INTO msgs (body,creatstamp,hdrall,hdrdate,hdrfrom,hdrfromspc,hdrsubject,hdrto,list) VALUES (%s,getdate(),%s,%s,%s,%s,%s,%s,%s)", $dbh->quote($bodyencode),$dbh->quote($hdrall), $dbh->quote($hdrdate), $dbh->quote($hdrfrom), $dbh->quote($hdrfrom),$dbh->quote($hdrsubject),$dbh->quote($hdrto),$dbh->quote($listname);

                unless($dbh->do($SQL)) {
                        do_log('err',"Error $DBI::errstr for $SQL");
                }
          }
  }

   $dbh->disconnect;

   return 1;
}

do_openlog("LOCAL2", "unix", 'archivetosql');

&msgtosql();

Au passage, un excellent site sur Perl, sur tous les sujets du langage.

Conclusion

Lorsque dans un système d’information, il cohabite un grand nombre de serveurs hétérogènes, le portage de protocoles peut-être un moyen de faire communiquer les systèmes. Une autre solution, de plus en plus répandue, est l’utilisation des services Web (SOAP, REST, ...), dans une approche SOA.

Ressources

Quelques sur le sujet.

Debian, FreeTDS et SQL Server

Retour à La Une de Logo Paperblog

A propos de l’auteur


Olivier Duval 4 partages Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte

Dossiers Paperblog