Page précédente Page suivante Table des matières

5. Une interrogation dynamique

Mon premier objectif est de construire un système d'interrogation entièrement dynamique, sans écrire aucune page HTML en direct. Pour ce faire, il faut 3 scripts perl :

  1. base.pl : Liste les bases et les tables existantes sur le système.
  2. criteres.pl : Liste les champs de la tables choisit et permet d'entrer les critères.
  3. interrog.pl : Interroge la tables choisit en fonction des critères.

Perl est un langage très particulier, les personnes connaissant un peu la programmation et les commandes Unix (sed notamment) devraient s'y retrouver, pour les autres, cela va être un peu plus dur. De toutes façon, si vous ne connaissez pas, je vous conseil une introduction : http://www2.ec-lille.fr/~seguy/francais/perl/index.html.

Les scripts suivants donne une sortie brute (sans aucune mise en page si ce n'est les retours à la ligne) dans le but de les alléger. Vous trouverez en annexe les scripts complets, avec toute la mise en forme HTML.

Je vais présenter les scripts de la façon suivante :

une partie du script
Explication

... Je n'expliquerai pas deux fois les mêmes choses, juste les nouveautés.

Il ne faut pas oublier de placer les attributs d'exécution sur les scripts :

# chmod 755 base.pl criteres.pl interrog.pl

De façon à m'assurer que le code HTML généré est correct, j'ai utilisé weblint :

Dans les scripts, j'utilise des champs de formulaire pour passer des informations d'un script à l'autre :

5.1 La liste des bases du système (base.pl)

Voici ce que génère ce script sur mon système : sbase.pl.html

#!/usr/bin/perl

use CGI;
$html = new CGI;

print $html->header;
print "<HTML>\n";
print "<HEAD>\n";
print "<TITLE>Liste des bases et tables du systeme</TITLE>\n";
print "</HEAD>\n";
print "<BODY>\n";
Une partie classique qui construit l'en-tête du fichier HTML.

Après le #, c'est le programme qui va interpréter le script. Ici, perl.

L'instruction use permet d'utiliser le module CGI, voir perldoc CGI.

le "\n" imprime un retour-chariot et ne sert qu'à aérer le code HTML généré.

@bases = `/usr/bin/psql -d template1 -c "\\l"`;
On place dans le tableau @bases le résultat de la commande /usr/bin/psql -d template1 -c "\l" Il faut remarquer les ` qui indique une commande système et le fait que certain caractères sont backquotés (par exemple, le \ qui doit être doublé pour être pris en compte).
print "<FORM ACTION=\"/cgi-bin/criteres.pl\" METHOD=POST>\n";
On commence un formulaire HTML qui lancera le script criteres.pl

Les doubles apostrophes (signes ") sont aussi backquotés car utilisées comme délimiteurs de chaîne sous Perl mais nécessaires en HTML.

foreach (@bases) {
   s/ //g;
   if ((m/\|/) and (!m/template1/) and (!m/datname/)) {
      ($base) = split (/[|]/);
      print "$base<P>\n";
      @tables = `/usr/bin/psql -d $base -c "\\dt"`;
      foreach (@tables) {
         s/ //g;
         ($temp0, $temp1, $temp2) = split (/[|]/);
         if (($temp2) and ($temp2 ne Relation)) {
            print "<INPUT TYPE=radio NAME=radio VALUE=\"$temp2:$base\">\n";
            print " $temp2<P>\n";
                     }
                  }
                }
             }
Pour chaque élément de @bases : fin du foreach(@bases)
print "<INPUT TYPE=submit VALUE=\"Module d interrogation\">\n";
print "<INPUT TYPE=reset VALUE=\"Remise a zero\">\n";
print "</FORM>\n";
print "</BODY>\n";
print "</HTML>\n";

exit;
Ensuite, on imprime le code HTML pour un bouton "OK" et un bouton "Reset" qui vont prendre le texte entre "" comme libellé.

La fin du formulaire.

Puis la fin classique d'un fichier HTML.

L'instruction exit n'est pas obligatoire mais c'est une bonne habitude à prendre.

5.2 Les champs et les critères (criteres.pl)

Voici ce que génère ce script sur mon système : scriteres.pl.html

#!/usr/bin/perl

use CGI;
$html = new CGI;

sub simple {
   print "$_[0] (de type <B>$_[1]</B>)";
   print "<INPUT TYPE=text NAME=input$_[0] SIZE=10>";
   print "<P>\n";
}
Ici, on crée une fonction qui sera appelée plus tard par &simple (param1, param2);

En premier lieu, on imprime $_0 qui correspond au premier paramètre, puis en gras $_1. Après, on imprime le code pour un champ de saisie et un retour à la ligne.

sub double {
   print "$_[0] (de type <B>$_[1]</B>)";
   &listesignes($_[0], 1);
   print "<INPUT TYPE=text NAME=input$_[0]inf SIZE=10>";
   &listesignes($_[0], 2);
   print "<INPUT TYPE=text NAME=input$_[0]sup SIZE=10>";
   print "<P>\n";
}
Même chose qu'au dessus mais on place deux champs de saisie et on appel la fonction &listesigne pour générer une liste de choix.
sub listesignes {
   if ($_[1] == 1) {
      print "<SELECT NAME=liste$_[0]inf SIZE=1>";
      print "<OPTION> >";
      print "<OPTION> >=";
                }
   elsif ($_[1] == 2) {
      print "<SELECT NAME=liste$_[0]sup SIZE=1>";
      print "<OPTION> <";
      print "<OPTION> <=";
                   }
   print "</SELECT>\n";
}
Fonction pour la liste de choix.
print $html->header;

print "<HTML>";
print "<HEAD>\n";
print "<TITLE> Creation de liste dynamique </TITLE>\n";
print "</HEAD>";
print "<BODY>\n";
print "Veuillez entrer vos criteres :";

$parametre = $html->param('radio');
($psqltable, $psqlbase) = split (/:/, $parametre);
On récupère le choix fait dans base.pl, c'est à dire la base et la table.

On place dans $parametre la valeur de la variable "radio" qui correspond au bouton radio choisit dans base.pl

On place dans $psqlbase la partie de $parametre se situant avant les ":" et dans $psqltable la deuxième partie.

@listechamps = `psql -d $psqlbase -c "\\d $psqltable"`;

foreach (@listechamps) {
   s/ //g;
   ($temp0, $temp1, $temp2) = split (/[|]/);
   if (($temp1) and ($temp1 ne Field)) {
      push (@champs, $temp1);
      push (@types, $temp2);
           }
         }
L'instruction push permet de placer un élément à la fin d'un tableau.
print "Choisissez les champs a afficher";

print "<FORM ACTION=\"/cgi-bin/interrog.pl\" METHOD=POST>\n";

$i = 0;
foreach (@champs) {
   print "<INPUT TYPE=checkbox NAME=check$champs[$i] VALUE=value$i CHECKED> $champs[$i]";
   $i++
        }
$i++ permet d'incrémenter i (i = i + 1).

print "<P>Eliminer les doublons : \n";
print "<INPUT TYPE=checkbox NAME=distinct VALUE=distinct CHECKED>\n";
print "<P>\n";

$i = 0;
foreach (@champs) {
   if ($types[$i] =~ /text|varchar/)
         { &simple($champs[$i], $types[$i]); }
   if ($types[$i] =~ /date|time|float|int/)
         { &double($champs[$i], $types[$i]); }
   $i++;
   }
Si le type de la données est text ou varchar, alors on lance la fonction &simple avec le champs et son type comme paramètre. Sinon, si le type est date ou time ou float ou int, on lance la fonction &double.

Beaucoup de types possibles ne sont pas inclus ici, notamment tout ce qui est géométrique.

D'autre part, certain type pose des petits problème comme char qui ne peut pas être utilisé avec l'opérateur ~~ (LIKE) et doit donc faire l'objet d'un traitement spécial. J'ai préféré le remplacer par varchar mais ce n'est pas infaisable, juste un peu plus long car cela ne touche pas que criteres.pl mais aussi interrog.pl puisque la construction de la requête change.

print "<INPUT TYPE=hidden NAME=base VALUE=$psqlbase>\n";
print "<INPUT TYPE=hidden NAME=table VALUE=$psqltable>\n";
Ceci permet de mettre des champs de formulaire qui n'apparaisent pas dans le navigateur, leur seul fonction est de passer des paramètres aux scripts suivant (le nom de la base et de la table).
print "<INPUT TYPE=submit VALUE=\"Interroger la base\">\n";
print "<INPUT TYPE=reset VALUE=\"Remettre a zero\">\n";
print "</FORM>";
print "</BODY>";
print "</HTML>";

exit;

5.3 L'intterrogation (interrog.pl)

Voici ce que génère ce script sur mon système : sinterrog.pl.html

#!/usr/bin/perl

use CGI;
use DBI;
$html = new CGI;
DBI est le module qui permet de communiquer avec les bases de données, voir perldoc DBI
# Les variables base et table :
$psqlbase = $html->param('base');
$psqltable = $html->param('table');

print $html->header;

print "<HTML>";
print "<HEAD>\n";
print "<TITLE> Creation de liste dynamique </TITLE>\n";
print "</HEAD>";
print "<BODY>\n";
print "Resultat de la requete<P>";

@parametre = $html->param();

foreach (@parametre) {

   if (m/^check/) {
      s/^check//g;
      $listechamps .= "$_" . ", ";
                  }
m/ˆcheck/ permet de tester la présence de la chaîne check en début (ˆ désignant le début et $ la fin) de la chaîne courante (l'élément en cour de @parametre).

Si c'est vrai, alors on l'enlève et on lui ajoute ", " (virgule + espace). Le . (point) étant l'opérateur de concaténation de chaînes.

   elsif (m/^input/) {
      $temp0 = $html->param($_);
      s/^input//g;
      if ((m/inf$/) and ($temp0)) {
         s/inf$//;
         $criteres0 .= "Fourchette de debut $_ : $temp0 ";
         $temp1 = $html->param("liste" . $_ . "inf");
         $criteres1 .= "and $_ $temp1 '$temp0' ";
                                  }
         elsif ((m/sup$/) and ($temp0)) {
            s/sup$//;
            $criteres0 .= "Fourchette de fin $_ : $temp0 ";
            $temp1 = $html->param("liste" . $_ . "sup");
            $criteres1 .= "and $_ $temp1 '$temp0' ";
                                        }
         elsif ($temp0) {
             $criteres0 .= "Parametre $_ : $temp0 ";
             $criteres1 .= "and $_ ~~ '$temp0' ";
                        }
                     }
                  }
le .= est l'équivalent de $critères1 = $critères1 . "and $_ ~~ '$temp0' ".
$listechamps =~ s/, $//;
$criteres1 =~ s/^and/where/;
$distinct = $html->param('distinct');
le ~= indique que la variable à gauche est l'entrée et la sortie de la commande.
$listechamps =~ s/, $//;

print "Criteres :";
print "$criteres0<P>";

if ($distinct) {
  print "Mode DISTINCT active<P>";
               }
else {
  print "Mode DISTINCT non active<P>";
     }

print "SELECT $distinct $listechamps FROM $psqltable $criteres1";

$heure0 = `date '+%X'`;

$dbh = DBI->connect("dbi:Pg:dbname=$psqlbase", , ) or die print "$DBI::errstr";
On se connecte à la base $psqlbase.

Attention, le deuxième champ est vide (, ,) et c'est donc l'utilisateur sous lequel tourne http qui se connecte. Il doit avoir accès à cette base. Par défaut, cet utilisateur est nobody; Voir /etc/httpd/conf/httpd.conf ou /usr/local/etc/httpd/conf/httpd.conf dans la rubrique User et GRANT.

$cursor0 = $dbh->prepare("SELECT $distinct $listechamps FROM $psqltable $criteres1");
$cursor0->execute or die print "$DBI::errstr";
while ( @row0 = $cursor0->fetchrow ) {
   print "@row0<P>\n";
      }
$cursor0->finish;

$cursor1 = $dbh->prepare("SELECT $distinct count(*) FROM $psqltable $criteres1");
$cursor1->execute or die print "$DBI::errstr";

$nbligne = $cursor1->fetchrow;
$cursor1->finish;

$dbh->disconnect;

$heure1 = `date '+%X'`;

print "Heure de debut : $heure0<P>";
print "Heure de fin   : $heure1";

print "</BODY>";
print "</HTML>";

exit;


Page précédente Page suivante Table des matières