Использовать «SET NAMES»

При чтении «High performance MySQL» от O'Reilly я наткнулся на следующее

Другой распространенный запрос на сбор мусора – SET NAMES UTF8, что является неправильным способом делать все равно (он не меняет набор символов клиентской библиотеки, а влияет только на сервер).

Я немного смущен, потому что я использовал «SET NAMES utf8» в верхней части каждого скрипта, чтобы db знал, что мои запросы закодированы в utf8.

Кто-нибудь может прокомментировать приведенную выше цитату или, если честно сказать, какие ваши предложения или рекомендации, чтобы мой рабочий процесс базы данных был unicode-aware.

Мои целевые языки – php и python, если это актуально.

mysql_set_charset() будет вариантом, но опция ограничена ext/mysql . Для ext/mysqli это mysqli_set_charset а для PDO ::mysql вам нужно указать параметр соединения.

Поскольку использование этой функции приводит к вызову API MySQL, ее следует рассматривать гораздо быстрее, чем выдача запроса.

В отношении производительности самый быстрый способ обеспечить совместимость с UTF-8 между вашим скриптом и сервером MySQL – правильно настроить сервер MySQL. Поскольку SET NAMES x эквивалентен

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

тогда как SET character_set_connection = x внутри также выполняет SET collation_connection = <<default_collation_of_character_set_x>> вы также можете установить эти серверные переменные статически в my.ini/cnf .

Помните о возможных проблемах с другими приложениями, запущенными на одном и том же экземпляре сервера MySQL, и требуйте другого набора символов.

TLDR

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

Этот ответ делает акцент на библиотеке pdo php, потому что он настолько вездесущ.

Краткое напоминание – mysql – это архитектура клиент-сервер. Это важно, потому что есть не только сервер mysql, где находится фактическая база данных, но также есть отдельный драйвер клиента mysql, который является тем, что говорит с сервером mysql (это отдельные объекты). Вы могли бы сказать, что клиент mysql и pdo смешиваются.

Когда вы используете set names utf8 , вы set names utf8 стандартный SQL-запрос в mysql. Хотя sql-запрос проходит через pdo, а затем через клиентскую библиотеку mysql, а затем, наконец, достигает сервера mysql, ТОЛЬКО сервер mysql анализирует и интерпретирует этот SQL-запрос. Это важно, потому что сервер mysql не отправляет какое-либо сообщение обратно в pdo или клиент mysql, позволяя ему знать, что набор символов и кодировка изменились, и поэтому pdo совершенно не осведомлен о том, что это произошло.

Важно не делать этого, потому что клиентская библиотека не может правильно обрабатывать строки, если она не знает текущий набор символов. Большинство обычных операций будут работать правильно, если клиент не знает правильный набор символов, но тот, который не будет сбрасывать строки, например PDO :: quote . Вы можете подумать, что вам не нужно беспокоиться об этом ручном примитивном стирании строки, потому что вы используете подготовленные инструкции, но правда в подавляющем большинстве пользователей pdo: mysql неосознанно используют эмулированные подготовленные операторы, потому что это была настройка по умолчанию для pdo: mysql драйвера в течение очень долгого времени. Эмулированный подготовленный оператор не использует реальные родные операторы mysql, предоставленные mysql api; вместо этого php делает эквивалент вызова PDO::quote() для всех ваших значений и str_replacinging всех ваших заполнителей с указанными вами значениями.

Поскольку вы не можете должным образом избегать строки, если не знаете набор символов, который вы используете, эти эмулированные подготовленные операторы уязвимы для SQL-инъекций, если вы изменили на некоторые наборы символов через имена наборов. Независимо от возможности внедрения sql, вы все равно можете сломать свои строки, если используете схему экранирования, предназначенную для другого набора символов.

Для драйвера pdo mysql вы можете указать набор символов при подключении, указав его в DSN . Клиентская библиотека и сервер будут знать о наборе символов, если вы это сделаете.

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

Но неправильное экранирование строк – не единственная проблема. Например, у вас также могут быть проблемы с использованием PDO :: bindColumn, потому что имена столбцов указаны как строки, и поэтому снова имеет смысл кодирование. Примером может быть имя столбца с именем ütube (обратите внимание на ütube ), и вы переключитесь с latin на utf8 помощью заданных имен, а затем попробуете $stmt->bindColumn('ütube', $var); с ütube является кодировкой utf8, потому что ваш php-файл закодирован в utf8. Это не сработает, вам нужно будет закодировать строку как вариант latin1 … и теперь у вас все виды сумасшедших.

Не уверен в py, но php теперь имеет mysql_set_charset , который утверждает, что это «предпочтительный способ изменить кодировку [и] с помощью mysql_query () для выполнения SET NAMES не рекомендуется». Обратите внимание, что эта функция была введена для MySQL 5.0.7, поэтому она не будет работать с более ранними версиями.

 mysql_set_charset('utf8', $link); 

Где $ link – это соединение, созданное с помощью mysql_connect