2011-03-09 14 views
5

Sto usando il pacchetto MySQLdb per interagire con MySQL. Sto riscontrando problemi nell'ottenere le conversioni di tipo corretto.python che inserisce e recupera dati binari in mysql

Sto usando un uuid binario a 16 byte come chiave primaria per la tabella e ho un mediumblob contenente informazioni json compresse su zlib.

sto usando il seguente schema:

CREATE TABLE repositories (
    added_id int auto_increment not null, 
    id binary(16) not null, 
    data mediumblob not null, 
    create_date int not null, 
    update_date int not null, 
    PRIMARY KEY (added_id), 
    UNIQUE(id) 
) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ENGINE=InnoDB; 

Poi ho creare una nuova riga nella tabella utilizzando il seguente codice:

data = zlib.compress(json.dumps({'hello':'how are you :D'}) 
row_id = uuid.uuid(4).hex 
added_id = cursor.execute(' 
    INSERT INTO repositories (id, data, create_date, update_date) 
    VALUES (%s, %s, %s, %s)', 
    binascii.a2b_hex(row_id), 
    data, 
    time.time(), 
    time.time() 
) 

Poi per recuperare i dati che uso una query simile:

query = cursor.execute('SELECT added_id, id, data, create_date, update_date ' \ 
    'FROM repositories WHERE id = %s', 
    binascii.a2b_hex(row_id) 
) 

Quindi la query restituisce un risultato vuoto.

Qualsiasi aiuto sarebbe apprezzato. Inoltre, per inciso, è meglio memorizzare date unix di epoca come numeri interi o TIMESTAMP?

NOTA: Non si verificano problemi nell'inserimento dei dati, solo nel tentativo di recuperarli dal database. La riga esiste quando controllo tramite mysqlclient. !

Grazie mille @

risposta

7

Un consiglio: si dovrebbe essere in grado di chiamare uuid.uuid4().bytes per ottenere le prime byte. Per quanto riguarda i timestamp, se si desidera eseguire la manipolazione di ora/data in SQL, è spesso più semplice gestire i tipi di TIMESTAMP reali.

ho creato una tabella di test per provare a riprodurre quello che stai vedendo:

CREATE TABLE xyz (
    added_id INT AUTO_INCREMENT NOT NULL, 
    id BINARY(16) NOT NULL, 
    PRIMARY KEY (added_id), 
    UNIQUE (id) 
) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ENGINE=InnoDB; 

mio script è in grado di inserire e query per le righe che utilizzano il campo binario come chiave senza problemi. Forse stai scorrendo erroneamente/iterando sui risultati restituiti dal cursore?

import binascii 
import MySQLdb 
import uuid 

conn = MySQLdb.connect(host='localhost') 

key = uuid.uuid4() 
print 'inserting', repr(key.bytes) 
r = conn.cursor() 
r.execute('INSERT INTO xyz (id) VALUES (%s)', key.bytes) 
conn.commit() 

print 'selecting', repr(key.bytes) 
r.execute('SELECT added_id, id FROM xyz WHERE id = %s', key.bytes) 
for row in r.fetchall(): 
    print row[0], binascii.b2a_hex(row[1]) 

uscita:

% python qu.py  
inserting '\x96\xc5\xa4\xc3Z+L\xf0\x86\x1e\x05\xebt\xf7\\\xd5' 
selecting '\x96\xc5\xa4\xc3Z+L\xf0\x86\x1e\x05\xebt\xf7\\\xd5' 
1 96c5a4c35a2b4cf0861e05eb74f75cd5 
% python qu.py 
inserting '\xac\xc9,jn\[email protected]\xbb\xa27h\xcd<B\xda' 
selecting '\xac\xc9,jn\[email protected]\xbb\xa27h\xcd<B\xda' 
2 acc92c6a6eb24f40bba23768cd3c42da 
+0

Proprio come un seguito; Sto usando Tornado Database Class come un bel wrapper conveniente su MySQLdb e quando usi una chiamata .get() su un oggetto di connessione non funziona per SELECT ma lo fa con un appropriato .query() o .execute() . –

2

A integrazione delle risposte esistenti, c'è anche un problema con il seguente avviso quando si tratta di stringhe binarie nelle query:

Warning: (1300, "Invalid utf8 character string: 'ABCDEF'") 

Si è riprodotto dal seguente :

cursor.execute(''' 
    CREATE TABLE `table`(
     bin_field` BINARY(16) NOT NULL 
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
''') 

bin_value = uuid.uuid4().bytes 
cursor.execute('INSERT INTO `table`(bin_field) VALUES(%s)', (bin_value,)) 

Ogni volta che MySQL rileva che una stringa letterale in una query non è valida rispetto all'attuale character_set_connection, emetterà l'avviso.Ci sono diverse soluzioni ad esso:

  1. impostato in modo esplicito _binarycharset literal

    INSERT INTO `table`(bin_field) VALUES(_binary %s) 
    
  2. costruzione manuale query con hexadecimal literals

    INSERT INTO `table`(bin_field) VALUES(x'abcdef') 
    
  3. Change connection charset se si lavora solo con le stringhe binarie

Per ulteriori dettagli, vedere MySQL Bug 79317.

Aggiornamento

Come @charlax ha sottolineato, c'è binary_prefix bandiera che può essere passato a initialiser della connessione ad anteporre automaticamente _binary prefisso quando interpolando argumetns. È supportato dalle versioni recenti di entrambi, mysql-client e pymysql.

+0

Puoi anche usare 'binary_prefix = True' come opzione di connessione. Vedi https://bitbucket.org/zzzeek/sqlalchemy/issues/3804/invalid-utf8-character-string-warning-on – charlax