> For the complete documentation index, see [llms.txt](https://docs.iexexchanger.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.iexexchanger.com/server-i-dannye/rabota-s-postgresql/migraciya-s-mysql-na-postgresql.md).

# Миграция с MySQL на PostgreSQL

Эта инструкция описывает перенос действующего проекта iEXExchanger с MySQL на PostgreSQL 18 с помощью встроенного инструмента `iEX DB Migrator`.

Мигратор создаёт структуру PostgreSQL, переносит данные, проверяет результат и переключает подключение проекта только после успешного завершения основных проверок. При необходимости отдельно переносится база Laravel Pulse.

Для обычной миграции используется пустая PostgreSQL-база, заранее созданная в FASTPANEL. В конфигурации необходимо указать:

```json
"schema_mode": "empty"
```

Другие режимы работы со схемой предназначены для технических специалистов и описаны отдельно в конце документа.

Перед началом выполните инструкцию

{% content-ref url="/pages/nTV8YubEHpIQWlwePofG" %}
[Подключение PostgreSQL](/server-i-dannye/rabota-s-postgresql/podklyuchenie-postgresql.md)
{% endcontent-ref %}

В FASTPANEL должны быть созданы пустые PostgreSQL-базы и отдельные пользователи для основной базы и Laravel Pulse.

{% hint style="danger" %}

## Внимание

Не удаляйте исходную MySQL-базу после переноса.

Она понадобится для контрольной проверки и возможного возврата проекта до начала записи новых данных в PostgreSQL.
{% endhint %}

## Как проходит миграция

<figure><img src="/files/VHaOaA9RGZrQmmQ1BRa9" alt="" width="375"><figcaption></figcaption></figure>

## Что переносит мигратор

`iEX DB Migrator` переносит:

* основную базу iEXExchanger;
* отдельную базу Laravel Pulse;
* таблицы и колонки;
* все строки выбранных таблиц;
* первичные ключи;
* уникальные ограничения;
* обычные и составные индексы;
* внешние ключи;
* `CHECK`-ограничения;
* identity и serial sequences;
* generated columns;
* аналоги `ON UPDATE CURRENT_TIMESTAMP`;
* значения JSON с преобразованием в `jsonb`;
* числовые, строковые, временные и логические значения.

Перед переносом проверяются:

* версии MySQL и PostgreSQL;
* кодировки баз;
* права пользователей;
* структура таблиц;
* определения колонок;
* индексы и ограничения;
* unsigned-значения;
* zero-date;
* значения `TIME`;
* JSON;
* generated columns;
* числовые диапазоны;
* количество строк.

После переноса мигратор сравнивает структуру и вычисляет SHA-256 digest данных выбранных таблиц.

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

***

## Промпт для выполнения миграции

Этот промпт можно скопировать и передать техническому специалисту или агенту, который имеет доступ к серверу.

{% prompt description="Промпт для безопасной миграции" %}

```markdown
Выполни безопасную миграцию действующего проекта iEXExchanger с MySQL на PostgreSQL 18 через встроенный инструмент:

tools/db-migrator/iex-db-migrator

Работай только в пределах Backend-проекта и не показывай пароли в консоли или итоговом отчёте.

Перед началом

1. Найди корневую директорию Laravel с файлами artisan и .env.
2. Определи владельца Backend-сайта в FASTPANEL.
3. Проверь, что PostgreSQL 18 установлен и работает.
4. Проверь, что PostgreSQL добавлен в FASTPANEL и имеет статус «Доступен».
5. Проверь, что для основной базы и Laravel Pulse созданы отдельные пустые PostgreSQL-базы.
6. Проверь наличие:
   - tools/db-migrator/iex-db-migrator;
   - tools/db-migrator/migration.example.json;
   - tools/db-backup/iex-db-backup.
7. Проверь наличие свободного места для переноса и резервной копии.

Обязательный режим схемы

Для обычной клиентской миграции используй только:

"schema_mode": "empty"

Также укажи этот режим в команде запуска:

--schema-mode empty

Не используй schema_mode=auto или schema_mode=existing, если пользователь отдельно не запросил расширенный технический сценарий.

Обязательные политики

"exact": true
"maintenance": false
"switch_env": true
"workers": 3
"schema_mode": "empty"
"allow_non_empty": false
"allow_target_reset": false

Границы безопасности

- MySQL используется как источник данных.
- Не выполняй DROP DATABASE для MySQL.
- Не запускай reset-target без отдельного явного разрешения.
- Не устанавливай allow_target_reset=true для обычной миграции.
- Не отключай exact mode.
- Не запускай второй экземпляр мигратора параллельно.
- Не переключай Laravel .env вручную.
- Не запускай php artisan migrate до завершения точной проверки.
- Не запускай Product Updates до завершения отдельной команды verify.
- Не выдавай RELOAD или FLUSH_TABLES обычному пользователю приложения.
- Для локального MySQL запускай мигратор от пользователя root, чтобы мигратор мог использовать /root/.my.cnf.
- Не используй fastuser как target.username. fastuser нужен только для управления PostgreSQL через FASTPANEL.

Подготовка конфигурации

Создай приватный файл:

tools/db-migrator/db-migrator.json

на основе:

tools/db-migrator/migration.example.json

Установи права:

chmod 600 tools/db-migrator/db-migrator.json

В source укажи действующие реквизиты MySQL.

В target укажи реквизиты пустых PostgreSQL-баз, созданных в FASTPANEL.

Для основной базы используй отдельного PostgreSQL-пользователя.

Для Laravel Pulse база и пользователь должны называться:

pulse_pg

Если Pulse не используется, отключи профиль:

"enabled": false

Проверки до переноса

Выполни:

./tools/db-migrator/iex-db-migrator config-check --profile all

./tools/db-migrator/iex-db-migrator target-check --profile all

./tools/db-migrator/iex-db-migrator plan --profile all

Все три команды должны завершиться успешно.

Резервная копия

До переключения .env создай резервную копию исходной MySQL:

./tools/db-backup/iex-db-backup export \
  --profile all \
  --tag before-mysql-to-postgresql

Проверь SQL-файлы и manifest. Сообщи пользователю, что резервную копию нужно скачать с сервера и сохранить во внешнем защищённом хранилище.

Окно обслуживания

Перед запуском run:

1. Запрети создание новых заявок.
2. Останови внешний write-трафик.
3. Останови очереди и планировщики, записывающие данные.
4. Останови процессы записи курсов, логов и метрик.
5. Убедись, что Product Updates и Laravel migrations не запущены.
6. Убедись, что другой экземпляр мигратора не работает.

Финальный перенос

Запусти от пользователя root из корня Backend:

./tools/db-migrator/iex-db-migrator run \
  --profile all \
  --schema-mode empty \
  --yes

Не прерывай процесс без необходимости.

После успешного run и до запуска приложения выполни:

./tools/db-migrator/iex-db-migrator verify --profile all

Только после успешного verify продолжай работу.

Product Updates

Переключись на владельца Backend-сайта.

Сначала выполни:

php artisan product-updates:run --dry-run -v

Если проверка успешна:

php artisan product-updates:run

Не используй --force вслепую и не создавай отсутствующие таблицы или колонки вручную.

После обновления проверь:

php artisan db:show --database=pgsql
php artisan product-updates:status --limit=5
php artisan migrate:status

Перезапусти очереди, Laravel Scheduler, Reverb, Horizon, PM2 и другие долгоживущие процессы, если они используются в проекте.

Проверь:

- вход в административную панель;
- пользователей;
- валюты и резервы;
- направления обмена;
- существующие заявки;
- создание тестовой заявки;
- изменение статуса тестовой заявки;
- очереди;
- CRON;
- Laravel Pulse;
- уведомления;
- логи Laravel;
- отсутствие новых ошибок PostgreSQL.

Итоговый отчёт

Укажи:

1. версию мигратора;
2. обработанные профили;
3. имена source и target без паролей;
4. количество перенесённых таблиц и строк;
5. результат SHA-256-проверки;
6. результат отдельной команды verify;
7. путь к persistent log;
8. путь к резервной копии .env;
9. результат переключения DB_CONNECTION;
10. результат Product Updates;
11. результат проверки приложения;
12. перечень оставшихся ручных действий.

Никогда не показывай пароли в итоговом отчёте.
```

{% endprompt %}

## Перед началом

Проверьте:

* PostgreSQL имеет версию `18.x`;
* PostgreSQL работает и доступен на локальном порту;
* PHP 8.4 видит модули `pdo_pgsql` и `pgsql`;
* PostgreSQL добавлен в FASTPANEL;
* PostgreSQL-сервер имеет статус «Доступен»;
* создана пустая основная PostgreSQL-база;
* создан отдельный пользователь основной базы;
* при необходимости создана пустая база Laravel Pulse;
* база и пользователь Pulse называются `pulse_pg`;
* PostgreSQL-базы используют кодировку `UTF8`;
* исходная MySQL доступна;
* в Backend-проекте присутствуют `.env` и `artisan`;
* на сервере достаточно свободного места;
* подготовлено окно обслуживания.

{% hint style="info" %}
До переноса не запускайте Laravel migrations и Product Updates для пустых PostgreSQL-баз.

Иначе базы перестанут быть пустыми и не пройдут проверку режима `empty`.
{% endhint %}

## Файлы системы

| Файл                                       | Назначение                                    |
| ------------------------------------------ | --------------------------------------------- |
| `tools/db-migrator/iex-db-migrator`        | Основная команда переноса                     |
| `tools/db-migrator/migration.example.json` | Пример конфигурации                           |
| `tools/db-migrator/db-migrator.json`       | Приватная конфигурация переноса               |
| `storage/logs/db-migrator/`                | Постоянные логи мигратора                     |
| `tools/db-backup/iex-db-backup`            | Инструмент резервного копирования             |
| `tools/db-backup/db-backup.json`           | Приватная конфигурация резервного копирования |
| `storage/logs/db-backup/`                  | Логи резервного копирования                   |
| `tools/db-auditor/iex-db-auditor`          | Аудит PostgreSQL после обновления             |

{% hint style="warning" %}
Импорт и экспорт SQL выполняются через `iex-db-backup`.

Не используйте мигратор как инструмент обычного импорта, экспорта или резервного копирования.
{% endhint %}

## Какие пользователи используются

Во время переноса задействованы разные учётные записи.

<table><thead><tr><th width="348.7421875">Пользователь</th><th>Назначение</th></tr></thead><tbody><tr><td><code>root</code></td><td>Пользователь операционной системы, от которого запускается мигратор</td></tr><tr><td>Пользователь MySQL приложения</td><td>Чтение исходной базы</td></tr><tr><td>Пользователь MySQL lock</td><td>Получение глобальной блокировки записи, если недоступен <code>/root/.my.cnf</code></td></tr><tr><td><code>fastuser</code></td><td>Управление PostgreSQL через FASTPANEL</td></tr><tr><td>Пользователь PostgreSQL приложения</td><td>Подключение к новой основной базе</td></tr><tr><td><code>pulse_pg</code></td><td>Подключение к новой базе Laravel Pulse</td></tr><tr><td>Владелец сайта</td><td>Запуск Laravel-команд и процессов проекта</td></tr></tbody></table>

Пример:

```
Администратор PostgreSQL в FASTPANEL:
fastuser

Владелец Backend-сайта:
example_com_usr

Основная PostgreSQL-база:
app_example_com_pg

Пользователь основной базы:
app_example_com_pg

PostgreSQL-база Laravel Pulse:
pulse_pg

Пользователь Laravel Pulse:
pulse_pg
```

{% hint style="warning" %}
Не указывайте `fastuser` в `target.username`.

Для приложения используется пользователь, созданный вместе с соответствующей базой в FASTPANEL.
{% endhint %}

## Основной режим схемы — `empty`

Для клиентской миграции используется:

```json
"schema_mode": "empty"
```

Этот режим предназначен для пустых PostgreSQL-баз, заранее созданных в FASTPANEL.

При запуске мигратор:

1. подключается через пользователя из `target.username`;
2. проверяет версию PostgreSQL;
3. проверяет кодировку `UTF8`;
4. проверяет права на создание объектов;
5. проверяет отсутствие пользовательских таблиц;
6. создаёт структуру PostgreSQL;
7. переносит данные;
8. создаёт индексы, ключи и ограничения;
9. настраивает sequences;
10. выполняет точную проверку.

Мигратор не меняет владельца базы и не удаляет саму базу при обычной ошибке переноса.

{% hint style="danger" %}
Не используйте `schema_mode=existing` для пустой базы FASTPANEL.

Режим `existing` ожидает, что необходимые таблицы уже созданы. Для стандартной миграции используйте только `empty`.
{% endhint %}

***

## Подготовка конфигурации

{% stepper %}
{% step %}

### Подключитесь к серверу

{% content-ref url="/spaces/uyjsNtEAtO6Sby8CHWyD/pages/cCEoFDjTDIufy3NUEakd" %}
[Подключение к серверу по SSH](/help-center/upravlenie-serverom/podklyuchenie-k-serveru-po-ssh.md)
{% endcontent-ref %}

```bash
ssh root@SERVER_IP
```

Мигратор запускается от пользователя `root`, чтобы точный режим мог получить MySQL read-lock через защищённый файл `/root/.my.cnf`.
{% endstep %}

{% step %}

### Перейдите в Backend-проект

{% content-ref url="/spaces/uyjsNtEAtO6Sby8CHWyD/pages/MDuqJRjp8L1cHG9i4sRB" %}
[Файлы сайта в FastPanel](/help-center/upravlenie-serverom/panel-fastpanel/faily-saita-v-fastpanel.md)
{% endcontent-ref %}

Пример:

```bash
cd /var/www/example_com_usr/data/www/app.example.com
```

Проверьте текущую директорию и обязательные файлы:

```bash
pwd
test -f artisan && echo "artisan найден"
test -f .env && echo ".env найден"
```

{% endstep %}

{% step %}

### Проверьте файлы мигратора

```bash
ls -la tools/db-migrator/
```

Должны присутствовать:

```
iex-db-migrator
migration.example.json
```

Проверьте версию:

```bash
./tools/db-migrator/iex-db-migrator plan --version
```

Устанавливать Go на клиентский сервер не требуется. Launcher выбирает бинарный файл, соответствующий архитектуре сервера.
{% endstep %}

{% step %}

### Создайте рабочую конфигурацию

Если файл ещё не существует:

```bash
cp tools/db-migrator/migration.example.json \
  tools/db-migrator/db-migrator.json
```

Установите защищённые права:

```bash
chmod 600 tools/db-migrator/db-migrator.json
```

Откройте файл:

```bash
nano tools/db-migrator/db-migrator.json
```

{% hint style="danger" %}
Если `db-migrator.json` уже существует, не перезаписывайте его командой `cp`.

Сначала сохраните защищённую копию и проверьте текущие настройки.
{% endhint %}
{% endstep %}
{% endstepper %}

## Пример `db-migrator.json`

```json
{
  "version": 1,
  "policy": {
    "exact": true,
    "maintenance": false,
    "switch_env": true,
    "workers": 3,
    "schema_mode": "empty",
    "allow_non_empty": false,
    "allow_target_reset": false
  },
  "profiles": {
    "main": {
      "enabled": true,
      "source": {
        "driver": "mysql",
        "host": "127.0.0.1",
        "port": 3306,
        "database": "old_app_mysql",
        "username": "old_app_mysql",
        "password": "ПАРОЛЬ_MYSQL",
        "socket": "",
        "tls": "false",
        "table_prefix": ""
      },
      "target": {
        "driver": "pgsql",
        "host": "127.0.0.1",
        "port": 5432,
        "database": "app_example_com_pg",
        "username": "app_example_com_pg",
        "password": "ПАРОЛЬ_POSTGRESQL",
        "sslmode": "disable",
        "table_prefix": ""
      },
      "include": [],
      "exclude": []
    },
    "pulse": {
      "enabled": true,
      "optional": true,
      "source": {
        "driver": "mysql",
        "host": "127.0.0.1",
        "port": 3306,
        "database": "old_pulse_mysql",
        "username": "old_pulse_mysql",
        "password": "ПАРОЛЬ_MYSQL_PULSE",
        "socket": "",
        "tls": "false",
        "table_prefix": ""
      },
      "target": {
        "driver": "pgsql",
        "host": "127.0.0.1",
        "port": 5432,
        "database": "pulse_pg",
        "username": "pulse_pg",
        "password": "ПАРОЛЬ_POSTGRESQL_PULSE",
        "sslmode": "disable",
        "table_prefix": ""
      },
      "include": [],
      "exclude": []
    }
  }
}
```

После сохранения повторно установите права:

```bash
chmod 600 tools/db-migrator/db-migrator.json
```

### Откуда брать реквизиты

<table><thead><tr><th width="285.5234375">Поле</th><th>Где взять значение</th></tr></thead><tbody><tr><td><code>main.source.database</code></td><td>Текущее <code>DB_DATABASE</code> из <code>.env</code></td></tr><tr><td><code>main.source.username</code></td><td>Текущее <code>DB_USERNAME</code></td></tr><tr><td><code>main.source.password</code></td><td>Текущее <code>DB_PASSWORD</code></td></tr><tr><td><code>main.target.database</code></td><td>Основная PostgreSQL-база из FASTPANEL</td></tr><tr><td><code>main.target.username</code></td><td>Пользователь основной PostgreSQL-базы</td></tr><tr><td><code>main.target.password</code></td><td>Пароль пользователя PostgreSQL</td></tr><tr><td><code>pulse.source.database</code></td><td>Текущее <code>PULSE_DB_DATABASE</code></td></tr><tr><td><code>pulse.source.username</code></td><td>Текущее <code>PULSE_DB_USERNAME</code></td></tr><tr><td><code>pulse.source.password</code></td><td>Текущее <code>PULSE_DB_PASSWORD</code></td></tr><tr><td><code>pulse.target.database</code></td><td><code>pulse_pg</code></td></tr><tr><td><code>pulse.target.username</code></td><td><code>pulse_pg</code></td></tr><tr><td><code>pulse.target.password</code></td><td>Пароль пользователя <code>pulse_pg</code></td></tr></tbody></table>

Посмотреть основные параметры `.env` без вывода паролей:

```bash
grep -E '^(DB_CONNECTION|DB_HOST|DB_PORT|DB_DATABASE|DB_USERNAME|PULSE_DB_CONNECTION|PULSE_DB_HOST|PULSE_DB_PORT|PULSE_DB_DATABASE|PULSE_DB_USERNAME)=' .env
```

{% hint style="warning" %}
Не меняйте рабочий `.env` вручную.

При `switch_env=true` мигратор самостоятельно переключит подключение после успешного переноса и проверки.
{% endhint %}

***

## Если Laravel Pulse не используется

Отключите профиль:

```json
"pulse": {
  "enabled": false
}
```

После этого команда с параметром `--profile all` обработает только основную базу.

Если Pulse настроен не у всех проектов, профиль можно оставить необязательным:

```json
"enabled": true,
"optional": true
```

При отсутствии доступной базы мигратор должен явно вывести:

```
PROFILE pulse SKIPPED
```

Отдельный запуск с параметром `--profile pulse` при недоступной базе завершится ошибкой.

## Политики миграции

Для стандартной клиентской миграции используйте следующие значения:

<table><thead><tr><th width="217.2265625">Параметр</th><th width="108.04296875">Значение</th><th>Назначение</th></tr></thead><tbody><tr><td><code>exact</code></td><td><code>true</code></td><td>Включает точную проверку и MySQL read-lock</td></tr><tr><td><code>maintenance</code></td><td><code>false</code></td><td>Мигратор не управляет Laravel maintenance mode</td></tr><tr><td><code>switch_env</code></td><td><code>true</code></td><td>Переключает <code>.env</code> после успешного переноса</td></tr><tr><td><code>workers</code></td><td><code>3</code></td><td>Количество параллельно обрабатываемых таблиц</td></tr><tr><td><code>schema_mode</code></td><td><code>empty</code></td><td>Заполняет пустые PostgreSQL-базы</td></tr><tr><td><code>allow_non_empty</code></td><td><code>false</code></td><td>Запрещает перенос в непустую базу</td></tr><tr><td><code>allow_target_reset</code></td><td><code>false</code></td><td>Запрещает удаление PostgreSQL target</td></tr></tbody></table>

{% hint style="info" %}
Значение `maintenance=false` не означает, что миграцию можно выполнять без остановки записи.

Оно означает только то, что мигратор не выполняет команды `php artisan down` и `php artisan up`. Остановить пользовательские и фоновые процессы записи нужно отдельно.
{% endhint %}

## Как работает точная блокировка MySQL

Для получения согласованного снимка мигратор использует:

```sql
FLUSH TABLES WITH READ LOCK
```

Пока блокировка активна, MySQL не принимает:

* `INSERT`;
* `UPDATE`;
* `DELETE`;
* изменение структуры таблиц;
* другие DDL-операции.

Чтение данных продолжает работать.

Блокировка удерживается до завершения переноса, проверки PostgreSQL, обработки Pulse и переключения `.env`.

{% stepper %}
{% step %}

### Подключение через `/root/.my.cnf`

При запуске от пользователя `root` мигратор проверяет:

```
/root/.my.cnf
```

Файл должен:

* принадлежать пользователю `root`;
* иметь права `0600` или строже;
* не быть символической ссылкой;
* содержать секцию `[client]` или `[mysql]`.

Мигратор читает из файла только имя пользователя и пароль. Пароль не выводится в консоль и постоянный лог.
{% endstep %}

{% step %}

### Если `/root/.my.cnf` недоступен

В секцию `source` можно добавить отдельное подключение:

```json
"lock": {
  "driver": "mysql",
  "username": "mysql_lock_user",
  "password": "ПАРОЛЬ_MYSQL_LOCK_USER"
}
```

Такой учётной записи требуется глобальное право:

```
FLUSH_TABLES
```

или:

```
RELOAD
```

{% hint style="danger" %}
Не выдавайте глобальные права `FLUSH_TABLES` или `RELOAD` обычному пользователю приложения.

Не отключайте `exact=true`, чтобы обойти ошибку доступа.
{% endhint %}
{% endstep %}
{% endstepper %}

***

## Проверка конфигурации

Выполните:

```bash
./tools/db-migrator/iex-db-migrator \
  config-check \
  --profile all
```

Команда не подключается к базам и не изменяет данные.

Она проверяет:

* формат JSON;
* обязательные поля;
* драйверы;
* настройки профилей;
* политики безопасности;
* неизвестные параметры.

Исправьте все найденные ошибки до продолжения.

## Проверка PostgreSQL-баз

Выполните:

```bash
./tools/db-migrator/iex-db-migrator \
  target-check \
  --profile all
```

Команда не блокирует MySQL и не переносит данные.

Она проверяет:

* PostgreSQL имеет версию `18.x`;
* кодировка базы равна `UTF8`;
* база существует;
* имя пользователя и пароль подходят;
* пользователь имеет право подключения;
* пользователь может создавать объекты;
* база доступна для записи;
* база не содержит пользовательских таблиц;
* база не содержит следов предыдущего переноса.

{% hint style="success" %}
`target-check` должен завершиться успешно для всех обязательных профилей.
{% endhint %}

## Проверка плана

Выполните от пользователя `root`:

```bash
./tools/db-migrator/iex-db-migrator \
  plan \
  --profile all
```

Команда не получает глобальную блокировку и не записывает данные.

Она проверяет:

* соединение с MySQL;
* учётную запись для read-lock;
* наличие требуемых прав;
* версию и кодировку MySQL;
* таблицы и колонки;
* индексы и внешние ключи;
* generated columns;
* значения JSON;
* zero-date;
* числовые диапазоны;
* количество строк;
* объём данных;
* режим схемы;
* число workers;
* параметры будущего `.env`.

Перед переносом должны успешно завершиться:

```bash
./tools/db-migrator/iex-db-migrator config-check --profile all
./tools/db-migrator/iex-db-migrator target-check --profile all
./tools/db-migrator/iex-db-migrator plan --profile all
```

***

## Создание резервной копии MySQL

Резервная копия создаётся до запуска мигратора, пока `.env` указывает на MySQL.

Для миграции и резервного копирования используются разные конфигурации:

```
Миграция:
tools/db-migrator/db-migrator.json

Резервное копирование:
tools/db-backup/db-backup.json
```

Если `db-backup.json` ещё не существует:

```bash
cp tools/db-backup/db-backup.example.json \
  tools/db-backup/db-backup.json

chmod 600 tools/db-backup/db-backup.json
```

Проверьте конфигурацию:

```bash
./tools/db-backup/iex-db-backup \
  config-check \
  --profile all
```

Создайте резервную копию:

```bash
./tools/db-backup/iex-db-backup \
  export \
  --profile all \
  --tag before-mysql-to-postgresql
```

По умолчанию файлы сохраняются в каталоге:

```
storage/app/backups/database/<UTC>/
```

Для каждого SQL-файла создаётся файл:

```
.manifest.json
```

Manifest содержит:

* профиль;
* метку запуска;
* тип базы данных;
* размер файла;
* контрольную сумму SHA-256;
* количество таблиц;
* индексы и ключи;
* количество строк.

После создания резервной копии:

1. Проверьте наличие SQL-файлов и manifest.
2. Скачайте файлы с сервера.
3. Сохраните их во внешнем защищённом хранилище.
4. Не оставляйте единственную копию на сервере с проектом.

***

## Подготовка окна обслуживания

Перед запуском `run`:

* запретите создание новых заявок;
* остановите внешний трафик, который может записывать данные;
* временно остановите очереди;
* остановите процессы записи курсов, логов и метрик;
* не запускайте Product Updates;
* не запускайте Laravel migrations;
* убедитесь, что второй экземпляр мигратора не работает;
* предупредите сотрудников о технических работах.

{% hint style="warning" %}
Процессы, продолжающие запись в MySQL, будут ждать снятия read-lock и могут завершиться по тайм-ауту.

Сайт должен оставаться закрытым для записи до успешного завершения отдельной команды `verify`.
{% endhint %}

## Запуск миграции

Находясь в корне Backend-проекта и работая от пользователя `root`, выполните:

```bash
./tools/db-migrator/iex-db-migrator run \
  --profile all \
  --schema-mode empty \
  --yes
```

Параметр `--schema-mode empty` явно подтверждает, что перенос выполняется в пустые базы, созданные в FASTPANEL.

Во время запуска мигратор:

1. повторно проверяет конфигурацию;
2. получает MySQL read-lock;
3. проверяет значения источника;
4. проверяет пустоту PostgreSQL-баз;
5. создаёт PostgreSQL DDL;
6. проверяет DDL внутри транзакции с `ROLLBACK`;
7. создаёт таблицы;
8. переносит строки;
9. создаёт первичные и уникальные ключи;
10. создаёт индексы;
11. создаёт внешние ключи;
12. создаёт `CHECK`-ограничения;
13. настраивает identity и sequences;
14. создаёт необходимые триггеры;
15. выполняет `ANALYZE`;
16. проверяет структуру;
17. сравнивает количество строк;
18. вычисляет SHA-256 digest;
19. обрабатывает профиль Pulse;
20. переключает `.env`;
21. удаляет Laravel config cache;
22. снимает MySQL read-lock.

{% hint style="danger" %}
Не закрывайте SSH-сессию и не запускайте второй экземпляр команды.

Для длительного переноса используйте терминал с возможностью сохранить серверную сессию.
{% endhint %}

{% stepper %}
{% step %}

### Отображение прогресса

Пример:

```
progress source validation  42.11% | tables 184/437 | table=file_parser_source_pairs | rows=31647 | elapsed=2s
progress copy               76.20% | tables 333/437 | table=reserve_closures | elapsed=14s
progress target verify     100.00% | tables 437/437 | table=orders | elapsed=29s
```

{% endstep %}

{% step %}

### Постоянный лог

Каждый запуск создаёт приватный лог:

```
storage/logs/db-migrator/<UTC>-<command>-<profile>.log
```

Посмотреть последние логи:

```bash
ls -lt storage/logs/db-migrator/
```

Пароли в постоянный лог не записываются.
{% endstep %}
{% endstepper %}

## Как понять, что перенос завершился успешно

Успешный результат должен подтверждать:

* профиль `main` завершён успешно;
* профиль `pulse` завершён или явно пропущен;
* все выбранные таблицы обработаны;
* все строки перенесены;
* структура PostgreSQL совпала с ожидаемой;
* количество строк совпало;
* SHA-256 digest совпал;
* `.env` переключён;
* Laravel config cache удалён;
* MySQL read-lock снят;
* команда завершилась кодом `0`.

Если в результате присутствует:

```
PROFILE main FAILED
```

или:

```
ERROR
```

миграция не считается завершённой.

***

## Автоматическое переключение `.env`

При настройке:

```json
"switch_env": true
```

мигратор записывает для основной базы:

```dotenv
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=app_example_com_pg
DB_USERNAME=app_example_com_pg
DB_PASSWORD="ПАРОЛЬ_POSTGRESQL"
DB_SSLMODE=disable
```

Для Laravel Pulse:

```dotenv
PULSE_DB_CONNECTION=pgsql-pulse
PULSE_DB_HOST=127.0.0.1
PULSE_DB_PORT=5432
PULSE_DB_DATABASE=pulse_pg
PULSE_DB_USERNAME=pulse_pg
PULSE_DB_PASSWORD="ПАРОЛЬ_POSTGRESQL_PULSE"
PULSE_DB_SSLMODE=disable
```

Перед изменением создаётся резервная копия:

```
.env.mysql-to-pg.<UTC>.bak
```

Проверить подключение без вывода паролей:

```bash
grep -E '^(DB_CONNECTION|DB_HOST|DB_PORT|DB_DATABASE|DB_USERNAME|DB_SSLMODE|PULSE_DB_CONNECTION|PULSE_DB_HOST|PULSE_DB_PORT|PULSE_DB_DATABASE|PULSE_DB_USERNAME|PULSE_DB_SSLMODE)=' .env
```

{% hint style="warning" %}
Не включайте сайт сразу после `run`.

Сначала выполните независимую команду `verify`.
{% endhint %}

## Независимая точная проверка

До запуска приложения, Product Updates и Laravel migrations выполните от пользователя `root`:

```bash
./tools/db-migrator/iex-db-migrator \
  verify \
  --profile all
```

Команда:

* повторно получает MySQL read-lock;
* заново читает исходную MySQL;
* заново читает PostgreSQL;
* сравнивает структуру;
* сравнивает ограничения;
* сравнивает sequences;
* сравнивает количество строк;
* заново вычисляет SHA-256 digest.

{% hint style="danger" %}
Между `run` и `verify` приложение не должно записывать данные в PostgreSQL.

Новые сессии, заявки, данные Pulse сделают базы закономерно различающимися.
{% endhint %}

Команда `verify` должна выполняться до Product Updates.

После Product Updates структура PostgreSQL может измениться. Для последующих проверок используется `iEX DB Auditor`.

## Обновление продукта после переноса

После успешного `verify` переключитесь на владельца Backend-сайта.

Пример:

```bash
su - example_com_usr
cd ~/www/app.example.com
```

{% hint style="warning" %}
Laravel-команды запускайте от владельца сайта, а не от `root`.

Иначе в проекте могут появиться файлы с неправильным владельцем.
{% endhint %}

Сначала проверьте план:

```bash
php artisan product-updates:run --dry-run -v
```

Если проверка завершилась успешно, примените обновления:

```bash
php artisan product-updates:run
```

Для строгой проверки можно использовать:

```bash
php artisan product-updates:run --strict
```

{% hint style="danger" %}
Если Product Updates или doctor сообщает об ошибке, не используйте `--force` вслепую.

Не создавайте отсутствующие таблицы или колонки вручную. Сначала изучите ошибку и постоянный лог мигратора.
{% endhint %}

Диагностика:

```bash
php artisan product-updates:doctor \
  --strict \
  --full \
  --support
```

Последние запуски:

```bash
php artisan product-updates:status --limit=5
```

## Проверка подключения и процессов

Проверьте PostgreSQL-подключение:

```bash
php artisan db:show --database=pgsql
```

Проверьте migrations:

```bash
php artisan migrate:status
```

Проверьте Product Updates:

```bash
php artisan product-updates:status --limit=5
```

При необходимости очистите runtime cache:

```bash
php artisan optimize:clear
```

Перезапустите очереди:

```bash
php artisan queue:restart
```

Если проект использует Horizon, Reverb, PM2 или systemd-службы, перезапустите их установленным для сервера способом.

Долгоживущие процессы необходимо перезапустить, потому что они могли сохранить старое MySQL-подключение в памяти.

***

## Функциональная проверка

Перед возвратом клиентского трафика проверьте:

* открытие административной панели;
* авторизацию администратора;
* список пользователей;
* валюты;
* резервы;
* направления обмена;
* платёжные системы;
* мерчанты;
* существующие заявки;
* создание тестовой заявки;
* изменение статуса тестовой заявки;
* поиск и сортировку;
* задания очередей;
* CRON;
* Laravel Pulse;
* Reverb;
* отправку уведомлений;
* логи приложения.

Отдельно проверьте функции, поведение которых может зависеть от конкретной базы данных:

* сортировку строк;
* полнотекстовый поиск;
* фильтры;
* JSON-поля;
* даты и время;
* generated values;
* автоматические временные метки.

Сортировка строк и полнотекстовый поиск в MySQL и PostgreSQL могут работать по-разному даже при точном переносе значений.

## Аудит PostgreSQL

После Product Updates строгий `verify` с MySQL больше не используется, потому что структура PostgreSQL могла измениться.

Выполните проверку конфигурации аудитора:

```bash
./tools/db-auditor/iex-db-auditor \
  config-check \
  --profile all
```

Запустите аудит:

```bash
./tools/db-auditor/iex-db-auditor \
  audit \
  --profile all \
  --strict
```

Аудитор проверяет:

* таблицы;
* первичные ключи;
* внешние ключи;
* индексы;
* sequences;
* triggers;
* статистику;
* блокировки;
* длительные транзакции;
* невалидные ограничения;
* неготовые индексы.

Аудитор работает в режиме чтения и не изменяет базу.

***

## Резервная копия PostgreSQL

После Product Updates и функциональной проверки создайте резервную копию PostgreSQL:

```bash
./tools/db-backup/iex-db-backup \
  export \
  --profile all \
  --tag after-mysql-to-postgresql
```

После переключения `.env` профили `main` и `pulse` будут использовать PostgreSQL.

Скачайте SQL-файлы и manifest с сервера и сохраните их во внешнем защищённом хранилище.

## Возврат клиентского трафика

Возвращайте трафик только после того, как подтверждено:

* `run` завершился успешно;
* отдельный `verify` завершился успешно;
* `.env` указывает на PostgreSQL;
* Product Updates завершился успешно;
* PostgreSQL-аудит не обнаружил критических ошибок;
* очереди и долгоживущие процессы перезапущены;
* основные функции приложения проверены;
* резервная копия PostgreSQL создана.

После этого:

1. Разрешите создание заявок.
2. Запустите очереди и остальные процессы.
3. Создайте контрольную заявку.
4. Проверьте её обработку.
5. Наблюдайте за логами приложения.

## Возврат подключения на MySQL

Перед переключением мигратор создаёт файл:

```
.env.mysql-to-pg.<UTC>.bak
```

Посмотрите доступные копии:

```bash
ls -la .env.mysql-to-pg.*.bak
```

Для восстановления используйте точные пути:

```bash
./tools/db-migrator/iex-db-migrator rollback-env \
  --env /var/www/example_com_usr/data/www/app.example.com/.env \
  --backup /var/www/example_com_usr/data/www/app.example.com/.env.mysql-to-pg.20260719T120000Z.bak
```

Команда:

* атомарно восстанавливает `.env`;
* удаляет Laravel config cache;
* не удаляет PostgreSQL;
* не изменяет MySQL;
* не запускает PHP или Artisan.

После восстановления перезапустите очереди и другие долгоживущие процессы.

{% hint style="danger" %}
Безопасный возврат на MySQL возможен только до появления новых рабочих записей в PostgreSQL.

Если после переключения пользователи создавали заявки или изменяли данные, старая MySQL больше не содержит актуальное состояние проекта. Простое восстановление `.env` может привести к потере новых изменений.
{% endhint %}

***

## Дополнительные режимы для технических специалистов

Стандартная клиентская миграция выполняется только в режиме:

```
schema_mode=empty
```

Другие режимы предназначены для нестандартной инфраструктуры и требуют понимания PostgreSQL, прав ролей и структуры проекта.

<table><thead><tr><th width="149.46484375">Режим</th><th>Назначение</th></tr></thead><tbody><tr><td><code>empty</code></td><td>Заполнить пустую базу, заранее созданную в FASTPANEL</td></tr><tr><td><code>auto</code></td><td>Автоматически создать роль, промежуточную базу и финальную базу</td></tr><tr><td><code>existing</code></td><td>Использовать полностью подготовленную заранее структуру PostgreSQL</td></tr></tbody></table>

{% stepper %}
{% step %}

### Режим `auto`

Режим может использоваться, когда мигратор должен самостоятельно создать PostgreSQL-роль и базы.

```json
"schema_mode": "auto"
```

Он требует административных прав PostgreSQL и не является стандартным вариантом для баз, созданных через FASTPANEL.

Не используйте этот режим только ради автоматизации обычного клиентского переноса.
{% endstep %}

{% step %}

### Режим `existing`

Режим предназначен для базы, в которой необходимая PostgreSQL-структура уже полностью создана:

```json
"schema_mode": "existing"
```

Мигратор проверяет существующую структуру перед переносом данных.

{% hint style="danger" %}
Режим `existing` нельзя использовать для пустой базы.

Если PostgreSQL-база создана в FASTPANEL и не содержит таблиц, используйте `schema_mode=empty`.
{% endhint %}
{% endstep %}

{% step %}

### Временное изменение режима

Режим можно передать через команду:

```bash
--schema-mode empty
```

```bash
--schema-mode auto
```

```bash
--schema-mode existing
```

Параметр командной строки должен соответствовать реальному состоянию целевой базы.
{% endstep %}
{% endstepper %}

***

## Тестовая репетиция

Для репетиции создайте отдельные пустые PostgreSQL-базы.

Укажите тестовые имена в `target`:

```json
"database": "app_example_com_migration_test"
```

Запустите перенос без переключения `.env`:

```bash
./tools/db-migrator/iex-db-migrator run \
  --profile all \
  --schema-mode empty \
  --yes \
  --no-switch
```

Параметр `--no-switch`:

* переносит структуру;
* переносит данные;
* выполняет проверку;
* не изменяет `.env`;
* не переключает приложение.

{% hint style="warning" %}
Не используйте финальные рабочие PostgreSQL-базы для тестового запуска.

Для репетиции создаются отдельные пустые базы и отдельные пользователи.
{% endhint %}

## Сброс целевой PostgreSQL-базы

Команда `reset-target` не используется при обычной клиентской миграции.

Для её разрешения необходимо временно установить:

```json
"allow_target_reset": true
```

После этого команда:

```bash
./tools/db-migrator/iex-db-migrator reset-target \
  --profile main \
  --yes
```

удалит выбранную PostgreSQL-базу целиком.

Команда не удаляет исходную MySQL и не удаляет PostgreSQL-роль.

{% hint style="danger" %}
Перед выполнением несколько раз проверьте `target.database`.

Для базы, созданной через FASTPANEL, после `reset-target` потребуется заново создать базу в панели.
{% endhint %}

После выполнения верните безопасное значение:

```json
"allow_target_reset": false
```

## Команды мигратора

<table><thead><tr><th width="192.94921875">Команда</th><th width="217.734375">Изменяет данные</th><th>Назначение</th></tr></thead><tbody><tr><td><code>config-check</code></td><td>Нет</td><td>Проверить приватную JSON-конфигурацию</td></tr><tr><td><code>target-check</code></td><td>Нет</td><td>Проверить PostgreSQL, кодировку, права и пустоту</td></tr><tr><td><code>plan</code></td><td>Нет</td><td>Проверить источник и построить план</td></tr><tr><td><code>schema</code></td><td>Только указанный файл</td><td>Сформировать PostgreSQL DDL</td></tr><tr><td><code>run</code></td><td>Да</td><td>Выполнить перенос и при необходимости переключить <code>.env</code></td></tr><tr><td><code>verify</code></td><td>Нет</td><td>Повторно сравнить MySQL и PostgreSQL</td></tr><tr><td><code>reset-target</code></td><td>Да</td><td>Удалить выбранную PostgreSQL-базу</td></tr><tr><td><code>rollback-env</code></td><td>Да</td><td>Восстановить <code>.env</code> из резервной копии</td></tr></tbody></table>

## Параметры мигратора

<table><thead><tr><th width="232.80859375">Параметр</th><th>Назначение</th></tr></thead><tbody><tr><td><code>--profile main</code></td><td>Обработать только основную базу</td></tr><tr><td><code>--profile pulse</code></td><td>Обработать только Laravel Pulse</td></tr><tr><td><code>--profile all</code></td><td>Обработать основную базу и Pulse</td></tr><tr><td><code>--config &#x3C;path></code></td><td>Указать путь к JSON-конфигурации</td></tr><tr><td><code>--env &#x3C;path></code></td><td>Указать путь к Laravel <code>.env</code></td></tr><tr><td><code>--workers &#x3C;n></code></td><td>Временно изменить количество workers</td></tr><tr><td><code>--no-switch</code></td><td>Не переключать <code>.env</code></td></tr><tr><td><code>--schema-mode empty</code></td><td>Использовать пустую PostgreSQL-базу</td></tr><tr><td><code>--schema-mode auto</code></td><td>Автоматически создать базу</td></tr><tr><td><code>--schema-mode existing</code></td><td>Использовать заранее подготовленную схему</td></tr><tr><td><code>--schema-out &#x3C;path></code></td><td>Указать путь для команды <code>schema</code></td></tr><tr><td><code>--backup &#x3C;path></code></td><td>Указать резервную копию <code>.env</code> для возврата</td></tr><tr><td><code>--yes</code></td><td>Подтвердить команду, изменяющую данные</td></tr><tr><td><code>--version</code></td><td>Показать версию</td></tr></tbody></table>

## Частые ошибки

Проверьте пользователя, от которого запущен мигратор:

```bash
whoami
```

Ожидаемый результат:

```
root
```

Проверьте файл:

```bash
ls -la /root/.my.cnf
```

Если файл отсутствует, настройте отдельное подключение `source.lock`.

Не выдавайте глобальные права обычному пользователю приложения и не отключайте `exact`.

Проверьте в секции `target`:

* имя базы;
* имя пользователя;
* пароль;
* порт;
* `sslmode`.

Используйте пользователя, созданного вместе с PostgreSQL-базой в FASTPANEL.

Не используйте `fastuser` как пользователя приложения.

Проверить подключение можно командой:

```bash
psql \
  --host=127.0.0.1 \
  --port=5432 \
  --username=app_example_com_pg \
  --dbname=app_example_com_pg
```

PostgreSQL-база уже использовалась мигратором и не считается чистой.

Для рабочего переноса создайте новую пустую базу через FASTPANEL. Не удаляйте служебные таблицы вручную, если не уверены в происхождении объектов.

Для пустой базы выбран режим:

```
existing
```

Используйте:

```json
"schema_mode": "empty"
```

И запустите:

```bash
--schema-mode empty
```

До переноса в ней были созданы таблицы.

Не выполняйте для пустой целевой базы:

```bash
php artisan migrate
php artisan product-updates:run
```

Не загружайте SQL-схему вручную. Создайте новую пустую PostgreSQL-базу через FASTPANEL.

Удаление базы не всегда удаляет PostgreSQL-роль.

Используйте новый уникальный логин или удалите старого пользователя через FASTPANEL, только если он больше нигде не используется.

Не выполняйте `DROP ROLE` без проверки зависимостей.

Отключите профиль:

```json
"pulse": {
  "enabled": false
}
```

Не оставляйте включённый профиль с вымышленными реквизитами.

Мигратор принимает PostgreSQL `18.x`.

Проверьте установленную версию и выполните инструкцию подключения PostgreSQL 18.

Точная команда `verify` должна выполняться до Product Updates.

Если Product Updates уже изменил структуру PostgreSQL, используйте аудитор:

```bash
./tools/db-auditor/iex-db-auditor audit \
  --profile all \
  --strict
```

Не создавайте объект вручную.

Проверьте:

1. завершился ли `run` без ошибок;
2. завершился ли `verify`;
3. присутствовал ли объект в исходной MySQL;
4. не запускалось ли приложение между `run` и `verify`;
5. какую ошибку показывает doctor.

Диагностика:

```bash
php artisan product-updates:doctor \
  --strict \
  --full \
  --support
```

Посмотрите последний постоянный лог:

```bash
ls -lt storage/logs/db-migrator/
```

Не удаляйте PostgreSQL-базу без проверки состояния переноса.

Если база была опубликована, сначала изучите лог и выполните `verify`.

Выполните:

```bash
chmod 600 tools/db-migrator/db-migrator.json
```

Файл:

* не должен быть символической ссылкой;
* не должен быть доступен группе;
* не должен быть доступен другим пользователям;
* не должен попадать в Git.

Проверьте права:

```bash
chmod 0755 tools/db-migrator/iex-db-migrator
chmod 0755 tools/db-migrator/iex-db-migrator-linux-amd64
chmod 0755 tools/db-migrator/iex-db-migrator-linux-arm64
```

Проверьте архитектуру:

```bash
uname -m
```

Launcher должен выбрать соответствующий бинарный файл.

***

## Короткая последовательность команд

Работа выполняется от пользователя `root` из корня Backend:

```bash
chmod 600 tools/db-migrator/db-migrator.json

./tools/db-migrator/iex-db-migrator \
  config-check \
  --profile all

./tools/db-migrator/iex-db-migrator \
  target-check \
  --profile all

./tools/db-migrator/iex-db-migrator \
  plan \
  --profile all

./tools/db-backup/iex-db-backup \
  export \
  --profile all \
  --tag before-mysql-to-postgresql

./tools/db-migrator/iex-db-migrator \
  run \
  --profile all \
  --schema-mode empty \
  --yes

./tools/db-migrator/iex-db-migrator \
  verify \
  --profile all
```

После успешного `verify` переключитесь на владельца сайта:

```bash
su - example_com_usr
cd ~/www/app.example.com
```

Выполните:

```bash
php artisan product-updates:run --dry-run -v
php artisan product-updates:run
php artisan db:show --database=pgsql
php artisan product-updates:status --limit=5
php artisan queue:restart
```

***

## Контрольный список

{% stepper %}
{% step %}

### До переноса

* [ ] PostgreSQL 18 установлен.
* [ ] PostgreSQL доступен только локально.
* [ ] В PHP 8.4 включены `pdo_pgsql` и `pgsql`.
* [ ] PostgreSQL добавлен в FASTPANEL.
* [ ] PostgreSQL имеет статус «Доступен».
* [ ] Создана пустая основная PostgreSQL-база.
* [ ] Создан отдельный пользователь основной базы.
* [ ] При необходимости создана пустая база `pulse_pg`.
* [ ] Пользователь Pulse называется `pulse_pg`.
* [ ] PostgreSQL-базы используют кодировку `UTF8`.
* [ ] `db-migrator.json` заполнен.
* [ ] Для файла установлены права `0600`.
* [ ] Указано `exact=true`.
* [ ] Указано `schema_mode=empty`.
* [ ] Указано `allow_non_empty=false`.
* [ ] Указано `allow_target_reset=false`.
* [ ] `config-check` завершился успешно.
* [ ] `target-check` завершился успешно.
* [ ] `plan` завершился успешно.
* [ ] Резервная копия MySQL создана.
* [ ] Резервная копия скачана с сервера.
* [ ] Подготовлено окно обслуживания.
  {% endstep %}

{% step %}

### После переноса

* [ ] `run` завершился кодом `0`.
* [ ] Профиль `main` перенесён.
* [ ] Pulse перенесён или явно пропущен.
* [ ] Количество строк совпало.
* [ ] SHA-256 digest совпал.
* [ ] `.env` переключён на PostgreSQL.
* [ ] Создан `.env.mysql-to-pg.<UTC>.bak`.
* [ ] Отдельный `verify` завершился успешно.
* [ ] Product Updates dry-run завершился успешно.
* [ ] Product Updates применён.
* [ ] PostgreSQL-аудит выполнен.
* [ ] Очереди и процессы перезапущены.
* [ ] Административная панель проверена.
* [ ] Тестовая заявка создана.
* [ ] Логи проверены.
* [ ] Резервная копия PostgreSQL создана.
* [ ] Клиентский трафик включён.
  {% endstep %}
  {% endstepper %}

***

## Коротко

Для обычной миграции в заранее созданные базы FASTPANEL используется:

```
schema_mode=empty
```

Перед переносом обязательно выполняются:

```bash
./tools/db-migrator/iex-db-migrator config-check --profile all
./tools/db-migrator/iex-db-migrator target-check --profile all
./tools/db-migrator/iex-db-migrator plan --profile all
```

Основная команда:

```bash
./tools/db-migrator/iex-db-migrator run \
  --profile all \
  --schema-mode empty \
  --yes
```

После неё до запуска приложения выполняется:

```bash
./tools/db-migrator/iex-db-migrator verify --profile all
```

Режимы `auto` и `existing` используются только в нестандартных технических сценариях. Для обычного клиента и пустых баз FASTPANEL они не требуются.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.iexexchanger.com/server-i-dannye/rabota-s-postgresql/migraciya-s-mysql-na-postgresql.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
