# Протокол MySQL

Manticore Search реализует SQL-интерфейс с использованием протокола MySQL, что позволяет использовать любую библиотеку или коннектор MySQL, а также многие MySQL-клиенты для подключения к Manticore Search и работы с ним так, как если бы это был сервер MySQL, а не Manticore.

Однако диалект SQL отличается и реализует только подмножество SQL-команд или функций, доступных в MySQL. Кроме того, существуют команды и функции, специфичные для Manticore Search, такие как клауза `MATCH()` для полнотекстового поиска.

Manticore Search поддерживает [подготовленные выражения на стороне сервера](../Connecting_to_the_server/MySQL_protocol.md#Prepared-statements) через протокол MySQL. Также можно использовать подготовленные выражения на стороне клиента. Важно отметить, что Manticore реализует типы данных multi-value (MVA) и `float_vector`, которые не имеют эквивалентов в MySQL или библиотеках, реализующих подготовленные выражения. В этих случаях значения должны быть сформированы в исходном запросе в виде списка чисел, разделённых запятыми.

Если [аутентификация и авторизация](../Security/Authentication_and_authorization.md) включены, клиенты MySQL должны подключаться с именем пользователя и паролем Manticore. Поддерживаемый режим аутентификации MySQL — `mysql_native_password`. Если аутентификация отключена, значения пользователя и пароля, переданные клиентами, игнорируются.

## Конфигурация

**Порт по умолчанию для SQL-интерфейса — 9306**, и он включен по умолчанию.

Вы можете настроить порт MySQL в разделе searchd конфигурационного файла, используя директиву `listen` следующим образом:

```ini
searchd {
...
   listen = 127.0.0.1:9306:mysql
...
}
```

Если аутентификация не включена, убедитесь, что порт MySQL недоступен никому за пределами вашей сети.

### VIP-подключение
Для выполнения "VIP" подключений можно использовать отдельный порт MySQL. При подключении к этому порту пул потоков обходится, и всегда создается новый выделенный поток. Это полезно в случае серьезной перегрузки, когда сервер либо зависает, либо препятствует подключению через обычный порт.

```ini
searchd {
...
   listen = 127.0.0.1:9306:mysql
   listen = 127.0.0.1:9307:mysql_vip
...
}
```

## Подключение через стандартный MySQL клиент
Самый простой способ подключения к Manticore — использование стандартного MySQL клиента:

```shell
mysql -P9306 -h0
```

Когда аутентификация включена, укажите пользователя и пароль:

```shell
MYSQL_PWD=StrongPass#2026 mysql -P9306 -h0 -uadmin
```

Если вашему клиенту явно требуется плагин аутентификации, задайте `mysql_native_password`:

```shell
MYSQL_PWD=StrongPass#2026 mysql -P9306 -h0 -uadmin --default-auth=mysql_native_password
```

## Защищённое MySQL-подключение

Протокол MySQL поддерживает [шифрование SSL](../Security/SSL.md). Безопасные подключения можно установить на том же порту `mysql`.

## Сжатое MySQL-подключение

Сжатие можно использовать при подключениях MySQL, и оно доступно клиентам по умолчанию. Клиенту просто нужно указать, что соединение должно использовать сжатие.

Пример использования MySQL-клиента:

```shell
mysql -P9306 -h0 -C
```

Сжатие можно использовать как в защищённых, так и в незашищённых подключениях.

## Примечания по коннекторам MySQL
Официальные коннекторы MySQL можно использовать для подключения к Manticore Search, однако им могут потребоваться определённые настройки, передаваемые в строке DSN, поскольку коннектор может попытаться выполнить SQL-команды, которые ещё не реализованы в Manticore.

JDBC коннектор 6.x и выше требует Manticore Search версии 2.8.2 или выше, и строка DSN должна содержать следующие опции:
```ini
jdbc:mysql://IP:PORT/DB/?characterEncoding=utf8&maxAllowedPacket=512000&serverTimezone=XXX
```

По умолчанию Manticore Search сообщает коннектору свою собственную версию, однако это может вызвать некоторые проблемы. Для их решения директива `mysql_version_string` в разделе searchd конфигурационного файла должна быть установлена на версию ниже 5.1.1:

```ini
searchd {
...
   mysql_version_string = 5.0.37
...
}
```

.NET коннектор MySQL по умолчанию использует пулы подключений. Для правильного получения статистики `SHOW META` запросы вместе с командой `SHOW META` должны отправляться как единое многокомандное выражение (`SELECT ...;SHOW META`). Если пулы включены, в строку подключения необходимо добавить опцию `Allow Batch=True`, чтобы разрешить многокомандность:
```ini
Server=127.0.0.1;Port=9306;Database=somevalue;Uid=somevalue;Pwd=;Allow Batch=True;
```

## Примечания по ODBC-подключению
К Manticore можно получить доступ с помощью ODBC. Рекомендуется устанавливать `charset=UTF8` в строке ODBC. Некоторые драйверы ODBC не понравится версия, сообщаемая сервером Manticore, так как они увидят её как очень старую версию сервера MySQL. Это можно переопределить с помощью опции `mysql_version_string`.

## Синтаксис комментариев

SQL Manticore через MySQL поддерживает синтаксис комментариев в стиле C. Всё от начальной последовательности `/*` до конечной последовательности `*/` игнорируется. Комментарии могут занимать несколько строк, не могут вкладываться и не должны записываться в лог. Специфичные для MySQL комментарии `/*! ... */` также в настоящее время игнорируются. (Поддержка комментариев была добавлена скорее для лучшей совместимости с дампами, созданными `mysqldump`, а не для улучшения общей интероперабельности запросов между Manticore и MySQL.)

```sql
SELECT /*! SQL_CALC_FOUND_ROWS */ col1 FROM table1 WHERE ...
```

# Подготовленные выражения

Manticore поддерживает подготовленные выражения через протокол MySQL.

В базах данных [подготовленные выражения](https://en.wikipedia.org/wiki/Prepared_statement) — это способ выполнения запросов, при котором SQL-код отделён от входных данных. Вместо написания полного SQL-запроса со значениями, включёнными напрямую в него, вы пишете запрос один раз, используя плейсхолдеры, а затем отдельно передаёте значения.

Некоторые клиентские библиотеки (например, [sqlx](https://docs.rs/sqlx/latest/sqlx/)) взаимодействуют с базами данных только таким образом.

## Почему подготовленные выражения важны?

* **Безопасность (предотвращение SQL-инъекций):** Это самая важная причина. SQL-инъекция — распространённая уязвимость веб-приложений, при которой злоумышленники вставляют вредоносный SQL в запросы. Подготовленные выражения гарантируют, что пользовательский ввод обрабатывается строго как данные, а не как исполняемый код, предотвращая его интерпретацию как часть SQL-команды.

* **Читаемость и поддерживаемость:** Подготовленные выражения улучшают ясность кода, отделяя SQL-логику от входных данных, что делает код более удобным для чтения и поддержки.

* **Производительность:** База данных может кэшировать планы запросов. Если один и тот же параметризованный запрос выполняется многократно с разными значениями, база данных может повторно использовать кэшированный план выполнения вместо того, чтобы каждый раз разбирать и оптимизировать запрос, что может повысить производительность.

<!-- example prepared-statements -->
## Как работают подготовленные выражения

1. **Подготовка запроса:** Отправьте SQL-запрос с заполнителями (такими как `?` или `?VEC?`) в Manticore. Manticore разбирает и компилирует запрос, сохраняет его и возвращает уникальный идентификатор, который можно использовать для последующего выполнения.
2. **Привязка параметров:** Отправьте значения для заполнителей отдельно. Manticore рассматривает эти значения строго как данные (не как SQL-код) и безопасно вставляет их в сохранённый запрос.
3. **Выполнение запроса:** Manticore выполняет запрос, используя предварительно скомпилированный план вместе с предоставленными значениями параметров.

<!-- intro -->
### Примеры

<!-- request SQL -->
```
INSERT INTO table (id, floatvec) VALUES (?, (?VEC?))
```

<!-- request PHP -->
```php
$stmt = $mysqli->prepare("INSERT INTO tbl (id, str, floatvec) VALUES (0, ?, (?VEC?))");
$str = "I'm a string";
$vec = "0.1,0.2,0.3";
$stmt->bind_param("ss", $str, $vec);
$stmt->execute();
```

<!-- response PHP -->

Будет выполнено:
```
INSERT INTO tbl (id, str, floatvec) VALUES (0, 'I\'m a string', (0.1,0.2,0.3))
```

<!-- end -->

## Заполнители параметров

* `?` (вопросительный знак): Представляет один параметр (целое число, число с плавающей точкой или строка). Строковые значения обрабатываются автоматически — например, специальные символы, такие как одинарные кавычки, экранируются, а значение заключается в кавычки. Логические значения можно передавать в виде строк (`'true'`, `'false'`).
* `?VEC?`: Представляет список числовых значений, предоставленных в виде строки (например, `1,2,3` или `0.3,15E+3, 20 ,INF`). Разрешены только числа, запятые и пробелы. После проверки значения вставляются точно так, как предоставлены (без дополнительного экранирования или кавычек).

## Векторные параметры (MVA/float_vector)

В стандартном SQL-синтаксисе Manticore [MVA](../Creating_a_table/Data_types.md#Multi-value-integer-%28MVA%29) или [векторы с плавающей точкой](../Creating_a_table/Data_types.md#Float-vector) записываются как список значений в круглых скобках, например `(1, 2, 3)`.

При использовании подготовленных запросов включите круглые скобки непосредственно в запрос и используйте заполнитель `?VEC?` только для значений внутри них. Например:

```php
$stmt = $mysqli->prepare("INSERT INTO tbl (id, floatvec) VALUES (0, (?VEC?))");
$vec = "0.1,0.2,0.3";
$stmt->bind_param("s", $vec);
```

## Ограничения

1. Разрешён только один SQL-запрос на подготовленный запрос. [Мультизапросы](../Searching/Multi-queries.md#Multi-queries) (например, `SELECT ...; SHOW META`) не поддерживаются. Если вам нужно выполнить несколько запросов, подготовьте отдельный запрос для каждого и выполните их последовательно в одной сессии.
2. Некоторые драйверы отправляют числовые параметры как DOUBLE (например, `mysql2` для Node.js). Если вам требуется строгое целочисленное поведение (например, отклонение отрицательных ID), отправляйте целые числа в виде строк или используйте специфичные для драйвера целочисленные типы (например, `BigInt`).
3. **Rust sqlx:** При чтении строк результирующего набора используйте индексы столбцов, а не их имена. Имена столбцов присутствуют в результирующем наборе, но sqlx не использует их для сопоставления. Например, используйте `row.try_get(0)?` вместо `row.try_get("id")?`.
