[precedente] T'N'T - Copertina - Forse Linux non finisce qui - 2 [successivo]

Articolo


Database interfacciati al Web:
un tutorial sull'uso di Postgres su Linux

Questo articolo è stato realizzato con la collaborazione di diverse persone, la stesura della bozza è opera di Guido Bozzetto mentre la correzione dei testi e suggerimenti sono di Marco Marongiu e Patrizio Righini.
Per la presentazione degli esempi ho preso come piattaforma Linux la distribuzione Debian 2.0 e tutte le procedure a questa si riferiscono.
L'articolo è stato diviso nei seguenti capitoli:

Introduzione

Un computer è veramente utile solo quando si riesce a disporre di tutti gli strumenti necessari per eseguire i lavori per cui si è acquisito. Nel numero di Luglio della rivista era stato mostrato come le tipiche attività di office automation si possano tranquillamente svolgere anche su Linux Debian 2.0 con StarOffice. Con questo articolo vorrei presentarvi come anche le tipiche attività svolte dai server aziendali, quali contabilità e gestione magazzino, possano venire svolte da una macchina Linux; queste sono in genere applicazioni scritte ad hoc direttamente per il cliente finale a differenza dei programmi per l'automazione d'ufficio che non hanno nulla di specifico rivolto allo specifico utente finale. Per la realizzazione di questa classe di programmi si utilizza solitamente un motore di database (Data Base Management System, DBMS) su cui si costruiscono poi le proprie applicazioni.
La realizzazione di una "applicazione" consiste nell'interfacciamento del DBMS prescelto verso l'utente finale. Il lavoro per lo più consiste nel creare delle maschere per l'inserimento dei dati e per l'estrazione degli stessi dal "database" [1]. La progettazione della struttura del database è la parte più delicata di tutta l'operazione perché poi influenza tutto il lavoro successivo ed una eventuale modifica della struttura può implicare anche la riscrittura di tutto il codice.
Il progettista dell'applicazione lavora ad alto livello utilizzando un linguaggio non procedurale, che permette l'astrazione rispetto ai dettagli implementativi riguardanti l'archiviazione e l'estrazione fisica dei dati dai supporti di massa o in rete o quant'altro venga utilizzato per la memorizzazione. In altre parole, la realizzazione dell'applicazione non richiede nessuna conoscenza su come si apra un file, su cosa sia un descrittore di file o come sia fatto il file system su cui risiede il database, a differenza di quanto avverrebbe se si realizzasse il tutto con un linguaggio procedurale come il C.

Nell'ambito dei DBMS linux è molto ricco e dispone di diverse possibilità dai semplicissimi database unix: dbm, hash, ecc., che sono utilizzabili dai programmi di sistema (si pensi ad esempio al sendmail), fino a database commerciali molto robusti quali Oracle[2].
Il presente articolo vuole soffermarsi sul database PostgreSQL: un potente database ad oggetti con il notevole vantaggio di essere completamente freeware e disponibile come sorgente quindi allineato con la politica della distribuzione Debian di Linux. Nella distribuzione Debian 2.0 (Hamm) esistono molti altri database tra cui miniSQL e MySQL ma quest'ultimi non sono freeware. MySQL è un database molto potente paragonabile a Postgres. Su miniSQL trovate un articolo sul numero di Luglio di P.J..

Questo articolo non presenta il solo database PostgreSQL ma anche un esempio di come realizzare una applicazione che utilizza il Web come interfaccia per il database.

[1] Qui per database si intende la struttura dati (databasespace) che si è costruita per la memorizzazione delle informazioni e non il motore stesso.

[2] In realtà Oracle non è direttamente disponibile per Linux ma lo è per SCO. Utilizzando la compatibilità binaria dello unix su architettura Intel 386 è possibile utilizzare la versione di Oracle per SCO in Linux. Sono recenti comunque gli annunci di Oracle e di Informix del porting dei rispettivi DBMS per Linux. Le ultime notizie danno Oracle 8i per Linux disponibile per il mercato italiano a Gennaio del 1999.

Cos'e' PostgreSQL

PostgreSQL è un DBMS Relazionale ad Oggetti con una sintassi d'interrogazione molto vicina agli standard ANSI SQL 1998,92,89. La versione attuale di PostgreSQL è la 6.3. È disponibile per diverse piattaforme e sistemi operativi. Attualmente il principale scopo degli sviluppatori del sistema è quello di renderlo al 100% rispondente allo standard ANSI/ISO SQL e cosi' farlo diventare il numero UNO dei database generici nel mondo.

Da una versione precedente di PostgreSQL è derivato Illustra che ha sua volta è stato integrato nell'Informix Universal server rilasciato nel 1997. Per chi volesse disporre di un PostgreSQL completo, bug-free, ben documentato e supportato, e cioè volesse una versione commerciale è disponibile sul mercato il database di Informix.

PostgreSQL è l'evoluzione del DBMS POSTGRES, un prototipo di ricerca per un database di prossima generazione. PostgreSQL utilizza la potenza della rappresentazione dei dati (data model) e la vastità dei tipi di dati disponibili con POSTGRES, ma rispetto al suo predecessore ha un diverso linguaggio d'interrogazione (query language). Il vecchio PostQuel è stato esteso con un sottoinsieme dell'SQL (l'ANSI SQL non risulta ancora completamente implementato), esteso con nuove direttive per poter supportare le caratteristiche non presenti nei DBMS tradizionali, quali la gestione degli oggetti.
Il lavoro originario è iniziato presso l'università californiana di Berkeley nel lontano 1985. Ora il sistema è sviluppato grazie al libero contributo di utenti Internet sotto il controllo di un team responsabile dello sviluppo corrente e futuro. Il nome originale del software sviluppato a Berkeley era Postgres. Quando nel 1995 furono aggiunte le funzionalità SQL il nome cambio' in Postgres95 (presente nella vecchia distribuzione BO di Debian). Il nome del DBMS divenne PostgreSQL alla fine del 1996, la numerazione delle versioni comincio' dalla 6.0. PostgreSQL non è un semplice database relazionale ma è un database relazionale a oggetti; questa classe di programmi viene comunemente indicata con l'acronimo ORDBMS (Object Relational Database Management System).

Installazione

I pacchetti della distribuzione Debian da installare sono:

postgresql, posgresql-doc, apache, apache-doc, php3, php3-doc, php3-pgsql, www-pgsql.

Questi pacchetti ci permettono tre tipi d'interazione col server database che possiamo riassumere con i seguenti diagrammi che dovrebbero rendere più chiaro lo sviluppo dell'articolo:

a. Server database                 client
     postgresql        <-->      postgresql
  (postgresql-doc)            (postgresql-doc)

b. Server database               server web                  client
     postgresql        <-->      www-pgsql       <-->       <browser>
  (postgresql-doc)                 apache               {lynx, netscape}
                                (apache-doc)            {arena, o altro}

c. Server database               server web                  client
     postgresql        <-->         php3         <-->       <browser>
  (postgresql-doc)               (php3-doc)             {lynx, netscape}
                                   apache               {arena, o altro}
                                (apache-doc)

Ovviamente per limitare il lavoro d'installazione e configurazione dei programmi è conveniente utilizzare direttamente i pacchetti che la distribuzione rende disponibili. L'uso dei pacchetti preparati comporta qualche inconveniente soprattutto per chi è abituato ad installarsi i pacchetti come e dove vuole (all'interno del file system della macchina) ma è decisamente comodo trovarseli già compilati, configurati e funzionanti a meno di piccoli ritocchi. Trovo molto utile la configurazione dei pacchetti che avviene durante l'installazione del singolo pacchetto Debian. Solitamente la fase di configurazione dei programmi prevede una certa interazione con l'utente permettendogli d'installare il programma nella maniera più consona alle proprie esigenze, senza per questo richiedere delle conoscenze specifiche sul programma che stà installando. Se è importante essere sicuri di avere le ultime versioni dei pacchetti conviene prelevarli dalla distribuzione "unstable" ed installarli manualmente o col vecchio dpkg oppure col nuovo gestore dei pacchetti (disponibile nella versione SLINK di Debian apt). Nel prosieguo dell'articolo faro' riferimento ad un'installazione della Debian HAMM considerando installati i pacchetti della versione stabile (nel momento della stesura dell'articolo).
Una precisazione forse non scontata: la macchina su cui si vuole utilizzare il sistema non deve obbligatoriamente essere in rete: il client (browser) e il server (apache e postgresql) possono risiedere sulla stessa macchina, quest'ultimo è il mio caso: ho realizzato tutto a casa mia, su un PC 486!".

postgresql


Per permettere l'uso di PostgreSQL tramite la rete bisogna attivare l'accesso TCP/IP. Quest'operazione avviene editando il file di configurazione di PostgreSQL /etc/postgresql/postmaster.init inserendovi la seguente riga:

PGALLOWTCPIP=yes

e lasciando invariate tutte le altre opzioni. Per verificare che funzioni è sufficiente riavviare PostgreSQL e provare la connessione al server del database, cioè il programma che rimane in attesa delle richieste (detto backend).


# /etc/init.d/postgresql restart
# su - postgres
$ psql -h localhost
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: template1

template1=>

A questo punto si può iniziare a "giocare" col database :-)). Si ponga attenzione che il superutente di postgres non è root che non ha nemmeno i permessi di accedere ad alcun database, ma postgres che è l'utente con cui viene eseguito il backend; attenzione quindi che in questo ambiente l'utente root non ha particolari privilegi, il vantaggio del superutente è quello di poter diventare l'utente postgres senza conoscerne la password come si è visto dall'esempio.
I diritti d'accesso dei singoli utenti e delle singole macchine vengono definiti nel file pg_hba.conf che nel pacchetto debian è in /etc/postgresql. Riporto le righe significative tratte dal mio file di configurazione:


local        all                                         trust
host         all         127.0.0.1     255.255.255.255   trust

Queste righe dicono che l'autenticazione degli utenti avviene sulla macchina client "trust", che tutti "all" i database sono accessibili dagli utenti della macchina server "local" e che gli host di rete che hanno il permesso d'accesso sono: se stesso "127.0.0.1/255.255.255.255". Per altre descrizioni si veda direttamente il file in questione che è ampiamente commentato.
Installati i pacchetti relativi al DBMS la documentazione oltre che nel manuale in linea si può trovare in:
/usr/doc/postgresql-doc
http://localhost/doc/postgresql-doc/

Apache


Per la configurazione di Apache non ci sono grosse cose da fare se non assicurarsi che i programmi che accedono al database (php3-pgsql e www-pgsql) funzionino correttamente quando richiamati dalle pagine web.
Per la configurazione grossolana del pacchetto si usa il programma apacheconfig che permette di modificare e rivedere la configurazione del server web. Per i nostri scopi possiamo verificare direttamente i file di configurazione che si trovano nella directory /etc/apache in particolare i file d'interesse sono httpd.conf e srm.conf.
Nel file httpd.conf le righe d'interesse particolare sono:


User www-datad
Group www-data

che istruiscono il server web ad essere eseguito come utente www-data e gruppo www-data. Questa soluzione differisce alquanto dalla soluzione adottata da altre distribuzioni che utilizzano solitamente l'utente nobody. Per i nostri scopi questo ha poca importanza, mentre è molto importante sapere esattamente quale sia l'utente. Il fatto che il gruppo sia www-data e non indefinito risulta invece molto comodo per non permettere la lettura delle pagine con i sorgenti dei nostri programmi a tutti gli utenti della macchina.


LoadModule action_module /usr/lib/apache/1.3/mod_actions.so
LoadModule php3_module /usr/lib/apache/1.3/libphp3.so

La prima riga è indispensabile se si utilizza una vecchia versione di apache che non supporta PHP3. PHP3 richiede l'ultima versione [2] di apache per essere eseguito come modulo mentre richiede l'attivazione del modulo action se si vuole utilizzarlo come cgi-bin. PHP3 è stato compilato con l'attivazione del controllo sulla redirezione per cui solo se il cgi-bin php3 (/usr/lib/cgi-bin/php3) viene chiamato direttamente dal server web viene eseguito mentre se viene chiamato direttamente inserendolo nell'URL invia una segnalazione d'errore, si veda il file di configurazione srm.conf.
Questa caratteristica diviene utile anche con www-pgsql in quanto permette la chiamata automatica del cgi-bin semplicemente modificando l'estensione del file da .html o .htm a qualcos'altro.
Andiamo ora a vedere le modifiche apportate al file srm.conf:


AddHandler php3-script   .pcgi
AddHandler www-pgsql     .pgsql

Action php3-script /cgi-bin/php3
Action www-pgsql   /cgi-bin/www-pgsql

La prima riga istruisce il server apache ad usare l'handler (manipolatore) php3-script quando trova un file con estensione .pcgi (si può usare qualsiasi altra estensione di proprio gradimento). La seconda riga allo stesso modo per l'handler www-pgsql sui file con estensione .pgsql.
La terza e la quarta riga utilizzano il modulo action che abbiamo precedentemente attivato: il gestore php3-script è il programma /cgi-bin/php3 mentre l'handler www-pgsql è il programma /cgi-bin/www-pgsql.
Per avere altre informazioni sulla configurazione di apache si può vedere la documentazione che viene installata con i pacchetti stessi ed in particolare si può puntare il proprio browser all'URL:
http://localhost/doc/apache/manual/index.html

php3


Per l'utilizzo di questo programma con PostgreSQL bisogna installare anche il pacchetto php3-pgsql che contiene il modulo php3 per accedere al DBMS. Installati i 2 pacchetti l'accesso al database non funziona fino a quando non si attiva l'uso di tale modulo questo avviene mettendo mano al file /etc/php3.ini.


;;;;;;;;;;;;;;;;;;;;;;
; Dynamic Extensions ;
;;;;;;;;;;;;;;;;;;;;;;
; if you wish to have an extension loaded automaticly, use the
; following syntax:  extension=modulename.extension
; for example, on windows,
; extension=msql.dll
; or under UNIX,
; extension=msql.so

; Uso di php3-pgsql
extension=pgsql.so
; Uso di php3-gd
extension=gd.so
; Uso di calendar
extension=calendar.so

Per completezza ho riportato anche le altre modifiche che ho apportato al file per l'attivazione di altri moduli php3 che ho installato sul mio sistema.
Anche con questi pacchetti viene installata della documentazione nella directory /usr/doc che è visibile da web all'URL:
http://localhost/doc/php3/html/manual.html

www-pgsql


Questo è l'ultimo pacchetto che ho installato ed è stato l'ultima risorsa quando non riuscivo a far funzionare php3. Questo pacchetto non richiede alcuna configurazione specifica. La manualistica relativa è accessibile da web all'indirizzo:
http://localhost/doc/php3/html/manual.html

[3] Utilizzando la versione SSL di apache (apache-ssl) è molto probabile che la versione installata non sia l'ultima disponibile mentre php3 viene compilato per la versione più recente del pacchetto apache rendendolo incompatibile con versioni meno recenti del server web (questo almeeno mi è capitato con la distribuzione Debian 2.0 di Linux).

Costruzione del database

A questo punto abbiamo installato e configurato tutto l'ambiente per poter iniziare il nostro esperimento sull'utilizzo di un database da web. Il primo passo è quello di realizzare il database vero e proprio.
La prima operazione è quella di creare un'utente che possa manipolare il database, supponiamo che tale utente sia gb (sostituitelo con lo username del gestore del database) e che esista già sul sistema (altrimenti si può inserire con un adduser). Il comando createuser attiva un nuovo utente per postgresql con i diritti desiderati:


# su - postgres
$ createuser
Enter name of user to add ---> gb
Enter user's postgres ID -> 1001
Is user "gb" allowed to create databases (y/n) y
Is user "gb" allowed to add users? (y/n) y
createuser: gb was successfully added

È consigliabile utilizzare come ID di postgresql lo stesso ID della macchina per semplificare la manutenzione del sistema. A questo punto l'utente gb può fare qualsiasi cosa sul database come se fosse postgres stesso. Aggiungiamo ora la possibilità d'accedere al server web. Aggiungiamo l'utente www_data senza alcun permesso speciale.

gb@ravel:~$ createuser
Enter name of user to add ---> www_data
Enter user's postgres ID -> 33
Is user "www_data" allowed to create databases (y/n) y
Is user "www_data" allowed to add users? (y/n) y
createuser: www_data was successfully added

Attenzione che l'utente di postgresql "www_data" differisce dall'utente di sistema "www-data" in quanto il carattere "-" per SQL (e quindi per PostgreSQL [4]) è un carattere speciale non utilizzabile nei nomi degli utenti o delle tabelle. Visto che il nome dell'utente di sistema è diverso da quello di postgres si poteva dare un nome qualsiasi anche quello più standard di "nobody". Ora possiamo creare il nostro database:


gb@ravel:~$ createdb drogheria
ravel:~$ psql drogheria
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: drogheria

drogheria=>

I comandi SQL possono venir inseriti direttamente al prompt, il formato è libero ed i comandi vengono eseguiti ogni volta l'interprete incontra il carattere di terminazione dell'istruzione ";".
Il database drogheria vorrebbe essere un semplicissimo esempio di come realizzare un sistema per raccogliere ordini tramite il web. I comandi seguenti costruiscono la struttura dell'archivio:


drogheria=> CREATE TABLE lista (
drogheria->   articolo      TEXT,
drogheria->   codvenditore  INT,
drogheria->   quantita      INT );
CREATE
drogheria=> CREATE TABLE venditori (
drogheria->   codvenditore  INT,
drogheria->   nomevenditore TEXT );
CREATE
drogheria=>

Il database è costituito da una lista di articoli lista e da un'elenco di venditori venditori. La tabella lista ha una chiave esterna (foreign key) codvenditore che si riferisce alla chiave primaria (primary key) codvenditore della tabella venditori. Il tipo di tali campi dovrà essere lo stesso INT per rendere possibili i confronti tra i due campi. Il nome di tali campi può essere qualsiasi e avere nomi completamente indipendenti. Una scelta alternativa poteva essere chiamare tale campo venditore nella tabella lista e codice nella tabella venditori in quanto il riferimento al campo va' comunque fatto specificando la tabella di appartenenza. Potrebbero essere ugualmente espressivi i due confronti:


lista.codvenditore = venditori.codvenditore
e
lista.venditore = venditori.codice

è solo una questione di gusto personale. Per vedere lo stato delle tabelle si possono dare i comandi:


drogheria=> \d lista

Table    = lista
+----------------------------------+----------------------------------+-------+
|              Field               |              Type                | Length|
+----------------------------------+----------------------------------+-------+
| articolo                         | text                             |   var |
| codvenditore                     | int4                             |     4 |
| quantita                         | int4                             |     4 |
+----------------------------------+----------------------------------+-------+
drogheria=> \d venditori

Table    = venditori
+----------------------------------+----------------------------------+-------+
|              Field               |              Type                | Length|
+----------------------------------+----------------------------------+-------+
| codvenditore                     | int4                             |     4 |
| nomevenditore                    | text                             |   var |
+----------------------------------+----------------------------------+-------+
drogheria=>

Il comando SQL INSERT a questo punto ci permette di inserire dei dati nell'archivio appena creato:


drogheria=> INSERT INTO venditori VALUES (100, 'Super Negoziante');
drogheria=> INSERT INTO venditori VALUES (101, 'Grande Magazzino');
drogheria=> INSERT INTO venditori VALUES (102, 'Ricambi Auto');
drogheria=> INSERT INTO lista VALUES ('Birra', 100, 3);
drogheria=> INSERT INTO lista VALUES ('Gelato', 100, 1);
drogheria=> INSERT INTO lista VALUES ('Tovaglioli', 101, 50);
drogheria=> INSERT INTO lista VALUES ('Vino', 101, 2);
drogheria=>

Il comando SQL SELECT ci permette di esaminare i dati presenti nel database:


drogheria=> SELECT codvenditore, nomevenditore FROM venditori;
codvenditore|nomevenditore   
------------+----------------
         100|Super Negoziante
         101|Grande Magazzino
         102|Ricambi Auto    
(3 rows)

drogheria=>

I campi da visualizzare possono essere modificati sia nell'ordine che nella quantità, es.:


drogheria=> SELECT nomevenditore FROM venditori;
nomevenditore   
----------------
Super Negoziante
Ricambi Auto    
Grande Magazzino
(3 rows)

drogheria=> SELECT quantita, articolo FROM lista;
quantita|articolo          
--------+------------------
       3|Birra             
       1|Gelato            
      50|Tovaglioli        
       2|Vino              
(4 rows)

drogheria=> SELECT * FROM lista;
articolo          |codvenditore|quantita
------------------+------------+--------
Birra             |         100|       3
Gelato            |         100|       1
Tovaglioli        |         101|      50
Vino              |         101|       2
(4 rows)

drogheria=>

Il valore speciale "*" intende tutti i campi. Un'interrogazione come quella dell'ultimo esempio è poco utile, se noi volessimo veramente conoscere lo stato degli ordini vorremmo conoscere il nome del venditore e non il suo codice. Questo si può ottenere instaurando una relazione tra le 2 tabelle mettendo in corrispondenza i codvenditore:


drogheria=> SELECT lista.articolo, venditori.nomevenditore, lista.quantita
drogheria->        FROM lista, venditori
drogheria->        WHERE lista.codvenditore = venditori.codvenditore;
articolo          |nomevenditore   |quantita
------------------+----------------+--------
Birra             |Super Negoziante|       3
Gelato            |Super Negoziante|       1
Tovaglioli        |Grande Magazzino|      50
Vino              |Grande Magazzino|       2
(4 rows)

drogheria=>

La clausola FROM specifica quali sono le tabelle da usare. La clausola WHERE specifica le condizioni della relazione. Questa relazione si chiama join in quanto permette di collegare, congiungere, tra loro diverse tabelle. Ci sono molte altre clausole utilizzabili con l'istruzione select molto utile risulta ORDER BY che permette l'ordinamento della tabella risultante:


drogheria=>  SELECT lista.articolo, venditori.nomevenditore, lista.quantita
drogheria->         FROM lista, venditori
drogheria->         WHERE lista.codvenditore = venditori.codvenditore
drogheria->         ORDER BY quantita;
articolo          |nomevenditore   |quantita
------------------+----------------+--------
Gelato            |Super Negoziante|       1
Vino              |Grande Magazzino|       2
Birra             |Super Negoziante|       3
Tovaglioli        |Grande Magazzino|      50
(4 rows)

Si può osservare che "Ricambi Auto" non compare anche se è stato inserito, è sufficiente inserire:

drogheria=> INSERT INTO lista VALUES ('Candele automobile', 102, 4);

e rilanciare il select per vedere riapparire il venditore Ricambi Auto.

Oltre ad INSERT e SELECT ci sono DELETE che permette di eliminare righe da una tabella e UPDATE che permette la modifica di una tabella:


drogheria=> DELETE  FROM lista WHERE articolo = 'Candele automobile';
drogheria=> UPDATE venditori SET nomevenditore = 'Autofficina' WHERE codvenditore = 102;

Il primo comando elimina dalla tabella lista la riga il cui campo articolo (prima colonna della tabella) contiene "Candele automobile" e cioè la riga contenente i dati:
'Candele automobile', 102, 4
la seconda istruzione invece modifica una riga ed in particolare modifica il campo nomevenditore in 'Autofficina' lasciando invariato il campo codvenditore per cui ora il 'Ricambi Auto' è diventato un'Autofficina. Per ripristinare il nome originale si potrebbe usare un comando come:

drogheria=> UPDATE venditori SET nomevenditore = 'Ricambi Auto' WHERE nomevenditore = 'Autofficina' ;

Per velocizzare le operazioni di confronto e collegamento tra tabelle si possono definire degli indici che sono delle strutture di supporto create e mantenute dal DBMS per permettere operazioni più veloci nei confronti tra campi, anche se hanno l'inconveniente di occupare spazio supplementare rispetto ai soli dati. Gli indici si possono definire su qualsiasi campo o su gruppi di campi e possono essere definiti come univoci utilizzando la direttiva unique. Questa è una funzionalità molto comoda da utilizzarsi per i campi che sono chiave primaria; la definizione di un indice come univoco, associato ad un campo di una tabella, assicura l'univocità dei valori presenti in tale colonna della tabella: c'è un controllo automatico all'atto dell'inserimento dei dati che impedisce l'inserimento di dati ripetuti su più righe. Per esempio sulla tabella venditori che abbiamo precedentemente creato supponiamo di aver definito un indice univoco sul campo codvenditore allora non potremmo inserire una riga del tipo:
(100, 'Fioraio da Marco')
in quanto il codvenditore 100 esiste già.
Definiamo gli indici per la nostra applicazione:


drogheria=> CREATE INDEX etichetta ON lista (codvenditore);
drogheria=> CREATE UNIQUE INDEX etichettavenditore ON venditori (codvenditore);

Una volta creato un indice non è semplice risalire alla sua struttura:


drogheria=> \d etichetta

Table    = etichetta
+----------------------------------+----------------------------------+-------+
|              Field               |              Type                | Length|
+----------------------------------+----------------------------------+-------+
| codvenditore                     | int4                             |     4 |
+----------------------------------+----------------------------------+-------+

Per ovviare a questo uso la convenzione di chiamare gli indici col nome della tabella seguito dai campi sui quali avviene l'indicizzazione per cui possiamo riscrivere i nostri indici come:


drogheria=> DROP INDEX etichetta;
drogheria=> DROP INDEX etichettavenditore;
drogheria=> CREATE INDEX lista_codvenditore ON lista (codvenditore);
drogheria=> CREATE UNIQUE INDEX venditori_codvenditore ON venditori (codvenditore);

Le prime 2 linee provvedono all'eliminazione dei vecchi indici. La gestione degli indici è completamente a carico del DBMS che provvede alla loro creazione in seguito al comando CREATE INDEX e all'aggiornamento degli stessi ogni qualvolta c'è una modifica nella tabella associata, anche l'uso degli stessi è del tutto trasparente per l'utente.
PostgreSQL conserva delle copie delle righe modificate o cancellate, per eliminare tali dati inutili si usa il comando vacuum:


drogheria=> VACUUM lista;
drogheria=> VACUUM venditori;

Questi comandi non sono indispensabili in quanto questa installazione di PostgreSQL prevede automaticamente il vacuum giornaliero (cfr.: /etc/cron.d/postgresql).

Nella prossima sezione affronteremo l'accesso del server web al nostro database drogheria; si rende quindi necessario vedere i comandi che regolano l'accesso alle risorse del database da parte degli utenti. Tali comandi sono REVOKE e GRANT.
Vogliamo che i nostri utenti che accedono da web vengano riconosciuti da PostgreSQL come www_data e a questo utente diamo la possibilità di vedere i contenuti delle tabelle lista e venditori:


drogheria=> REVOKE ALL ON lista FROM www_data;
CHANGE
drogheria=> GRANT SELECT,INSERT ON lista TO www_data;
CHANGE
drogheria=> REVOKE ALL ON venditori FROM www_data;
CHANGE
drogheria=> GRANT SELECT ON venditori TO www_data;
CHANGE

I comandi REVOKE ALL tolgono qualsiasi permesso d'accesso alle tabelle lista e venditori all'utente www_data. L'uso del comando REVOKE prima del GRANT assicura che qualsiasi permesso precedentemente impostato non rimanga assegnato ad un utente involontariamente. La seconda riga di comando assegna il diritto d'esecuzione dei comandi SELECT e INSERT sulla tabella lista permettendo all'utente www_data l'interrogazione di tale tabella e l'inserimento di dati in essa. La quarta riga concede il diritto ad accedere esclusivamente in lettura per la tabella venditori all'utente www_data.
Per avere ulteriori informazioni sui permessi che si possono assegnare e alla sintassi completa del comando si scriva:


drogheria=> \h grant
Command: grant
Description: grant access control from a user or group
Syntax:
grant <privilege[,privilege,...]> on <rel1>[,...<reln>] from 
[public | group <group> | <username>]
         privilege is {ALL | SELECT | INSERT | UPDATE | DELETE | RULE}

drogheria=>

Quando abbiamo finito con l'interprete SQL possiamo uscire digitando:

drogheria=> \q
ravel:~$

Il comando pg_dump è un programma molto utile che permette di creare facilmente dei backup dei database e il trasferimento dei dati ad un altro DBMS SQL. Il programma genera un'elenco completo dei comandi SQL necessari per ricreare il database. Per scaricare su un file di testo il nostro database dovremo usare qualcosa del tipo:

ravel:~$ pg_dump drogheria > drogheria.sql

Il file drogheria.sql può venire manualmente editato con qualsiasi editor per file di testo. Ecco lo stato attuale del mio database drogheria dopo aver fatto un po' di prove, non spaventatevi se non è proprio quello che vi aspettavate, confrontate la descrizione alla fine del paragrafo:


\connect - gb
CREATE TABLE lista (articolo text, codvenditore int4, quantita int4);
\connect - gb
CREATE TABLE venditori (codvenditore int4, nomevenditore text);
\connect - gb
CREATE TABLE pga_queries (queryname varchar(64), querytype char(1), querycommand
 text);
\connect - gb
CREATE TABLE pga_forms (formname varchar(64), formsource text);
\connect - gb
CREATE TABLE pga_scripts (scriptname varchar(64), scriptsource text);
\connect - gb
CREATE TABLE pga_reports (reportname varchar(64), reportsource text, reportbody 
text, reportprocs text, reportoptions text);
\connect - gb
CREATE TABLE pga_layout (tablename varchar(64), nrcols int2, colnames text, colw
idth text);
COPY lista FROM stdin;
Birra   100     3
Gelato  100     1
Tovaglioli      101     50
Candele automobile      102     4
Violetta africana       103     1
\connect - gbVino    101     2
Collana d'oro   101     2
\.
COPY venditori FROM stdin;
100     Super Negoziante
102     Ricambi Auto
101     Grande Magazzino
103     Fioreria ACME
\.
COPY pga_queries FROM stdin;
Merce venditori S       select t0.articolo, t1.nomevenditore, t0.quantita from l
ista t0,venditori t1  where  (t0.codvenditore=t1.codvenditore)  order by  t0.art
icolo asc , t1.nomevenditore asc  
Elenco venditori        S       select codvenditore, nomevenditore from venditor
i order by  nomevenditore asc   
\.
COPY pga_forms FROM stdin;
\.
COPY pga_scripts FROM stdin;
\.
COPY pga_reports FROM stdin;
\.
COPY pga_layout FROM stdin;
venditori       2       codvenditore nomevenditore      150 150
lista   3       articolo codvenditore quantita  150 150 150
nolayoutneeded  1       nomevenditore   150
Elenco venditori        2       codvenditore nomevenditore      150 150
Merce venditori 3       articolo nomevenditore quantita 150 150 150
\.
CREATE  INDEX lista_codvenditore on lista using btree ( codvenditore int4_ops );
CREATE UNIQUE INDEX venditori_codvenditore on venditori using btree ( codvendito
re int4_ops );

A partire da questo file si può ricostruire il database oppure crearne uno nuovo con un altro nome. Per far questo è sufficiente dare in pasto il file a psql. Ad esempio se vogliamo congelare lo stato attuale del nostro database ed assegnargli il nome di alimentari è sufficiente dare i seguenti comandi:


ravel:~$ psql 
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: template1

template1=> create database alimentari;
CREATEDB
template1=> \q
ravel:~$ psql -e alimentari < drogheria.sql 
...
EOF
ravel:~$ psql alimentari
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: alimentari

alimentari=> REVOKE ALL ON lista FROM www_data;
CHANGE
alimentari=> GRANT SELECT,INSERT ON lista TO www_data;
CHANGE
alimentari=> REVOKE ALL ON venditori FROM www_data;
CHANGE
alimentari=> GRANT SELECT ON venditori TO www_data;
CHANGE
alimentari=> \q
ravel:~$

Osservate che i diritti d'accesso alle tabelle non sono una proprietà del database drogheria ma vengono definite internamente a PostgreSQL per cui vanno ridefiniti nuovamente per il nuovo database alimentari. Cercando nella documentazione di pg_dump ho scoperto un'interessante opzione -z che permette il dump anche dei comandi per l'assegnazione dei diritti d'accesso alle tabelle. Lascio per esercizio la verifica della funzionalità di tale comando.

Nell'elenco delle tabelle costruite avrete senz'altro notato le tabelle: pga_queries, pga_forms, pga_scripts, pga_reports, pga_layout; queste sono tabelle associate al database drogheria ma utilizzate da un programma che si chiama PgAccess, un'interfaccia Tcl/Tk verso PostgreSQL che permette un'accesso di tipo visuale al database. PgAccess da fvwm95 si può lanciare dal menu Start [5]:

Start -> Apps -> Misc -> PostgreSQL for X


Personalmente preferisco inserire e modificare i dati del database da questo programma. Anche le interrogazioni preferisco costruirle con questo "tool" (strumento) che mette a disposizione un'interfaccia molto simile a Microsoft Access per la costruzione delle interrogazioni con la tecnica del drag and drop (trascinamento delle immagini).

Come ultimo punto psql usa le librerie Readline GNU. Se non diversamente configurato il sistema accetta le scorciatoie di Emacs per l'inserimento dei comandi (le frecce su e giù permettono di andare in dietro e in avanti nell'elenco dei comandi inseriti, Control-A muove il cursore all'inizio della riga, e cosi via). Inserendo la seguente linea:

set editing-mode vi

nel file ~/.inputrc si possono usare i comandi VI per l'inserimento dei comandi. Attenzione perché questa modifica influenza tutti i comandi che utilizzano le Readline includendo anche Bash e Gdb.

[4] A dire il vero il problema risiede nell'utility GRANT di postgres che non riconosce il carattere trattino "-" anche se viene inserito tra doppi apici a quanto segnalato da Hamish Moffatt il manutentore del pacchetto www-pgsql

[5] Questo menu viene generato automaticamente dal sistema se risulta installato il pacchetto update-menus. Questo si incarica di ricostruire i menu di sistema (e personali) ogni qualvolta si procede con l'installazione di un programma che preveda l'uso dei menu.

Usare SELECT dal WEB

Eccoci finalmente all'interfacciamento del database con i protocolli disponibili su internet ed in particolare tramite http.
L'uso del protocollo HTTP per l'accesso ai database è motivato da 2 considerazioni principali:

  1. Interfaccia grafica standard
  2. Indipendenza dalla piattaforma

Ovviamente questo articolo non vuole presentare la creazione di pagine interattive ma il puro interfacciamento con un database, per cui do' per scontata la conoscenza dell'HTML. Se avete seguito tutte le operazioni descritte fino a questo punto siete pronti per costruire pagine web che utilizzino l'SQL (SQL embedded).

Come primo passo utilizziamo il pacchetto WWW-PGSQL che è più semplice da utilizzare rispetto a php3 essendo progettato per adempiere esclusivamente a questo scopo. Nella distribuzione Debian la directory radice dei documenti apache è /var/www; io mi sono creato una directory figlia di questa, in cui inserire tutto il lavoro.
Ecco la nostra prima pagina attiva contenente comandi SQL che ho salvato col nome: /var/www/gb/db/drogheria/www-pgsql/dbprint.pgsql


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>Vista dei record del Database</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFEE">

<DIV ALIGN="center">
<H2>Vista dei record del Database</H2>

<! SQL connect localhost www_data >
<! SQL database alimentari>

<!-- Interroga il database -->
<!sql query "SELECT lista.articolo, venditori.nomevenditore, lista.quantita
                    FROM lista, venditori
                    WHERE lista.codvenditore = venditori.codvenditore
                    ORDER BY lista.articolo" esito>
<!-- Visualizza il risultato dell'interrogazione -->
<!--Versione semplificata: sql qtable esito-->

<! sql print "<PRE>Ci sono $NUM_ROWS articoli</PRE>\n"&
gt;

<TABLE Border="0">
<TR>
  <TH>articolo</TH>
  <TH>codvenditore</TH>
  <TH>quantita</TH>
</TR>

<! sql print_rows esito 
  "<TR><TD>@esito.0</TD>
       <TD>@esito.1</TD>
       <TD>@esito.2</TD></TR>\n">
<!sql free esito>

<! SQL close>
</TABLE>

<DIV ALIGN=right>
<HR WIDTH=40%>
Autore documento: Guido Bozzetto (<A HREF = 
  "mailto:GB@Nauta.it?Subject=SQL"
  >GB@nauta.it</A>)
<BR>Data creazione : 19 Lug 1998
<BR>Ultima modifica: 19 Lug 1998
</DIV>

</BODY>
</HTML>

Potete collocare questo file ovunque vogliate all'interno della directory dove risiedono le pagine servite da Apache (directory detta anche DocumentRoot, dal nome della direttiva di configurazione che la definisce), e potete cambiarne il nome. Ciò che è assolutamente necessario è che l'estensione sia .pgsql. In caso contrario il server web non è in grado d'interpretare correttamente le direttive in esso contenute, in quanto non avrebbe modo di sapere che tali direttive devono essere filtrate ed eseguite da www-pgsql.

Per vedere il risultato prodotto sarà sufficiente richiamare con il nostro browser il URL:

http://localhost/gb/db/alimentari/dbprint.pgsql

Che produrrà il seguente risultato:


Vista dei record del Database

                       Vista dei record del Database

Ci sono 7 articoli
               
   articolo codvenditore quantita
   Birra Super Negoziante 3
   Candele automobile Ricambi Auto 4
   Collana d'oro Grande Magazzino 2
   Gelato Super Negoziante 1
   Tovaglioli Grande Magazzino 50
   Vino Grande Magazzino 2
   Violetta africana Fioreria ACME 1
                        ____________________________
        
                           Autore documento: Guido Bozzetto (GB@Nauta.it)
                                             Data creazione : 19 Lug 1998
                                             Ultima modifica: 19 Lug 1998

Quello riportato è l'output ottenuto con lynx ma il risultato è simile se si utilizza Netscape o un altro browser.

www-pgsql è molto semplice da utilizzare, come avete visto dall'esempio le direttive del linguaggio vengono inserite nelle pagine HTML all'interno dei "tag" (marcatori) "<!" e ">". La sintassi prevede la presenza del "tag" SQL affinché www-pgsql interpreti il testo racchiuso come comandi a lui destinati.
La struttura della pagina attiva è molto semplice:

  1. Accesso al database.
  2. Interrogazione ed estrazione dei dati desiderati.
  3. Stampa dei dati estratti.
  4. Chiusura dell'accesso al database.

La prima operazione viene realizzata dalle righe:


<! SQL connect localhost www_data >
<! SQL database alimentari>

La prima riga è la più importante per la distribuzione Debian in quanto permette di specificare l'utente "www_data" per accedere a PostgreSQL che altrimenti sarebbe www-data, utente non permesso; "localhost" ovviamente specifica la macchina (server) su cui risiede il DBMS. La seconda riga seleziona il database da utilizzare.
Connessi al database ora bisogna estrarne i dati d'interesse con la seguente interrogazione:


SELECT lista.articolo, venditori.nomevenditore, lista.quantita
       FROM lista, venditori
       WHERE lista.codvenditore = venditori.codvenditore
       ORDER BY lista.articolo

Che estrae il nome dell'articolo, il nome del venditore e la quantità dalle due tabelle. L'interrogazione avviene utilizzando la funzione query che richiede come parametro una query e una variabile a cui viene associato l'esito dell'interrogazione stessa. Quando www-pgsql scorre il documento setta automaticamente delle variabili predefinite, ad es. io ho utilizzato $NUM_ROWS che ritorna il numero di righe restituito dall'ultima interrogazione (query).
La stampa dei dati estratti l'ho ottenuta utilizzando la funzione print_rows che richiede 2 parametri: la variabile passata alla funzione query e una stringa per la formattazione dei dati estratti. Come è facile intuire la sintassi:

@esito.0

indica l'accesso al primo campo della riga corrente dell'interrogazione esito. Questa funzione semplifica notevolmente la stampa dei dati in quanto ci pensa direttamente lei a scorrere tutte le righe estratte. Conclusa la nostra interrogazione possiamo procedere al rilascio delle risorse allocate: con la funzione free esito rilasciamo la memoria di sistema utilizzata per memorizzare i dati estratti mentre con la funzione close procediamo alla chiusura della connessione col database.

Vediamo ora come lo stesso risultato si possa ottenere con PHP3. Con questo pacchetto ho avuto non pochi problemi prima di riuscire a farlo funzionare con la distribuzione Debian. Il principale ostacolo che ho dovuto affrontare è stato quello di riuscire ad accedere al database come utente www_data e non come www-data.
Per accedere al database esiste la funzione pg_Connect che utilizza la seguente sintassi:

int pg_connect(string host, string port, string options, string tty, string dbname);

L'int iniziale si riferisce al fatto che la funzione restituisce un'intero. PHP3 ha una sintassi simile al linguaggio C ecco perché la riga termina col ";". Come potete notare possiamo definire la macchina, la porta e il database su cui collegarsi ma non c'è modo di definire l'utente con cui accedere al database. In un primo tempo avevo sperato che la stringa options potesse risolvere il problema, ma ha tutt'altra utilità. La soluzione l'ho trovata nella mailing-list php3-general dove ho trovato dei riferimenti ad una sintassi non documentata della funzione pg_connect:


pg_Connect("dbname=mydb host=myhost user=$PHP_AUTH_USER".
           "password=$PHP_AUTH_PW authtype=password");
pg_Connect("host=localhost user=[username] port=5432 dbname=[database]");

Trovata la soluzione all'unico vero problema con cui bisogna scontrarsi per l'uso dei pacchetti di Linux Debian possiamo facilmente realizzare la pagina HTML che esegue l'interrogazione del database:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>Vista dei record del Database</TITLE>
</HEAD>
<BODY>

<DIV ALIGN="center">
<P>Vista dei record del Database</P>

<TABLE Border="0">
<TR>
  <TH>articolo</TH>
  <TH>codvenditore</TH>
  <TH>quantita</TH>
</TR>

<?
   $dbserver = "localhost";
   $dbport   = "5432";
   $database = "alimentari";

   $dbuser   = "www_data";
   $dbpasswd = "";
   $authtype = "password";

   $conn = pg_Connect("host=$dbserver port=$dbport dbname=$database ".
                       "user=$dbuser password=$dbpasswd authtype=$authtype");
   if (!$conn) {
     echo "Si &egrave; verificato un errore.\n";
     exit;
   } // Errore accesso al database
   $esito = pg_Exec($conn,
            "SELECT lista.articolo, venditori.nomevenditore, lista.quantita
                    FROM lista, venditori
                    WHERE lista.codvenditore = venditori.codvenditore
                    ORDER BY lista.articolo;");
   if (!$esito) {
     echo "Si &egrave; verificato un errore.\n";
     exit;
   } // Errore interrogazione database
   $num = pg_NumRows($esito);
   $i = 0;
   while ($i < $num) {
         echo "<TR><TD>";
         echo pg_Result($esito, $i, "articolo");
         echo "</TD><TD>";
         echo pg_Result($esito, $i, "nomevenditore");
         echo "</TD><TD>";
         echo pg_Result($esito, $i, "quantita");
         echo "</TD></TR>";
         $i++;
   } // Estrazione delle righe dell'interrogazione
   pg_FreeResult($esito);
   pg_Close($conn);
?>

</TABLE>

<DIV ALIGN=right>
<HR WIDTH=40%>
Autore documento: <A HREF="http://www.Nauta.it/~bozzetto/"
>Guido Bozzetto</A> (<A HREF = 
  "mailto:GB@nauta.it?Subject=<?
  echo "$SERVER_NAME$REQUEST_URI";
  ?>"
  >GB@nauta.it</A>)
<BR>Data creazione :<I>
<? 
   echo date( "M d Y H:i:s", mktime( 18,33,10,7,18,1998 ));
?></I>
<BR>Ultima modifica:<I>
<?
   echo date( "M d Y H:i:s", getlastmod() );
?></I>
<BR><I><? echo "<FONT SIZE=1><A HREF=
          \"http://www.php.net/\">PHP3</A> enhanced</FONT>" ?></I>
</DIV>

</BODY>
</HTML>

Ho salvato tale file col nome /var/www/gb/db/alimentari/dbprint.php3 e quindi l'ho provato. Ecco un dump dell'interrogazione fatta al database utilizzando Lynx tramite il comando:
lynx -dump http://localhost/gb/db/alimentari/dbprint.php3 :


Vista dei record del Database
                                      
   articolo codvenditore quantita
   Birra Super Negoziante 3
   Candele automobile Ricambi Auto 4
   Collana d'oro Grande Magazzino 2
   Gelato Super Negoziante 1
   Tovaglioli Grande Magazzino 50
   Vino Grande Magazzino 2
   Violetta africana Fioreria ACME 1
                        ____________________________
                                                                         
                     Autore documento: [1]Guido Bozzetto ([2]GB@nauta.it)
                                    Data creazione : Jul 18 1998 18:33:10
                                    Ultima modifica: Nov 01 1998 15:22:19
                                                         [3]PHP3 enhanced

References

   1. http://www.Nauta.it/~bozzetto/
   2. mailto:GB@nauta.it?Subject=localhost/gb/db/alimentari/dbprint.php3
   3. http://www.php.net/

Come si può osservare l'utilizzo di PHP3 è significativamente più complesso in quanto bisogna gestire completamente da programma anche la stampa dei risultati.

La prima osservazione da fare è che l'estensione del nome del file è .php3 che identifica l'uso del modulo libphp3.so di apache. Su alcune macchine ho installato la vecchia versione 1.2 di apache (con l'estensione SSL) utilizzando pero' la versione recente di php3 che non è compatibile con esso; in tal caso all'estensione .php3 ho fatto corrispondere l'handler php3-script che mi ha permesso di far funzionare correttamente il sistema. Come estensione del file non è possibile utilizzare .html in quanto per ragioni di sicurezza nella versione compilata per la Debian di PHP3 risulta disabilitata la possibilità di chiamare il file facendolo processare da un cgi-bin con un'URL del tipo:

http://localhost/cgi-bin/php3?gb/db/alimentari/dbprint.html

I comandi PHP3 possono essere inseriti in 3 modi all'interno delle pagine HTML:

  1. <? echo "PHP3 enhanced" ?>
  2. <?PHP echo "PHP3 enhanced" ?>
  3. <SCRIPT Language="PHP">


echo "PHP3 enhanced"
</SCRIPT>

La scrittura coi tag <?PHP e ?> rende il codice più comprensibile e ricorda molto la sintassi utilizzata da WWW-PGSQL. L'ultima sintassi risulta essere molto simile a quella utilizzata per delimitare il codice JavaScript.
Le semplici righe appena riportate non creano alcun problema se vengono salvate con estensione .pcgi o .php3 purtroppo questo non vale per altre funzioni come pg_Connect, Header e GregorianToJD per cui possono essere eseguite o solo come modulo di Apache oppure solo tramite l'handler esterno.

La struttura del codice rispecchia quella della versione WWW-PGSQL anche se viene realizzato in maniera diversa.
La funzione pg_Connect permette di definire l'utente, la macchina e il database in un'unica operazione.
La funzione pg_Exec permette d'eseguire l'interrogazione il cui risultato risulterà in seguito accessibile tramite la variabile $esito, le variabili in PHP3 vanno prefissate dal carattere $ sia che esse siano scalari, vettori, liste o array associativi. Come in awk e perl, e non in C, anche in PHP3 esistono gli array associativi.
La funzione pg_NumRows restituisce il numero delle righe estratte con l'ultima interrogazione.
La funzione echo scrive sulla pagina HTML che viene visualizzata dal browser.
pg_Result permette di estrarre la riga e i campi desiderati. Utilizzata in un ciclo permette di stampare il contenuto di tutta la query eseguita col comando pg_Exec e di formattare il risultato all'interno di una tabella.
pg_FreeResult e pg_Close ci permettono di rilasciare le risorse richieste dall'esecuzione del programma.
Nel programma ho inserito anche le funzioni date, mktime e getlastmod che non hanno nulla a che vedere con PostgreSQL ma sono offerte da PHP3 per creare procedure più complete e flessibili rispetto a quelle più rigide realizzabili con WWW-PGSQL.

Usare INSERT dal Web

A questo punto il più è stato fatto, ora si tratta solo di assemblare le conoscenze acquisite per assolvere al compito di inserire i dati nel nostro database in maniera semplice. In questo caso bisogna utilizzare i FORM per inserire i dati nel database attraverso un browser. Ovviamente do' per scontato che si sappiano utilizzare i tag <FORM>, <INPUT>, <SELECT>, <OPTION>, ecc.
Iniziamo con l'utilizzare WWW_PGSQL, creiamo il file dbform.pgsql che sarà l'interfaccia per l'inserimento dei dati:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>Inserimento di record nel Database</TITLE>
</HEAD>
<BODY BGCOLOR="#FFEEFF">

<DIV ALIGN="center">
<H1>Inserimento di record nel Database</H1>

<FORM METHOD="post" ACTION="dbinsert.pgsql">

<! SQL connect localhost www_data >
<! SQL database alimentari>

<!-- Interroga il database -->
<!sql query "SELECT nomevenditore
                    FROM venditori
                    ORDER BY nomevenditore" esito>

<TABLE Border="0">
<TR>
  <TD>Nuovo Articolo</TD>
  <TD><INPUT NAME="articolo"></TD>
</TR>
<TR>
  <TD>Venditore:</TD>
  <TD><SELECT NAME="venditore" SIZE="1">
      <! sql print_rows esito 
         "<OPTION>@esito.0\n">
      </SELECT></TD>
</TR>
<TR>
  <TD>Quantit&agrave;:</TD>
  <TD><INPUT NAME="quantita"></TD>
</TR>
</TABLE>

<!sql free esito>
<! SQL close>

<INPUT TYPE="submit" VALUE="Inserisci">
</FORM>

<DIV ALIGN=right>
<HR WIDTH=40%>
Autore documento: <A HREF="http://www.Nauta.it/~bozzetto/"
>Guido Bozzetto</A> (<A HREF = 
  "mailto:GB@nauta.it?Subject=SQL"
  >GB@nauta.it</A>)
<BR>Data creazione : 19 Lug 1998
<BR>Ultima modifica: 19 Lug 1998
</DIV>

</BODY>
</HTML>

Questa procedura ci permette di creare una pagina che si costruirà dinamicamente ogni qualvolta vi si accede. Inserendo lo stesso URL in momenti diversi si otterranno risultati diversi, se ci sono state modifiche nel database, in quanto i dati contenuti in essa provengono dal database. L'output di questo programma è del tipo:


Inserimento di record nel Database
                                      
   Nuovo Articolo ____________________
   Venditore: [Fioreria ACME...]
   Quantità: ____________________
   
                                 Inserisci
                        ____________________________
                                                                         
                     Autore documento: [1]Guido Bozzetto ([2]GB@nauta.it)
                                             Data creazione : 19 Lug 1998
                                             Ultima modifica: 19 Lug 1998

References

   1. http://www.Nauta.it/~bozzetto/
   2. mailto:GB@nauta.it?Subject=SQL

Se non fosse chiaro il campo Venditore è una lista dei venditori registrati nel database; ad ognuno di essi si può associare, nell'inserimento dell'ordine, un'articolo e la relativa quantità.
A questo punto abbiamo realizzato un "front end" che ci permette l'invio dei dati al server web ma non ancora l'inserimento degli stessi nel database, per far questo abbiamo bisogno di un "back end" sul server web che permetta l'inserimento dei dati ricevuti nel database. Creiamo il file dbinsert.pgsql che risulta essere un programma CGI-BIN di gestione del modulo HTML ("form") realizzato tramite WWW-PGSQL


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>Conferma Inserimento nel Database</TITLE>
</HEAD>
<BODY BGCOLOR="#FFDDFF">

<DIV ALIGN="center">

<! SQL connect localhost www_data >
<! SQL database alimentari>

<!-- Interroga il database -->
<!sql query "SELECT codvenditore
                    FROM venditori
                    WHERE nomevenditore='?venditorè" esito>
<!sql setexpr codice @esito.0>
<!sql free esito>

Inserisco:
<! sql print "?quantita ?articolo presso ?venditore(#codice)<BR>">

<!sql query "INSERT INTO lista
                    VALUES ('?articolo', ?codice, ?quantita)">

<! SQL close>

Database aggiornato.

<DIV ALIGN=right>
<HR WIDTH=40%>
Autore documento: <A HREF="http://www.Nauta.it/~bozzetto/"
>Guido Bozzetto</A> (<A HREF = 
  "mailto:GB@nauta.it?Subject=SQL"
  >GB@nauta.it</A>)
<BR>Data creazione : 19 Lug 1998
<BR>Ultima modifica: 19 Lug 1998
</DIV>

</BODY>
</HTML>

Questa pagina inserisce il nuovo record nella tabella lista dando poi una comunicazione dell'avvenuta operazione.
La variabile codice ci permette di memorizzare il codice del venditore che ha inserito i dati, le variabili precedute da ? sono i valori passati dal modulo HTML al server web.

Le stesse operazioni si possono eseguire realizzando il tutto con PHP3. Seguono la mie implementazioni del front end dpform.php3:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>Inserimento di record nel Database</TITLE>
</HEAD>
<BODY>

<DIV ALIGN="center">
<P>Inserimento di record nel Database</P>

<FORM METHOD="post" ACTION="dbinsert.php3">

<TABLE Border="0">
<TR>
  <TD>Nuovo Articolo:</TD>
  <TD><INPUT NAME="articolo">
</TR>
<TR>
  <TD>Venditore:</TD>
  <TD><SELECT NAME="venditore" SIZE="1">
<?PHP
   $dbserver = "localhost";
   $dbport   = "5432";
   $database = "alimentari";
   $dbuser   = "www_data";
   $dbpasswd = "";
   $authtype = "password";

   $conn = pg_Connect ("host=$dbserver port=$dbport ".
                       "user=$dbuser dbname=$database");
   if (!$conn) { // Errore accesso al database
     echo "Si &egrave; verificato un errore.\n";
   } else {
     $esito = pg_Exec($conn,
            "SELECT nomevenditore
                    FROM venditori
                    ORDER BY nomevenditore;");
     if (!$esito) { // Errore interrogazione database
       echo "Si &egrave; verificato un errore.\n";
     }else { // Accesso ai dati
       $num = pg_NumRows($esito);
       $i = 0;
       while ($i < $num) {
         echo "<OPTION>";
         echo pg_Result($esito, $i, "nomevenditore");
         $i++;
       } // Estrazione delle righe dell'interrogazione
     pg_FreeResult($esito);
     } // Accesso alla tabella
     pg_Close($conn);
   }
?>
</SELECT></TD>
</TR>

</TR>
  <TD>Quantit&agrave;:</TD>
  <TD><INPUT NAME="quantita"</TD>
</TR>

</TABLE>
<INPUT TYPE="submit" VALUE="Inserisci il record">
</FORM>

<DIV ALIGN=right>
<HR WIDTH=40%>
Autore documento: <A HREF="http://www.Nauta.it/~bozzetto/"
>Guido Bozzetto</A> (<A HREF = 
  "mailto:GB@Nauta.it?Subject=<?
  echo "$SERVER_NAME$REQUEST_URI";
  ?>"
  >GB@nauta.it</A>)
<BR>Data creazione :<I>
<? 
   echo date( "M d Y H:i:s", mktime( 19,53,00,7,22,1998 ));
?></I>
<BR>Ultima modifica:<I>
<?
   echo date( "M d Y H:i:s", getlastmod() );
?></I>
<BR><I><? echo "<FONT SIZE=1><A HREF=
          \"http://www.php.net/\">PHP3</A> enhanced</FONT>" ?></I>
</DIV>

</BODY>
</HTML>

e del back end dbinsert.php3:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>Conferma inserimento del record nel Database</TITLE>
</HEAD>
<BODY>

<DIV ALIGN="center">

<?PHP
   $dbserver = "localhost";
   $dbport   = "5432";
   $database = "alimentari";
   $dbuser   = "www_data";
   $dbpasswd = "";
   $authtype = "password";

   $conn = pg_Connect ("host=$dbserver port=$dbport ".
                       "user=$dbuser dbname=$database");
   if (!$conn) { // Errore accesso al database
     echo "Si &egrave; verificato un errore.\n";
   } else {
     $esito = pg_Exec($conn,
            "SELECT codvenditore
                    FROM venditori
                    WHERE nomevenditore='$venditorè;");
     if (!$esito) { // Errore interrogazione database
       echo "Si &egrave; verificato un errore.\n";
     }else { // Accesso ai dati
       $codicevenditore = pg_Result($esito, 0, "codvenditore");
       pg_FreeResult($esito);

       pg_Exec($conn,
               "INSERT INTO lista
                       VALUES ('$articolo', $codicevenditore, $quantita);");
     }
     pg_Close($conn);
   }
?>

Database aggiornato.

<DIV ALIGN=right>
<HR WIDTH=40%>
Autore documento: <A HREF="http://www.Nauta.it/~bozzetto/"
>Guido Bozzetto</A> (<A HREF = 
  "mailto:GB@Nauta.it?Subject=<?
  echo "$SERVER_NAME$REQUEST_URI";
  ?>"
  >GB@nauta.it</A>)
<BR>Data creazione :<I>
<? 
   echo date( "M d Y H:i:s", mktime( 20,35,00,7,22,1998 ));
?></I>
<BR>Ultima modifica:<I>
<?
   echo date( "M d Y H:i:s", getlastmod() );
?></I>
<BR><I><? echo "<FONT SIZE=1><A HREF=
          \"http://www.php.net/\">PHP3</A> enhanced</FONT>" ?></I>
</DIV>

</BODY>
</HTML>

In PHP3 i valori inseriti nel "form" sono accessibili come qualsiasi altra variabile prefissando il nome del campo col carattere "$", il codice rimanente è pari pari quello che abbiamo visto per la versione scritta servendosi di WWW-PGSQL.

Conclusioni

È relativamente facile realizzare dei database utilizzando PostgreSQL, se poi si usa la distribuzione Debian di Linux l'uso risulta notevolmente facilitato. Purtroppo per riuscire ad utilizzare sia WWW-PGSQL sia PHP3 come interfaccia per PostgreSQL bisogna conoscere alcuni meccanismi non cosi' banali, soprattutto per il secondo.
L'utilizzo di WWW-PGSQL è molto semplice sia nell'installazione che nell'uso e tutto risulta ben documentato permettendo di realizzare molto velocemente qualcosa di concreto. Il pacchetto WWW-PGSQL è stato realizzato appositamente per l'accesso ai database e pertanto è ottimizzato per questa operazione, inoltre la versione per Debian è già compilata tenendo conto della sicurezza per cui non può venir utilizzato direttamente come un cgi-bin. La facilità d'uso del sistema ne penalizza un po' l'elasticità ma senz'altro è utilissimo per realizzare velocemente l'applicazione desiderata.
L'uso di PHP3 è decisamente complesso sia per quanto riguarda l'installazione che, soprattutto, per riuscire a risolvere gli inconvenienti legati alla distribuzione Debian. Il fatto di dover utilizzare sintassi NON DOCUMENTATE risulta decisamente un controsenso rispetto al resto della distribuzione Debian. PHP3 è un pacchetto molto flessibile che permette di realizzare cose molto interessanti anche se questo rende il tutto molto più difficoltoso.
In questo articolo si è utilizzato PostgreSQL come se fosse un qualsiasi DBMS relazionale SQL senza utilizzare nessuna delle caratteristiche proprie come gli oggetti e l'ereditarietà. Nel mondo dei database ci sono ditte del calibro di Oracle e Informix che ora stanno scommettendo su Linux considerandolo un sistema operativo maturo per essere utilizzato nelle grosse organizzazioni e non solo dagli "smanettoni" del PC.
L'interfacciamento del database tramite web non è molto efficiente se utilizzato localmente. Se si vuole utilizzare un database che risiede nella stessa macchina da cui vogliamo utilizzarlo è ben più veloce, e tutto sommato pratico, utilizzarlo tramite psql e PgAccess senza ricorrere al server web. Io utilizzo un 486 come PC e le risposte arrivano anche se prima c'è un certo frullare dei dischi dovuto al server web e al DBMS, l'accesso tramite PgAccess oltre ad essere molto più veloce risulta anche molto pratico in quanto si possono "editare" direttamente i campi dei record delle tabelle permettendo operazioni di cancellazioni/inserimento/aggiornamento dirette. Lo schema d'interfacciamento tramite web è molto statico e non risulta per nulla elastico, tutte le operazioni che si vogliono fare sul database devono essere precedentemente previste ed implementate mentre con un accesso diretto al DBMS, come si può ottenere con psql o con pgaccess, anche se avviene tramite la rete risulta molto adattabile e praticamente illimitato nelle funzionalità disponibili. Dopotutto psql è la facciata del DBMS per cui mette a disposizione tutte le caratteristiche utilizzabili con PostgreSQL. Anche PgAccess è un'interfaccia standard per PostgreSQL anche se non cosí duttile, l'unico inconveniente che ho riscontrato, se cosi' si può definire, nel suo utilizzo è che per poterlo sfruttare appieno, e realizzare delle applicazioni complete, bisogna conoscere Tcl/Tk che d'altronde è la stessa fatica che bisogna fare per apprendere VisualBasic in ambiente MS Windows. Il vantaggio nell'imparare Tcl/Tk è risulta essere sempre piú multipiattaforma ed èfacilmente interfacciabile con altri linguaggi ben noti nel mondo multipiattaforma come Perl che non sono nati per il mondo grafico ma tramite le librerie Tk mettono a disposizione la loro potenza anche in tale contesto. Considerato come la Sun sta coccolando il suo linguaggio in questi ultimi anni non credo che impararselo sia una perdita di tempo.
La scrittura del codice risulta alquanto complessa in quanto non conosco ambienti integrati che ne facilitino la scrittura. L'uso di DBMS commerciali come Oracle permette di disporre di un server molto potente e sofisticato su macchina Linux e di sviluppare l'interfaccia sotto Windows disponendo cosi' di un sistema molto noto e familiare per la realizzazione di applicazioni anche complesse. Per la scrittura delle pagine che ho riportato mi sono creato una pagina tipo con Netscape Gold e quindi l'ho rinominata mettendo come suffisso .php3 o .pgsql al posto di .html, successivamente ho editato la pagina direttamente in modalità testuale con vim (gvim). Anche l'uso di prodotti come MS FrontPage è piuttosto difficile in quanto mi è parso non preveda la possibilità che l'estensione di una pagina attiva possa essere diversa da .htm o .html, come d'altronde succede anche per Netscape.

Ho provato a realizzare un'applicazione limitatamente complessa che potesse essere utilizzata in ufficio dai miei colleghi che per lo più utilizzano Windows95/NT e dai pochi che utilizzano Linux, il risultato è stato abbastanza positivo anche se ci sarebbe ancora da adoperarsi per rifinirlo e renderlo efficiente e intuitivo. L'operazione è consistita nel realizzare un'agenda elettronica in cui tutti possono inserire e modificare lo stato degli appuntamenti. La pagina che più mi ha soddisfatto è quella che visualizza gli appuntamenti della settimana in modalità grafica, per ogni giorno ci sono delle fasce orarie e, se ci sono appuntamenti all'interno della fascia, viene riportata la persona a cui si riferisce, cliccandovi sopra si visualizza l'appuntamento che può venire modificato. Da questo lavoro ho percepito che sarebbe molto comodo se parte dei controlli avvenissero sul lato client. Quello che ho riportato in questo articolo si riferisce tutto ad elaborazioni che vengono svolte sui server web e DBMS, non c'è nulla che possa fare il client se non inserire dati nel modulo. Ritengo che l'integrazione di questo con delle pagine intelligenti realizzate in JavaScript migliorerebbe notevolmente l'utilizzabilità di applicazioni interattive come quella dell'agenda citata.
Personalmente è ormai più di un anno e mezzo che sono passato alla distribuzione Debian di Linux dopo anni di utilizzo della Slackware e ne sono molto soddisfatto. Certo ci sono alcune incongruenze nel sistema per cui non risulta essere utilizzabile da novizi di Linux ma è molto flessibile e si riesce a fare proprio di tutto. Non sarebbe male se in una futura versione del pacchetto php3-pgsql ci fosse una documentazione specifica che ne spiegasse l'uso con Debian.
Spero di essere stato abbastanza comprensibile e che sia chiaro, tranne piccoli dettagli, come realizzare l'integrazione tra Web e DBMS. Vi sarete resi conto come questo sia semplice da realizzare in Linux almeno, visto che non ho altre esperienze, adoperando la distribuzione Debian (2.0).
L'utilizzo dell'accoppiata Linux/PostgreSQL rispetto all'accoppiata WindowsNT/DBMS-SQL comporta un marcato risparmio sia per il software che per l'hardware. Per quanto riguarda l'hardware l'utilizzo di Linux consente di realizzare applicazioni su sistemi di caratteristiche praticamente dimezzate, in RAM e CPU, rispetto a quanto richiesto da applicazioni simili su sistemi Windows NT. Anche per ciò che riguarda il costo del software non ci sono paragoni: per realizzare un sistema basato su Windows NT occorre pagare lo stesso S.O., più le licenze di MS Backoffice, più tutti i programmi extra che si vogliono installare (statistiche, backup, sistemi d'allerta, ecc.) per un totale di diversi milioni di lire. Linux ha per lo più il solo costo del supporto (il CD-ROM). Per quanto riguarda il DBMS ora che anche Oracle è disponibile per Linux i costi sono equiparabili anche se l'utilizzo di PostgreSQL permette un'altra drastica riduzione del "cost of ownership". Considerando che Linux oltre a tutto ciò mette a disposizione, già incluso nella distribuzione, tantissimo altro software: SMB server, SQL server, terminal server, FTP, WWW, Telnet, X Windows, NFS, mail server (POP, IMAP, SMTP), SNMP, file server, printer server, ecc.; si capisce perché tante aziende si stiano interessando al fenomeno LINUX. Non da ultimo un sistema rimane efficiente solo se viene mantenuto aggiornato specialmente nel mondo Internet dove le novità sono frequentissime. Ebbene ogniqualvolta si vorrà procedere con un aggiornamento software su Linux non dovrete pagare alcunché (licenza GPL) mentre su WindowsNT dovrete, perlopiù ripagare la licenza daccapo per ogni prodotto.
Un grosso vantaggio d'utilizzare Linux con PostgreSQL è che si dispone dei sorgenti, questo è molto diverso dall'avere software gratis (freeware) il cui utilizzo è gratuito ma non si può minimamente intervenire per risolvere eventuali anomalie o per implementare caratteristiche peculiari non previste dallo sviluppatore del software. Soprattutto se ci sono problemi di segretezza e riservatezza dei dati, sapere esattamente cosa si è installato sul server e poterne verificare il lavoro svolto è un'esigenza. In questo Linux non ha certo rivali, non per nulla i firewall vengono realizzati per lo più con sistemi Unix. Il software disponibile con Linux ha l'ulteriore vantaggio di venire continuamente collaudato e verificato da milioni di utenti che possono cercare tutti i buchi ("bug") che ci sono e che vengono prontamente risolti non appena qualcuno si accorge di qualche problema.

Bibliografia

Tutto quanto riportato nell'articolo è da prendersi cosí com'è, errori ed omissioni sono senz'altro presenti, i codici riportati sono semplici esempi e non garantisco per il loro funzionamento.
Per la realizzazione del lavoro ho attinto da diverse fonti, riporto un breve elenco dei principali riferimenti che non è certamente esaustivo:

  1. Addison-Wesley "Introduzione a SQL". Rick F. van der Lans.
  2. Linux HOWTO "Database-SQL-RDBMS HOW-TO document for Linux (PostgreSQL Object Relational Database System)". Alavoor Vasudevan.
  3. UnixWorld Online: Tutorial Article: No. 018 "Linux, SQL, and Web". Charles Fisher.
  4. Documentazione on line PostgreSQL: http://www.postgresql.org/. AA.VV..
  5. Documentazione on line Apache: http://www.apache.org/. AA.VV..
  6. Documentazione on line PHP3: http://www.php.net/. AA.VV..
  7. Documentazione on line WWW-PGSQL: //www.daa.com.au/~james/www-sql/. James Henstridg.
  8. Documentazione on line Debian: http://www.debian.org/. AA.VV..
  9. Mailing list: utenti Debian: debian-users@debian.org. AA.VV..
  10. Mailing list: PHP3: php3-general. AA.VV..

[precedente] T'N'T - Copertina - Forse Linux non finisce qui - 2 [successivo]