jeudi 29 décembre 2011

Informix et Perl: un vieux couple plein de ressources

Le shell unix c’est bien, mais il y a beaucoup mieux et  vous l'avez déjà

En tant que DBA émérite, vous avez développé à coup sûr un certain nombre de scripts en command shell. Ce langage est  très pratique et versatile pour assurer vos tâches d’administration IBM Informix Dynamic Server. En le combinant avec les commandes habituelles comme onmode, onstat, oncheck, onparams et autres, vous avez mis en place toute une gamme de scripts qui vous permettent de gérer automatiquement vos sauvegardes, l’exécution des UPDATE STATISTICS, la gestion de certaines alarmes, le démarrage-arrêt de vos instances, ainsi que la connexion auxdites instances. Vous avez probablement ajouté des scripts de monitoring basés sur onstat et oncheck.
Mais soyons sincères, quand nous relisons ces scripts pour les maintenir, ou pire quand nous devons modifier un script écrit par quelqu’un d’autre, le niveau de complexité de l’écriture est très souvent inversement proportionnel  à la facilité de lecture. Il est vrai qu’après le 7ème niveau d’imbrication de « pipe », on ne sait plus trop où l’on est arrivé.
En fait, si le shell unix comprend un très grand nombre de commandes extrêmement utiles et pertinentes, le ciment ( matérialisé généralement le « pipe », assorti de ls, grep, awk, cat, echo et expr ) qui les unit pour en faire de véritables applications est loin d’être simple à comprendre et encore moins simple à débugger. De plus, si vous devez installer ces scripts sur un autre parfum d’Unix, tout se complique car une foule de commandes soit ne réagissent pas de la même manière, soit produisent directement des erreurs de syntaxe.  Je n’évoque même pas le scénario catastrophe du déploiement d’une instance sous Windows ou Mc Intosh, qui font désormais partie des plateformes habituelles de IBM Informix, où vous devrez probablement tout réécrire…

Vous l’avez sous les yeux, et il en fait des choses!

C’est vrai, le shell Unix répond efficacement aux besoins immédiats quand il faut obtenir rapidement un résultat, cependant trop de problèmes de compatibilité, performance et facilité de maintenance viennent impacter négativement l’industrialisation de ces  fonctionnalités d’administration. Je commençais à mesurer la véritable portée de ce problème quand, en 1997, un ami m’a recommandé de regarder de près le langage Perl ( Practical Extraction Report Langage). 

Il a tout d’un grand

Au-delà du rôle originel de Perl qui est le parsing de texte, j’ai très rapidement compris que j’avais entre les mains un véritable langage de développement qui combinait les attributs d’un langage évolué comme C, les utilitaires awk et sed, incluant la majorité des commandes du shell.  Gérant les  variables scalaires, tableaux, hash, visibilité des variables,  opérateurs binaires, unaires, logiques, blocs d’ordres, gestion de fonctions avec passage et réception de paramètres par référence ou non,  un système de gestion d’entrées-sorties très proche du système et enfin les très puissantes expressions régulières, on sent fortement le lien de parenté avec C, mais tout cela reste malgré tout beaucoup plus facile et rapide à coder et à lire ( adieu aussi les core dumped !)

Même pas peur !

Vous disposez également d’un très bon debugger interactif ( adieu les « set –x » oubliés dans le code !). Au sujet performance, Perl n’a pas la rapidité de C puisque les modules ne sont pas compilés, mais reste cependant très honorable ( Ex 2 sec. pour lire entièrement un fichier de 32 Mb/640 000 lignes). 
Mais quel est le véritable intérêt par rapport au shell ?
Perl peut déjà faire tout ce que le shell peut faire, mais de façon beaucoup plus simple. Finies les contorsions et galipettes entre cat, awk, sort ,grep ls, read etc… !  Avec Perl, le code va directement au but recherché.
Par contre, Perl sait faire des choses que le shell ne sait pas faire, notamment dans le domaine de la programmation-système. Par exemple, il est  simple de gérer la communication entre processes via les IPC, gérer des files de messages In/Out et autres friandises du genre.

Il se sent bien partout

Un autre avantage de Perl réside dans sa portabilité. Dans la mesure où vous n’incluez pas dans vos programmes Perl  des commandes shell spécifiques à telle ou telle plateforme, votre programme Perl fonctionnera à l’identique sur des plateformes différentes, que ce soit sur n’importe quel Unix, Windows ou Mac Intosh, pour ce qui nous intéresse.
Mais l’argument de poids est la richesse de la librairie de modules disponible gratuitement pour la communauté.  Ces modules couvrent les fonctionnalités les plus diverses et variées, allant d’extrêmement utiles à plus anecdotiques. Des fonctionnalités indispensables comme une fonction qui traduit n’importe quelle date en format caractère ( ex « lun. Déc 26 17 :45 :14 2011 CET » ) en un nombre entier de type « epoch », permettant en autres de calculer des intervalles entre deux timestamps (Time ::¨ParseDate, utile quand on parse un fichier log par exemple), ou bien obtenir toutes les propriétés d’un fichier  ( stat ), ou bien lire et se déplacer dans un fichier en mode binaire ( IO ::Handle ), mais aussi lire les propriétés d’une image (Image::ExifTool ), et la très puissante Win32::OLE qui permet d’accéder en lecture ou écriture à pratiquement tous les objets Microsoft comme boite email Outlook, documents Word, Excel etc...

Dans cette foule de modules, il en existe un qui nous intéresse particulièrement : DBD::Informix. Vous l’avez compris, ce module est le module de communication avec les bases de données IBM Informix. Développé et maintenu par un Informixien de longue date, et maintenant vénérable IBMer barbu, ce module contient toutes les fonctionnalités nécessaires à pouvoir utiliser le langage SQL pratiquement comme avec Esql/C, Informix 4GL ou dbaccess, dans vos applications Perl.

On essaye ?

Pour des raisons que vous imaginez sans doute, le module DBD:Informix ne fait pas partie des packages de base généralement livrés avec Perl ( pas plus que le module du « Evil Empire kind of database »  d'ailleurs ). Il faudra aller le chercher sur CPAN, l'immense répositoire de packages Perl. Après avoir « éprouvé quelque difficulté » avec la version précédente de l'installation, il suffit de savoir comment faire et l'installation se fait sans sueur ni larmes.
Les prérequis obligatoires:
-    avoir Perl version supérieure ou égale à 5.14 pour être en totale conformité,
-    avoir esql/C > 7.31 ou mieux Informix CSDK 3,X ( généralement inclus dans IDS à partir de la version 10.00 )
-    avoir un moteur Informix ou IBM Informix ( SE ou IDS ) « on-line »
-    avoir créé la base de démonstration stores
-    avoir créé l'utilisateur informix
-    une connexion internet en état de marche,
-    Sans problème sous Linux, et à ma connaissance sous les autres plateformes Unix, plus délicat pour l'instant sur plateforme Windows, mais affaire à suivre...

Avant de lancer l’installation, vous devez, sous l’utilisateur root, pouvoir vous connecter à la base stores avec dbaccess, donc avoir les variables d’environnement $INFORMIXDIR, $INFORMIXSERVER et $PATH correctement valorisées. Vous devez également pouvoir exécuter un binaire esql/c, donc avoir la variable $LD_LIBRARY_PATH ( linux , AIX) ou $LIBPATH ( solaris etc ) valorisée à « $INFORMIXDIR/lib :$INFORMIXDIR/lib/esql :$INFORMIXDIR/lib/tools »

Une fois ces test préliminaires accomplis,  vous pouvez taper :
perl -MCPAN -e 'install Bundle::DBD::Informix'

Laissez ensuite le système ( l’outil CPAN ) gérer l'affaire. Dans la mesure où les prérequis sont respectés, l'installation se fait sans aucune anicroche. Par contre, en cas d'oubli, les messages d'erreur sont quelque peu démotivants. Il suffit alors de remédier aux prérequis manquants et relancer la commande citée plus haut. Perl CPAN reprendra l'installation là où il s'était arrêté.

Quelques messages d'erreur peuvent apparaître en cours de route. Tant que l'installation ne s'arrête pas, tout va bien. L'installation se termine par « /usr/bin/make install -- OK ».

A la vue de ce message, vous êtes prêt à utiliser le module DBD::Informix. Une petite vérification par un find /usr –name Informix.pm, pour vous assurer que vous avez bien Informix.pm quelque part dans /usr…lib64 … /DBD/Informix.pm.
Vous pourrez aussi  le trouver dans ~root/.cpan/build/DBD-Informix-2011.0612-*, qui est la directory d’installation, mais il doit être absolument dans /usr/…libXX.

Installation OK! voyons les rudiments.
Pour pouvoir utiliser DBD::Informix, il faut l’inclure dans le programme Perl, en notant en tête du module :
use DBD::Informix qw(:ix_types) ;

Pour se connecter à une base :
$dbh=DBI-connect(« dbi:Informix:$databasename »,$username,$password) ;

Pour préparer un SELECT :
$stmt=sprintf «SELECT col1,col2,col3,col4 FROM mytable ORDER BY col2» ;
$sth = $dbh->prepare($stmt);
$sth->execute() ;

Pour lire le curseur, et afficher le contenu :
$sqlcode=0;
while ( $sqlcode != 100 ) {
   ($var1,$var2,$var3,$var4) = $sth->fetchrow_array() ;
   printf “%s %s %s %d\n”,$var1,$var2,$var3,$var4 ;
   $sqlcode=$sth->{ix_sqlcode};
}

Mais vous pouvez également remplir un array contenant le résultat complet du curseur, pour le traiter ensuite en mémoire comme une structure organisée à l’image de la table, à l’image du RECORD LIKE d’Informix 4GL :
$array_ref = $sth->fetchall_arrayref ( {col1=>1,col2=>1,col3=>1,col4=>1}) ;
foreach $row ( @$array_ref ) {
   printf “%s %s %s %d\n”,$row->{col1},$row->{col2},$row->{col3},
 $row->{col4} ;
}

Pour ce qui est du SQL habituel , INSERT, UPDATE, DELETE, vous alliez le trouver tout seul :
$stmt=sprintf «INSERT INTO mytable ( col1,col2,col3) VALUES (?,?,? )» ;
$sth = $dbh->prepare($stmt);
$sth->execute($val1,$val2,$val3) ;
Vous verrez au passage que l’utilisation des placeholders est extrêmement simple, puisqu’il suffit d’en mettre les valeurs entre parenthèses lors de l’execute.

Pour consolider l’utilisation des placeholders, voici un exemple avec UPDATE
$stmt=sprintf «UPDATE mytable SET (col2,col3) = (?,?) WHERE col1=?» ;
$sth = $dbh->prepare($stmt);
$sth->execute($val2,$val3,$val1) ;

Les transactions? Très simple:
$dbh->commit;
$dbh->rollback;
A moins que vous ne désiriez simuler le mode ANSI, en ouvrant la connexion de cette manière, le comportement de vos transaction est identique à celui dont vous avez l’habitude.
$dbh = DBI->connect("dbi:Informix:$databasename",$username,$password,
                        { AutoCommit => 1});

Et mon CURSOR WITH HOLD ?
Trivial, il suffit de le passer comme argument lors du PREPARE, comme ceci :
$sth = $dbh->prepare("SELECT col1,col2 FROM mytable",{'ix_CursorWithHold' => 1});

Et les BLOBS ?
Soyons francs : l’implémentation des BLOBS n’est pour l’instant pas aussi riche qu’avec ESQL/C, mais on s’en approche.
Pour le type TEXT, ou tout autre BLOBS dont le contenu peut être mis dans une variable Perl, on utilisera le bind de paramètres, comme ce qui suit :

$textlocation = sprint “/home/informix/textfiles/file1.txt” ;
fopen TEXT,$textlocation or die “Cannot open file “ . $texlocation ;
@FileContents=<TEXT>;
$text_val = join ( @FileContents ) ;
$pkey = 13452 ;
$timestamp = «2011-12-28 15:00:00» ;
$stmt = sprint “INSERT mytable VALUES (?,?,?)” ;   
$sth = $dbh->prepare($stmt);
$sth->bind_param(1, $pkey);
$sth->bind_param(2, $timestamp ) ;
$sth->bind_param(3, $text_val, { ix_type => IX_TEXT });
$sth->execute;

Pour la lecture des blobs, ceux-ci sont mappés dans une variable Perl de type string. On peut toutefois procéder à un LOCATE, de la façon suivante :
$sth->{ix_BlobLocation} = 'InMemory';   # Comportement par défaut
$sth->{ix_BlobLocation} = 'InFile';     # dans un fichier dont le nom est rendu
$sth->{ix_BlobLocation} = 'DummyValue'; # retourne <text> ou <byte>
$sth->{ix_BlobLocation} = 'NullValue';  # retourne valeur indéfinie

Les SMARTBLOBS ne sont pas (encore ?) supportés directement, MAIS on peut les manipuler avec les fonctions LOTFILE pour lire, FILETOBLB ou FILETOCLOB pour écrire.

Pour insérer:
$stmt = qq!INSERT INTO item2 VALUES (1234,'Desk', FILETOBLOB('image.bmp','client'))!;
$sth = $dbh->prepare($stmt);
$sth->execute();

Pour lire:
$stmt = qq!SELECT LOTOFILE(myimage, ?,'client') FROM mytable!;
$sth = $dbh->prepare($stmt);
$sth->execute($imagefilename) ;
$sth->bind_columns($Image));
while($sth->fetch) {
   print $Image;
…..
}

Alors finalement, quel est l’intérêt d’utiliser Perl avec DBD::Informix ?
En conclusion, nous pourrions continuer longtemps comme cela, mais le réveillon approche et la concentration commence à s’évaporer.
Pour un DBA Informix, cette combinaison permet de développer des scripts beaucoup plus puissants en terme de fonctionnalités, notamment pour les facilités de programmation système « abordables » qu’il offre. Les objets comme les tableaux, les tableaux de hachage, les expressions régulières et surtout l’immense bibliothèque de modules traitant de pratiquement tout ce qui peut se faire, confèrent au développeur une puissance que le shell n’atteindra jamais.
Il permet également de rationaliser les scripts d’administration, de les rendre beaucoup plus lisibles et simples à maintenir. Enfin, argument non négligeable, Perl offre une grande portabilité de vos développements approchant des 100%, pour autant que l’on n’utilise pas de syntaxe spécifique à chaque OS. Un script fonctionnant sur Linux fonctionnera sans mauvaises surprises sur AIX, hp-ux, Solaris, Mac OS X et même Windows !

La cerise sur le gâteau est la possibilité d’accéder en mode natif à vos bases de données Informix comme le fait Informix 4GL, ESQL/C ou java. A partir de ce point s’ouvrent des perspectives de développements encore plus vastes, comme parser et intégrer des documents Microsoft Office dans une base de données Informix, typifier et classer des images dans une base Informix, mais aussi aller fouiller dans vos favoris et historique de Mozilla Firefox, en les intégrant dans une base de données Informix. De façon plus générale vous pouvez envisager de développer très rapidement et à coût très réduit un gateway entre d’autres bases de données et votre base de données préférée, par exemple !

Des exemples concrets sont disponibles sur mon autre blog www.vercelletto.com , ainsi que sur le site de l'UGIF www.informix.fr .
N'hésitez pas à les télécharger et à les utiliser.


En attendant, et pendant que vous réflechissez à tout cela, je vous souhaite plein de bonnes choses pour l’année 2012 !

A lire :
le document de référence de Jonathan Leffler:
http://search.cpan.org/~johnl/DBD-Informix-2011.0612/Informix.pm
sa présentation à la conférence IIUG 2001:
http://www.ibdk.dk/seminar/DBD-Informix-20010508.ppt

Chez O’reilly: 
- Programming perl DBI by Alligator Descartes and Time Bunce,
Learning Perl,  by Tom Phoenix, Randal L. Schwartz
- Programming Perl, by  Larry Wall ( l'inventeur de Perl), Tom Christiansen, Jon  Orwant
- Perl advanced Programming, By Simon Cozens
- Perl for System Administration by David Blank-Edelman 
- La doc Perl en français: http://perl.enstimac.fr/DocFr.html
- Rechercher dans la bibliothèque CPAN : http://search.cpan.org/
- Et toute une foule de turoriels perl sur le net