Université Paris XII-Val de Marne
Ce document est disponible en ligne sur ftp://ftp.ensta.fr/pub/linux/applis.rh50/postgres.html
La distribution CNRS-ENSTA contient un SGBD relationnel orienté objets appelé postgreSQL, version 6.2.1. 1 Ce système est dérivé du SGBD POSTGRES, prototype de recherche conçu sous la direction du Professeur Michael STONEBRAKER à l'université de Berkeley. Dans cet article, nous décrivons
Le serveur postmaster est lancé au boot par le script /etc/rc.d/init.d/postgresql. Les fichiers exécutables pour postgreSQL se trouvent dans /usr/bin et les autres dans le répertoire /var/lib/pgsql.
# su postgres bash$ createuser pierre Enter user's postgres ID or RETURN to use unix user ID: 502 -> Is user "pierre" allowed to create databases (y/n) y Is user "pierre" allowed to add users? (y/n) y createuser: pierre was successfully addedL'utilisateur pierre peut ensuite créer une base avec
bash$ createdb mabaseOn dispose du client psql pour travailler sur les bases auxquelles on a accès.
bash$ psql mabase Welcome to the POSTGRESQL interactive sql monitor: Please read the file COPYRIGHT for copyright terms of POSTGRESQL type \? for help on slash commands type \q to quit type \g or terminate with semicolon to execute query You are currently connected to the database: mabase mabase=>
mabase=> create table journaux ( nom varchar(40), jour date)\g CREATEOn la remplit.
mabase=> insert into journaux values ('Liberation','4/22/98')\g INSERT 17601 1 mabase=> insert into journaux values ('Monde','4/22/98')\g INSERT 17602 1 mabase=> insert into journaux values ('Parisien','4/21/98')\g INSERT 17603 1On peut la consulter.
mabase=> select nom from journaux where jour='4/22/98/'\g nom ---------- Liberation Monde (2 rows)Le langage supporté est un sous ensemble de SQL-92. En plus du modèle relationnel standard, postgreSQL permet de définir des tables à partir d'autres tables par héritage. Il permet aussi de définir des attributs qui ne sont pas atomiques mais des tableaux.
mabase=> create table journ_spec(theme char16[]) inherits (journaux)\g CREATE mabase=> insert into journ_spec values ('Byte','4/1/98','{"informatique","pc"}')\g INSERT 17660 1On peut effectuer des commandes sur l'ensemble des tables dérivées d'une table.
mabase=> select * from journaux* \g nom | jour ----------+---------- Liberation|04-22-1998 Monde |04-22-1998 Parisier |04-21-1998 Byte |04-01-1998 (4 rows)Certaines fonctionnalités comme les sous requêtes ou la clause HAVING dans un un GROUP BY manquent. Toutefois, l'utilisateur a la possibilité d'ajouter au système des fonctions écrites en SQL ou en C.
host all 192.168.25.1 255.255.255.0 ident sameuserEnsuite il faut relancer postmaster sur serveur1. L'utilisateur root doit effectuer les commandes
# /etc/rc.d/init.d/postgresql stop # /etc/rc.d/init.d/postgresql startOn se connecte à partir de machine1 au postmaster de serveur1 lorqu'on est un utilisateur autorisé avec
bash$ psql -h serveur1 mabase
#include <stdio.h> #include <postgres.h> #include <utils/elog.h> #include <utils/palloc.h> typedef struct Complex { double x; double y; } Complex; /* fonctions pour les entrees/sorties et l'addition de deux complexes */ /* postgresql a une fonction specifique palloc() pour gerer la memeire */ Complex * complex_in(char *str){ double x, y; Complex *result; if (sscanf(str, " %lf +i %lf ", &x, &y) != 2) { elog(WARN, "complex_in: error in parsing \"%s\"", str); return NULL; } result = (Complex *)palloc(sizeof(Complex)); result->x = x; result->y = y; return (result); } char *complex_out(Complex *complex){ char *result; if(complex == NULL) return(NULL); result = (char *) palloc(60); sprintf(result, "%lg +i %lg", complex->x, complex->y); return(result); } Complex *complex_add(Complex *a, Complex *b){ Complex *result; result = (Complex *)palloc(sizeof(Complex)); result->x = a->x + b->x; result->y = a->y + b->y; return (result); }Les fichiers de déclaration qui apparaissent au début du programme se trouvent dans le répertoire /usr/include/pgsql. Pour inclure ces fonctions dans postgreSQL, il faut les transformer en objet dynamique. En supposant que les fonctions se trouvent dans le fichier complex.c, la commande compilation est
bash$ cc -shared -o complex.so -I/usr/include/pgsql complex.cOn charge ensuite le contenu du fichier complex.so sous psql à l'aide des commandes SQL qui suivent:
-- chargement des fonctions d'entrees/sorties CREATE FUNCTION complex_in(opaque) RETURNS complex AS '/home/pierre/complex.so' LANGUAGE 'c'\g CREATE FUNCTION complex_out(opaque) RETURNS opaque AS '/home/pierre/complex.so' LANGUAGE 'c'\g -- creation du type complex CREATE TYPE complex ( internallength = 16, input = complex_in, output = complex_out )\g -- creation d'un table contenant des nombres complexes CREATE TABLE test_complex ( a complex, b complex )\g INSERT INTO test_complex VALUES ('4.0+i2.5','2.2+i3.0'); INSERT INTO test_complex VALUES ('3.3+i5.4', '6.+i9.5'); -- chargement de la fonction d'addition CREATE FUNCTION complex_add(complex, complex) RETURNS complex AS '/home/pierre/complex.so' LANGUAGE 'c'\g -- on peut meme definir un operateur + pour les complexes CREATE OPERATOR + ( leftarg = complex, rightarg = complex, procedure = complex_add, commutator = + )\gOn peut stocker ces commandes dans un fichier complex.sql et dire à psql d'interpréter le contenu du fichier avec
mabase=> \i complex.sqlVérifions le contenu de la table test_complex.
mabase=> SELECT * FROM test_complex; a |b ----------+-------- 4 +i 2.5 |2.2 +i 3 3.3 +i 5.4|6 +i 9.5 (2 rows)Utilisons la fonction complex_add() et l'opérateur + pour les nombres complexes.
mabase=> SELECT complex_add(a,b) as c FROM test_complex; c ----------- 6.2 +i 5.5 9.3 +i 14.9 (2 rows) mabase=> SELECT (a + b + a) AS d FROM test_complex; d ------------ 10.2 +i 8 12.6 +i 20.3 (2 rows)
#include <stdio.h> #include <libpq-fe.h> main() { char *pghost, *pgport, *pgoptions, *pgtty; char* dbName; int i,j, nbField,nbTuple; PGconn* conn; PGresult* res; pghost=NULL; pgport=NULL; pgoptions=NULL; pgtty=NULL; dbName="mabase"; /* etablissement de la connexion a la base de donnees */ conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); if(PQstatus(conn) == CONNECTION_BAD) { fprintf(stderr,"Connection to database '%s' failed.\n", dbName); fprintf(stderr,"%s",PQerrorMessage(conn)); PQfinish(conn); exit(1); } /* debut d'une transaction */ res = PQexec(conn,"BEGIN"); PQclear(res); /* Consultation d'une table de la base */ res = PQexec(conn,"select * from test_complex"); if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr,"command didn't return tuples properly\n"); if (res) PQclear(res); PQfinish(conn); exit(1); } nbField=PQnfields(res); nbTuple=PQntuples(res); for(i=0;i<nbField;i++){ printf("%s\n------------\n",PQfname(res,i)); for(j=0;j<nbTuple;j++){ printf("%s\n",PQgetvalue(res,j,i)); } } /* fin de la transaction */ res = PQexec(conn, "END"); PQclear(res); /* fermeture de la connection */ PQfinish(conn); }Supposons que le programme ci-dessus se trouve dans le fichier acces.c. A la compilation, il doit être lié avec la libraire /usr/lib/libpq.so. La commande correspondante est
bash$ cc -o acces -I/usr/include/pgsql -lpq acces.cL'exécution donne
bash$ acces a ------------ 4 +i 2.5 3.3 +i 5.4 b ------------ 2.2 +i 3 6 +i 9.5
Voici un exemple de programme Tcl/TK qui ouvre une fenêtre dans laquelle un bouton lance la commande select *from test_complex; et dont le résultat de la requête s'affiche dans la fenêtre.
#!/usr/bin/pgtksh frame .top -borderwidth 10 pack .top -side top -fill x button .top.quit -text Quit -command exit button .top.stc -text "select * from test_complex" -command Sel pack .top.stc .top.quit -side left frame .t set log [text .t.log -width 40 -height 10 \ -borderwidth 2 -relief raised -setgrid true ] pack .t.log -side left -fill both -expand true pack .t -side top -fill both -expand true proc Sel {} { global input log set conn [pg_connect mabase] set res [pg_exec $conn "select * from test_complex"] set ntups [pg_result $res -numTuples] for {set i 0} {$i < $ntups} {incr i} { set val [pg_result $res -getTuple $i] $log insert end $val\n } $log insert end "-----------------------\n" pg_disconnect $conn }Supposons que le programme se trouve dans le fichier mabase.tcl. Pour l'exécuter, on peut le rendre exétable et le lancer directement.
bash$ chmod +x mabase.tcl bash$ mabase.tclOn peut aussi invoquer l'interpréteur pgtksh.
bash$ pgtksh mabase.tclDans les deux cas, une fenêtre s'ouvre et lorsqu'on clique sur le bouton select * from test_complex, on voit
bash$ tar xvfz postgresql-6.2.1.tar.gz bash$ cd postgresql-6.2.1/src bash$ ./configureVous pouvez donner les réponses par défaut aux questions posées par ce programme.
bash$ cd interfaces/jdbc bash$ makeCette commande doit avoir créé le fichier postgresql.jar.
Pour terminer l'installation, l'utilisateur root peut recopier le fichier postgresql.jar et le répertoire postgresql dans le répertoire /usr/lib/jdk1.1.5/classes avec
# mkdir /usr/lib/jdk-1.1.5/classes # cp postgresql.jar /usr/lib/jdk-1.1.5/classes # cp -r postgresql /usr/lib/jdk-1.1.5/classesPour tester l'installation, on peut éditer le fichier JDBC_Test.java et remplacer à la ligne 31 la chaîne de caractères "adrian" par un nom d'utilisateur habilité à utiliser mabase, soit "pierre" dans notre session. On compile JDBC_Test.java avec
bash$ export JAVA_HOME=/usr/lib/jdk-1.1.5 bash$ javac JDBC_Test.javaL'exécution du programme donne
bash$ java JDBC_Test jdbc:postgresql://localhost/mabase Connecting to Database URL = jdbc:postgresql://localhost/mabase Connected...Now creating a statement Ok...now we will create a table Now we will insert some columns Inserted some data Now lets try a select Back from the select...the following are results row 0 1 1 row 1 2 1 row 2 3 1 Ok...dropping the table Now closing the connectionL'inconvénient de ce programme est qu'il ne laisse pas de trace dans la base de données. Voici une adaptation de ce programme qui ajoute une entrée dans la table test_complex.
import java.io.*; import java.lang.*; import java.sql.*; class piebase { public piebase(){} public static void main(String argv[]) { String url = new String(argv[0]); Connection db; Statement s; ResultSet rs; // Chargement du driver try { Class.forName("postgresql.Driver"); } catch (ClassNotFoundException e) { System.err.println("Exception: " + e.toString()); } try { System.out.println("Connexion a l'URL = " + url); db = DriverManager.getConnection(url, "pierre", ""); System.out.println("Connecte..."); s = db.createStatement(); System.out.println("Ajout de (1.+i3., -5.+i2.) dans test_complex"); s.executeUpdate("insert into test_complex values ('1.+i3.','-5.+i2.')"); System.out.println("Consultation de test_complex"); rs = s.executeQuery("select * from test_complex"); int i = 0; while (rs.next()) { String a = rs.getString("a"); String b = rs.getString("b"); System.out.println("row " + i + " " + a + " " + b); i++; } System.out.println("Fermeture de la connexion"); s.close(); db.close(); } catch (SQLException e) { System.out.println("Exception: " + e.toString()); } } }Pour le tester, il faut que le programme ci-dessus se trouve dans un fichier appelé piebase.java. Ce fichier peut se trouver n'importe où si JDK les postgresql.jar et postgreql se trouvent dans /usr/lib/jdk-1.1.5/classes. Sinon, il faut créer ce fichier dans postgresql-6.2.1/src/interfaces/jdbc. On lance ensuite
bash$ javac piebase.java bash$ java piebase jdbc:postgresql://localhost/mabase Connexion a l'URL = jdbc:postgresql://localhost/mabase Connecte... Ajout de (1.+i3., -5.+i2.) dans test_complex Consultation de test_complex row 0 4 +i 2.5 2.2 +i 3 row 1 3.3 +i 5.4 6 +i 9.5 row 2 1 +i 3 -5 +i 2 Fermeture de la connexionOn peut alors vérifier l'état de la table test_complex sous psql.
Plusieurs pages de manuel sont accessibles avec la commande man. Essayez par exemple man postmaster, man pgintro, man psql, ...
1 Le système postgreSQL fait partie de la Redhat 5.0.
2 Signalons que la chaîne de caractères \g qui indique la fin d'une commande est plus robuste que le point virgule qui n'est pas toujours interprété par psql.
3 Ce programme se contente de récupérer les champs de chaque tuple sous forme de chaîne de caractères, mais il est aussi tout à fait possible de les obtenir directement en binaire en définissant un BINARY CURSOR. Des exemples de tels programmes sont présentés dans le manuel de postgreSQL.
bash$ tar xvfz postgresql-6.2.1.tar.gz bash$ cd postgresql-6.2.1/src bash$ ./configureVous pouvez donner les réponses par défaut aux questions posées par ce programme.
bash$ cd interfaces/libpgtcl bash$ makeCette commande a pour résultat la création de la librairie libpgtcl.so.1. L'utilisateur root doit ensuite effectuer
# cp libpgtcl.so.1 /usr/lib # ldconfig # ln -s /usr/lib/libpgtcl.so.1 /usr/lib/libpgtcl.soPuis il faut construire les interpréteurs pgtclsh et pgtksh. L'utilisateur qui conduit l'installation doit se placer dans postgresql-6.2.1/src/, éditer le fichier Makefile.global et remplacer respectivement les chaînes de caractères tcl7.5 par tcl8.0, tk4.1 par tk8.0 et X11_LIBDIR= /usr/lib par X11_LIBDIR= /usr/X386/lib. Ensuite il faut faire
bash$ cd bin/pgtclsh bash$ makeLes deux interpréteurs doivent alors se trouver dans dans le répertoire courant postgresql-6.2.1/src/bin/pgtclsh. L'utilisateur root peut les recopier dans le répertoire /usr/bin avec
# cp pgtclsh /usr/bin # cp pgtksh /usr/bin