Представьте, что вы создаёте мощное поисковое приложение. Пользователи вводят ключевые слова, а ваш бэкенд должен выполнять запрос к базе данных Manticore Search, чтобы найти подходящие результаты. Распространённый (и соблазнительный!) подход — напрямую вставлять ввод пользователя в SQL‑запросы. Например, вы можете фильтровать по числовому полю, такому как категория или идентификатор записи. Если пользователь передаёт обычное значение, например 5, запрос будет SELECT * FROM products WHERE id=5. А что, если он передаст 1 OR 1=1? Запрос станет SELECT * FROM products WHERE id=1 OR 1=1 — условие всегда истинно, поэтому запрос вернёт все строки вместо одной. Это SQL‑инъекция.
К счастью, существует более безопасный и эффективный способ: подготовленные запросы. По сути, подготовленные запросы отделяют ваш SQL‑код от передаваемых данных. Вместо того чтобы каждый раз собирать всю строку запроса, вы определяете структуру запроса один раз с заполнителями, а затем отдельно передаёте поисковые термины. Подробнее о концепции можно узнать на Wikipedia .
Manticore Search поддерживает подготовленные запросы через стандартный протокол MySQL, предоставляя мощный инструмент для создания безопасных поисковых приложений. Используя подготовленные запросы, вы не только значительно снизите риск SQL‑инъекций, но и улучшите читаемость вашего кода.
Подготовленные запросы — это не просто функция; иногда они являются обязательными. Например, библиотека Rust sqlx работает с MySQL‑конечным пунктом, используя исключительно подготовленные запросы. Кроме того, некоторые OLE DB‑коннекторы, позволяющие MS SQL работать с сервером MySQL, используют подготовленные запросы внутри.
Почему использовать подготовленные запросы?
Безопасность превыше всего (SQL‑инъекция): SQL‑инъекция — это уязвимость веб‑безопасности, позволяющая злоумышленникам вмешиваться в запросы, которые приложение отправляет в базу данных. Это происходит, когда ввод пользователя неправильно включается в SQL‑запрос, позволяя выполнить вредоносный код. Например, рассмотрим простой поисковый запрос, построенный конкатенацией поискового термина пользователя непосредственно в SQL:
// Vulnerable code example (DO NOT USE!)
$productId = $_GET['search'];
$query = "SELECT * FROM products WHERE id= " . $productId;
Если $productId содержал, например, 0 OR 1=1, запрос превратится в SELECT * FROM products WHERE id= 0 OR 1=1, эффективно обходя условие WHERE и возвращая все товары.
Подготовленные запросы предотвращают это, рассматривая ввод пользователя строго как данные, а не как часть SQL‑команды. Драйвер базы данных обрабатывает экранирование и кавычки, гарантируя нейтрализацию потенциально опасных символов. Ниже тот же запрос, использующий подготовленный запрос:
// Secure code example using a prepared statement
$productId = $_GET['search'];
$stmt = $mysqli->prepare("SELECT * FROM products WHERE id= ?");
$stmt->bind_param("i", $productId);
$stmt->execute();
В этом случае, даже если $productId содержит вредоносный код, он будет рассматриваться как буквальное значение, а не как исполняемый SQL.
Как они работают
Подготовленные запросы работают по простому трёхшаговому процессу:
- Подготовка: Сначала вы отправляете SQL‑оператор с заполнителями (например
?или?VEC?) в Manticore Search. Manticore разбирает этот оператор и создаёт план запроса. Затем он возвращает уникальный идентификатор для этого подготовленного запроса. - Привязка: Затем вы отправляете фактические данные — значения для заполнителей — в Manticore отдельно. Здесь проявляется безопасность; данные рассматриваются исключительно как данные, а не как SQL‑код.
- Выполнение: Наконец, вы инструктируете Manticore выполнить подготовленный запрос, используя сохранённый план запроса и привязанные параметры.
Подумайте об этом как о создании шаблона. Вы строите структуру один раз, а затем заполняете пустые места разной информацией каждый раз, когда нужно её использовать.
Заполнители параметров: ? & ?VEC?
Manticore Search использует специальные заполнители для идентификации параметров в ваших подготовленных запросах:
?представляет один параметр — это может быть целое число, число с плавающей точкой или строка. При использовании этого заполнителя Manticore автоматически обрабатывает экранирование и кавычки для строковых значений, защищая от SQL‑инъекций и обеспечивая правильное форматирование данных.?VEC?предназначен для списков числовых значений. Он ожидает строку, содержащую числа, разделённые запятыми и необязательными пробелами (например,1, 2.3, 4, 1e-10, INF). Важно, что к значениям внутри?VEC?не применяется экранирование и кавычки. Корректный ввод состоит только из чисел, запятых и пробелов; любые другие символы, скорее всего, вызовут ошибку. Это делает его идеальным для прямой вставки числовых векторов в ваши данные — как векторов с плавающей точкой, так и целочисленных MVA (многозначных атрибутов).
Пример: подготовленные запросы в PHP
Посмотрим, как подготовленные запросы работают на практике с использованием PHP. Мы продемонстрируем как простой вставкой со строковыми значениями, так и более сложной вставкой, включающей вектор с плавающей точкой, используя заполнитель ?VEC?.
Сначала простая вставка:
<?php
// Assuming you have a valid MySQLi connection established ($mysqli)
$stmt = $mysqli->prepare("INSERT INTO products (name, description) VALUES (?, ?)");
$productName = "Awesome Widget";
$productDescription = "A truly amazing widget for all your needs.";
$stmt->bind_param("ss", $productName, $productDescription); // "ss" indicates two strings
$stmt->execute();
echo "Product added successfully!";
?>
Этот код подготавливает оператор INSERT, привязывает строковые значения для названия продукта и описания, а затем выполняет запрос. Полученный SQL, выполненный Manticore, будет выглядеть так:
INSERT INTO products (name, description) VALUES ('Awesome Widget', 'A truly amazing widget for all your needs.');
Теперь разберём пример с вектором с плавающей точкой. Что такое ?VEC?? Это заполнитель (используется только в подготовленных запросах) для вектора — списка чисел, например для эмбеддингов или аналогичных данных. В Manticore SQL векторный литерал всегда записывается в скобках: (0.1, 0.2, 0.3). Поэтому, когда вы используете подготовленный запрос и имеете параметр‑вектор, вы пишете эти скобки в строке SQL и используете ?VEC? там, где находятся числа. Вы привязываете только числа, разделённые запятыми (например, "0.1,0.2,0.3"); скобки ( и ) не привязываются — они остаются в запросе. Без подготовленных запросов вам пришлось бы самостоятельно формировать полный литерал (0.1, 0.2, 0.3) в строке запроса. Without
В PHP mysqli обычный способ привязывать значения ?VEC? — как строки, поэтому в этом примере нормальный выбор — iss. Если вы хотите передавать более крупный векторный полезный нагрузка, вы также можете привязать параметр как b и отправить содержимое с помощью send_long_data().
<?php
// Assuming you have a valid MySQLi connection established ($mysqli)
$stmt = $mysqli->prepare("INSERT INTO items (item_id, coords, features) VALUES (?, (?VEC?),(?VEC?))");
$itemId = 123;
$coordVector = "20.245,54.354,30.000"; // that is vector of floats
$featureSet = "1,4,20,456,112,3"; // that is set of integer values (MVA)
$stmt->bind_param("iss", $itemId, $coordVector, $featureSet); // "i" for integer (itemId), "s" for string
$stmt->execute();
echo "Item with feature vector added successfully!";
$itemId = 124;
$coordVector = "18.500,42.000,31.125"; // Another float vector
$featureSet = "0,6,34,665,22,3445,221,564,2232,5644,43"; // Example with more feature values
// For larger payloads you can bind the second ?VEC? as a blob and stream it.
$featurePlaceholder = "";
$stmt->bind_param("isb", $itemId, $coordVector, $featurePlaceholder); // "b" is for blob data
// bind_param() must be called before send_long_data().
$stmt->send_long_data(2, $featureSet); // zero-based index: 2 means the third bound parameter
$stmt->execute();
echo "Item with feature vector added successfully!";
?>
Обратите внимание, что скобки являются частью SQL‑строки в вызове prepare(). Мы привязываем только значения внутри скобок, используя заполнитель ?VEC?. Полученный SQL, выполненный Manticore, будет:
INSERT INTO items (item_id, coords, features) VALUES (123, (20.245,54.354,30.000), (1,4,20,456,112,3));
INSERT INTO items (item_id, coords, features) VALUES (124, (18.500,42.000,31.125), (0,6,34,665,22,3445,221,564,2232,5644,43));
Использование ?VEC? в подготовленном запросе дает те же преимущества, что и заполнитель ?: значения вектора отправляются как данные, а не как часть текста SQL, поэтому их нельзя интерпретировать как SQL и они не могут вызвать инъекцию. Вы также избегаете необходимости вручную строить или экранировать векторный литерал в вашем приложении — Manticore получает привязанные числа и правильно форматирует вектор, что сохраняет запрос безопасным и данные согласованными.
Важные соображения и ограничения
Хотя мощные, подготовленные запросы Manticore имеют несколько ограничений, которые следует учитывать.
Мульти‑запросы: Для каждого подготовленного запроса разрешён только один SQL‑оператор. Попытки использовать мульти‑запросы (например, SELECT ...; SHOW META) завершатся неудачей. Если вам нужно выполнить несколько операторов, подготовьте отдельный запрос для каждого и выполните их последовательно в рамках одной сессии.
Числовые типы: Некоторые драйверы баз данных (например, mysql2 для Node.js) могут по умолчанию отправлять числовые параметры как DOUBLE. Это может привести к неожиданному поведению, если вам требуется строгая целочисленная обработка (например, отклонение отрицательных ID). В таких случаях рассмотрите возможность отправки целых чисел как строк или используйте типы целых чисел, специфичные для драйвера (например, BigInt), чтобы обеспечить корректную обработку данных.
Пользователи Rust sqlx: Если вы используете crate sqlx в Rust, имейте в виду, что при чтении строк результата вы должны использовать индексы столбцов, а не имена столбцов. Хотя имена столбцов присутствуют в наборе результатов, sqlx не использует их для сопоставления. Например, используйте row.try_get(0)? вместо row.try_get("id")?.
Заключение
Подготовленные запросы предлагают критическое сочетание безопасности, читаемости и потенциальных улучшений производительности при работе с Manticore Search. Отделяя вашу SQL‑логику от данных, вы значительно снижаете риск атак SQL‑инъекций, улучшаете поддерживаемость кода и потенциально ускоряете выполнение запросов. Мы настоятельно рекомендуем использовать подготовленные запросы в ваших приложениях Manticore Search.
Для более подробной информации обязательно ознакомьтесь с этими ресурсами:
- Документация Manticore Search о подготовленных запросах: https://manual.manticoresearch.com/Connecting_to_the_server/MySQL_protocol#Prepared-statements
- Wikipedia — Подготовленные запросы: https://en.wikipedia.org/wiki/Prepared_statement
Это руководство предоставляет прочную основу для эффективного использования подготовленных запросов в ваших проектах Manticore Search, приводя к более безопасным, эффективным и поддерживаемым приложениям.
