Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Utilizzo multiutente di un database

Normalmente l'accesso ai dati avviene in maniera concorrente da parte di piu' utenti contemporaneamente
Normalmente l'accesso ai dati avviene in maniera concorrente da parte di piu' utenti contemporaneamente
Link copiato negli appunti

Fino ad ora abbiamo esaminato le caratteristiche del linguaggio SQL che
riguardano la definizione e la manipolazione dei dati presenti in un database,
senza preoccuparci del fatto che normalmente l'accesso a tali dati avviene
in maniera concorrente da parte di piu' utenti contemporaneamente.
I meccanismi a sostegno di tale metodo di accesso riguardano principalmente la
sicurezza dei dati, la gestione delle transazioni e la possibilita' di
definire delle viste sulle tabelle del database.

1. Sicurezza

L'esecuzione di un operazione sui dati del database da parte di un utente
e' subordinata al possesso da parte dell'utente dei necessari privilegi per
la particolare operazione eseguita sullo specifico insieme di dati.
In generale i privilegi vengono attribuiti nella seguente maniera:

  • Un utente che crea una tabella o qualunque altro oggetto del database ne e'
    il proprietario e gli vengono automaticamente garantiti tutti i privilegi
    applicabili a tale oggetto, con la possibilita' di impostare anche ad altri
    utenti tali privilegi (privilegio di concessione).
  • Un utente che abbia un privilegio ed abbia in oltre su di esso il
    privilegio di concessione puo' assegnare tale privilegio ad un altro
    utente e passare ad esso anche il privilegio di concessione.
  • I privilegi sono concessi da chi ne abbia il permesso (cioe' dal proprietario
    dell'oggetto e da chi abbia il privilegio di concessione) mediante il
    comando GRANT e revocati mediante il comando REVOKE.

La sintassi del comando GRANT e' la seguente:

GRANT elenco_privilegi ON oggetto TO elenco_utenti [ WITH GRANT OPTION ]

Esso assegna all'utente i privilegi presenti nell'elenco_privilegi sull'oggetto
specificato.
I privilegi assegnabili sono i seguenti (con le relative sintassi):

USAGE
Privilegio per usare uno specifico dominio o altro oggetto del database.

SELECT
Privilegio per accedere a tutte le colonne di una tabella o di una vista.

INSERT [ (nome_colonna) ]
Se viene specificata l'opzione nome_colonna, e' il privilegio per inserire
valori nella colonna indicata di una tabella o di una vista. Senza il
nome_colonna e' il privilegio per inserire valori in tutte le colonne,
comprese quelle che saranno aggiunte in seguito.

UPDATE [ (nome_colonna) ]
Se viene specificata l'opzione nome_colonna, e' il privilegio per aggiornare
il valore nella colonna indicata di una tabella o di una vista. In caso
contrario permette di aggiornare il valore di tutte le colonne, comprese
quelle che saranno aggiunte in seguito.

DELETE
Privilegio per eliminare righe da una tabella o da una vista.

REFERENCES [ (nome_colonna) ]
Se viene specificata l'opzione nome_colonna, e' il privilegio di riferirsi
alla colonna indicata di una tabella o di una vista nella definizione di un
vincolo di integrita'. Senza l'opzione concede tale privilegio per tutte le
colonne, comprese quelle aggiunte in seguito.

L'oggetto a cui il privilegio si riferisce e' generalmente una tabella o una
vista. La sintassi per la sua specificazione e' in tal caso:

[table] nome_tabella

Nel caso di altri oggetti segue la sintassi:

tipo_oggetto nome_oggetto

dove tipo_oggetto puo' essere DOMAIN, CHARACTER SET, COLLATION o trANSLATION (si veda C.J. Date - "A Guide to The SQL Standard" per una spiegazione di tali oggetti).
Nel caso di oggetti diversi da tabelle o viste, l'unico privilegio applicabile
e' quello di USAGE.

L'elenco_utenti e' un elenco di identificativi di utenti o gruppi di utenti.
Puo' anche essere utilizzata la parola chiave PUBLIC, che indica tutti gli
utenti e i gruppi conosciuti nel sistema.

Se e' presente l'opzione [ WITH GRANT OPTION ], viene assegnato inoltre il
privilegio di concessione, che permette agli utenti di trasferire
ulteriormente i privilegi loro assegnati.

Ad esempio:

GRANT SELECT, INSERT, UPDATE(nome) ON persona TO benfante WITH GRANT OPTION

assegna all'utente benfante i privilegi di SELECT e INSERT su tutte le colonne
della tabella persona e quello di UPDATE sulla sola colonna nome di tale
tabella. Gli viene inoltre garantito il privilegio di assegnare tali permessi
ad altri utenti.

Per togliere i privilegi agli utenti si usa invece REVOKE:

REVOKE [ GRANT OPTION FOR ] elenco_privilegi ON oggetto FROM elenco_utenti
{ REStrIC | CASCADE }

elenco_privilegi, oggetto ed elenco_utenti hanno le stesso significato delle
corrispondenti opzioni di GRANT. L'opzione GRANT OPTION FOR revoca il
privilegio di concessione. Se viene specificata la clausola REStrICT, il
comando REVOKE puo' fallire nel caso in cui l'utente a cui vengono revocati
i privilegi li abbia ulteriormente concessi ad altri utenti. Se e' presente
invece la clausola CASCADE, l'istruzione verra' sempre completata con successo
e verranno revocati i privilegi anche di quegli utenti e di tutti gli utenti
a cui essi li hanno concessi (...e cosi' via, finche' non ci saranno piu'
permessi "abbandonati", cioe' concessi senza che chi li ha concessi ne sia
ancora in possesso). Verranno inoltre distrutti gli oggetti del database
costruiti grazie a tali permessi.

2. Gestione delle transazioni

Le transazioni SQL sono insiemi di istruzioni che devono essere trattati come
delle unita' atomiche, cioe' non scomponibili nelle singole istruzioni da cui
sono formate. Grazie a tale atomicita' le transazioni permettono di eseguire
operazioni complesse sul database mantenendone l'integrita'. Infatti una
transazione viene eseguita con successo se e solo se tutte le operazioni che
la compongono terminano con successo. In caso contrario, cioe' se una delle
operazioni fallisce o se la transazione viene esplicitamente annullata, tutte
le operazioni precedenti vengono annullate anch'esse. Le operazioni di una
transazione non hanno alcun effetto sul database fino a quando la transazione
non viene completata con successo.

Dal momento che ad un database possono accedere piu' utenti contemporanamente,
in ogni istante potremmo avere piu' transazioni che manipolano il database in
maniera concorrente. Lo standard SQL prevede che normalmente le transazioni
debbano essere eseguite nel "livello di isolamento serializzabile" (isolation
level SERIALIZABLE), cioe' in una modalita' di esecuzione che garantisca la
"serializzabilita'" delle transazioni. Il fatto che le transazioni siano
serializzabili significa che il loro effetto complessivo sul database e'
quello che si otterrebbe se esse venissero eseguite in maniera non concorrente
l'una di seguito all'altra.
Nel linguaggio SQL standard non esiste un'istruzione che faccia iniziare
esplicitamente una transazione. Le istruzioni vengono divise in due classi:
quelle che possono iniziare una transazione e quelle che non la fanno iniziare.
Nel momento in cui si cerca di eseguire un'istruzione della prima classe, se
non e' gia' in corso una transazione, ne viene cominciata una. La transazione
continua fino a quando una delle istruzioni fallisce, causando l'annullamento
dell'intera transazione, o se vengono eseguite le istruzioni COMMIT WORK o ROLLBACK WORK.

L'istruzione COMMIT WORK termina la transazione confermandola, rendendo quindi
definitivi gli effetti delle sue istruzioni sul database. L'istruzione ROLLBACK
WORK invece la termina annullandola.
Spesso i DBMS che si trovano in commercio implementano la gestione delle
transazioni in maniera differente da quanto previsto dallo standard (almeno
nelle loro impostazioni di default). In tal caso, di solito e' previsto un
comando che inizia esplicitamente una transazione (BEGIN trANSACTION, START WORK, o altro). Se una transazione non e' stata iniziata esplicitamente, le
singole istruzioni ne compongono una ciascuna.
Per capire meglio quali potrebbero essere le conseguenze della manipolazione concorrente dei dati di un database senza l'utilizzo delle transazioni, vediamone un'esempio. Supponiamo di avere un database con il quale gestiamo gli ordini dei prodotti che vendiamo. In particolare, quando un cliente ci sottopone una richiesta per un prodotto, ne verifichiamo la disponibilita' e nel caso in cui possiamo soddisfare l'ordine, sottraiamo alla quantita' in giacenza la quantita' che ci e' stata richiesta. traducendo tutto cio' in SQL, otteniamo la quantita' in giacenza con l'istruzione (istruzione A):

SELECT giacenza FROM prodotti
WHERE prodottoID=1453

L'aggiornamento della giacenza, una volta verificata la disponibilita', e' ottenuta dalla seguente istruzione (istruzione B):

UPDATE prodotti
SET giacenza=giacenza-1
WHERE prodottoID=1453

Se due utenti cercano di eseguire questa operazione, senza che le due istruzioni che la compongono siano state raggruppate in una transazione, potrebbe accadere che le istruzioni vengano eseguite nell'ordine e con i risultati seguenti :

  • Istruzione A eseguita dall'utente 1: viene restituita una giacenza del prodotto pari a 1, quindi l'ordine verra' approvato.
  • Istruzione A eseguita dall'utente 2: come prima la giacenza e' 1 e anche in questo caso l'ordine verra' approvato.
  • Istruzione B eseguita dall'utente 1: a questo punto nel database la giacenza per il prodotto vale 0.
  • Istruzione B eseguita dall'utente 2: ora la giacenza vale -1, che e' ovviamente un valore errato.

Come si vede il risultato finale e' che uno dei due clienti non potra' ricevere (almeno non subito) la merce, dato che non ne avevamo in giacenza una quantita' sufficiente per entrambi i clienti.
Se le due istruzioni fossero state inserite in una transazione, il problema non sarebbe sorto, dato che la transazione del secondo utente non avrebbe potuto leggere il valore della giacenza fino a quando non fosse stata completata la transazione del primo utente. A quel punto, la giacenza avrebbe avuto valore 0 e l'ordine non sarebbe stato erratamente approvato.

3. Viste

Fino ad ora le uniche tabelle con cui abbiamo avuto a che fare sono state quelle definite con il comando CREATE table. Il linguaggio SQL mette anche a disposizione la possibilita' di definire delle tabelle "virtuali", le viste, calcolate a partire da altre tabelle. Esse sono virtuali nel senso che non occupano spazio su disco, ma sono il risultato di interrogazioni su altre tabelle e quindi sempre allineate con i valori contenuti in tali tabelle.
L'istruzione SQL per definire una vista e' la seguente:

CREATE VIEW nome_vista [ ( elenco_nomi_colonne ) ]
AS espressione_tabella

Essa crea una vista chiamata nome_vista definita dall'espressione_tabella. Tipicamente espressione_tabella e' un'instruzione select che produrra' la tabella che interessa. l'elenco_nomi_colonne puo' essere usata per assegnare dei nomi alle colonne della vista. Cio' e' utile nel caso in cui le colonne derivanti dall'espressione tabella siano il risultato di un calcolo (ad esempio COUNT(nome_colonna)) e non abbiano quindi un nome esplicito.
Una volta creata, una vista puo' essere utilizzata come una normale tabella. Le uniche limitazioni riguardano le operazioni che modificano i dati in essa contenuti. Infatti, non tutte le viste sono aggiornabili. Le regole che discriminano fra una vista aggiornabile e una non aggiornabile sono piuttosto complesse e non e' questa la sede per descriverle (si vedano i libri in bibliografia, in particolare quello di C.J. Date). Qui ci limiteremo a cercare di capire, mediante un esempio, perche' questo accade.
Proviamo ad utilizzare la seguente vista sul nostro database bibliografico:

CREATE VIEW book_publisher89
AS SELECT B.title, P.name
FROM Book B, Publisher P
WHERE B.publisher = P.ID
AND B.pub_year=1989

Essa ci permette di eseguire la query che la definisce semplicemente utilizzando l'istruzione:

SELECT * FROM book_publisher89

Possiamo anche impostare ulteriori condizioni (o fare in modo che il risultato sia ordinato secondo una particolare colonna della vista, ecc...):

SELECT title FROM book_publisher89
WHERE name = "ACM Press"

Quest'ultima interrogazione ci fornisce l'elenco dei titoli dei libri pubblicati dall'ACM Press nel 1989.

Come si vede per quanto riguarda operazioni di interrogazione una vista si comprta come una normale tabella.
Le differenze sorgono quando si cerca di applicare ad una vista delle operazioni di aggiornamento. Ad esempio, se cerchiamo di eseguire la seguente istruzione:

INSERT INTO book_publisher89
VALUES( "Nuovo libro", "publisher")

Il DBMS non riuscira' ad eseguirla, restituendo un errore tipo "No INSERT permission". Il motivo e' che non e' in grado di creare le righe corrispondendi al nostro nuovo record nelle due tabelle "reali" da cui la vista e' originata (i problemi sono vari: deve creare solo una righa nella tabella Book e collegarla ad una particolare riga nella tabella Publisher, o creare una riga in entrambe le tabelle; come decidere quali valori assegnare alle chiavi primarie degli eventuali nuovi record; quali valori assegnare agli altri campi delle due tabelle; ecc...)

Grazie alle viste (e all'assegnazione oculata dei permessi agli utenti) e' possibile fare in modo che utenti diversi abbiano una percezione della struttura del database anche molto diversa da quella che ha realmente e impedire che particolari categorie di utenti possano accedere ad informazioni che non competono loro.
Ad esempio, supponiamo di avere una tabella in cui sono memorizzati i dati anagrafici dei dipendenti di una ditta e l'ammontare del loro stipendio mensile. Ovviamente si vorrebbe impedire la consultazione dei dati relativi agli stipendi a tutti gli utenti, eccetto a quelli che si devono occupare dell'erogazione/amministrazione degli stessi. Un sistema per fare cio' e' quello di definire una vista contenente solo le colonne dei dati anagrafici. In questo modo tutti gli utenti autorizzati ad accedere ai dati anagrafici, ma non a quelli degli stipendi, lo potranno fare solo attraverso tale vista.
Ulteriori partizionamenti potrebbero essere fatti in senso orizzontale, creando ad esempio un vista che contiene solo le informazioni sui dirigenti ed una che contiene i dati degli altri dipendenti.
Inoltre, le viste spesso contribuiscono a facilitare quella indipendenza fra applicazioni e struttura dei dati, che rende i database degli strumenti tanto utili. Infatti se ad un certo punto fosse necessario cambiare la struttura del database (scomponendo, ad esempio, una tabella in due tabelle per motivi di efficienza), non sarebbe necessario modificare tutte le applicazioni adattandole alla nuova struttura, ma sarebbe sufficiente creare delle opportune view, in modo che dal punto di vista delle applicazioni niente sia stato cambiato.

Ti consigliamo anche