17. Utilizzo del DBMS PostgreSQL
Il DBMS PostgreSQL è disponibile gratuitamente. È un'alternativa alla versione "community" di MySQL.
Lo stiamo utilizzando qui per dimostrare che è abbastanza semplice migrare gli script Python/MySQL in script Python/PostgreSQL.
Con il DBMS MySQL, l'architettura dei nostri script era la seguente:

Con il DBMS PostgreSQL, sarà la seguente:

17.1. Installazione del DBMS PostgreSQL
Le distribuzioni del DBMS PostgreSQL sono disponibili all'URL [https://www.postgresql.org/download/] (maggio 2019). Mostriamo l'installazione della versione Windows a 64 bit:


- In [1-4], scaricare il programma di installazione del DBMS;
Eseguire il programma di installazione scaricato:

- In [6], specificare una directory di installazione;

- In [8], l'opzione [Stack Builder] non è necessaria per ciò che stiamo facendo qui;
- in [10], lasciare il valore predefinito;

- In [12-13], abbiamo inserito qui la password [root]. Questa sarà la password per l'amministratore del DBMS, che si chiama [postgres]. PostgreSQL lo chiama anche superutente;
- In [15], lasciare il valore predefinito: questa è la porta di ascolto del DBMS;

- in [17], lasciare il valore predefinito;
- In [19], il riepilogo della configurazione di installazione;


Su Windows, il DBMS PostgreSQL viene installato come servizio Windows che si avvia automaticamente. Nella maggior parte dei casi, ciò non è auspicabile. Modificheremo questa configurazione. Digitare [services] nella barra di ricerca di Windows [24-26]:

- in [29], puoi vedere che il servizio DBMS PostgreSQL è impostato in modalità automatica. Modificalo accedendo alle proprietà del servizio [30]:

- In [31-32], impostare il tipo di avvio su Manuale;
- in [33], arrestare il servizio;
Se si desidera avviare manualmente il DBMS, tornare all'applicazione [Servizi], fare clic con il tasto destro del mouse sul servizio [postgresql] (34) e avviarlo (35).
17.2. Amministrazione di PostgreSQL con lo strumento [pgAdmin]
Avvia il servizio Windows per il DBMS PostgreSQL (vedi paragrafo precedente). Quindi, nello stesso modo in cui hai avviato lo strumento [Servizi], avvia lo strumento [pgAdmin], che ti consente di amministrare il DBMS PostgreSQL [1-3]:

A un certo punto potrebbe essere richiesta la password del superutente. Il superutente si chiama [postgres]. Questa password viene impostata durante l'installazione del DBMS. In questo documento, durante l'installazione abbiamo assegnato la password [root] al superutente.
- In [4], [pgAdmin] è un'applicazione web;
- in [5], l'elenco dei server PostgreSQL rilevati da [pgAdmin], qui 1;
- in [6], il server PostgreSQL che abbiamo avviato;
- in [7], i database del DBMS, qui 1;
- in [8], il database [postgresql] è gestito dal superutente [postgres];
Per prima cosa, creiamo un utente [admpersonnes] con la password [nobody]:


- in [17], abbiamo inserito [nobody];

- in [21], il codice SQL che lo strumento [pgAdmin] invierà al DBMS PostgreSQL. Questo è un modo per imparare il linguaggio SQL proprietario di PostgreSQL;
- In [22], dopo aver confermato con la procedura guidata [Salva], l'utente [admpersonnes] è stato creato;
Ora creiamo il database [dbpersonnes]:

Fare clic con il tasto destro del mouse su [23], quindi su [24-25] per creare un nuovo database. Nella scheda [26], definire il nome del database [27] e il suo proprietario [admpersonnes] [28].

- In [30], il codice SQL per la creazione del database;
- In [31], dopo aver confermato con la procedura guidata [Salva], il database [dbpersonnes] viene creato;
Utilizzeremo il database [dbpersonnes] con gli script Python.
17.3. Installazione del connettore Python per il DBMS PostgreSQL

Il diagramma sopra mostra un connettore che collega gli script Python al DBMS PostgreSQL. Ne sono disponibili diversi. Installeremo il connettore [psycopg2]. Questa operazione viene eseguita in un terminale Python (indipendentemente dalla directory in cui è aperto il terminale). Il connettore viene installato utilizzando il comando [pip install psycopg2]:
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\troiscouches\v01\tests>pip install psycopg2
Collecting psycopg2
Downloading psycopg2-2.8.5-cp38-cp38-win_amd64.whl (1.1 MB)
|| 1.1 MB 3.2 MB/s
Installing collected packages: psycopg2
Successfully installed psycopg2-2.8.5
17.4. Conversione degli script MySQL in script PostgreSQL

- La cartella [1] contenente gli script MySQL viene duplicata (Ctrl-C / Ctrl-V), quindi i nomi dei file vengono modificati in modo da corrispondere al loro contenuto;
17.4.1. [pgres_module]
Questo modulo è una copia del modulo [mysql_module] (vedere la sezione |script [mysql-04]: esecuzione di un file di comandi SQL|). Modificare le importazioni:
Invece di:
scriviamo:
La firma della funzione [display_info] era:
def afficher_infos(curseur: MySQLCursor):
Diventa:
def afficher_infos(curseur: cursor)
La firma della funzione [execute_list_of_commands] era:
def execute_list_of_commands(connexion: MySQLConnection, sql_commands: list,
suivi: bool = False, arrêt: bool = True, with_transaction: bool = True)
Diventa:
def execute_list_of_commands(connexion: connection, sql_commands: list,
suivi: bool = False, arrêt: bool = True, with_transaction: bool = True):
Altrimenti, non cambia nient'altro.
17.4.2. script [pgres_01]
Lo script [pgres_01] è una copia dello script [mysql_01] (vedere la sezione |script [mysql-01]: connessione a un database MySQL - 1|). Sono state apportate le seguenti modifiche:
Invece di:
Scriviamo:
Il resto rimane invariato. I risultati sono gli stessi di MySQL.
17.4.3. script [pgres_02]
Lo script [pgres_02] è una copia dello script [mysql_02] (vedere la sezione |script [mysql-02]: connessione a un database MySQL - 2|). Apportare le seguenti modifiche:
Invece di:
scriviamo:
I risultati non sono gli stessi dello script [mysql_02]:
Lo script [pgres_02] è il seguente:
Sebbene le righe 36–41 avrebbero dovuto visualizzare un messaggio di errore indicando che la connessione al DBMS non è riuscita, non viene visualizzato nulla. Infatti, dopo un'ulteriore analisi, vediamo che il codice entra effettivamente nel blocco [except] nelle righe 35–37, ma la variabile [error] è impostata su [None]. Ciò si verifica con la versione 2.8.4 del connettore [psycopg2].
Possiamo aggirare questo problema scrivendo un messaggio generico ma meno preciso:
I risultati sono i seguenti:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/databases/postgresql/pgres_02.py
Connexion réussie à la base database=dbpersonnes, host=localhost sous l'identité user=admpersonnes, passwd=nobody
Déconnexion réussie
Erreur de connexion à la base [dbpersonnes] par l'utilisateur [xx/yy]
Process finished with exit code 0
17.4.4. script [pgres_03]
Lo script [pgres_03] è una copia dello script [mysql_03] (vedere la sezione |script [mysql-03]: creazione di una tabella MySQL|). Sono state apportate le seguenti modifiche:
Invece di:
from mysql.connector import DatabaseError, InterfaceError, connect
from mysql.connector.connection import MySQLConnection
scriviamo:
from psycopg2 import DatabaseError, InterfaceError, connect
from psycopg2.extensions import connection
Inoltre, la firma della funzione [execute_sql], che era:
def execute_sql(connexion: MySQLConnection, update: str):
diventa:
def execute_sql(connexion: connection, update: str):
Il resto rimane invariato. Il risultato è il seguente:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/databases/postgresql/pgres_03.py
create table personnes (id int PRIMARY KEY, prenom varchar(30) NOT NULL, nom varchar(30) NOT NULL, age integer NOT NULL, unique(nom,prenom)) : requête réussie
Process finished with exit code 0
È possibile verificare l'esistenza della tabella [people] utilizzando lo strumento di amministrazione [pgAdmin]:

17.4.5. script [pgres_04]
Lo script [pgres_04] è una copia dello script [mysql_04] (vedere la sezione |script [mysql-04]: esecuzione di un file di comandi SQL|). Utilizza il modulo [pgres_module]:
Il resto rimane invariato.
Creiamo una configurazione [pgres pgres-04 without_transaction] come fatto nella sezione |script [mysql-04]: esecuzione di un file di comandi SQL|. Creiamo anche una configurazione [pgres pgres-04 with_transaction].
L'esecuzione della configurazione [pgres pgres-04 without_transaction] produce i seguenti risultati:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/databases/postgresql/pgres_04.py false
--------------------------------------------------------------------
Exécution du fichier SQL C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\databases\postgresql/data/commandes.sql sans transaction
--------------------------------------------------------------------
[drop table if exists personnes] : Exécution réussie
nombre de lignes modifiées : -1
[create table personnes (id int primary key, prenom varchar(30) not null, nom varchar(30) not null, age integer not null, unique (nom,prenom))] : Exécution réussie
nombre de lignes modifiées : -1
[insert into personnes(id, prenom, nom, age) values(1, 'Paul','Langevin',48)] : Exécution réussie
nombre de lignes modifiées : 1
[insert into personnes(id, prenom, nom, age) values (2, 'Sylvie','Lefur',70)] : Exécution réussie
nombre de lignes modifiées : 1
[select prenom, nom, age from personnes] : Exécution réussie
prenom, nom, age,
*****************
('Paul', 'Langevin', 48)
('Sylvie', 'Lefur', 70)
*****************
xx : Erreur (ERREUR: erreur de syntaxe sur ou près de « xx »
LINE 1: xx
^
)
[insert into personnes(id, prenom, nom, age) values (3, 'Pierre','Nicazou',35)] : Exécution réussie
nombre de lignes modifiées : 1
[insert into personnes(id, prenom, nom, age) values (4, 'Geraldine','Colou',26)] : Exécution réussie
nombre de lignes modifiées : 1
[insert into personnes(id, prenom, nom, age) values (5, 'Paulette','Girond',56)] : Exécution réussie
nombre de lignes modifiées : 1
[select prenom, nom, age from personnes] : Exécution réussie
prenom, nom, age,
*****************
('Paul', 'Langevin', 48)
('Sylvie', 'Lefur', 70)
('Pierre', 'Nicazou', 35)
('Geraldine', 'Colou', 26)
('Paulette', 'Girond', 56)
*****************
[select nom,prenom from personnes order by nom asc, prenom desc] : Exécution réussie
nom, prenom,
************
('Colou', 'Geraldine')
('Girond', 'Paulette')
('Langevin', 'Paul')
('Lefur', 'Sylvie')
('Nicazou', 'Pierre')
************
[select nom,prenom,age from personnes where age between 20 and 40 order by age desc, nom asc, prenom asc] : Exécution réussie
nom, prenom, age,
*****************
('Nicazou', 'Pierre', 35)
('Colou', 'Geraldine', 26)
*****************
[insert into personnes(id, prenom, nom, age) values(6, 'Josette','Bruneau',46)] : Exécution réussie
nombre de lignes modifiées : 1
[update personnes set age=47 where nom='Bruneau'] : Exécution réussie
nombre de lignes modifiées : 1
[select nom,prenom,age from personnes where nom='Bruneau'] : Exécution réussie
nom, prenom, age,
*****************
('Bruneau', 'Josette', 47)
*****************
[delete from personnes where nom='Bruneau'] : Exécution réussie
nombre de lignes modifiées : 1
[select nom,prenom,age from personnes where nom='Bruneau'] : Exécution réussie
nom, prenom, age,
*****************
*****************
--------------------------------------------------------------------
Exécution terminée
--------------------------------------------------------------------
Il y a eu 1 erreur(s)
xx : Erreur (ERREUR: erreur de syntaxe sur ou près de « xx »
LINE 1: xx
^
)
Process finished with exit code 0
- Riga 5: Abbiamo dovuto modificare il comando per eliminare la tabella [people]. A differenza del connettore MySQL, il connettore PostgreSQL genera un'eccezione se la tabella da eliminare non esiste. Il comando [drop table] ha una variante, [drop table if exists], che non genera un'eccezione se la tabella non esiste. L'abbiamo utilizzata qui. Questo è un esempio in cui due DBMS non si comportano allo stesso modo in situazioni simili;
La tabella [people] nello strumento [pgAdmin] è la seguente:

L'esecuzione della configurazione [pgres pgres_04 with_transaction] produce i seguenti risultati:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/databases/postgresql/pgres_04.py true
--------------------------------------------------------------------
Exécution du fichier SQL C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\databases\postgresql/data/commandes.sql avec transaction
--------------------------------------------------------------------
[drop table if exists personnes] : Exécution réussie
nombre de lignes modifiées : -1
[create table personnes (id int primary key, prenom varchar(30) not null, nom varchar(30) not null, age integer not null, unique (nom,prenom))] : Exécution réussie
nombre de lignes modifiées : -1
[insert into personnes(id, prenom, nom, age) values(1, 'Paul','Langevin',48)] : Exécution réussie
nombre de lignes modifiées : 1
[insert into personnes(id, prenom, nom, age) values (2, 'Sylvie','Lefur',70)] : Exécution réussie
nombre de lignes modifiées : 1
[select prenom, nom, age from personnes] : Exécution réussie
prenom, nom, age,
*****************
('Paul', 'Langevin', 48)
('Sylvie', 'Lefur', 70)
*****************
xx : Erreur (ERREUR: erreur de syntaxe sur ou près de « xx »
LINE 1: xx
^
)
--------------------------------------------------------------------
Exécution terminée
--------------------------------------------------------------------
Il y a eu 1 erreur(s)
xx : Erreur (ERREUR: erreur de syntaxe sur ou près de « xx »
LINE 1: xx
^
)
Process finished with exit code 0
La tabella [people] nello strumento [pgAdmin] è la seguente:

Qui, il risultato differisce da quello ottenuto con MySQL. Se eseguiamo gli script nelle stesse condizioni, ovvero dopo aver eseguito lo script senza una transazione, otteniamo i seguenti risultati:
- Con MySQL, la tabella [people] è vuota;
- con PostgreSQL, la tabella [people] non è vuota;
La differenza sta nei diversi modi in cui questi due DBMS eseguono il rollback della transazione:
- MySQL non esegue il rollback dei comandi [drop table] e [create table]. Ci ritroviamo con una tabella [people] vuota;
- PostgreSQL annulla i comandi [drop table] e [create table]. La tabella viene riportata allo stato in cui si trovava prima dell'esecuzione dello script tramite una transazione;
17.4.6. script [pgres_05]
Lo script [pgres_05] è una copia dello script [mysql_05] (vedere la sezione |script [mysql-05]: utilizzo di query parametrizzate|). Lo script è modificato come segue:
Invece di:
scriviamo:
Il resto rimane invariato.
I risultati ottenuti in [pgAdmin] sono i seguenti:

17.5. Conclusione
Il porting degli script MySQL in script PostgreSQL è stato relativamente semplice. Si tratta di un'eccezione. I due DBMS non supportano le stesse convenzioni di denominazione per gli oggetti SQL (database, tabelle, colonne, vincoli, tipi di dati, ecc.) e presentano estensioni SQL incompatibili... Per garantire un porting semplice, è necessario attenersi allo standard SQL in entrambi i casi senza tentare di utilizzare le estensioni proprietarie del DBMS. Ciò va a discapito delle prestazioni.