Introduzione
Questa API e' stata definita per incoraggiare l'uniformita' tra i
moduli Python usati per accedere ai database. Facendo questo si
vogliono raggiungere una buona consistenza per una
piu' semplice comprensione dei moduli, un codice che sia
piu' portabile su diversi database ed offrire un piu' ampio raggio di
connettivita' ai database con Python.
La specifica dell' interfaccia consiste di diverse sezioni:
* Interfaccia del modulo
* Oggetti Connection
* Oggetti Cursor
* Oggetti DBI Helper
* Oggetti Type e Costruttori
* Suggerimenti per l'implementazione
* Principali modifiche dalla 1.0 alla 2.0
Commenti e domande su queste specifiche debbono essere inviate
al SIG per l'interfacciamento ai database con Python
(db-sig@python.org).
Per maggiori informazioni sull'interfacciamento con i database con
python e per i pacchetti disponibili si veda il Database Topic
Guide su http://www.python.org/topics/database/.
Questo documento descrive le specifiche API dei Database in Python 2.0
ed un insieme di estensioni opzionali comuni. La precedente versione 1.0
e' comunque disponibile come riferimento, in PEP 248. Gli autori di
pacchetti sono incoraggiati ad usare questa versione delle specifiche
come base per le nuove interfacce.
Interfaccia del modulo
L'accesso al database è reso possibile tramite oggetti di connessione.
I moduli devono fornire il seguente costruttore per questi:
connect(parameters...)
Costruttore per la creazione di una connessione al database.
Ritorna un oggetto Connection. Richiede un numero di
parametri che sono dipendenti dal database. [1]
Devono essere definiti questi moduli globali:
apilevel
Costante stringa che specifica il livello della API DB supportata.
Attualmente sono permesse solo le stringhe '1.0' e '2.0'.
Se non specificata, sarà assunto che l'interfaccia si basi sulla
DataBase-API 1.0.
threadsafety
Costante intera che specifica il livello di supporto al multithreading
offerto. I valori possibili sono:
0 i thread non possono condividere il modulo.
1 i thread possono condividere il modulo ma non le connessioni.
2 i thread possono condividere il modulo e le connessioni.
3 i thread possono condividere il modulo, le connessioni e i cursori.
Condivisione nel contesto soprastante significa che due thread
possono usare una risorsa senza aver bisogno di implementare un
meccanismo di lock tramite semafori mutex. Si noti che non sempre si può
rendere 'a prova di thread' ['thread safe' NdT] una risorsa
esterna con un controllo degli accessi tramite mutex: la risorsa
potrebbe fare riferimento a variabili globali o altre risorse
esterne che sono al di fuori del nostro controllo.
paramstyle
Costante stringa che specifica il tipo di formattazione del marcatore
di parametro previsto dall'interfaccia. I valori possibili sono [2]:
'qmark' con punto interrogativo,
p.e. '...WHERE name=?'
'numeric' in stile numerico, posizionale,
p.e. '...WHERE name=:1'
'named' per nome,
p.e. '...WHERE name=:name'
'format' nel codice di formato della printf ANSI C,
p.e. '...WHERE name=%s'
'pyformat' nei codici di formato estesi Python,
p.e. '...WHERE name=%(name)s'
Il modulo deve rendere disponibile ogni informazione d'errore attraverso
le eccezioni seguenti o loro sottoclassi:
Warning
Eccezione sollevata per avvisi importanti, quali troncamento di dati nel
corso del loro inserimento, ecc. Dev'essere una sottoclasse dell'eccezione
Python StandardError (definita nel modulo exceptions).
Error
Eccezione che costituisce la classe base di tutte le altre eccezioni
di errore. La si può utilizzare per catturare tutti gli errori con un
singolo comando di 'except'. I warning non sono considerati errori
perciò non debbono usare questa classe come base. Dev'essere una
sottoclasse dell'eccezione Python StandardError (definita nel modulo
exceptions).
InterfaceError
Eccezione sollevata per errori legati all'interfaccia più che al database
stesso. Dev'essere una sottoclasse di Error.
DatabaseError
Eccezione sollevata per errori sul database. Dev'essere una sottoclasse
di Error.
DataError
Eccezione sollevata per errori dovuti a problemi con dati calcolati
come divisioni per zero, valori numerici fuori dall'intervallo consentito,
ecc. Dev'essere una sottoclasse di DatabaseError.
OperationalError
Eccezione sollevata per errori legati a operazioni del database non
necessariamente sotto il controllo del programmatore, per esempio quando
avviene una disconnessione inaspettata, non viene trovato il nome della
sorgente dei dati, non è possibile effettuare una transazione, si ha un
errore di memoria durante l'elaborazione, ecc.
Dev'essere una sottoclasse di DatabaseError.
IntegrityError
Eccezione sollevata quando è interessata l'integrità relazionale del
database, p.e. fallisce l'ispezione di una chiave esterna. Dev'essere
una sottoclasse di DatabaseError.
InternalError
Eccezione sollevata per un errore interno del database, p.e. il cursore
non è più valido, la transazione non è sincronizzata ecc. Dev'essere una
sottoclasse di DatabaseError.
ProgrammingError
Eccezione sollevata in caso si tratti di errori diretti di programmazione,
p.e. una tabella non viene trovata o è già esistente, c'è un errore di
sintassi nel comando SQL, è stato specificato un numero errato di
parametri, ecc. Dev'essere una sottoclasse di DatabaseError.
NotSupportedError
Eccezione sollevata quando viene usato un metodo o un'API non supportato
dal database, p.e. viene richiesto un .rollback() su una connessione che
non supporta le transazioni o le ha disabilitate. Dev'essere una
sottoclasse di DatabaseError.
Questo è lo schema di ereditarietà delle eccezioni:
StandardError
|__Warning
|__Error
|__InterfaceError
|__DatabaseError
|__DataError
|__OperationalError
|__IntegrityError
|__InternalError
|__ProgrammingError
|__NotSupportedError
Nota: I valori di tali eccezioni non sono definiti. Dovrebbero comunque fornire
all'utente un'idea abbastanza chiara sull'origine del problema.
Oggetti Connection
Gli Oggetti Connection debbono rispondere ai metodi seguenti:
.close()
Chiude la connessione immediatamente (piuttosto che quando __del__ è invocato).
La connessione sarà inutilizzabile da qui in avanti: in caso venga tentata
una qualsiasi operazione sulla connessione verrà sollevata un'eccezione
Error (o una sua sottoclasse). Lo stesso accade a tutti gli oggetti Cursor
che tentino di usare la connessione. Chiudendo una connessione
senza effettuare un commit sulle modifiche si causa un rollback implicito sui dati.
.commit()
Effettua il commit di qualsiasi transazione in sospeso. Si noti che se
il database supporta nativamente una funzionalità di auto-commit essa
dev'essere disabilitata all'inizio. L'interfaccia può comprendere un
metodo per rimetterla in funzione.
I moduli per database che non supportano le transazioni debbono
comunque implementarlo con funzionalità nulla.
.rollback()
Questo metodo è opzionale, dato che non tutti i database offrono supporto
alle transazioni. [3]
In caso un database supporti le transazioni, questo metodo fa sì che esso
ritorni allo stato precedente a ogni transazione in sospeso. Chiudere una
connessione senza prima effettuare il commit delle modifiche provocherà
l'esecuzione di un rollback implicito.
.cursor()
Restituisce un nuovo oggetto Cursore utilizzando la connessione. Se il
database non fornisce supporto diretto al concetto di cursore, il modulo
dovrà emulare i cursori con altri mezzi realizzando quanto e'
necessario per soddisfare queste specifiche. [4]
Oggetti Cursore
Questi oggetti rappresentano un cursore del database, usato nella gestione del
contesto di un'operazione di fetch. Cursori creati dalla stessa connessione
non sono isolati, ad esempio, qualsiasi cambiamento fatto al database da un cursore
è immediatamente visibile dall'altro cursore. Cursori creati da differenti connessioni
possono essere o meno isolati, in base a come è implementata la gestione delle transazioni (vedi
anche i metodi di connessione rollback() e commit().)
Gli oggetti Cursore debbono rispondere ai seguenti metodi e attributi:
.description
È un attributo a sola lettura che consiste di una sequenza di sequenze
a sette elementi. Ognuna di queste sequenze contiene informazioni che
descrivono una colonna di risultati: (name, type_code, display_size,
internal_size, precision, scale, null_ok). I primi due campi (name e
type_code) sono obbligatori, gli altri cinque sono opzionali e devono
essere impostati a None se non sono forniti valori significativi.
L'attributo sarà None per
operazioni che non restituiscono righe, o nel caso non sia stata ancora
effettuata un'operazione tramite il metodo executeXXX().
type_code può essere interpretato comparandolo agli oggetti Tipo
specificati più sotto.
.rowcount
È un attributo a sola lettura che specifica il numero di righe che
l'ultimo executeXXX() ha prodotto (per comandi DQL come select)
o comunque interessato (per comandi DML come update o insert).
Il valore dell'attributo è pari a -1 nel caso non sia stato eseguito
alcun executeXXX() sul cursore o il conteggio delle righe dell'ultima
operazione non sia calcolabile dall'interfaccia. [7]
Nota: Versioni future delle specifiche DB API potrebbero ridefinire
quest'ultimo caso e far restituire None anziche' -1.
.callproc(nomeprocedura[,parametri])
(Questo metodo è opzionale, dato che non tutti i database supportano
le "stored procedure". [3])
Invoca la stored procedure del database passata con nomeprocedura.
La sequenza dei parametri deve contenere una voce per ogni argomento
richiesto dalla procedura. Il risultato della chiamata è restituito
come copia modificata della sequenza in ingresso. I parametri in
ingresso non vengono toccati, i parametri in uscita e ingresso/uscita
vengono eventualmente rimpiazzati con nuovi valori.
La procedura può anche fornire come risultato un insieme di righe,
che dev'essere quindi reso disponibile attraverso i metodi standard
fetchXXX().
.close()
Chiude il cursore al momento (piuttosto che alla chiamata di __del__).
Il cursore non sarà più utilizzabile da qui in avanti: in caso venga
tentata una qualsiasi operazione sul cursore verrà sollevata un'eccezione
Error (o una sua sottoclasse).
.execute(operazione[,parametri])
Prepara ed esegue un'operazione sul database (interrogazione o comando).
I parametri possono essere passati come sequenza o dizionario e verranno
associati a variabili nell'operazione. Le variabili sono specificate in
una notazione specifica al database (si veda l'attributo di modulo
paramstyle per i dettagli). [5]
Il cursore conserverà un riferimento all'operazione. Se lo stesso oggetto
operazione gli verrà passato nuovamente, il cursore sara' così in grado
di ottimizzare il suo comportamento. Questo è molto efficace in caso di
algoritmi in cui la stessa operazione viene svolta molte volte con
parametri diversi.
Per garantire la massima efficienza nel riuso di un'operazione è bene
usare il metodo setinputsizes() per specificare anzitempo i tipi
e le dimensioni dei parametri. Un parametro può anche non
corrispondere all'informazione definita in tal modo; l'implementazione
deve compensare automaticamente, a prezzo di un'eventuale perdita
in efficienza.
I parametri possono anche essere specificati come lista di tuple,
p.e. per inserire più righe in una singola operazione, ma quest'uso
è sconsigliato, si deve piuttosto usare executemany().
I valori restituiti non sono definiti.
executemany(operazione,sequenza_di_parametri)
Prepara un'operazione sul database (interrogazione o comando),
quindi la esegue usando tutte le sequenze o dizionari di parametri
trovati in sequenza_di_parametri.
I moduli sono liberi di implementare questo metodo usando
invocazioni multiple del metodo execute() o usando operazioni
con array per far sì che il database processi la sequenza come
un tutt'uno in una singola chiamata.
L'uso di questo metodo per una operazione che produce uno o più set
di risultati ha un comportamento non stabilito ed e' possibile (ma non richiesto)
che venga sollevata un'eccezione per indicare che e' stato generato un
result set.
A questo metodo si applicano le stesse considerazioni espresse per
execute().
I valori restituiti non sono definiti.
.fetchone()
Preleva la riga seguente dal risultato di un'interrogazione,
restituendo una singola sequenza oppure None quando non ci sono
più dati disponibili. [6]
In caso l'ultima invocazione di executeXXX() non abbia prodotto
alcun risultato o non sia ancora stata inoltrata alcuna invocazione,
verrà sollevata un'eccezione Error (o una sua sottoclasse).
.fetchmany([size=cursor.arraysize])
Preleva l'insieme successivo di righe facenti parti del risultato
di un'interrogazione, restituendo una sequenza di sequenze (p.e.
una lista di tuple). In caso non siano più disponibili altre righe,
viene restituita una sequenza vuota.
Il numero di righe da prelevare a ogni chiamata è specificato dal
parametro. Se non viene passato esplicitamente, sarà l'attributo
arraysize del cursore a determinare il numero di righe che verranno
prelevate. Il metodo deve tentare di prelevare tante righe quante
indicate dal parametro size. Ove questo non fosse possibile perché
non ci sono abbastanza righe disponibili, ne verranno restituite in
numero minore.
In caso l'ultima invocazione di executeXXX() non abbia prodotto
alcun risultato o non sia ancora stata inoltrata alcuna invocazione,
verrà sollevata un'eccezione Error (o una sua sottoclasse).
Si noti che ci sono alcune considerazioni da fare circa l'influenza
del parametro size sulle prestazioni. Per ottenere prestazioni
ottimali di solito la cosa migliore è usare l'attributo arraysize.
Se viene passato esplicitamente un parametro size, risulta ottimale
mantenere lo stesso valore nelle invocazioni successive di fetchmany().
.fetchall()
Preleva tutte le righe (restanti) dal risultato di una interrogazione,
restituendole come sequenza di sequenze (p.e. una lista di tuple). Si
noti che l'attributo arraysize del cursore può influenzare la velocità
dell'operazione.
In caso l'ultima invocazione di executeXXX() non abbia prodotto alcun
risultato o non sia ancora stata inoltrata alcuna invocazione, verrà
sollevata un'eccezione Error (o una sua sottoclasse).
.nextset()
Questo metodo è opzionale, dato che non tutti i database supportano
risultati multipli. [3]
Questo metodo farà saltare il cursore al prossimo risultato disponibile,
scartando tutte le righe rimanenti del risultato corrente.
Se non ci sono più altri risultati, il metodo restituisce None.
In caso contrario restituisce un valore vero, e le chiamate ai
metodi fetch che seguono restituiranno righe appartenenti al nuovo
risultato.
In caso l'ultima invocazione di executeXXX() non abbia prodotto alcun
risultato o non sia ancora stata inoltrata alcuna invocazione verrà
sollevata un'eccezione Error (o una sua sottoclasse).
.arraysize
Questo attributo scrivibile specifica il numero di righe da prelevare
in una singola invocazione di fetchmany(). Il valore predefinito è 1,
il che significa che verrà prelevata una sola riga alla volta.
Le diverse implementazioni devono rispettare questo valore per
quanto concerne il metodo fetchmany(), ma sono libere di interagire
con il database una singola riga alla volta. Tale valore può essere
usato anche nell'implementazione di executemany().
.setinputsizes(sizes)
Questo attributo può essere usato prima di un executeXXX()
per definire in anticipo aree di memoria per i parametri
dell'operazione.
sizes è specificato come sequenza, in cui a ogni elemento corrisponde
un parametro in ingresso. L'elemento deve essere un oggetto Tipo
che corrisponda a quanto verrà usato in ingresso, oppure un intero che
specifichi la lunghezza massima di una stringa parametro. Se l'elemento
è None, allora non verrà riservata un'area di memoria predefinita per
la colonna corrispondente (utile per nel caso di input molto grossi).
Questo metodo deve essere usato prima dell'invocazione di
executeXXX().
Le varie implementazioni sono libere di non fargli fare in realtà
nulla e gli utenti possono tranquillamente fare a meno di usarlo.
.setoutputsize(size[,column])
Imposta la dimensione del buffer di una colonna, utile nel caso
di prelievi di dati voluminosi (p.e. LONG, BLOB, ecc.). La colonna
è specificata secondo l'indice nella sequenza risultato. Se non viene
specificata, allora tali dimensioni di default verranno applicate a
tutte le colonne molto ampie del cursore.
Questo metodo deve essere usato prima dell'invocazione di
executeXXX().
Le varie implementazioni sono libere di non fargli fare in realtà
nulla e gli utenti possono tranquillamente fare a meno di usarlo.
Oggetti Tipo e Costruttori
Molti database hanno bisogno di avere l'input in un particolare formato per
associarlo ai parametri di ingresso di un'operazione. Per esempio, se un input
è destinato a finire in una colonna DATE allora deve essere
fornita al database con stringa di un formato particolare. Problemi simili
esistono per colonne rowid
[indicatore univoco di una riga in una tabella, tipicamente utilizzato nei database NdT]
o dati binari molto voluminosi (p.e. BLOB o colonne di tipo RAW).
Tutto ciò è fonte di potenziali problemi per Python, dato che i parametri per metodo executeXXX()
non sono tipizzati. Quando il modulo database vede un oggetto stringa Python, non sa se debba
essere connesso come una semplice colonna CHAR, come un elemento BINARY o come una DATE.
Per risolvere questo problema, un modulo deve fornire i costruttori definiti più avanti
per creare oggetti che possono conservare valori speciali. Quando verranno passati ai
metodi del cursore il modulo potrà discernere il tipo appropriato del parametro
d'ingresso e connetterlo opportunamente.
L'attributo description di un oggetto Cursore restituisce informazioni su ciascuna delle colonne
del risultato di un'interrogazione. type_code dev'essere uguale a uno degli oggetti
Tipo definiti più. Gli oggetti Tipo possono essere uguali a più di un codice tipo
(p.e. DATETIME potrebbe essere uguale ai codici tipo per le colonne date, time e
timestamp si veda Suggerimenti per l'implementazione per i dettagli).
Il modulo esporta i seguenti costruttori e insiemi a singolo elemento:
Date(anno,mese,giorno)
Questa funzione crea un oggetto che contiene un valore DATE.
Time(ore,minuti,secondi)
Questa funzione crea un oggetto che contiene un valore TIME.
Timestamp(anno,mese,giorno,ore,minuti,secondi)
Questa funzione crea un oggetto che contiene un valore TIMESTAMP.
DateFromTicks(istante)
Questa funzione crea un oggetto che contiene un valore DATE che parte da un dato
valore di tick (in secondi dall'ora zero del sistema, si veda la documentazione
del modulo Python standard time per dettagli).
TimeFromTicks(ticks)
Questa funzione crea un oggetto che contiene un valore TIME che parte dal numero
di tick dato (in secondi dall'ora zero del sistema, si veda la documentazione del
modulo Python standard time per dettagli).
TimestampFromTicks(ticks)
Questa funzione crea un oggetto che contiene un valore TIMESTAMP che parte da un
dato valore di tick (in secondi dall'ora zero del sistema, si veda la documentazione
del modulo Python standard time per dettagli).
Binary(string)
Questa funzione crea un oggetto capace di contenere un valore stringa binario (lungo).
STRING
Questo oggetto tipo è usato per descrivere colonne basate su stringhe (p.e. CHAR).
BINARY
Questo oggetto tipo è usato per descrivere colonne di dati binari (lunghi)
(p.e. LONG, RAW, BLOB).
NUMBER
Questo oggetto tipo è usato per descrivere colonne numeriche.
DATETIME
Questo oggetto tipo è usato per descrivere colonne DATE/TIME.
ROWID
Questo oggetto tipo è usato per descrivere colonne Identificativo.
I valori NULL di SQL sono rappresentati in Python da None sia in ingresso che in uscita.
Nota: l'uso dei tick Unix per interfacciarsi a database può causare problemi a causa dell'intervallo
limitato di date che sono in grado di coprire.
Suggerimenti per l'implementazione
* I tipi di oggetto consigliati per date e orari sono quelli definiti nel package mxDateTime.
Esso fornisce tutti i costruttori e i metodi necessari sia a livello Python che C.
* Il tipo consigliato per gli oggetti Binary sono i buffer, disponibili come standard in Python a
partire dalla versione 1.5.2. Si faccia riferimento alla documentazione Python per i dettagli.
Per informazioni sull'interfaccia C si veda Include/bufferobject.h e Objects/bufferobject.c
nella distribuzione dei sorgenti Python.
* A partire da Python 2.3, gli autori dei moduli possono anche usare l'oggetto types definito
nel modulo standard datetime per la gestione di date/tempi.
Comunque, deve essere chiaro che questo non rende disponibile una API C come mxDateTime,
il che significa che l'integrazione con moduli per database basati su C è più difficoltosa.
* Ecco una semplice implementazione dei costruttori per date e orari basati su tick Unix che
delegano il lavoro ai costruttori generici:
import time
def DateFromTicks(ticks):
return apply(Date,time.localtime(ticks)[:3])
def TimeFromTicks(ticks):
return apply(Time,time.localtime(ticks)[3:6])
def TimestampFromTicks(ticks):
return apply(Timestamp,time.localtime(ticks)[:6])
* Questa classe Python permette di implementare i tipi di oggetti summenzionati anche nel caso
in cui il campo type_code dell'attributo description fornisce valori multipli per un oggetto Tipo:
class DBAPITypeObject:
def __init__(self,*values):
self.values = values
def __cmp__(self,other):
if other in self.values:
return 0
if other < self.values:
return 1
else:
return -1
L'oggetto Tipo risultante eguaglia tutti i valori passati al costruttore.
* Ecco un pezzetto di codice Python che implementa la gerarchia delle eccezioni definita in precedenza:
import exceptions
class Error(exceptions.StandardError):
pass
class Warning(exceptions.StandardError):
pass
class InterfaceError(Error):
pass
class DatabaseError(Error):
pass
class InternalError(DatabaseError):
pass
class OperationalError(DatabaseError):
pass
class ProgrammingError(DatabaseError):
pass
class IntegrityError(DatabaseError):
pass
class DataError(DatabaseError):
pass
class NotSupportedError(DatabaseError):
pass
In C si può usare l'API PyErr_NewException(fullname, base, NULL) per creare gli oggetti Eccezione.
Estensioni DB API opzionali
Durante l'esistenza della DB API 2.0, gli autori di moduli hanno
spesso estero le loro implementazioni oltre quanto richiesto da
questa specifica DB API. Per aumentare la compatibilità e per
fornire un percorso chiaro di aggiornamento alle possibili versioni
future della specifica, questa sezione definisce un insieme di
estensioni comuni al nucleo della specifica DB API 2.0.
Come con tutte le opzioni facoltative di DB API, gli autori del modulo
del database sono liberi di non implementare questi attributi e metodi
opzionali (usandoli verrà provocata una AttributeError) o raggiunta
una NotSupportedError nel caso la disponibilità possa essere controllata
soltanto durante l'esecuzione.
È stato proposto di rendere l'uso di queste estensioni facoltativamente
visibile al programmatore sollevando warning con il meccanismo di warning di Python. Per rendere
questa caratteristica utile, i messaggi d'avvertimento devono
essere standardizzati per poterli mascherare.
Questi messaggi standard verranno chiamati sotto "Warning Message".
Cursor Attribute .rownumber
Questo attributo in sola lettura deve fornire l'indice
corrente, a partire da 0, del cursore nel set di risultati o None se
l'indice non può essere determinato.
L'indice può essere visto come indice del cursore in una sequenza
(il set di risultati). Le successive operazioni di fetch prenderanno
la colonne indicizzata da .rownumber nella sequenza.
Warning Message: "DB-API extension cursor.rownumber used"
Connection Attributes .Error, .ProgrammingError, etc.
Tutte le classi d'eccezione definite dalla DB API
debbono essere rese disponibili sugli oggetti Connection
come attributi (oltre ad essere disponibili a livello di modulo).
Questi attributi facilitano il trattamento degli errori negli
ambienti in multi-collegamento.
Warning Message: "DB-API extension connection.<exception> used"
Cursor Attributes .connection
Questo attributo in sola lettura ritorna un riferimento al
Connection object sul quale il cursore è stato creato.
L'attributo semplifica la scrittura di codice polimorfo
in ambienti in multi-connessione.
Warning Message: "DB-API extension cursor.connection used"
Cursor Method .scroll(value[,mode='relative'])
Sposta il cursore nel result-set alla nuova posizione a secondo dell'impostazione di mode.
Se mode è 'relative'(di default), il valore è preso come
uno spostamento rispetto all'attuale posizione nel set di risultato, se
il valore di mode e' 'absolute' il valore viene interpretato come una posizione assoluta.
Un IndexError deve essere sollevato nel caso una operazione
di scroll cerchi di spostare il cursore al di fuori del result set.
In questo caso la posizione del cursore resta indefinita (l'ideale
sarebbe che il cursore non si spostasse per nulla).
Warning Message: "DB-API extension cursor.scroll() used"
Cursor Attribute .messages
Questo è un oggetto lista di Python a cui l'interfaccia
appende le tuples (classe exception, valore exception) per tutti
i messaggi che le interfacce ricevono dal database utilizzato
per questo cursore.
La lista è pulita automaticamente da tutte le chiamate standard ai metodi su cursori
(prima dell'esecuzione della chiamata) tranne per le chiamate
.fetchXXX() per evitare l'uso eccessivo di memoria
e può anche essere pulita eseguendo "del cursor.messages[:]".
Tutti i messaggi di warning e errore generati dal database sono
posti in questa lista, così che il controllo della lista
permetta all'utente di verificare la correttezza dell'operazione
del metodo chiamato.
Lo scopo di questa lista di attributi è di eliminare la necessita
per una eccezione Warning che causa spesso problemi (alcuni
warning hanno realmente spesso solo carattere informativo).
Warning Message: "DB-API extension cursor.messages used"
Connection Attribute .messages
Uguale come cursor.messages a parte che i messaggi nella
lista sono orientati alla connessione.
La lista è pulita automaticamente da tutte le chiamate standard ai metodi su connection
(prima dell'esecuzione della chiamata) tranne per le chiamate
.fetchXXX() per evitare l'uso eccessivo di memoria
e può anche essere pulita eseguendo "del connection.messages[:]".
Warning Message: "DB-API extension connection.messages used"
Cursor Method .next()
Ritorna la successiva colonna dallo statement sql in esecuzione
usando la stessa semantica di .fetchone(). Una eccezione
StopIteration è raggiunta quando il set di risultati è terminato
per le versioni Python 2.2 e successive. Versioni precedenti non
hanno l'eccezione StopIteration e così il metodo deve raggiungere
invece un errore IndexError.
Warning Message: "DB-API extension cursor.next() used"
Cursor Method .__iter__()
Restituisce self per rendere i cursori compatibili con il protocollo di iterazione.
Warning Message: "DB-API extension cursor.__iter__() used"
Cursor Attribute .lastrowid
Questo attributo in sola lettura fornisce il rowid della
ultima colonna modificata (molti database ritornano un rowid
quando una operazione di INSERT singola è eseguita).
Se l'operazione non dovesse restituire un rowid o se il database non li supportasse
l'attributo deve essere impostato a None.
Le semantiche di .lastrowid sono indefinite nel caso l'ultimo
statement eseguito ha modificato più di una riga, e.g.
usando INSERT con .executemany().
Warning Message: "DB-API extension cursor.lastrowid used"
Estensioni alla gestione d'errore opzionali
Il nucleo della specifica DB API introduce soltanto un
insieme di eccezioni che possono essere sollevate per segnalare
gli errori all'utente. In alcuni casi, le eccezioni possono essere
troppo distruttive per il flusso di un programma o persino rendere
l'esecuzione impossibile.
Per questi casi e per facilitare il trattamento degli errori quando
si lavora con database, gli autori del modulo del database possono
scegliere di implementare gestori di eccezioni definibili dall'utente.
Questa sezione descrive una via standard di definizione di
questi gestori di eccezioni.
Cursor/Connection Attribute .errorhandler
Attributi modificabili che riferiscono un gestore di eccezioni
da chiamare nel caso venga incontrata una condizione d'errore.
Il gestore di eccezioni deve essere un chiamabile Python con i
i seguenti parametri: errorhandler(connection, cursor, errorclass, errorvalue)
dove connection e' un riferimento alla connessione su cui il cursore opera,
il cursor e' un riferimento al cursore (o None nel caso in cui l'eccezione non si
riferisca ad un cursore), errorclass e' una classe d'errore da istanziare utilizzando
errorvalue come costruttore.
Il gestore di errori standard deve aggiungere le informazioni di
errore all'attributo .messages adatto
(connection.messages o cursor.messages) e sollevare l'eccezione
definita dai dati errorclass e parametri errorvalue.
Se non e' definito alcune errorhandler (l'attributo e' impostato a None), la gestione
standard degli errori deve essere applicata come indicato sopra.
Warning Message: "DB-API extension .errorhandler used"
I cursori debbono ereditare le impostazioni di errorhandler dagli oggetti connection
al momento della creazione del cursore.
Frequently Asked Questions
Il SIG dei database vede spesso ricorrenti domande
circa la specifica DB API. Questa sezione copre alcune delle
problematiche piu' comuni sulla specifica.
Domanda:
Come si puo' costruire un dizionario di tuples ottenuto
da .fetchxxx():
Risposta:
Ci sono parecchi tools attualmente disponibili che forniscono
assistenza per questa operazione. La maggior parte di loro usa
i nomi della colonna contenuti nell'attributo
.description del cursore come base per definire le chiavi nel dizionario.
La ragione per non estendere le specifiche delle DB API
e' che presenta alcuni svantaggi:
* Alcuni database non supportano case-sensitive nomi di colonne
o auto convertono a lowercase o uppercase tutti i caratteri.
* Le colonne nel set di risultati i quali sono generati dalla
query (e.g. utilizzando funzioni SQL) non fanno riferimento
a colonne e i database definiscono nomi che sono specifici per ogni database.
Come risultato, accedendo alle colonne attraverso chiavi del
dizionario varia fra database e rende la scrittura
di codice portabile impossibile.
Principali modifiche dall'API v.1.0 alla v.2.0
L'API Python per i Database v.2.0 introduce un numero limitato di cambiamenti importanti rispetto
alla versione 1.0. Dato che alcuni di questi saranno causa di errori fatali negli script basati
sulla vecchia DB API 1.0, si è deciso di variare il numero di versione principale.
Ecco i cambiamenti più significativi dalla versione 1.0:
* Non è più necessario un modulo dbi separato, le sue funzionalità sono state fuse
nell'interfaccia di modulo.
* Sono stati aggiunti nuovi costruttori e oggetti Tipo per i valori di date e orari. Il tipo
di oggetto RAW è stato rinominato come BINARY. L'insieme di tipi risultante deve
coprire tutti i tipi di dato fondamentali che si trovano comunemente nei moderni database SQL.
* Sono state aggiunte nuove costanti (apilevel, threadlevel, paramstyle) e metodi (executemany, nextset)
per garantire connessioni migliori ai database.
* Le semantiche di .callproc() necessarie a chiamare stored procedure sono ora definite chiaramente.
* La definizione del valore restituito da .execute() è stata cambiata. In precedenza il valore
restituito era basato sul tipo di operazione SQL (difficile da implementare in modo soddisfacente).
Ora il tipo non è definito, si usi piuttosto il più flessibile attributo .rowcount. I moduli sono
liberi di restituire valori nel vecchio stile, ma le specifiche non se ne occupano più e debbono
essere considerati dipendenti dalle singole interfacce [e da non utilizzarsi nell'ottica di
portabilità del codice NdT].
* È stata incorporata nelle specifiche una gerarchia di eccezioni basata sulle classi. Gli
implementatori del modulo sono liberi di estendere lo schema presentato in questo documento
tramite sottoclassi.
Aggiunte alle specifiche DB API 2.0 successive alla pubblicazione:
* Sono state inserite estensioni opzionali alle DB API.
Questioni ancora in sospeso
Sebbene la versione 2.0 delle specifiche chiarisca molti punti che erano stati lasciati in sospeso
nella versione 1.0, rimangono ancora alcune questioni da affrontare:
Definire un valore di ritorno utile per .nextset() nel caso in cui un nuovo risultato sia
effettivamente disponibile.
Creare un tipo numerico a virgola fissa da usare come formato di scambio senza arrotondamenti
per valori monetari e decimali.
Note a pie' di pagina
[1] Come linea guida, i parametri del costruttore di Oggetti Connection debbono essere implementati
come argomenti a parola chiave per un uso piu' intuitivo e seguire quest'ordine:
dsn = Nome della sorgente dei dati come stringa
user = Nome dell'utente come stringa (opzionale)
password = Password come stringa (opzionale
host = Nome dell'host (opzionale)
database = Nome del database (opzionale)
ecco un esempio di un tale uso di connect:
connect(dsn='myhost:MYDB',user='guido',password='234$')
[2] Gli autori del modulo debbono preferire i formati 'numeric', 'named' o 'pyformat'
sugli altri, dato che offrono maggior chiarezza e flessibilità.
[3] Se il database non supporta la funzionalità richiesta dal metodo, l'interfaccia deve
sollevare un'eccezione in caso venga invocato.
L'approccio preferito è non implementare il metodo, in modo che Python generi un'eccezione
AttributeError in caso di richiesta del metodo. Ciò permette al programmatore di verificare
quanto può offrire il database usando la funzione standard hasattr().
Per alcune interfacce configurate dinamicamente potrebbe non essere appropriato richiedere
di rendere disponibile il metodo dinamicamente. Queste interfacce debbono quindi sollevare
un'eccezione NotSupportedError per indicare l'incapacità di eseguire il roll-back allorché il
metodo venisse invocato.
[4] Un'interfaccia può scegliere di supportare i cursori con nome permettendo l'aggiunta di un argomento
stringa al metodo. Questa caratteristica non viene contemplata nelle specifiche, dato che
complicherebbe le semantiche dei metodi .fetchXXX().
[5] Il modulo userà il metodo __getitem__ degli oggetti dei parametri per mappare o le posizioni
(interi) o i nomi (stringhe) dei valori dei parametri. Questo permette di accettare in ingresso
sia sequenze che dizionari.
Il termine "associato" si riferisce al processo di connettere un valore in ingresso a un buffer
di esecuzione del database. In termini pratici questo significa che il valore in ingresso è
usato direttamente come valore in un'operazione. Al client non deve essere richiesto di
dover proteggere il valore in modo che possa essere usato, esso deve infatti essere uguale
al valore reale del database.
[6] Si noti che l'interfaccia potrebbe implementare il prelievo delle righe usando array e altre
ottimizzazioni. Nulla garantisce che una chiamata a tale metodo muova il cursore associato in
avanti di una sola riga.
[7] L'attributo rowcount può essere codificato in modo che aggiorni dinamicamente il proprio
valore. Ciò può essere utile per database che restituiscono valori di rowcount
utilizzabili solo dopo una prima chiamata al metodo .fetchXXX().
Riconoscimenti
Sentiti ringraziamenti vanno a Andrew Kuchling che ha convertito
la specifica del Python Database API Specification 2.0
dall'originale formato HTML nel formato dei PEP.
Copyright
This document has been placed in the Public Domain.