2009-10-30 1 views
53

Nel leggere "MySQL ad alte prestazioni" da O'Reilly ho inciampato sulla seguente queryse utilizzare "SET nomi"

Un'altra spazzatura comune è impostato NOMI UTF8, che è il modo sbagliato di fare le cose comunque (non cambia il set di caratteri della libreria client, ma riguarda solo il server).

Sono un po 'confuso, perché ho usato mettere "SET NAMES utf8" nella parte superiore di ogni script per far sapere al db che le mie query sono codificate in utf8.

Qualcuno può commentare la citazione sopra o, per dirla in modo più formale, quali sono i suggerimenti/le migliori pratiche per garantire che il flusso di lavoro del mio database sia in unicode.

Le lingue di destinazione sono php e python se questo è rilevante.

+2

quale tecnica hai finito per implementare? –

risposta

28

mysql_set_charset() sarebbe un'opzione - ma un'opzione limitata al ext/mysql. Per ext/mysqli è mysqli_set_charset e per PDO::mysql è necessario specificare un parametro di connessione.

Poiché l'utilizzo di questa funzione genera una chiamata API MySQL, è necessario considerarlo molto più rapido rispetto all'emissione di una query.

Per quanto riguarda le prestazioni, il modo più veloce per garantire una comunicazione basata su UTF-8 tra lo script e il server MySQL è configurare correttamente il server MySQL. Come SET NAMES x è equivalent a

SET character_set_client = x; 
SET character_set_results = x; 
SET character_set_connection = x; 

mentre SET character_set_connection = x esegue internamente anche SET collation_connection = <<default_collation_of_character_set_x>> è anche possibile impostare these server variables staticamente nel vostro my.ini/cnf.

Si prega di essere consapevoli dei possibili problemi con altre applicazioni in esecuzione sulla stessa istanza del server MySQL e che richiedono un altro set di caratteri.

+3

A partire da PHP 5.0.5, esiste un metodo in mysqli: http://php.net/mysqli_set_charset – xofer

+0

Ho menzionato 'mysql_set_charset()' - questa è una funzione inclusa nel vecchio 'ext/mysql'. Come detto sopra, né 'PDO' né' ext/mysqli' forniscono direttamente alcun supporto per questa operazione. –

+1

Sembra che il link che ho postato non sia affidabile. Eccone una migliore: http://php.net/manual/en/mysqli.set-charset.php Non sai come intendi che mysqli non supporta questa operazione. – xofer

9

Non sono sicuro di py, ma php ha mysql_set_charset ora, che afferma che questo è il "modo preferito per cambiare il set di caratteri [e] usando mysql_query() per eseguire SET NAMES non è raccomandato." Si noti che questa funzione è stata introdotta per MySQL 5.0.7, quindi non funzionerà con le versioni precedenti.

mysql_set_charset('utf8', $link); 

Dove $ link è una connessione creata con mysql_connect

21

TLDR

// The key is the "charset=utf8" part. 
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8'; 
$dbh = new PDO($dsn, 'user', 'pass'); 

Questa risposta ha l'accento sulla libreria PDO di PHP, perché è così onnipresente.

Un breve promemoria: mysql è un'architettura client-server. Questo è significativo perché non c'è solo il server mysql in cui si trova il vero database, ma c'è anche il driver client mysql separato, che è la cosa che parla al server mysql (sono entità separate). Si potrebbe quasi dire che il client mysql e pdo sono mescolati insieme.

Quando si utilizza set names utf8, si invia una query sql standard a mysql.Mentre la query sql passa attraverso pdo, e quindi attraverso la libreria client mysql, e infine raggiunge il server mysql, SOLO il server mysql analizza e interpreta quella query sql. Questo è significativo perché il server mysql non invia alcun messaggio a pdo o al client mysql, facendogli sapere che il set di caratteri e la codifica sono cambiati, e quindi pdo è totalmente all'oscuro del fatto che sia successo.

È importante non eseguire questa operazione perché la libreria client non può gestire correttamente le stringhe se non è a conoscenza del set di caratteri corrente. Le operazioni più comuni funzioneranno correttamente senza che il client conosca il set di caratteri corretto, ma uno che non eseguirà l'escaping delle stringhe, come ad esempio PDO::quote. Potresti pensare che non ti devi preoccupare di tale escape manuale delle stringhe primitive perché usi istruzioni preparate, ma la verità è la stragrande maggioranza di pdo: gli utenti mysql utilizzano inconsapevolmente emulated prepared statements perché è stata l'impostazione predefinita per il driver pdo: mysql per un tempo molto lungo ora. Una dichiarazione preparata emulata non usa le istruzioni preparate in mysql native come fornite da mysql api; al contrario, php fa l'equivalente di chiamare PDO::quote() su tutti i tuoi valori e str_replacinginging tutti i tuoi segnaposto con i valori quotati per te.

Poiché non è possibile scappare correttamente una stringa a meno che non si conosca il set di caratteri che si sta utilizzando, queste istruzioni preparate emulate sono vulnerabili all'iniezione di SQL se si è passati a determinati set di caratteri tramite nomi di set. Indipendentemente dalla possibilità di SQL injection, è comunque possibile interrompere le stringhe se si utilizza uno schema di escape destinato a un set di caratteri diverso.

Per il driver pdo mysql, è possibile specificare il set di caratteri quando ci si connette, tramite specifying it in the DSN. La libreria client e il server saranno entrambi a conoscenza del set di caratteri se lo fai.

// The key is the "charset=utf8" part. 
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8'; 
$dbh = new PDO($dsn, 'user', 'pass'); 

Ma l'escape non corretto delle stringhe non è l'unico problema. Ad esempio, puoi anche avere problemi con l'uso di PDO::bindColumn perché i nomi delle colonne sono specificati come stringhe, e quindi di nuovo la codifica è importante. Un esempio potrebbe essere un nome di colonna denominato ütube (annotare la umlaut) e passare da latin a utf8 tramite nomi di set e quindi provare a $stmt->bindColumn('ütube', $var); con ütube come stringa codificata utf8 perché il file php è codificato in utf8. Non funzionerà, avresti bisogno di codificare la stringa come una variante latin1 ... e ora hai tutti i tipi di follia in corso.

+2

Come oggi (settembre 2014) PDO è il modo più nuovo e più robusto per connettere PHP con un database, penso che questa sia la risposta giusta. – rogeriopradoj