Хост Postgres
Хост Postgres
Хост Postgres построен на базе фреймворка @morphcluster/core и предоставляет доступ к базе данных PostgreSQL в стиле, совместимом с Oracle (через адаптер). Он состоит из нескольких внутренних сервисов, каждый из которых выполняет свою роль: загрузка метаданных, выполнение запросов, управление пулом соединений, поддержка длинных транзакций, уведомления и тестовые инструменты.
Все сервисы наследуют ServiceRequire и регистрируются в ServiceHost с именем "Postgres". Конфигурация по умолчанию определена в src/config.mjs и может быть переопределена через CommonRegistry.
Сервис PgFunctions
Назначение: Загружает и хранит метаданные о функциях и процедурах PostgreSQL из системного каталога (pg_proc). Предоставляет методы для поиска функций по имени и пространству имён.
Наследует: ServiceRequire
Зависимости (requirements): отсутствуют (использует только config.common)
Конфигурация:
-
common.postgresUri– строка подключения к PostgreSQL -
common.timeZone– часовой пояс (например,'Europe/Moscow')
Методы (запросы):
| Метод | Параметры | Возвращает | Описание |
|---|---|---|---|
reload |
{} |
void |
Перезагружает список функций и типов из БД |
find |
{namespace, name} |
FuncRec | undefined |
Ищет функцию по полному имени (без учёта регистра) |
findByName |
{name} |
FuncRec | undefined |
Ищет функцию по имени (без учёта регистра, только по имени) |
Структура FuncRec:
{
oid: number; // OID функции
name: string; // Имя функции
kind: string; // 'f' - функция, 'p' - процедура
nsp: string; // Пространство имён (схема)
result: string|null; // Тип возвращаемого значения
args: Array<{
name: string;
isOut: boolean;
hasDefault: boolean;
type: string | null;
}>;
}
Жизненный цикл:
- При старте автоматически вызывает
reload(). - При вызове
reload()подключается к БД, запрашиваетpg_typeи список функций, разбирает аргументы с помощьюArgumentParser.
Особенности:
- Игнорирует схемы
pg_catalogиinformation_schema. - Для разбора аргументов используется встроенный парсер
ArgumentParser, который обрабатывает стандартный выводpg_get_function_arguments.
Сервис OraFunctions
Назначение: Предоставляет информацию о функциях PostgreSQL в нотации Oracle (адаптирует типы, имена, параметры). Используется клиентами, ожидающими Oracle-совместимый интерфейс.
Зависимости: ["PgFunctions"]
Конфигурация:
disableAutoload – если true, не вызывает reload() при старте (по умолчанию false).
Методы (запросы):
| Метод | Параметры | Возвращает | Описание |
|---|---|---|---|
reload |
{} |
void |
Делегирует перезагрузку в PgFunctions.reload |
list |
{} |
string[] |
Список имён схем (пакетов) в верхнем регистре |
getPackage |
{packageName} |
{result: {id:-1, name, funcs[]}} |
Возвращает список функций пакета с именами в верхнем регистре |
getFunc |
{packageName, funcName} |
{result: {name, overload, descr, params[]}} |
Возвращает метаданные одной функции (Oracle-стиль) |
Структура параметра функции в ответе getFunc:
{
pos: number; // Позиция параметра
name: string|null; // Имя
type: string; // Oracle-тип (VARCHAR2, NUMBER, CURSOR и т.д.)
out: boolean; // Является ли OUT-параметром
IN_OUT: "IN"|"OUT";// Строковое представление направления
required: boolean; // Обязательность (нет значения по умолчанию)
}
Особенности:
- Преобразует PG-типы в Oracle-типы с помощью
oraUtils.convertType. - Игнорирует функции из служебных схем.
- Все имена схем и функций приводятся к верхнему регистру.
Сервис PgPool
Назначение: Управляет пулом простых (не транзакционных) соединений с PostgreSQL. Выполняет SQL-запросы и вызовы функций/процедур. Поддерживает очередь запросов с автоматическим переподключением и записью ошибок в лог.
Зависимости: ["PgFunctions", "RegistryHelper"]
Конфигурация:
-
common.postgresUri– строка подключения -
common.timeZone– часовой пояс -
pgQueryPool.connectionCount– количество соединений в пуле (изCommonRegistry, схемаregistrySchema)
Методы (запросы):
| Метод | Параметры | Возвращает | Описание |
|---|---|---|---|
reconnect |
{} |
void |
Пересоздаёт пул соединений (закрывает старые, открывает новые согласно connectionCount) |
execSql |
{sql, values, session?, noLog?} |
Promise<Array<{paramName, type, value}>> |
Выполняет сырой SQL-запрос |
execFunc |
{namespace, funcName, params, count?, offset?, session?, noLog?} |
Promise<Array<{paramName, type, value}>> |
Выполняет функцию/процедуру по метаданным из PgFunctions |
Дополнительно:
-
getState()– не запрос, а метод для получения состояния пула (возвращает массив объектов сidиstatusкаждого соединения).
Жизненный цикл:
- При
start()создаёт подписку на изменения реестра черезRegistryHelper.onDataChanged, при изменении пересоздаёт пул. - При
stop()отключает все соединения и очищает таймер переподключения.
Внутреннее устройство:
- Пул состоит из объектов
PgConnection. - Запросы помещаются в очередь
queueи обрабатываются последовательно с помощьюprocessQueue(). - При ошибке соединение переподключается автоматически (до 3 попыток).
- Для установки контекста пользователя вызывается
carabimeta.set_user_id(userId)перед каждым запросом. - Ошибки запросов логируются в БД через функцию
logs.write_error_log.
Формат возвращаемых данных:
-
PgConnectionвозвращает результаты в виде массива{paramName, type, value}. - Типы данных преобразуются через
PgFormats.formatValue().
Сервис PgTransactions
Назначение: Предоставляет механизм длинных транзакций (long transactions) с состоянием, возможностью выполнения нескольких запросов в одной транзакции, коммитом/откатом и фетчем курсоров.
Зависимости: ["RegistryHelper", "DbServices", "PgFunctions"]
Конфигурация (из CommonRegistry):
-
PgTransactions.cleanTimeout– время в мс, через которое закрытые транзакции удаляются из пула (по умолчанию 600000) -
PgTransactions.idleTimeout– максимальное время простоя транзакции в мс (0 – без ограничения, по умолчанию 60000)
Методы (запросы):
| Метод | Параметры | Возвращает | Описание |
|---|---|---|---|
create |
{noUser?, session?} |
{trxId: number} |
Создаёт новую транзакцию, возвращает её ID |
execSql |
{trxId?, sql, values, session?} |
{trxId} |
Выполняет SQL в новой (если trxId не указан) или существующей транзакции |
execFunc |
{trxId?, namespace, funcName, params, session?} |
{trxId} |
Выполняет функцию из PgFunctions в транзакции |
getExecResult |
{trxId} |
Array<Result> |
Ожидает завершения выполнения и возвращает результаты последнего запроса в транзакции |
fetch |
{trxId, count} |
{rows} |
Извлекает строки из курсора |
commit |
{trxId, keep?} |
void |
Фиксирует транзакцию; если keep=true, сразу начинает новую транзакцию |
rollback |
{trxId} |
void |
Откатывает транзакцию |
poolStatus |
{} |
{lastOltId, connections[]} |
Возвращает состояние пула транзакций |
Внутреннее устройство:
- Использует пул
PGTPool, который управляет наборомPGTransaction. - Каждая транзакция (
PGTransaction) проходит через состояния: created → connecting → ready → executing → executed → (fetching) → closing → closed. - Транзакция начинается с
BEGIN, выполняет запросы, и завершаетсяCOMMITилиROLLBACK. - Поддерживается работа с курсорами (
refcursor): после выполнения функции, возвращающей курсор, можно вызыватьfetch. - При возникновении ошибки в транзакции, она сохраняется в
error, и может быть получена черезgetExecResult. - Ошибки логируются в
logs.write_error_logаналогично PgPool. - Устаревшие (закрытые) транзакции периодически удаляются из пула.
Формат результата getExecResult:
[
{
paramName: string;
type: string; // PG-тип
value: any; // для скалярных
columns?: [string, string][]; // для курсоров
}
]
Важно: Все методы, изменяющие состояние транзакции, являются асинхронными и не блокируют вызывающий поток (кроме getExecResult и fetch, которые ожидают готовности результата).
Сервис PgTools
Назначение: Утилита для генерации заготовки функции, возвращающей TABLE, на основе существующей функции, возвращающей refcursor.
Зависимости: отсутствуют (только config)
Методы (запросы):
| Метод | Параметры | Возвращает | Описание |
|---|---|---|---|
funcTableFromRefcursor |
{funcFrom, funcTo} |
Array<{name, type, modifier, size, tableID}> |
Анализирует структуру курсора, возвращает метаданные колонок и создаёт новую функцию с RETURNS TABLE |
Параметры:
-
funcFrom– строка видаschema.func_name(args)(например,public.my_func(123)) -
funcTo– полное имя новой функции (по умолчаниюschema.func_name_table)
Пример использования:
{
"funcFrom": "public.my_cursor_func(42)",
"funcTo": "public.my_table_func"
}
Создаст функцию public.my_table_func(...) RETURNS TABLE(...) на основе структуры курсора.
Особенности:
- Выполняет
FETCH 0для получения структуры курсора без перемещения. - Автоматически преобразует типы через
pg_type. - Требует прав администратора (
needAdmin: true).
Сервис PgNotify
Назначение: Обеспечивает механизм подписки на асинхронные уведомления PostgreSQL (LISTEN/NOTIFY). Уведомления доставляются через событие onNotify.
Зависимости: []
Конфигурация:
common.postgresUri – строка подключения.
Методы (запросы):
| Метод | Параметры | Возвращает | Описание |
|---|---|---|---|
isConnected |
{} |
{result: boolean} |
Проверяет, установлено ли соединение с БД для нотификаций |
getSubscriptions |
{} |
{result: string[]} |
Возвращает список активных подписок (имен каналов) |
subscribe |
{channelName} |
void |
Подписывается на канал уведомлений |
unsubscribe |
{channelName} |
void |
Отписывается от канала |
События:
| Событие | Параметры | Описание |
|---|---|---|
onNotify |
{channel, payload} |
Вызывается при получении уведомления. payload уже распарсен из JSON. |
Жизненный цикл:
- При старте подключается к PostgreSQL через
PgNotifyConnectionи восстанавливает все активные подписки. - При остановке корректно отключается.
- Встроенный таймер (
ReconnectTimer) каждые 5 секунд проверяет соединение и переподключается при обрыве.
Особенности:
- Идентификаторы каналов экранируются для безопасного использования в SQL.
- Уведомления автоматически парсятся из JSON (если возможно), иначе передаётся строка.
Сервис OraQueries
Назначение: Выполняет запросы к PostgreSQL в стиле Oracle. Преобразует именованные параметры в позиционные, конвертирует типы результатов, валидирует вызовы через DbServices.
Зависимости: ["PgPool", "DbServices"]
Методы (запросы):
| Метод | Параметры | Возвращает | Описание |
|---|---|---|---|
exec |
{queryName, params?, count?, offset?} |
{results: [...]} |
Основной метод: парсит queryName в формате package.funcName и вызывает execFunc |
execFunc |
{package, func, params?, count?, offset?, session?} |
{results: [...]} |
Выполняет функцию, предварительно получив SQL-текст через DbServices.getOraSql |
execFuncInner |
{package, func, params?, count?, offset?, session?} |
{results: [...]} |
Выполняет функцию напрямую через PgPool, без проверки через DbServices (требует needAdmin: true) |
execSql |
{sql, params?, session?} |
{results: [...]} |
Выполняет произвольный SQL (административный доступ) |
resetUser |
{} |
(не реализован) | Планируется сброс сессии текущего пользователя |
resetAllUsers |
{} |
(не реализован) | Планируется сброс всех сессий |
Возвращаемое значение:
{
results: Array<{
paramName: string; // имя выходного параметра (в верхнем регистре)
type: string; // Oracle-тип: "VARCHAR2", "NUMBER", "CURSOR", "CLOB", "DATE"...
value: any; // значение, для курсоров - {columns, list}
}>
}
Особенности:
- Автоматически преобразует именованные параметры (
:name) в позиционные ($1) с помощьюoraUtils.convertNameToPos. - Конвертирует PG-типы в Oracle-типы (
convertType). - Для курсоров преобразует структуру через
convertColumns. - Логирует результат с ограничением размера (если задан
limitLogger). - В случае отсутствия функции в
PgFunctions, записывает информацию о недостающем вызове вlogs.write_error_logи выбрасываетComplexError.
Сервис OraLongTransactions
Назначение: Предоставляет Oracle-совместимый интерфейс для длинных транзакций. Является обёрткой над PgTransactions, адаптируя имена методов и параметры.
Зависимости: ["PgTransactions", "DbServices"]
Методы (запросы):
| Метод | Параметры | Возвращает | Описание |
|---|---|---|---|
create |
{noUser?, session?} |
{oltId} |
Создаёт новую транзакцию (аналог PgTransactions.create) |
execFunc |
{oltId?, package, func, params?, session?} |
{oltId} |
Выполняет функцию пакета (перед выполнением проверяет через DbServices) |
execSql |
{oltId?, SQL, rawParams?, session?} |
{oltId} |
Выполняет произвольный SQL с преобразованием именованных параметров |
getExecResult |
{oltId} |
Array<Result> |
Ожидает результат транзакции |
fetch |
{oltId, count} |
{rows} |
Извлекает строки из курсора |
commit |
{oltId, keep?} |
void |
Фиксирует транзакцию |
rollback |
{oltId} |
void |
Откатывает транзакцию |
poolStatus |
{} |
(состояние пула) | Делегирует в PgTransactions.getStatus |
Особенности:
- Параметры
execFuncпринимаютpackageиfuncвместоnamespace/funcName. - При выполнении SQL через
execSql, параметры могут быть переданы в нотации Oracle (:paramName), они автоматически конвертируются в позиционные черезoraUtils.convertNameToPos. - Все выходные параметры и курсоры приводятся к Oracle-типам.
Сервис OraQueriesPool
Назначение: Управление пулом обычных (не транзакционных) сессий OraQueries. Предоставляет административные методы для мониторинга и управления.
Зависимости: ["PgPool", "DbServices"]
Методы (запросы):
| Метод | Параметры | Возвращает | Описание |
|---|---|---|---|
state |
{} |
{result: [{name, pid, sessionsCount, workedSessions[], ...}]} |
Возвращает состояние пула (основано на PgPool.getState()) |
recreate |
{} |
ошибка | Планируется пересоздание пула, в текущей версии не реализован |
brakeQuery |
{oraSid} |
ошибка | Планируется прерывание запроса по идентификатору сессии |
Формат state:
{
"result": [
{
"name": "main",
"pid": 0,
"sessionsCount": 4,
"workedSessions": [{"id": "pool_0", "status": "exec"}, ...],
"queryQueue": [],
"killerSession": null,
"usage": { ... }
}
]
}
Особенности:
- Фактически является административной панелью для
PgPool. - Методы
recreateиbrakeQueryпока не реализованы (выбрасывают ошибку).
Общая архитектура и зависимости
graph TD
subgraph "Внешние сервисы"
DbServices[DbServices]
CommonRegistry[CommonRegistry]
RegistryHelper[RegistryHelper]
end
subgraph "Сервисы PostgreSQL"
PgFunctions --> |загрузка метаданных| PostgreSQL
OraFunctions --> PgFunctions
PgPool --> PgFunctions
PgPool --> RegistryHelper
PgTransactions --> PgFunctions
PgTransactions --> RegistryHelper
PgTransactions --> DbServices
PgTools --> PostgreSQL
PgNotify --> PgNotifyConnection --> PostgreSQL
OraQueries --> PgPool
OraQueries --> DbServices
OraLongTransactions --> PgTransactions
OraLongTransactions --> DbServices
OraQueriesPool --> PgPool
OraQueriesPool --> DbServices
end
RegistryHelper --> CommonRegistry
Все соединения с PostgreSQL используют строку подключения из конфигурации common.postgresUri. Настройки пулов и таймаутов управляются через CommonRegistry, изменения вступают в силу после перезагрузки пула (для PgPool — автоматически при изменении схемы).
Конфигурация по умолчанию
Файл config.mjs:
{
"NatsConnection": {
"queue": "pgqueries",
"timeout": 60000
},
"NatsPublisher": {
"interval": 60000
}
}
Дополнительные параметры (из config.common):
-
postgresUri– строка подключения к PostgreSQL. -
timeZone– часовой пояс для сессий (например,'Europe/Moscow').
Примечания по использованию
- Большинство сервисов требуют наличия
DbServicesдля проверки и выполнения хранимого кода.DbServices– это внешний сервис, который должен быть доступен в кластере (предоставляет запросыgetOraSqlи др.). - Для работы с длинными транзакциями используйте
OraLongTransactions, а неPgTransactionsнапрямую, чтобы обеспечить совместимость с Oracle-клиентами. - Административные методы защищены флагом
needAdmin: true, который требует аутентифицированной сессии с правами администратора. - Все ошибки базы данных логируются в
logs.write_error_log(если функция доступна и не отключена флагомnoLog).
Данный хост является ключевым компонентом для миграции с Oracle на PostgreSQL, обеспечивая прозрачную работу приложений, написанных под Oracle.