Download Documentazione FAQ Aiutaci!!!
Avanti Indietro Indice

2. Il modulo _mysql

Se si vogliono scrivere applicazioni portabili su altri database, si eviti di usare direttamente questo modulo. _mysql fornisce un'interfaccia che perlopiù implementa l'API C di MySQL. Per maggiori informazioni si consulti la documentazione MySQL. La documentazione di questo modulo è scarna di proposito, poiché nella gran parte dei casi è molto meglio usare il modulo MySQLdb . Se davvero si ha bisogno di usarlo, si faccia riferimento ai doc standard di MySQL e li si adatti secondo necessità.

2.1 Traduzione dell'API C di MySQL

L'API C di MySQL è stata incapsulata in stile orientato agli oggetti. Le uniche strutture dati MySQL implementate sono i tipi MYSQL (per gestire le connessioni al database) e MYSQL_RES (per gestire i risultati). In generale, qualunque funzione che prende MYSQL *mysql come argomento diventa un metodo dell'oggetto connessione e qualunque funzione che prende MYSQL_RES *result come argomento è un metodo dell'oggetto risultato. Le funzioni che non richiedeno alcuna struttura dati di MySQL sono implementate come funzioni nel modulo. Le funzioni che richiedono strutture dati di MySQL diverse dalle due sopra in genere non sono implementate. Le funzioni deprecate non sono implementate. In tutti i casi il prefisso mysql_ è stato eliminato. La maggior parte dei metodi conn elencati sono anche disponibili come metodi dell'oggetto Connessione di MySQLdb. Il loro uso impedisce la portabilità.


C API
_mysql
mysql_affected_rows() conn.affected_rows()
mysql_close() conn.close()
mysql_connect() _mysql.connect()
mysql_data_seek() result.data_seek()
mysql_debug() _mysql.debug()
mysql_dump_debug_info conn.dump_debug_info()
mysql_escape_string() _mysql.escape_string()
mysql_fetch_row() result.fetch_row()
mysql_get_client_info() _mysql.get_client_info()
mysql_get_host_info() conn.get_host_info()
mysql_get_proto_info() conn.get_proto_info()
mysql_get_server_info() conn.get_server_info()
mysql_info() conn.info()
mysql_insert_id() conn.insert_id()
mysql_num_fields() result.num_fields()
mysql_num_rows() result.num_rows()
mysql_options() _mysql.connect()
mysql_ping() conn.ping()
mysql_query() conn.query()
mysql_real_connect() _mysql.connect()
mysql_real_query() conn.query()
mysql_real_escape_string() conn.escape_string()
mysql_row_seek() result.row_seek()
mysql_row_tell() result.row_tell()
mysql_select_db() conn.select_db()
mysql_stat() conn.stat()
mysql_store_result() conn.store_result()
mysql_thread_id() conn.thread_id()
mysql_use_result() conn.use_result()
CLIENT_* MySQLdb.constants.CLIENT.*
CR_* MySQLdb.constants.CR.*
ER_* MySQLdb.constants.ER.*
FIELD_TYPE_* MySQLdb.constants.FIELD_TYPE.*
FLAG_* MySQLdb.constants.FLAG.*
Mappatura delle funzioni C dell'API MySQL

2.2 Alcuni esempi con _mysql

Va bene, volete usare comunque _mysql. Ecco degli esempi.

La connessione più semplice al database è:


import _mysql
db=_mysql.connect()

Così si crea una connessione al server MySQL in esecuzione sulla macchina locale usando i socket UNIX standard, il proprio nome di login (preso dalla variabile di ambiente USER), nessuna password. E non USA un database. Magari funzionerà, soprattutto se si ha impostato un file di configurazione, per dire ~/.my.cnf [notare che si sta parlando dell'uso su *ix NdT], ma è probabile che sia necessario fornire maggiori informazioni.


db=_mysql.connect("localhost","joebob","moonpie","thangs")

Questo crea una connessione al server MySQL in esecuzione sulla macchina locale usando TCP sulla porta standard (3306), il nome utente è "joebob", la password "moonpie" e si lavora inizialmente sul database "thangs".

Non abbiamo nemmeno iniziato a parlare di tutti i parametri che connect() può ricevere e si noterà che se si usano parametri posizionali si è costretti di fatto a usare TCP, perdendo parecchio in velocità rispetto ai socket UNIX. (Ovviamente si deve usare comunque TCP se si tratta di un host remoto). Per questa ragione preferisco usare i parametri a parola chiave:


db=_mysql.connect(host="localhost",user="joebob",
                  passwd="moonpie",db="thangs")

Questo fa esattamente la stessa cosa dell'esempio precedente, ma è certo più facile da leggere. Ora se si volessero usare proprio i socket UNIX e il nome di login fosse "joebob", si potrebbe abbreviare il tutto con:


db=_mysql.connect(passwd="moonpie",db="thangs")

Ci sono alcuni altri parametri che si potrebbero usare. Per la maggior parte non sono necessari, tranne uno. Pel resto si legga la documentazione inclusa nel modulo. Il modulo pydoc di Python 2.1 è davvero utile a questo scopo.

Dunque, ora abbiamo una connessione aperta db e vogliamo sottoporre una interrogazione. Bene, non ci sono cursori in MySQL, nemmeno la sostituzione di parametri, quindi tocca passare un'intera interrogazione come stringa a db.query():


db.query("""SELECT spam, eggs, sausage FROM breakfast
            WHERE price < 5""")

Questo non restituisce alcun valore, ma possono venir sollevate eccezioni. Le eccezioni sono definite in un modulo separato, _mysql_exceptions, ma _mysql le esporta. Si leggano le Specifiche API DB v.2.0 per sapere quali siano, oppure si usi MySQLError, che cattura qualunque cosa.

A questo punto l'interrogazione è stata eseguita e si vogliono ricevere i risultati. Ci sono due opzioni:


r=db.store_result()
# ...oppure...
r=db.use_result()

Entrambi i metodi restituiscono un oggetto Risultato. Qual'è la differenza? store_result() restituisce immediatamente l'intero risultato al client. In caso esso sia molto ampio, questo comportamento potrebbe causare dei problemi. Un modo per evitarlo è aggiungere una clausola LIMIT all'interrogazione, per limitare il numero di righe restituite. L'altro è usare use_result(), che conserva il risultato nel server e lo invia riga per riga quando lo si preleva. Tutto ciò comunque pesa parecchio sulle risorse del server e sulla connessione: non si possono fare altre interrogazioni fino a che non si sono recuperate tutte le righe. In genere raccomando di usare store_result() a meno che il risultato dell'interrogazione non sia davvero di dimensioni enormi e che per qualche ragione non si possa usare LIMIT.

Ora, per ottenere effettivamente i risultati reali:


>>> r.fetch_row()
(('3','2','0'),)

Potrebbe sembrare un po' insolito. La prima cosa da sapere è che fetch_row() accetta alcuni parametri aggiuntivi. Il primo dice quante righe (maxrows) dovrebbero venir restituite. Per default viene restituita una riga. Potrebbero venir restituite meno righe di quanto richiesto, ma mai di più. Impostando maxrows=0 vengono restituite tutte le righe del risultato. In caso si riceva indietro una tupla vuota, significa che si sono esaurite le righe.

Il secondo parametro (how) dice come dovrebbe venir rappresentata la riga. Per default è zero, il che significa che va restituita come tupla. how=1 significa che va restituita come dizionario, dove le chiavi sono i nomi delle colonne, o tabella.colonna se ci sono due colonne con lo stesso nome (per dire nel caso di un join). how=2 significa lo stesso di how=1 eccetto che le chiavi sono sempre tabella.colonna; serve per compatibilità con il vecchio modulo Mysqldb.

Bene, allora perché abbiamo ottenuto una tupla di un solo elemento con dentro una tupla? Perché abbiamo implicitamente richiesto una sola riga, dato che non abbiamo specificato maxrows.

L'altra cosa singolare è: assumendo che si tratti di colonne numeriche, perché vengono restituite come stringhe? Perché MySQL restituisce tutti i dati come stringhe e si aspetta che la conversione venga fatta in proprio. Questa rischia di essere una vera fregatura, ma in effetti lo può fare _mysql. (E MySQLdb lo fa). Per ottenere la conversione automatica di tipi è necessario creare un dizionario apposito e passarlo a connect() come parametro a parola chiave conv.

Le chiavi di conv dovrebbero essere i tipi delle colonne MySQL, che nell'API C sono FIELD_TYPE_*. Si possono ottenere tali valori con:


from MySQLdb.constants import FIELD_TYPE

Per default qualsiasi tipo di colonna che non viene trovato in conv è restituito come stringa, il che funziona bene per un sacco di roba. Per i nostri scopi vogliamo probabilmente questo:


my_conv = { FIELD_TYPE.LONG: int }

Ciò significa che se c'è un FIELD_TYPE_LONG verrà chiamata su di esso la funzione primitiva int(). Si noti che FIELD_TYPE_LONG è una colonna INTEGER, che corrisponde a un long C, che è anche il tipo usato per i normali interi Python. Ma attenzione: se si tratta di una colonna UNSIGNED INTEGER potrebbero capitare degli overflow. Per questa ragione MySQLdb usa in realtà dei long() per fare la conversione. Per ora ignoreremo questo potenziale problema.

Quindi, se si usa db=_mysql.connect(conv=my_conv...) i risultati verranno restituiti come ((3, 2, 0),), che è quanto ci si aspetterebbe.


Avanti Indietro Indice