T'N'T - Copertina - Forse Linux non finisce qui - 2 |
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:
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.
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).
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!".
/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/
/etc/apache
in particolare i file
d'interesse sono httpd.conf
e srm.conf
. 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
/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
[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).
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.
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:
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:
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 è 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 è 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:
<? echo "PHP3 enhanced" ?>
<?PHP echo "PHP3 enhanced" ?>
<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.
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à:</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 è verificato un errore.\n";
} else {
$esito = pg_Exec($conn,
"SELECT nomevenditore
FROM venditori
ORDER BY nomevenditore;");
if (!$esito) { // Errore interrogazione database
echo "Si è 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à:</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 è verificato un errore.\n";
} else {
$esito = pg_Exec($conn,
"SELECT codvenditore
FROM venditori
WHERE nomevenditore='$venditorè;");
if (!$esito) { // Errore interrogazione database
echo "Si è 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.
È 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.
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:
T'N'T - Copertina - Forse Linux non finisce qui - 2 |