@f_u_s_s
Любопытный кодер

Почему драйвер ODBC (SQL Server 2012) возвращает данные в неверной кодировке?

Столкнулся с такой проблемой - есть удаленная БД SQL Server 2012, в которой есть функций, которые возвращают данных.
Приложение развернуто в виртуалке (ubuntu 18.04), подключение настроено через ODBC драйвер 17й версии (т.к. со встроенным драйвером не удалось "договориться"):
user@vm:~$ odbcinst -j
unixODBC 2.3.7
DRIVERS............: /etc/odbcinst.ini
SYSTEM DATA SOURCES: /etc/odbc.ini
FILE DATA SOURCES..: /etc/ODBCDataSources
USER DATA SOURCES..: /home/user/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8

посылаю в консоли sqlcmd запрос
SELECT fld FROM foo.fnBar(123, 'username');
Получаю ожидаемый тип данных (кириллическая строка):
5cdeb0be1dd1f278878637.png
Но когда посылаю этот же запрос через DB::select() получаю результат:
5cdeb10b55d16609147865.png
Через tinker тоже самое. Кодировка определяется как UTF-8, в бд данные в UTF-8 (так меня уверяют по крайней мере, к самой БД доступа нет, с выборками и просмотрами таблиц тоже самое - только набор функций и хранимок)
Если привести "насильно" к UTF-8 (mb_convert_encoding) получается результат получше:
5cdeb1f87177b141689529.png
Но остается хвост, который никак не привести к нормальному виду, да и видно, что там мусор по сути а не те данные, что в БД. Есть подозрение, что где-то происходит обрезка строки по количеству бит или что-то типа того, потому как все битые строки длинной не более 57-59 символов, а те, что короче отображаются нормально.
Явное указание кодировки при подключении не спасает ситуацию. Если при подключении передать параметр AutoTranslate = no, то вместо кириллицы возвращаются знаки вопроса, но по количеству символов видно что строка полная и не обрезана, левых hex-кодов нет.:
5cdeb3c293218168087980.png
Если не конвертировать в UTF-8:
5cdeb69347b83335937553.png
вопрос в том, как это дело побороть?
  • Вопрос задан
  • 860 просмотров
Решения вопроса 1
@f_u_s_s Автор вопроса
Любопытный кодер
В общем проблема оказалась в неверной кодировке на стороне SQL Server, как выяснилось отдавал он все же cp1251. Но сами "хвосты" все же отдавал драйвер, потому пришлось при подключении прописать
'AutoTranslate' => 'no'
И все ответы от базы прогонять через свою функцию (оставлю тут, может кому пригодится)
function fixCharset($data, $from = 'cp1251', $to = 'utf8'){
    if(is_scalar($data)){
        return mb_convert_encoding($data, $to, $from);
    }
    else {
        if ($to_object = is_object($data))
            $data = (array)$data;

        array_walk_recursive($data, function (&$item, $key) use ($from, $to) {
            $item = mb_convert_encoding($item, $to, $from);
        });

        return $to_object ? (object)$data : $data;
    }
}


Т.к. приложение только на чтение работает, этого достаточно. Для ускорения, все перекодированные результаты кешируются в redis, но, скорее всего это экономия на спичках.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
profesor08
@profesor08 Куратор тега PHP
Используй sqlsrv драйвер для работы с бд.

Укажи кодировку.
ini_set('mssql.charset', 'UTF-8');

Ну и можешь пользоваться mb_detect_encoding, mb_convert_encoding
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы