Download Documentazione FAQ Aiutaci!!!
Avanti Indietro Indice

3. MySQLdb -- interfaccia API verso i DB

MySQLdb avvolge _mysql rendendolo compatibile con l'interfaccia API Python per i DB (versione 2). In realtà una piccola parte del codice che implementa l'API si trova in _mysql per ragioni di efficienza.

Le Specifiche dell'API DB 2.0 dovrebbero costituire la guida principale nell'uso di questo modulo. Verrà qui documentato solo quanto si scosta da tali specifiche e altre faccende strettamente dipendenti dal database.

3.1 Funzioni e attributi

Solo poche funzioni e attributi al livello più alto sono definiti all'interno di MySQLdb.

connect(parametri...)

Il costruttore per creare una connessione al database. Restituisce un Oggetto Connessione. I parametri sono gli stessi dell'API C MySQL. In aggiunta, ci sono alcune parole chiave addizionali che corrispondono a quanto si passerebbe a mysql_options() prima di connettersi. Si noti che alcuni parametri devono essere specificati come argomenti a parole chiave! Il valore predefinito per ciascun parametro è NULL o zero, come appropriato. Si consulti la documentazione MySQL per maggiori dettagli. I parametri importanti sono:

host

nome dell'host a cui connettersi. Default: usa localhost

user

utente con il quale autenticarsi. Default: l'utente attuale.

passwd

password con la quale autenticarsi. Default: nessuna password.

db

database da usare. Default: nessun database.

port

Porta TCP del server MySQL. Default: la sua porta standard (3306).

unix_socket

collocazione del socket UNIX. Default: usa TCP.

conv

dizionario di conversione dei tipi. Default: una copia di MySQLdb.converters.conversions

compress

abilita il protocollo di compressione. Default: nessuna compressione.

connect_timeout

fallisce se la connessione non è completata entro un dato numero di secondi. Default: nessun timeout (?)

named_pipe

usa una pipe con nome (Windows). Default: non usarla.

init_command

comando iniziale da inviare al server subito dopo la connessione. Default: nessuno.

read_default_file

file di configurazione MySQL da leggere; si veda la documentazione MySQL per mysql_options().

read_default_group

gruppo di default con cui leggere il file di configurazione; si veda la documentazione MySQL per mysql_options().

cursorclass

classe cursore usata da cursor(), a meno di sovrascritture. Default: MySQLdb.cursors.Cursor. Questo dev'essere un parametro a parola chiave.

apilevel

costante stringa che specifica il livello dell'API DB supportato. È pari a '2.0'.

threadsafety

costante intera che specifica il livello di supporto ai thread dell'interfaccia. Per MySQLdb versione 0.9.0 è pari a 1, che significa: i thread possono condividere il modulo.

Il protocollo MySQL non può gestire più thread usando una stessa connessione in modo immediato. Alcune versioni precedenti di MySQLdb utilizzavano i lock per raggiungere un livello di supporto ai thread pari a 2. Non è cosa difficile da realizzare con la classe Cursore standard (che usa mysql_store_result()), è invece complicato per SSCursor (che usa mysql_use_result()). Nel secondo caso ci si deve assicurare che tutte le righe siano state lette prima che possa essere eseguita un'altra interrogazione. Le cose si complicano ulteriormente con l'aggiunta delle transazioni, dato che queste iniziano quando un cursore esegue un'interrogazione ma finiscono quando l'oggetto Connessione esegue un COMMIT o un ROLLBACK. Due thread non possono condividere una connessione mentre una transazione è in corso, oltre a non essere in grado di condividerla durante l'esecuzione di un'interrogazione. Tutto questo comporta una complicazione eccessiva a livello del codice, al punto che non ne vale proprio la pena.

La conclusione generale è: meglio non condividere connessioni fra thread. Dato lo sforzo necessario non vale la pena e alla fine si rischia anche di compromettere le prestazioni del programma, dato che il server MySQL lancia un thread separato per ciascuna connessione. Si possono certamente fare cose come tenere un pool delle connessioni e passarle a un thread in una volta sola. Se invece si permette a due thread di usare simultaneamente una stessa connessione, con tutta probabilità la libreria client MySQL farà una brutta fine. L'avviso l'ho dato.

paramstyle

costante stringa che specifica il tipo di formattazione del marcatore di parametro che l'interfaccia si aspetta. Impostato a 'format' usa i codici di formato della printf di ANSI C, p.e. '...WHERE name=%s'. Se a conn.execute() viene passato un dizionario, allora l'interfaccia userà in realtà 'pyformat', cioè i codici di formato Python estesi, p.e. '...WHERE name=%(name)s'. In ogni caso l'API attualmente non permette di specificare più di uno stile.

Nota di compatibilità: il vecchio MySQLmodule usa uno schema simile, ma richiede che le stringhe di formato che contengono stringhe, date e altri dati carattere simili siano circondate da virgolette. Questo non è necessario per MySQLdb. Si raccomanda di usare %s (e non '%s') per tutti i parametri, di qualunque tipo siano. L'interfaccia aggiungerà tutte le virgolette necessarie.

conv

un dizionario che mappa i tipi MySQL (da FIELD_TYPE.*) in oggetti invocabili Python (di solito funzioni), che si occupano della conversione da stringa al tipo desiderato, e i tipi Python in oggetti invocabili Python, che effettuano la conversione opposta in stringhe letterali SQL. Il dizionario viene inizializzato con contenuti predefiniti appropriati per la gran parte dei tipi. Quando si crea un oggetto Connessione, è possibile passare il proprio dizionario di conversione come parametro a parola chiave. Altrimenti verrà usata una copia di MySQLdb.converters.conversions. Il dizionario include alcune delle funzioni fabbrica del modulo DateTime, se esso risulta disponibile. Parecchi tipi non standard sono restituiti come stringhe, che è il modo in cui MySQL restituisce tutte le colonne. Per maggiori dettagli si veda la documentazione incorporata nel modulo.

A partire da MySQL-3.23, MySQL supporta set differenti di caratteri nel server e una nuova funzione di protezione per le stringhe nelle interrogazioni SQL, mysql_real_escape_string(). Questo richiede che la funzione sia un metodo associato a un oggetto Connessione. MySQLdb gestisce tutto ciò automaticamente. Nondimeno se si pensa di aver bisogno di fare qualcosa di strambo con le proprie stringhe, si potrà modificare il dizionario una volta aperta la connessione. In pratica non ci si dovrà mai preoccupare di tutto questo.

3.2 Oggetti Connessione

Gli oggetti Connessione sono il prodotto della funzione connect().

commit()

se il database e le tabelle supportano le transazioni questo metodo effettua il commit della transazione corrente, altrimenti non fa nulla [senza creare problemi ma nemmeno avvisare NdT].

rollback()

se il database e le tabelle supportano le transazioni questo metodo fa il "roll back", in parole povere annulla, la transazione corrente, altrimenti viene sollevata un'eccezione NotSupportedError.

Nota di compatibilità: nel vecchio MySQLmodule questo metodo invece di sollevare un'eccezione in caso le transazioni non siano supportate non fa proprio nulla, portando a credere che il rollback abbia avuto successo. Questo è un comportamento pericoloso, dato che un rollback riuscito indica che la transazione corrente è stata ritirata, il che non è vero, e fallisce nel comunicare al programmatore che ora il database dev'essere ripulito in altri modi.

cursor([cursorclass])

MySQL non supporta nativamente i cursori. Ad ogni modo essi possono venir facilmente emulati. Si può fornire una classe cursore alternativa come parametro opzionale. In caso non sia presente, viene preso come default il valore passato al momento della creazione dell'oggetto Connessione, o la classe standard Cursor. Si vedano anche le classi cursori aggiuntive nella sezione Usare ed estendere il modulo.

begin()

inizia in modo esplicito una transazione. Normalmente non è necessario usarlo: l'esecuzione di un'interrogazione inizia in modo implicito una nuova transazione se non ce ne sono altre in corso. Se è attivato il modo AUTOCOMMIT, si può usare begin() per disattivarlo temporaneamente. AUTOCOMMIT si riattiverà alla prossimo commit() o rollback.

3.3 Oggetti Cursore

callproc()

non implementato.

close()

chiude il cursore. Da qui in poi se si tenteranno operazioni sul cursore verrà sollevata un'eccezione ProgrammingError. Se si stanno usando i cursori lato server ["Server-Side Cursors"], è molto importante chiudere il cursore quando si ha finito di usarlo e prima di crearne uno nuovo.

insert_id()

restituisce l'ultimo valore AUTO_INCREMENT inserito nel database. (Non-standard)

info()

restituisce alcune informazioni sull'ultima interrogazione. Di norma non serve. Con il cursore predefinito, qualsiasi avviso emesso da MySQL causerà un'eccezione Warning. Se invece si sta usando una classe cursore senza avvisi, allora si potrebbe volerlo usare. Si veda la documentazione MySQL per mysql_info(). (Non-standard)

setinputsizes()

non fa nulla, senza avvisare.

setoutputsizes()

non fa nulla, senza avvisare.

3.4 Alcuni esempi

Il metodo connect() lavora all'incirca come con _mysql:


import MySQLdb
db=MySQLdb.connect(passwd="moonpie",db="thangs")

Per effettuare un'interrogazione serve innanzitutto un cursore, sul quale poi si andranno a eseguire le interrogazioni.


c=db.cursor()
max_price=5
c.execute("""SELECT spam, eggs, sausage FROM breakfast
            WHERE price < %s""", (max_price,))

In questo esempio max_price=5. Quindi perché usare %s nella stringa? Perché MySQLdb la convertirà in un valore letterale SQL, la stringa '5'. Una volta finito, l'interrogazione in realtà reciterà: "...WHERE price < 5".

Perché la tupla? Perché la API DB richiede di passare come sequenza qualsiasi parametro.

Ora ecco i risultati:


>>> c.fetchone()
(3L, 2L, 0L)

Diversamente da quanto accadeva con _mysql, il nostro esempio restituisce una singola tupla, che sarebbe la riga, e i valori sono adeguatamente convertiti per default... tranne... ma cosa sono le L?

Come detto in precedenza, mentre una colonna di MySQL di tipo INTEGER si traduce perfettamente in un intero Python, un UNSIGNED INTEGER potrebbe causare overflow. Tali valori sono quindi convertiti invece in interi long Python. Prima di Python 1.6, gli interi long conservavano la L quando convertiti in stringhe con str(). Da 1.6 in poi str() non include più la L. Naturalmente la L viene sempre stampata quando si usa repr(), il che è quanto è successo nel nostro caso.

Quando si ha finito con una transazione, si dovrebbe eseguire db.commit() o db.rollback(). Se il server e le tabelle non supportano le transazioni, commit() funzionerà ancora, mentre rollback() solleverà un'eccezione. Si noti che questi sono metodi di connection, non di cursor, anche se è stato c.execute(...) a iniziare la transazione.

Se si vogliono più righe si possono usare c.fetchmany(n) o c.fetchall(). Questi due fanno esattamente quanto ci si aspetta. Per c.fetchmany(n), n è opzionale e il valore predefinito è c.arraysize, che di norma è 100. Entrambi i metodi restituiscono una sequenza di righe, o una sequenza vuota se le righe sono finite. Se si usa una classe cursore personalizzata in modo particolare, le righe stesse potrebbero non essere tuple.

Si noti che al contrario di quanto sopra, c.fetchone() restituisce None quando non ci sono più righe da poter prelevare.

L'unico altro metodo che si userà quasi sicuramente serve negli inserimenti multiriga:


c.execute("""INSERT INTO breakfast (name, spam, eggs, sausage, price)
             VALUES (%s, %s, %s, %s, %s)""",
             [ ("Spam and Sausage Lover's Plate", 5, 1, 8, 7.95 ),
               ("Not So Much Spam Plate", 3, 2, 0, 3.95 ),
               ("Don't Wany ANY SPAM! Plate", 0, 4, 3, 5.95 )
             ]
         )

In questo esempio stiamo inserendo tre righe di cinque valori. Si noti che c'è una mescolanza di tipi (stringhe, interi, numeri a virgola mobile) ma stiamo ancora usando il solo %s. Si noti anche che abbiamo incluso le stringhe di formato per una sola riga. MySQLdb le individua e le duplica per ciascuna riga.

I fagioli al forno sono finiti!


Avanti Indietro Indice