Vi state chiedendo cosa sono gli schemi Postgresql e perché sono importanti e come potete usare gli schemi per rendere le vostre implementazioni di database più robuste e manutenibili? Questo articolo introdurrà le basi degli schemi in Postgresql e vi mostrerà come crearli con alcuni esempi di base. Gli articoli futuri approfondiranno gli esempi di come assicurare e usare gli schemi per applicazioni reali.
Prima di tutto, per chiarire una potenziale confusione terminologica, cerchiamo di capire che nel mondo Postgresql, il termine “schema” è forse un po’ purtroppo sovraccarico. Nel contesto più ampio dei sistemi di gestione di database relazionali (RDBMS), il termine “schema” potrebbe essere inteso per riferirsi al design complessivo logico o fisico del database, cioè la definizione di tutte le tabelle, colonne, viste e altri oggetti che costituiscono la definizione del database. In questo contesto più ampio uno schema potrebbe essere espresso in un diagramma entità-relazione (ER) o in uno script di istruzioni del linguaggio di definizione dei dati (DDL) usato per istanziare il database dell’applicazione.
Nel mondo Postgresql, il termine “schema” potrebbe essere meglio inteso come “spazio dei nomi”. Infatti, nelle tabelle di sistema di Postgresql, gli schemi sono registrati in colonne di tabella chiamate “spazio dei nomi”, che, IMHO, è una terminologia più accurata. Come questione pratica, ogni volta che vedo “schema” nel contesto di Postgresql lo reinterpreto silenziosamente per dire “spazio dei nomi”.
Ma potreste chiedere: “Cos’è uno spazio dei nomi?” In generale, uno spazio dei nomi è un mezzo piuttosto flessibile per organizzare e identificare le informazioni per nome. Per esempio, immaginate due famiglie vicine, gli Smith, Alice e Bob, e i Jones, Bob e Cathy (cfr. Figura 1). Se usassimo solo i nomi, potremmo confonderci su quale persona intendiamo quando parliamo di Bob. Ma aggiungendo il cognome, Smith o Jones, identifichiamo in modo unico quale persona intendiamo.
Spesso gli spazi dei nomi sono organizzati in una gerarchia annidata. Questo permette una classificazione efficiente di grandi quantità di informazioni in una struttura a grana molto fine, come per esempio il sistema dei nomi di dominio internet. Al livello superiore, “.com”, “.net”, “.org”, “.edu” e così via definiscono ampi spazi di nomi all’interno dei quali sono registrati nomi per entità specifiche, così, per esempio, “severalnines.com” e “postgresql.org” sono definiti in modo unico. Ma sotto ognuno di questi ci sono un certo numero di sottodomini comuni come “www”, “mail”, e “ftp”, per esempio, che da soli sono duplicati, ma all’interno dei rispettivi spazi dei nomi sono unici.
Gli schemi di Postgresql servono questo stesso scopo di organizzare e identificare, tuttavia, a differenza del secondo esempio sopra, gli schemi di Postgresql non possono essere annidati in una gerarchia. Mentre un database può contenere molti schemi, c’è sempre e solo un livello e quindi all’interno di un database, i nomi degli schemi devono essere unici. Inoltre, ogni database deve includere almeno uno schema. Ogni volta che viene istanziato un nuovo database, viene creato uno schema predefinito chiamato “public”. Il contenuto di uno schema include tutti gli altri oggetti del database come tabelle, viste, stored procedure, trigger, ecc. Per visualizzare, fare riferimento alla figura 2, che rappresenta una matrioska a forma di bambola che mostra dove gli schemi si inseriscono nella struttura di un database Postgresql.
Oltre ad organizzare semplicemente gli oggetti del database in gruppi logici per renderli più gestibili, gli schemi servono allo scopo pratico di evitare collisioni di nomi. Un paradigma operativo comporta la definizione di uno schema per ogni utente del database in modo da fornire un certo grado di isolamento, uno spazio in cui gli utenti possono definire le proprie tabelle e viste senza interferire tra loro. Un altro approccio è quello di installare strumenti di terze parti o estensioni di database in schemi individuali in modo da mantenere tutti i componenti correlati logicamente insieme. Un articolo successivo di questa serie descriverà in dettaglio un nuovo approccio alla progettazione di applicazioni robuste, utilizzando gli schemi come mezzo di indirezione per limitare l’esposizione del design fisico del database e presentare invece un’interfaccia utente che risolve le chiavi sintetiche e facilita la manutenzione a lungo termine e la gestione della configurazione man mano che i requisiti del sistema si evolvono.
Facciamo un po’ di codice!
Il comando più semplice per creare uno schema all’interno di un database è
CREATE SCHEMA hollywood;
Questo comando richiede i privilegi di creazione nel database, e lo schema appena creato “hollywood” sarà di proprietà dell’utente che invoca il comando. Un’invocazione più complessa può includere elementi opzionali che specificano un diverso proprietario, e può anche includere istruzioni DDL che istanziano oggetti del database all’interno dello schema, il tutto in un solo comando!
Il formato generale è
CREATE SCHEMA schemaname ]
dove “username” è chi possiede lo schema e “schema_element” può essere uno di certi comandi DDL (fare riferimento alla documentazione di Postgresql per le specifiche). Per usare l’opzione AUTHORIZATION sono necessari i privilegi di superutente.
Così, per esempio, per creare uno schema chiamato “hollywood” contenente una tabella chiamata “film” e una vista chiamata “winners” con un solo comando, si potrebbe fare
CREATE SCHEMA hollywood CREATE TABLE films (title text, release date, awards text) CREATE VIEW winners AS SELECT title, release FROM films WHERE awards IS NOT NULL;
Oggetti aggiuntivi del database possono essere creati direttamente, per esempio una tabella aggiuntiva verrebbe aggiunta allo schema con
CREATE TABLE hollywood.actors (name text, dob date, gender text);
Nota nell’esempio precedente il prefisso del nome della tabella con quello dello schema. Questo è necessario perché per default, cioè senza esplicita specificazione dello schema, i nuovi oggetti del database sono creati all’interno di qualunque sia lo schema corrente, di cui parleremo in seguito.
Ricordate come nell’esempio del primo spazio dei nomi sopra, abbiamo avuto due persone di nome Bob, e abbiamo descritto come deconferire o distinguerle includendo il cognome. Ma all’interno di ciascuna delle famiglie Smith e Jones separatamente, ogni famiglia capisce che “Bob” si riferisce a quello che va con quella particolare famiglia. Così, per esempio, nel contesto di ciascuna famiglia, Alice non ha bisogno di rivolgersi a suo marito come Bob Jones, e Cathy non ha bisogno di riferirsi a suo marito come Bob Smith: possono semplicemente dire “Bob”.
Lo schema corrente di Postgresql è un po’ come la famiglia nell’esempio precedente. Gli oggetti nello schema corrente possono essere referenziati senza qualifiche, ma riferirsi a oggetti con nomi simili in altri schemi richiede la qualificazione del nome con il prefisso del nome dello schema come sopra.
Lo schema corrente è derivato dal parametro di configurazione “search_path”. Questo parametro memorizza una lista separata da virgole di nomi di schemi e può essere esaminato con il comando
SHOW search_path;
o impostato ad un nuovo valore con
SET search_path TO schema ;
Il primo nome di schema nella lista è lo “schema corrente” ed è dove i nuovi oggetti sono creati se specificato senza qualificazione del nome dello schema.
La lista separata da virgole dei nomi di schema serve anche a determinare l’ordine di ricerca con cui il sistema individua gli oggetti esistenti senza nome qualificato. Per esempio, tornando al quartiere Smith e Jones, una consegna di pacchi indirizzata solo a “Bob” richiederebbe una visita ad ogni casa fino a quando non viene trovato il primo residente chiamato “Bob”. Da notare che questo potrebbe non essere il destinatario previsto. La stessa logica si applica a Postgresql. Il sistema cerca tabelle, viste e altri oggetti all’interno degli schemi nell’ordine di search_path, e poi viene usato il primo oggetto con nome corrispondente trovato. Gli oggetti con nome qualificato dallo schema sono usati direttamente senza riferimento al search_path.
Nella configurazione predefinita, l’interrogazione della variabile di configurazione search_path rivela questo valore
SHOW search_path; Search_path-------------- "$user", public
Il sistema interpreta il primo valore mostrato sopra come il nome dell’utente attualmente loggato e soddisfa il caso d’uso menzionato prima in cui ad ogni utente è assegnato uno schema con nome utente per uno spazio di lavoro separato dagli altri utenti. Se non è stato creato alcuno schema con nome utente, questa voce viene ignorata e lo schema “pubblico” diventa lo schema corrente dove vengono creati i nuovi oggetti.
Quindi, tornando al nostro precedente esempio di creazione della tabella “hollywood.actors”, se non avessimo qualificato il nome della tabella con il nome dello schema, allora la tabella sarebbe stata creata nello schema pubblico. Se abbiamo previsto di creare tutti gli oggetti all’interno di uno schema specifico, allora potrebbe essere conveniente impostare la variabile search_path come
SET search_path TO hollywood,public;
facilitando l’abbreviazione di digitare nomi non qualificati per creare o accedere agli oggetti del database.
C’è anche una funzione di informazioni di sistema che restituisce lo schema corrente con una query
select current_schema();
In caso di fat-fingering dell’ortografia, il proprietario di uno schema può cambiare il nome, purché l’utente abbia anche i privilegi di creazione per il database, con il
ALTER SCHEMA old_name RENAME TO new_name;
E infine, per eliminare uno schema da un database, c’è un comando drop
DROP SCHEMA schema_name;
Il comando DROP fallisce se lo schema contiene degli oggetti, quindi devono essere cancellati prima, oppure si può opzionalmente cancellare ricorsivamente uno schema con tutto il suo contenuto con l’opzione CASCADE
DROP SCHEMA schema_name CASCADE;
Queste nozioni di base vi faranno iniziare a capire gli schemi!