kovalensky
@kovalensky
Будущий инженер / UPD 2020: Будущий бомж

Некоторые бинарные строки не определяются по оператору LIKE, проблема не дебажится, в чём может быть причина?

Опишу проблему максимально подробно.

В базе есть два столбца двоично закодированных строк инфо-хешей (BitTorrent), один из них 20 байт (sha1 хеш), второй равен 40 байтам (sha2).
Из-за кривой реализации в протоколе, клиенты отправляют sha2 хеш тоже в усечённом виде до 20 байт, вместо 40.

Код запроса (разбит для понимания):
$info_hash = rtrim(DB()->escape($info_hash), ' ');

$info_hash_where = "WHERE tor.info_hash = '$info_hash' OR tor.info_hash_v2 LIKE '$info_hash%'";

$sql = "
    SELECT tor.topic_id, tor.poster_id, tor.tor_type, tor.info_hash, tor.info_hash_v2, u.*
    FROM " . BB_BT_TORRENTS . " tor
    LEFT JOIN " . BB_BT_USERS . " u ON u.auth_key = '$passkey_sql'
    $info_hash_where
    LIMIT 1
";
$row = DB()->fetch_row($sql);


Что интересно, что запрос отлично работает с sha1, с sha2 проблем тоже нет, но на некоторых специфичных sha2 усечённых до 20 байт наблюдаются проблемы.
Сверял строки, отправляемые данные, но запрос всё равно не идёт.
Строки отправляются url закодированном виде, PHP автоматом их помещает в массив в исправном виде, с ними проблем вообще нет, иначе бы ничего не работало.

Если дать полную строку (40 байт) для поиска без LIKE то всё нормально, проблема именно с укороченным вариантом.

Вот пример sha2 строк:

Полный: 97978479e7eba00af6f24953f6b94b229b556627167b710472ad4c9bc4ceac41 // Найден

Укороченный: 97978479e7eba00af6f24953f6b94b229b556627 // Найден

Закодированный url: %97%97%84y%e7%eb%a0%0a%f6%f2IS%f6%b9K%22%9bUf%27

Проблема:

Полный: b5c69a98c0235cc2af0fa0c956a2984dc10410b1ab0eee46c9cb0c876d0c5189 // Найден

Укороченный: b5c69a98c0235cc2af0fa0c956a2984dc10410b1 // НЕ НАЙДЕН!

Закодированный url: %b5%c6%9a%98%c0%23%5c%c2%af%0f%a0%c9V%a2%98M%c1%04%10%b1
  • Вопрос задан
  • 159 просмотров
Решения вопроса 1
Daemon23RUS
@Daemon23RUS
"WHERE tor.info_hash = '$info_hash' OR SUBSTRING(tor.info_hash_v2, 1,20)= '$info_hash'"
что то вроде такого.
SUBSTRING- может подойдет для бинарных данных, не уверен, но Вам этот способ проверить проще. Помню, где то применял подобную конструкцию. Возможно будет решением Вашей проблемы.
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
@Akina
Сетевой и системный админ, SQL-программист.
LIKE в принципе не пригоден для указанной операции. Это чисто текстовый оператор. В частности, он безусловно применяет к обоим операндам регистронезависимый collation - в этих условиях сравнение БИНАРНЫХ строк способно давать весьма неожиданные эффекты.

Если надо работать именно с бинарными строками - всё плохо. В MySQL нет нормальных функций для работы с ними.

Для решения задачи рекомендую преобразовать бинарную строку в шестнадцатеричное представление, укоротить до 40-символьного (что соответствует 20-байтовому бинарному значению), преобразовать обратно, и сравнить.

WHERE CONV(LEFT(HEX('$entered_hash'), 40), 16, 10) = correct_hash


К тому же такое условие SARGable.

PS. В выражении
WHERE '{value1}' = '{value2}' OR '{value1}' LIKE '{value2}%'
первое условие заведомо лишнее (ведь если оно выполняется, то и второе тоже выполняется).
Ответ написан
Vapaamies
@Vapaamies
Разработчик будущей ОС для ПК размером 250 МБ
Из-за кривой реализации в протоколе, клиенты отправляют sha2 хеш тоже в усечённом виде до 20 байт, вместо 40.

Это не кривая реализация, а гибридный торрент. Всё по стандарту.

Полный: b5c69a98c0235cc2af0fa0c956a2984dc10410b1ab0eee46c9cb0c876d0c5189 // Найден
Укороченный: b5c69a98c0235cc2af0fa0c956a2984dc10410b1 // НЕ НАЙДЕН!
Закодированный url: %b5%c6%9a%98%c0%23%5c%c2%af%0f%a0%c9V%a2%98M%c1%04%10%b1

Вы фигню какую-то делаете. В закодированном URL указан двоичный хеш, а вы его преобразовываете в незнамо что. Используйте тип binary или varbinary в БД, раскодируйте хеш и храните как есть.

Плюс обязательно подготовленные запросы, полностью согласен с alexalexes.
Ответ написан
Комментировать
@alexalexes
Вероятно, проблема в состоянии индекса на поле info_hash_v2 (вы же используете индексы в базе для ускорения поиска?). Запустите перерасчет индекса для устранения глюка.

PS:
$info_hash_where = "WHERE tor.info_hash = '$info_hash' OR tor.info_hash_v2 LIKE '$info_hash%'";

Ай-ай-ай. Склейка параметров запроса с текстом запроса, ой не хорошо. Не хотите познакомиться с подготовленными запросами?
Или нормально исследуйте класс объекта DB, вдруг там есть нормальный метод bind_param, а то вдруг используете инструмент не до конца по назначению.
Ответ написан
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы