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 1
On 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 1
On 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 = +
)\g
On 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