Переход на UTF-8 и нюансы с mysql

Духовность™

Продвинутый новичок
Привет.
Интересует вопрос по кодировкам в mysql, доки читал, но остались вопросы.

Имеем 5 mysql.

Для базы я сделал
ALTER DATABASE `mybase` CHARACTER SET utf8;
Таблицы и столбцы остались со старым collation и charset -- cp1251_general_ci

В начале инициализации БД стоит mysql_set_charset('SET NAMES utf8').

Данные в скрипт приходят в UTF-8, но хранятся и записываются в базу как cp1251, ведь на таблицах стоит cp1251_general_ci. Так по идее? Идем дальше, цитируем вики:

SET NAMES эквивалентна этим трем командам:

Переменная character_set_client устанавливает кодировку данных отправляемых от клиента
character_set_results устанавливает кодировку данных отправляемых клиенту
character_set_connection устанавливает кодировку, в которую преобразуется информация пришедшая от клиента, перед выполнением запроса на сервере.
Тут мне не понятно. Если я делаю SET NAMES utf8, то "информация пришедшая от клиента" должна сохраняться как UTF8, но если таблица имеет CHARSET=cp1251 то character_set_connection фактически игнорируется? Правильно я понимаю? Так вот, я провел два эксперимента:

POST запрос идёт в UTF8:
На таблице charset и collation = cp1251_general_ci

1. отключил SET NAMES utf8 и данные в базу записались как UTF-8.
Не смотря на то, что таблица настроена на cp1251_general_ci

2. включил SET NAMES utf8 и данные в базу записались как cp1251.

Почему в первом не произошло преобразование и пришедшие из запроса данные в UTF не преобразовались в cp1251?

Почему во втором случае данные в UTF8 записались как cp1251?
 

Вурдалак

Продвинутый новичок
Таблицы и столбцы остались со старым collation и charset -- cp1251_general_ci
Фактическая кодировка — это та, которая указана в столбце. Та, что указывается в БД и в таблицах лишь означает какая кодировка будет по умолчанию для новых таблиц и столбцов соответственно.

mysql_set_charset('SET NAMES utf8')
Ничего не перепутал?

Почему во втором случае данные в UTF8 записались как cp1251?
А откуда СУБД знает в какой кодировке входящие данные, если ты убрал SET NAMES? Она, вероятно, думает, что ты ей даёшь cp1251.
 

itprog

Cruftsman
значит плохо читал;P
http://dev.mysql.com/doc/refman/5.0/en/charset-connection.html
What character set should the server translate a statement to after receiving it?
For this, the server uses the character_set_connection and collation_connection system variables.
Если указать character_set_connection utf8 и данные будут в utf8 то никакого преобразования не будет до сохранения значения в таблице в cp1251 (тогда будет выполнено utf8 -> cp1251 и все хорошо, пока mysql знает как преобразовывать символы).
Если указать cp1251, а данные будут в utf-8, то они тоже не будут преобразованы, но будут восприняты как набор символов cp1251 и сохранены в cp1251 без всякого преобразования (вместо русских букв получаем "кривые" знаки).
 

Духовность™

Продвинутый новичок
itprog
Это фитча что ли?

Т.е.
если данные в UTF приходят и настроить set names utf8, то UTF преобразуется в то, что указано в столбце как charset.
если данные в UTF приходят и настроить set names сз1251, то никаких преобразований и данные пишутся "как есть"

Так??!!
 

vovanium

Новичок
SET NAMES по сути указывает в какой кодировке клиент будет общаться с MySQL. Если SET NAMES не указано, то будет кодировка по умолчанию (обычно latin1).
Дальше MySQL действует так, если кодировка в которой хочет общаться клиент совпадает с кодировкой столбца, то никаких преобразований не будет, а если кодировки отличаются, то MySQL пытается преобразовать из кодировки столбца в кодировку которую хочет клиент.

Таким образом если к примеру столбцы в cp1251, то можно спокойно доставать оттуда данные в utf8, просто указав соответсвующий SET NAMES.
Основные проблемы с кодировками возникают из-за того, что многие не знают о кодировках по умолчанию, и особенно о latin1. Усугубляется дело тем, что зачастую проблему с кодировками не видно, когда и сохраняешь данные криво, и так же криво их читаешь.

Описал в статье несколько популярных косяков с кодировками MySQL
 

флоппик

promotor fidei
Команда форума
Партнер клуба
Таким образом если к примеру столбцы в cp1251, то можно спокойно доставать оттуда данные в utf8, просто указав соответсвующий SET NAMES.
Тут еще есть такие моменты, что если кодировка все же меняется, то не всегда можно достать то, что пытался положить.
 

itprog

Cruftsman
А где, кстати, в документации mysql указано про преобразование кодировок при их сохранении (если данные пришли в utf8 и сохранены в столбец с cp1251)? Я нашел только о конвертации кодировки возвращаемого результата.
 

Фанат

oncle terrible
Команда форума
Я уже не помню, где читал, когда фак писал, но где-то было.
А вообще, это, вроде бы, логично следует из наличия чарактер сет клиент.
 

fixxxer

К.О.
Партнер клуба
alter table .. convert to character set utf8

если для столбцов явно задана кодировка - им так же
 

флоппик

promotor fidei
Команда форума
Партнер клуба
PHP:
mysql --database=databasename -u username -ppassword -B -N -e "SHOW TABLES" 
| awk '{print "ALTER TABLE", $1, "CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;"}' 
| mysql --database=databasename -u username -ppassword &
В одну строчку.
 

fixxxer

К.О.
Партнер клуба
Человеку, который такие вещи копипастит, а не пишет сам, должно быть стыдно. :)
 

fixxxer

К.О.
Партнер клуба
Вполне можно использовать, если помнить об strlen: зачастую нужна как раз длина в байтах.
 

Absinthe

жожо
Что скажете про перегрузку? http://ru2.php.net/manual/ru/mbstring.overload.php стоит её использовать?
Не советую, т.к. нельзя определить, считались ли ранее байты или символы. Особенно в библиотеках всяких.

что-то мне влом во всем проекте этой ерундой заниматься
А что там заниматься? Поиск - заменить(и прикреплять mb_). Только каждый конкретный случай просматривать, чтобы не попасть на те, где имелись ввиду байты.
 

fixxxer

К.О.
Партнер клуба
Мало ли. Заголовок content-length отдать, или что-то вроде.
 
Сверху